[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit

Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/app/busybox/src/util-linux/Config.src b/ap/app/busybox/src/util-linux/Config.src
new file mode 100644
index 0000000..6c1b928
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/Config.src
@@ -0,0 +1,948 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Linux System Utilities"
+
+INSERT
+
+config ACPID
+	bool "acpid"
+	default y
+	select PLATFORM_LINUX
+	help
+	  acpid listens to ACPI events coming either in textual form from
+	  /proc/acpi/event (though it is marked deprecated it is still widely
+	  used and _is_ a standard) or in binary form from specified evdevs
+	  (just use /dev/input/event*).
+
+	  It parses the event to retrieve ACTION and a possible PARAMETER.
+	  It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
+	  (if the resulting path is a directory) or directly as an executable.
+
+	  N.B. acpid relies on run-parts so have the latter installed.
+
+config FEATURE_ACPID_COMPAT
+	bool "Accept and ignore redundant options"
+	default y
+	depends on ACPID
+	help
+	  Accept and ignore compatibility options -g -m -s -S -v.
+
+config BLKID
+	bool "blkid"
+	default y
+	select PLATFORM_LINUX
+	select VOLUMEID
+	help
+	  Lists labels and UUIDs of all filesystems.
+	  WARNING:
+	  With all submodules selected, it will add ~8k to busybox.
+
+config FEATURE_BLKID_TYPE
+	bool "Print filesystem type"
+	default n
+	depends on BLKID
+	help
+	  Show TYPE="filesystem type"
+
+config DMESG
+	bool "dmesg"
+	default y
+	select PLATFORM_LINUX
+	help
+	  dmesg is used to examine or control the kernel ring buffer. When the
+	  Linux kernel prints messages to the system log, they are stored in
+	  the kernel ring buffer. You can use dmesg to print the kernel's ring
+	  buffer, clear the kernel ring buffer, change the size of the kernel
+	  ring buffer, and change the priority level at which kernel messages
+	  are also logged to the system console. Enable this option if you
+	  wish to enable the 'dmesg' utility.
+
+config FEATURE_DMESG_PRETTY
+	bool "Pretty dmesg output"
+	default y
+	depends on DMESG
+	help
+	  If you wish to scrub the syslog level from the output, say 'Y' here.
+	  The syslog level is a string prefixed to every line with the form
+	  "<#>".
+
+	  With this option you will see:
+	    # dmesg
+	    Linux version 2.6.17.4 .....
+	    BIOS-provided physical RAM map:
+	     BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
+
+	  Without this option you will see:
+	    # dmesg
+	    <5>Linux version 2.6.17.4 .....
+	    <6>BIOS-provided physical RAM map:
+	    <6> BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
+
+config FBSET
+	bool "fbset"
+	default y
+	select PLATFORM_LINUX
+	help
+	  fbset is used to show or change the settings of a Linux frame buffer
+	  device. The frame buffer device provides a simple and unique
+	  interface to access a graphics display. Enable this option
+	  if you wish to enable the 'fbset' utility.
+
+config FEATURE_FBSET_FANCY
+	bool "Turn on extra fbset options"
+	default y
+	depends on FBSET
+	help
+	  This option enables extended fbset options, allowing one to set the
+	  framebuffer size, color depth, etc. interface to access a graphics
+	  display. Enable this option if you wish to enable extended fbset
+	  options.
+
+config FEATURE_FBSET_READMODE
+	bool "Turn on fbset readmode support"
+	default y
+	depends on FBSET
+	help
+	  This option allows fbset to read the video mode database stored by
+	  default as /etc/fb.modes, which can be used to set frame buffer
+	  device to pre-defined video modes.
+
+config FDFLUSH
+	bool "fdflush"
+	default y
+	select PLATFORM_LINUX
+	help
+	  fdflush is only needed when changing media on slightly-broken
+	  removable media drives. It is used to make Linux believe that a
+	  hardware disk-change switch has been actuated, which causes Linux to
+	  forget anything it has cached from the previous media. If you have
+	  such a slightly-broken drive, you will need to run fdflush every time
+	  you change a disk. Most people have working hardware and can safely
+	  leave this disabled.
+
+config FDFORMAT
+	bool "fdformat"
+	default y
+	select PLATFORM_LINUX
+	help
+	  fdformat is used to low-level format a floppy disk.
+
+config FDISK
+	bool "fdisk"
+	default y
+	select PLATFORM_LINUX
+	help
+	  The fdisk utility is used to divide hard disks into one or more
+	  logical disks, which are generally called partitions. This utility
+	  can be used to list and edit the set of partitions or BSD style
+	  'disk slices' that are defined on a hard drive.
+
+config FDISK_SUPPORT_LARGE_DISKS
+	bool "Support over 4GB disks"
+	default y
+	depends on FDISK
+	depends on !LFS   # with LFS no special code is needed
+	help
+	  Enable this option to support large disks > 4GB.
+
+config FEATURE_FDISK_WRITABLE
+	bool "Write support"
+	default y
+	depends on FDISK
+	help
+	  Enabling this option allows you to create or change a partition table
+	  and write those changes out to disk. If you leave this option
+	  disabled, you will only be able to view the partition table.
+
+config FEATURE_AIX_LABEL
+	bool "Support AIX disklabels"
+	default n
+	depends on FDISK && FEATURE_FDISK_WRITABLE
+	help
+	  Enabling this option allows you to create or change AIX disklabels.
+	  Most people can safely leave this option disabled.
+
+config FEATURE_SGI_LABEL
+	bool "Support SGI disklabels"
+	default n
+	depends on FDISK && FEATURE_FDISK_WRITABLE
+	help
+	  Enabling this option allows you to create or change SGI disklabels.
+	  Most people can safely leave this option disabled.
+
+config FEATURE_SUN_LABEL
+	bool "Support SUN disklabels"
+	default n
+	depends on FDISK && FEATURE_FDISK_WRITABLE
+	help
+	  Enabling this option allows you to create or change SUN disklabels.
+	  Most people can safely leave this option disabled.
+
+config FEATURE_OSF_LABEL
+	bool "Support BSD disklabels"
+	default n
+	depends on FDISK && FEATURE_FDISK_WRITABLE
+	help
+	  Enabling this option allows you to create or change BSD disklabels
+	  and define and edit BSD disk slices.
+
+config FEATURE_GPT_LABEL
+	bool "Support GPT disklabels"
+	default n
+	depends on FDISK && FEATURE_FDISK_WRITABLE
+	help
+	  Enabling this option allows you to view GUID Partition Table
+	  disklabels.
+
+config FEATURE_FDISK_ADVANCED
+	bool "Support expert mode"
+	default y
+	depends on FDISK && FEATURE_FDISK_WRITABLE
+	help
+	  Enabling this option allows you to do terribly unsafe things like
+	  define arbitrary drive geometry, move the beginning of data in a
+	  partition, and similarly evil things. Unless you have a very good
+	  reason you would be wise to leave this disabled.
+
+config FINDFS
+	bool "findfs"
+	default y
+	select PLATFORM_LINUX
+	select VOLUMEID
+	help
+	  Prints the name of a filesystem with given label or UUID.
+	  WARNING:
+	  With all submodules selected, it will add ~8k to busybox.
+
+config FLOCK
+	bool "flock"
+	default y
+	help
+	  Manage locks from shell scripts
+
+config FREERAMDISK
+	bool "freeramdisk"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Linux allows you to create ramdisks. This utility allows you to
+	  delete them and completely free all memory that was used for the
+	  ramdisk. For example, if you boot Linux into a ramdisk and later
+	  pivot_root, you may want to free the memory that is allocated to the
+	  ramdisk. If you have no use for freeing memory from a ramdisk, leave
+	  this disabled.
+
+config FSCK_MINIX
+	bool "fsck_minix"
+	default y
+	help
+	  The minix filesystem is a nice, small, compact, read-write filesystem
+	  with little overhead. It is not a journaling filesystem however and
+	  can experience corruption if it is not properly unmounted or if the
+	  power goes off in the middle of a write. This utility allows you to
+	  check for and attempt to repair any corruption that occurs to a minix
+	  filesystem.
+
+config MKFS_EXT2
+	bool "mkfs_ext2"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Utility to create EXT2 filesystems.
+
+config MKFS_MINIX
+	bool "mkfs_minix"
+	default y
+	select PLATFORM_LINUX
+	help
+	  The minix filesystem is a nice, small, compact, read-write filesystem
+	  with little overhead. If you wish to be able to create minix
+	  filesystems this utility will do the job for you.
+
+config FEATURE_MINIX2
+	bool "Support Minix fs v2 (fsck_minix/mkfs_minix)"
+	default y
+	depends on FSCK_MINIX || MKFS_MINIX
+	help
+	  If you wish to be able to create version 2 minix filesystems, enable
+	  this. If you enabled 'mkfs_minix' then you almost certainly want to
+	  be using the version 2 filesystem support.
+
+config MKFS_REISER
+	bool "mkfs_reiser"
+	default n
+	select PLATFORM_LINUX
+	help
+	  Utility to create ReiserFS filesystems.
+	  Note: this applet needs a lot of testing and polishing.
+
+config MKFS_VFAT
+	bool "mkfs_vfat"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Utility to create FAT32 filesystems.
+
+config GETOPT
+	bool "getopt"
+	default y
+	help
+	  The getopt utility is used to break up (parse) options in command
+	  lines to make it easy to write complex shell scripts that also check
+	  for legal (and illegal) options. If you want to write horribly
+	  complex shell scripts, or use some horribly complex shell script
+	  written by others, this utility may be for you. Most people will
+	  wisely leave this disabled.
+
+config FEATURE_GETOPT_LONG
+	bool "Support option -l"
+	default y if LONG_OPTS
+	depends on GETOPT
+	help
+	  Enable support for long options (option -l).
+
+config HEXDUMP
+	bool "hexdump"
+	default y
+	help
+	  The hexdump utility is used to display binary data in a readable
+	  way that is comparable to the output from most hex editors.
+
+config FEATURE_HEXDUMP_REVERSE
+	bool "Support -R, reverse of 'hexdump -Cv'"
+	default y
+	depends on HEXDUMP
+	help
+	  The hexdump utility is used to display binary data in an ascii
+	  readable way. This option creates binary data from an ascii input.
+	  NB: this option is non-standard. It's unwise to use it in scripts
+	  aimed to be portable.
+
+config HD
+	bool "hd"
+	default y
+	depends on HEXDUMP
+	help
+	  hd is an alias to hexdump -C.
+
+config HWCLOCK
+	bool "hwclock"
+	default y
+	select PLATFORM_LINUX
+	help
+	  The hwclock utility is used to read and set the hardware clock
+	  on a system. This is primarily used to set the current time on
+	  shutdown in the hardware clock, so the hardware will keep the
+	  correct time when Linux is _not_ running.
+
+config FEATURE_HWCLOCK_LONG_OPTIONS
+	bool "Support long options (--hctosys,...)"
+	default y
+	depends on HWCLOCK && LONG_OPTS
+	help
+	  By default, the hwclock utility only uses short options. If you
+	  are overly fond of its long options, such as --hctosys, --utc, etc)
+	  then enable this option.
+
+config FEATURE_HWCLOCK_ADJTIME_FHS
+	bool "Use FHS /var/lib/hwclock/adjtime"
+	default n  # util-linux-ng in Fedora 13 still uses /etc/adjtime
+	depends on HWCLOCK
+	help
+	  Starting with FHS 2.3, the adjtime state file is supposed to exist
+	  at /var/lib/hwclock/adjtime instead of /etc/adjtime. If you wish
+	  to use the FHS behavior, answer Y here, otherwise answer N for the
+	  classic /etc/adjtime path.
+
+	  pathname.com/fhs/pub/fhs-2.3.html#VARLIBHWCLOCKSTATEDIRECTORYFORHWCLO
+
+config IPCRM
+	bool "ipcrm"
+	default y
+	help
+	  The ipcrm utility allows the removal of System V interprocess
+	  communication (IPC) objects and the associated data structures
+	  from the system.
+
+config IPCS
+	bool "ipcs"
+	default y
+	select PLATFORM_LINUX
+	help
+	  The ipcs utility is used to provide information on the currently
+	  allocated System V interprocess (IPC) objects in the system.
+
+config LOSETUP
+	bool "losetup"
+	default y
+	select PLATFORM_LINUX
+	help
+	  losetup is used to associate or detach a loop device with a regular
+	  file or block device, and to query the status of a loop device. This
+	  version does not currently support enabling data encryption.
+
+config LSPCI
+	bool "lspci"
+	default y
+	#select PLATFORM_LINUX
+	help
+	  lspci is a utility for displaying information about PCI buses in the
+	  system and devices connected to them.
+
+	  This version uses sysfs (/sys/bus/pci/devices) only.
+
+config LSUSB
+	bool "lsusb"
+	default y
+	#select PLATFORM_LINUX
+	help
+	  lsusb is a utility for displaying information about USB buses in the
+	  system and devices connected to them.
+
+	  This version uses sysfs (/sys/bus/usb/devices) only.
+
+config MKSWAP
+	bool "mkswap"
+	default y
+	help
+	  The mkswap utility is used to configure a file or disk partition as
+	  Linux swap space. This allows Linux to use the entire file or
+	  partition as if it were additional RAM, which can greatly increase
+	  the capability of low-memory machines. This additional memory is
+	  much slower than real RAM, but can be very helpful at preventing your
+	  applications being killed by the Linux out of memory (OOM) killer.
+	  Once you have created swap space using 'mkswap' you need to enable
+	  the swap space using the 'swapon' utility.
+
+config FEATURE_MKSWAP_UUID
+	bool "UUID support"
+	default y
+	depends on MKSWAP
+	help
+	  Generate swap spaces with universally unique identifiers.
+
+config MORE
+	bool "more"
+	default y
+	help
+	  more is a simple utility which allows you to read text one screen
+	  sized page at a time. If you want to read text that is larger than
+	  the screen, and you are using anything faster than a 300 baud modem,
+	  you will probably find this utility very helpful. If you don't have
+	  any need to reading text files, you can leave this disabled.
+
+config MOUNT
+	bool "mount"
+	default y
+	select PLATFORM_LINUX
+	help
+	  All files and filesystems in Unix are arranged into one big directory
+	  tree. The 'mount' utility is used to graft a filesystem onto a
+	  particular part of the tree. A filesystem can either live on a block
+	  device, or it can be accessible over the network, as is the case with
+	  NFS filesystems. Most people using BusyBox will also want to enable
+	  the 'mount' utility.
+
+config FEATURE_MOUNT_FAKE
+	bool "Support option -f"
+	default y
+	depends on MOUNT
+	help
+	  Enable support for faking a file system mount.
+
+config FEATURE_MOUNT_VERBOSE
+	bool "Support option -v"
+	default y
+	depends on MOUNT
+	help
+	  Enable multi-level -v[vv...] verbose messages. Useful if you
+	  debug mount problems and want to see what is exactly passed
+	  to the kernel.
+
+config FEATURE_MOUNT_HELPERS
+	bool "Support mount helpers"
+	default n
+	depends on MOUNT
+	help
+	  Enable mounting of virtual file systems via external helpers.
+	  E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
+	  "obexfs -b00.11.22.33.44.55 /mnt"
+	  Also "mount -t sometype [-o opts] fs /mnt" will try
+	  "sometype [-o opts] fs /mnt" if simple mount syscall fails.
+	  The idea is to use such virtual filesystems in /etc/fstab.
+
+config FEATURE_MOUNT_LABEL
+	bool "Support specifying devices by label or UUID"
+	default y
+	depends on MOUNT
+	select VOLUMEID
+	help
+	  This allows for specifying a device by label or uuid, rather than by
+	  name. This feature utilizes the same functionality as blkid/findfs.
+	  This also enables label or uuid support for swapon.
+
+config FEATURE_MOUNT_NFS
+	bool "Support mounting NFS file systems on Linux < 2.6.23"
+	default n
+	depends on MOUNT
+	select FEATURE_HAVE_RPC
+	select FEATURE_SYSLOG
+	help
+	  Enable mounting of NFS file systems on Linux kernels prior
+	  to version 2.6.23. Note that in this case mounting of NFS
+	  over IPv6 will not be possible.
+
+	  Note that this option links in RPC support from libc,
+	  which is rather large (~10 kbytes on uclibc).
+
+config FEATURE_MOUNT_CIFS
+	bool "Support mounting CIFS/SMB file systems"
+	default y
+	depends on MOUNT
+	help
+	  Enable support for samba mounts.
+
+config FEATURE_MOUNT_FLAGS
+	depends on MOUNT
+	bool "Support lots of -o flags in mount"
+	default y
+	help
+	  Without this, mount only supports ro/rw/remount. With this, it
+	  supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
+	  noatime, diratime, nodiratime, loud, bind, move, shared, slave,
+	  private, unbindable, rshared, rslave, rprivate, and runbindable.
+
+config FEATURE_MOUNT_FSTAB
+	depends on MOUNT
+	bool "Support /etc/fstab and -a"
+	default y
+	help
+	  Support mount all and looking for files in /etc/fstab.
+
+config PIVOT_ROOT
+	bool "pivot_root"
+	default y
+	select PLATFORM_LINUX
+	help
+	  The pivot_root utility swaps the mount points for the root filesystem
+	  with some other mounted filesystem. This allows you to do all sorts
+	  of wild and crazy things with your Linux system and is far more
+	  powerful than 'chroot'.
+
+	  Note: This is for initrd in linux 2.4. Under initramfs (introduced
+	  in linux 2.6) use switch_root instead.
+
+config RDATE
+	bool "rdate"
+	default y
+	help
+	  The rdate utility allows you to synchronize the date and time of your
+	  system clock with the date and time of a remote networked system using
+	  the RFC868 protocol, which is built into the inetd daemon on most
+	  systems.
+
+config RDEV
+	bool "rdev"
+	default y
+	help
+	  Print the device node associated with the filesystem mounted at '/'.
+
+config READPROFILE
+	bool "readprofile"
+	default y
+	#select PLATFORM_LINUX
+	help
+	  This allows you to parse /proc/profile for basic profiling.
+
+config RTCWAKE
+	bool "rtcwake"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Enter a system sleep state until specified wakeup time.
+
+config SCRIPT
+	bool "script"
+	default y
+	help
+	  The script makes typescript of terminal session.
+
+config SCRIPTREPLAY
+	bool "scriptreplay"
+	default y
+	help
+	  This program replays a typescript, using timing information
+	  given by script -t.
+
+config SETARCH
+	bool "setarch"
+	default y
+	select PLATFORM_LINUX
+	help
+	  The linux32 utility is used to create a 32bit environment for the
+	  specified program (usually a shell). It only makes sense to have
+	  this util on a system that supports both 64bit and 32bit userland
+	  (like amd64/x86, ppc64/ppc, sparc64/sparc, etc...).
+
+config SWAPONOFF
+	bool "swaponoff"
+	default y
+	select PLATFORM_LINUX
+	help
+	  This option enables both the 'swapon' and the 'swapoff' utilities.
+	  Once you have created some swap space using 'mkswap', you also need
+	  to enable your swap space with the 'swapon' utility. The 'swapoff'
+	  utility is used, typically at system shutdown, to disable any swap
+	  space. If you are not using any swap space, you can leave this
+	  option disabled.
+
+config FEATURE_SWAPON_PRI
+	bool "Support priority option -p"
+	default y
+	depends on SWAPONOFF
+	help
+	  Enable support for setting swap device priority in swapon.
+
+config SWITCH_ROOT
+	bool "switch_root"
+	default y
+	select PLATFORM_LINUX
+	help
+	  The switch_root utility is used from initramfs to select a new
+	  root device. Under initramfs, you have to use this instead of
+	  pivot_root. (Stop reading here if you don't care why.)
+
+	  Booting with initramfs extracts a gzipped cpio archive into rootfs
+	  (which is a variant of ramfs/tmpfs). Because rootfs can't be moved
+	  or unmounted*, pivot_root will not work from initramfs. Instead,
+	  switch_root deletes everything out of rootfs (including itself),
+	  does a mount --move that overmounts rootfs with the new root, and
+	  then execs the specified init program.
+
+	  * Because the Linux kernel uses rootfs internally as the starting
+	  and ending point for searching through the kernel's doubly linked
+	  list of active mount points. That's why.
+
+config UMOUNT
+	bool "umount"
+	default y
+	select PLATFORM_LINUX
+	help
+	  When you want to remove a mounted filesystem from its current mount
+	  point, for example when you are shutting down the system, the
+	  'umount' utility is the tool to use. If you enabled the 'mount'
+	  utility, you almost certainly also want to enable 'umount'.
+
+config FEATURE_UMOUNT_ALL
+	bool "Support option -a"
+	default y
+	depends on UMOUNT
+	help
+	  Support -a option to unmount all currently mounted filesystems.
+
+comment "Common options for mount/umount"
+	depends on MOUNT || UMOUNT
+
+config FEATURE_MOUNT_LOOP
+	bool "Support loopback mounts"
+	default y
+	depends on MOUNT || UMOUNT
+	help
+	  Enabling this feature allows automatic mounting of files (containing
+	  filesystem images) via the linux kernel's loopback devices.
+	  The mount command will detect you are trying to mount a file instead
+	  of a block device, and transparently associate the file with a
+	  loopback device. The umount command will also free that loopback
+	  device.
+
+	  You can still use the 'losetup' utility (to manually associate files
+	  with loop devices) if you need to do something advanced, such as
+	  specify an offset or cryptographic options to the loopback device.
+	  (If you don't want umount to free the loop device, use "umount -D".)
+
+config FEATURE_MOUNT_LOOP_CREATE
+	bool "Create new loopback devices if needed"
+	default y
+	depends on FEATURE_MOUNT_LOOP
+	help
+	  Linux kernels >= 2.6.24 support unlimited loopback devices. They are
+	  allocated for use when trying to use a loop device. The loop device
+	  must however exist.
+
+	  This feature lets mount to try to create next /dev/loopN device
+	  if it does not find a free one.
+
+config FEATURE_MTAB_SUPPORT
+	bool "Support for the old /etc/mtab file"
+	default n
+	depends on MOUNT || UMOUNT
+	select FEATURE_MOUNT_FAKE
+	help
+	  Historically, Unix systems kept track of the currently mounted
+	  partitions in the file "/etc/mtab". These days, the kernel exports
+	  the list of currently mounted partitions in "/proc/mounts", rendering
+	  the old mtab file obsolete. (In modern systems, /etc/mtab should be
+	  a symlink to /proc/mounts.)
+
+	  The only reason to have mount maintain an /etc/mtab file itself is if
+	  your stripped-down embedded system does not have a /proc directory.
+	  If you must use this, keep in mind it's inherently brittle (for
+	  example a mount under chroot won't update it), can't handle modern
+	  features like separate per-process filesystem namespaces, requires
+	  that your /etc directory be writable, tends to get easily confused
+	  by --bind or --move mounts, won't update if you rename a directory
+	  that contains a mount point, and so on. (In brief: avoid.)
+
+	  About the only reason to use this is if you've removed /proc from
+	  your kernel.
+
+config VOLUMEID
+	bool #No description makes it a hidden option
+	default n
+
+menu "Filesystem/Volume identification"
+	depends on VOLUMEID
+
+config FEATURE_VOLUMEID_EXT
+	bool "Ext filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+config FEATURE_VOLUMEID_BTRFS
+	bool "btrfs filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+config FEATURE_VOLUMEID_REISERFS
+	bool "Reiser filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+config FEATURE_VOLUMEID_FAT
+	bool "fat filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+config FEATURE_VOLUMEID_EXFAT
+	bool "exFAT filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  exFAT (extended FAT) is a proprietary file system designed especially
+	  for flash drives. It has many features from NTFS, but with less
+	  overhead. exFAT is used on most SDXC cards for consumer electronics.
+
+config FEATURE_VOLUMEID_HFS
+	bool "hfs filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+config FEATURE_VOLUMEID_JFS
+	bool "jfs filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+### config FEATURE_VOLUMEID_UFS
+###	bool "ufs filesystem"
+###	default y
+###	depends on VOLUMEID
+###	help
+###	  TODO
+
+config FEATURE_VOLUMEID_XFS
+	bool "xfs filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+config FEATURE_VOLUMEID_NILFS
+	bool "nilfs filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+config FEATURE_VOLUMEID_NTFS
+	bool "ntfs filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+config FEATURE_VOLUMEID_ISO9660
+	bool "iso9660 filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+config FEATURE_VOLUMEID_UDF
+	bool "udf filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+config FEATURE_VOLUMEID_LUKS
+	bool "luks filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+config FEATURE_VOLUMEID_LINUXSWAP
+	bool "linux swap filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+### config FEATURE_VOLUMEID_LVM
+###	bool "lvm"
+###	default y
+###	depends on VOLUMEID
+###	help
+###	  TODO
+
+config FEATURE_VOLUMEID_CRAMFS
+	bool "cramfs filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+### config FEATURE_VOLUMEID_HPFS
+###	bool "hpfs filesystem"
+###	default y
+###	depends on VOLUMEID
+###	help
+###	  TODO
+
+config FEATURE_VOLUMEID_ROMFS
+	bool "romfs filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+config FEATURE_VOLUMEID_SQUASHFS
+	bool "SquashFS filesystem"
+	default y
+	depends on VOLUMEID && FEATURE_BLKID_TYPE
+	help
+	  Squashfs is a compressed read-only filesystem for Linux. Squashfs is
+	  intended for general read-only filesystem use and in constrained block
+	  device/memory systems (e.g. embedded systems) where low overhead is
+	  needed.
+
+config FEATURE_VOLUMEID_SYSV
+	bool "sysv filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+### config FEATURE_VOLUMEID_MINIX
+###	bool "minix filesystem"
+###	default y
+###	depends on VOLUMEID
+###	help
+###	  TODO
+
+### These only detect partition tables - not used (yet?)
+### config FEATURE_VOLUMEID_MAC
+###	bool "mac filesystem"
+###	default y
+###	depends on VOLUMEID
+###	help
+###	  TODO
+###
+### config FEATURE_VOLUMEID_MSDOS
+###	bool "msdos filesystem"
+###	default y
+###	depends on VOLUMEID
+###	help
+###	  TODO
+
+config FEATURE_VOLUMEID_OCFS2
+	bool "ocfs2 filesystem"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+### config FEATURE_VOLUMEID_HIGHPOINTRAID
+###	bool "highpoint raid"
+###	default y
+###	depends on VOLUMEID
+###	help
+###	  TODO
+
+### config FEATURE_VOLUMEID_ISWRAID
+###	bool "intel raid"
+###	default y
+###	depends on VOLUMEID
+###	help
+###	  TODO
+
+### config FEATURE_VOLUMEID_LSIRAID
+###	bool "lsi raid"
+###	default y
+###	depends on VOLUMEID
+###	help
+###	  TODO
+
+### config FEATURE_VOLUMEID_VIARAID
+###	bool "via raid"
+###	default y
+###	depends on VOLUMEID
+###	help
+###	  TODO
+
+### config FEATURE_VOLUMEID_SILICONRAID
+###	bool "silicon raid"
+###	default y
+###	depends on VOLUMEID
+###	help
+###	  TODO
+
+### config FEATURE_VOLUMEID_NVIDIARAID
+###	bool "nvidia raid"
+###	default y
+###	depends on VOLUMEID
+###	help
+###	  TODO
+
+### config FEATURE_VOLUMEID_PROMISERAID
+###	bool "promise raid"
+###	default y
+###	depends on VOLUMEID
+###	help
+###	  TODO
+
+config FEATURE_VOLUMEID_LINUXRAID
+	bool "linuxraid"
+	default y
+	depends on VOLUMEID
+	help
+	  TODO
+
+endmenu
+
+endmenu
diff --git a/ap/app/busybox/src/util-linux/Kbuild.src b/ap/app/busybox/src/util-linux/Kbuild.src
new file mode 100644
index 0000000..468fc6b
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/Kbuild.src
@@ -0,0 +1,46 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+lib-$(CONFIG_ACPID)             += acpid.o
+lib-$(CONFIG_BLKID)             += blkid.o
+lib-$(CONFIG_DMESG)             += dmesg.o
+lib-$(CONFIG_FBSET)             += fbset.o
+lib-$(CONFIG_FDFLUSH)           += freeramdisk.o
+lib-$(CONFIG_FDFORMAT)          += fdformat.o
+lib-$(CONFIG_FDISK)             += fdisk.o
+lib-$(CONFIG_FINDFS)            += findfs.o
+lib-$(CONFIG_FLOCK)             += flock.o
+lib-$(CONFIG_FREERAMDISK)       += freeramdisk.o
+lib-$(CONFIG_FSCK_MINIX)        += fsck_minix.o
+lib-$(CONFIG_GETOPT)            += getopt.o
+lib-$(CONFIG_HEXDUMP)           += hexdump.o
+lib-$(CONFIG_HWCLOCK)           += hwclock.o
+lib-$(CONFIG_IPCRM)             += ipcrm.o
+lib-$(CONFIG_IPCS)              += ipcs.o
+lib-$(CONFIG_LOSETUP)           += losetup.o
+lib-$(CONFIG_LSPCI)             += lspci.o
+lib-$(CONFIG_LSUSB)             += lsusb.o
+lib-$(CONFIG_MKFS_EXT2)         += mkfs_ext2.o
+lib-$(CONFIG_MKFS_MINIX)        += mkfs_minix.o
+lib-$(CONFIG_MKFS_REISER)       += mkfs_reiser.o
+lib-$(CONFIG_MKFS_VFAT)         += mkfs_vfat.o
+lib-$(CONFIG_MKSWAP)            += mkswap.o
+lib-$(CONFIG_MORE)              += more.o
+lib-$(CONFIG_MOUNT)             += mount.o
+lib-$(CONFIG_PIVOT_ROOT)        += pivot_root.o
+lib-$(CONFIG_RDATE)             += rdate.o
+lib-$(CONFIG_RDEV)              += rdev.o
+lib-$(CONFIG_READPROFILE)       += readprofile.o
+lib-$(CONFIG_RTCWAKE)           += rtcwake.o
+lib-$(CONFIG_SCRIPT)            += script.o
+lib-$(CONFIG_SCRIPTREPLAY)      += scriptreplay.o
+lib-$(CONFIG_SETARCH)           += setarch.o
+lib-$(CONFIG_SWAPONOFF)         += swaponoff.o
+lib-$(CONFIG_SWITCH_ROOT)       += switch_root.o
+lib-$(CONFIG_UMOUNT)            += umount.o
diff --git a/ap/app/busybox/src/util-linux/acpid.c b/ap/app/busybox/src/util-linux/acpid.c
new file mode 100644
index 0000000..38421c2
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/acpid.c
@@ -0,0 +1,358 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * simple ACPI events listener
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define acpid_trivial_usage
+//usage:       "[-df] [-c CONFDIR] [-l LOGFILE] [-a ACTIONFILE] [-M MAPFILE] [-e PROC_EVENT_FILE] [-p PIDFILE]"
+//usage:#define acpid_full_usage "\n\n"
+//usage:       "Listen to ACPI events and spawn specific helpers on event arrival\n"
+//usage:     "\n	-d	Log to stderr, not log file (implies -f)"
+//usage:     "\n	-f	Run in foreground"
+//usage:     "\n	-c DIR	Config directory [/etc/acpi]"
+//usage:     "\n	-e FILE	/proc event file [/proc/acpi/event]"
+//usage:     "\n	-l FILE	Log file [/var/log/acpid.log]"
+//usage:     "\n	-p FILE	Pid file [/var/run/acpid.pid]"
+//usage:     "\n	-a FILE	Action file [/etc/acpid.conf]"
+//usage:     "\n	-M FILE Map file [/etc/acpi.map]"
+//usage:	IF_FEATURE_ACPID_COMPAT(
+//usage:     "\n\nAccept and ignore compatibility options -g -m -s -S -v"
+//usage:	)
+//usage:
+//usage:#define acpid_example_usage
+//usage:       "Without -e option, acpid uses all /dev/input/event* files\n"
+//usage:       "# acpid\n"
+//usage:       "# acpid -l /var/log/my-acpi-log\n"
+//usage:       "# acpid -e /proc/acpi/event\n"
+
+#include "libbb.h"
+#include <syslog.h>
+#include <linux/input.h>
+
+#ifndef EV_SW
+# define EV_SW         0x05
+#endif
+#ifndef EV_KEY
+# define EV_KEY        0x01
+#endif
+#ifndef SW_LID
+# define SW_LID        0x00
+#endif
+#ifndef SW_RFKILL_ALL
+# define SW_RFKILL_ALL 0x03
+#endif
+#ifndef KEY_POWER
+# define KEY_POWER      116     /* SC System Power Down */
+#endif
+#ifndef KEY_SLEEP
+# define KEY_SLEEP      142     /* SC System Sleep */
+#endif
+
+enum {
+	OPT_c = (1 << 0),
+	OPT_d = (1 << 1),
+	OPT_e = (1 << 2),
+	OPT_f = (1 << 3),
+	OPT_l = (1 << 4),
+	OPT_a = (1 << 5),
+	OPT_M = (1 << 6),
+	OPT_p = (1 << 7) * ENABLE_FEATURE_PIDFILE,
+};
+
+struct acpi_event {
+	const char *s_type;
+	uint16_t n_type;
+	const char *s_code;
+	uint16_t n_code;
+	uint32_t value;
+	const char *desc;
+};
+
+static const struct acpi_event f_evt_tab[] = {
+	{ "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRF 00000080" },
+	{ "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRB 00000080" },
+	{ "EV_SW", 0x05, "SW_LID", 0x00, 1, "button/lid LID0 00000080" },
+};
+
+struct acpi_action {
+	const char *key;
+	const char *action;
+};
+
+static const struct acpi_action f_act_tab[] = {
+	{ "PWRF", "PWRF/00000080" },
+	{ "LID0", "LID/00000080" },
+};
+
+struct globals {
+	struct acpi_action *act_tab;
+	int n_act;
+	struct acpi_event *evt_tab;
+	int n_evt;
+} FIX_ALIASING;
+#define G (*ptr_to_globals)
+#define act_tab         (G.act_tab)
+#define n_act           (G.n_act  )
+#define evt_tab         (G.evt_tab)
+#define n_evt           (G.n_evt  )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+/*
+ * acpid listens to ACPI events coming either in textual form
+ * from /proc/acpi/event (though it is marked deprecated,
+ * it is still widely used and _is_ a standard) or in binary form
+ * from specified evdevs (just use /dev/input/event*).
+ * It parses the event to retrieve ACTION and a possible PARAMETER.
+ * It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
+ * (if the resulting path is a directory) or directly.
+ * If the resulting path does not exist it logs it via perror
+ * and continues listening.
+ */
+
+static void process_event(const char *event)
+{
+	struct stat st;
+	char *handler = xasprintf("./%s", event);
+	const char *args[] = { "run-parts", handler, NULL };
+
+	// debug info
+	if (option_mask32 & OPT_d) {
+		bb_error_msg("%s", event);
+	}
+
+	// spawn handler
+	// N.B. run-parts would require scripts to have #!/bin/sh
+	// handler is directory? -> use run-parts
+	// handler is file? -> run it directly
+	if (0 == stat(event, &st))
+		spawn((char **)args + (0==(st.st_mode & S_IFDIR)));
+	else
+		bb_simple_perror_msg(event);
+
+	free(handler);
+}
+
+static const char *find_action(struct input_event *ev, const char *buf)
+{
+	const char *action = NULL;
+	int i;
+
+	// map event
+	for (i = 0; i < n_evt; i++) {
+		if (ev) {
+			if (ev->type == evt_tab[i].n_type && ev->code == evt_tab[i].n_code && ev->value == evt_tab[i].value) {
+				action = evt_tab[i].desc;
+				break;
+			}
+		}
+
+		if (buf) {
+			if (strncmp(buf, evt_tab[i].desc, strlen(buf)) == 0) {
+				action = evt_tab[i].desc;
+				break;
+			}
+		}
+	}
+
+	// get action
+	if (action) {
+		for (i = 0; i < n_act; i++) {
+			if (strstr(action, act_tab[i].key)) {
+				action = act_tab[i].action;
+				break;
+			}
+		}
+	}
+
+	return action;
+}
+
+static void parse_conf_file(const char *filename)
+{
+	parser_t *parser;
+	char *tokens[2];
+
+	parser = config_open2(filename, fopen_for_read);
+
+	if (parser) {
+		while (config_read(parser, tokens, 2, 2, "# \t", PARSE_NORMAL)) {
+			act_tab = xrealloc_vector(act_tab, 1, n_act);
+			act_tab[n_act].key = xstrdup(tokens[0]);
+			act_tab[n_act].action = xstrdup(tokens[1]);
+			n_act++;
+		}
+		config_close(parser);
+	} else {
+		act_tab = (void*)f_act_tab;
+		n_act = ARRAY_SIZE(f_act_tab);
+	}
+}
+
+static void parse_map_file(const char *filename)
+{
+	parser_t *parser;
+	char *tokens[6];
+
+	parser = config_open2(filename, fopen_for_read);
+
+	if (parser) {
+		while (config_read(parser, tokens, 6, 6, "# \t", PARSE_NORMAL)) {
+			evt_tab = xrealloc_vector(evt_tab, 1, n_evt);
+			evt_tab[n_evt].s_type = xstrdup(tokens[0]);
+			evt_tab[n_evt].n_type = xstrtou(tokens[1], 16);
+			evt_tab[n_evt].s_code = xstrdup(tokens[2]);
+			evt_tab[n_evt].n_code = xatou16(tokens[3]);
+			evt_tab[n_evt].value = xatoi_positive(tokens[4]);
+			evt_tab[n_evt].desc = xstrdup(tokens[5]);
+			n_evt++;
+		}
+		config_close(parser);
+	} else {
+		evt_tab = (void*)f_evt_tab;
+		n_evt = ARRAY_SIZE(f_evt_tab);
+	}
+}
+
+/*
+ * acpid [-c conf_dir] [-r conf_file ] [-a map_file ] [-l log_file] [-e proc_event_file]
+ */
+
+int acpid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int acpid_main(int argc UNUSED_PARAM, char **argv)
+{
+	int nfd;
+	int opts;
+	struct pollfd *pfd;
+	const char *opt_dir = "/etc/acpi";
+	const char *opt_input = "/dev/input/event";
+	const char *opt_logfile = "/var/log/acpid.log";
+	const char *opt_action = "/etc/acpid.conf";
+	const char *opt_map = "/etc/acpi.map";
+#if ENABLE_FEATURE_PIDFILE
+	const char *opt_pidfile = CONFIG_PID_FILE_PATH "/acpid.pid";
+#endif
+
+	INIT_G();
+
+	opt_complementary = "df:e--e";
+	opts = getopt32(argv, "c:de:fl:a:M:" IF_FEATURE_PIDFILE("p:") IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"),
+		&opt_dir, &opt_input, &opt_logfile, &opt_action, &opt_map
+		IF_FEATURE_PIDFILE(, &opt_pidfile)
+		IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL)
+	);
+
+	if (!(opts & OPT_f)) {
+		/* No -f "Foreground", we go to background */
+		bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
+	}
+
+	if (!(opts & OPT_d)) {
+		/* No -d "Debug", we log to log file.
+		 * This includes any output from children.
+		 */
+		xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
+		xdup2(STDOUT_FILENO, STDERR_FILENO);
+		/* Also, acpid's messages (but not children) will go to syslog too */
+		openlog(applet_name, LOG_PID, LOG_DAEMON);
+		logmode = LOGMODE_SYSLOG | LOGMODE_STDIO;
+	}
+	/* else: -d "Debug", log is not redirected */
+
+	parse_conf_file(opt_action);
+	parse_map_file(opt_map);
+
+	xchdir(opt_dir);
+
+	/* We spawn children but don't wait for them. Prevent zombies: */
+	bb_signals((1 << SIGCHLD), SIG_IGN);
+	// If you enable this, (1) explain why, (2)
+	// make sure while(poll) loop below is still interruptible
+	// by SIGTERM et al:
+	//bb_signals(BB_FATAL_SIGS, record_signo);
+
+	pfd = NULL;
+	nfd = 0;
+	while (1) {
+		int fd;
+		char *dev_event;
+
+		dev_event = xasprintf((opts & OPT_e) ? "%s" : "%s%u", opt_input, nfd);
+		fd = open(dev_event, O_RDONLY | O_NONBLOCK);
+		if (fd < 0) {
+			if (nfd == 0)
+				bb_simple_perror_msg_and_die(dev_event);
+			break;
+		}
+		free(dev_event);
+		pfd = xrealloc_vector(pfd, 1, nfd);
+		pfd[nfd].fd = fd;
+		pfd[nfd].events = POLLIN;
+		nfd++;
+	}
+
+	write_pidfile(opt_pidfile);
+
+	while (safe_poll(pfd, nfd, -1) > 0) {
+		int i;
+		for (i = 0; i < nfd; i++) {
+			const char *event;
+
+			if (!(pfd[i].revents & POLLIN)) {
+				if (pfd[i].revents == 0)
+					continue; /* this fd has nothing */
+
+				/* Likely POLLERR, POLLHUP, POLLNVAL.
+				 * Do not listen on this fd anymore.
+				 */
+				close(pfd[i].fd);
+				nfd--;
+				for (; i < nfd; i++)
+					pfd[i].fd = pfd[i + 1].fd;
+				break; /* do poll() again */
+			}
+
+			event = NULL;
+			if (option_mask32 & OPT_e) {
+				char *buf;
+				int len;
+
+				buf = xmalloc_reads(pfd[i].fd, NULL);
+				/* buf = "button/power PWRB 00000080 00000000" */
+				len = strlen(buf) - 9;
+				if (len >= 0)
+					buf[len] = '\0';
+				event = find_action(NULL, buf);
+				free(buf);
+			} else {
+				struct input_event ev;
+
+				if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev)))
+					continue;
+
+				if (ev.value != 1 && ev.value != 0)
+					continue;
+
+				event = find_action(&ev, NULL);
+			}
+			if (!event)
+				continue;
+			/* spawn event handler */
+			process_event(event);
+		}
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		while (nfd--)
+			close(pfd[nfd].fd);
+		free(pfd);
+	}
+	remove_pidfile(opt_pidfile);
+
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/blkid.c b/ap/app/busybox/src/util-linux/blkid.c
new file mode 100644
index 0000000..1bbc803
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/blkid.c
@@ -0,0 +1,31 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Print UUIDs on all filesystems
+ *
+ * Copyright (C) 2008 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define blkid_trivial_usage
+//usage:       "[BLOCKDEV]..."
+//usage:#define blkid_full_usage "\n\n"
+//usage:       "Print UUIDs of all filesystems"
+
+#include "libbb.h"
+#include "volume_id.h"
+
+int blkid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int blkid_main(int argc UNUSED_PARAM, char **argv)
+{
+	int scan_devices = 1;
+
+	while (*++argv) {
+		/* Note: bogus device names don't cause any error messages */
+		add_to_uuid_cache(*argv);
+		scan_devices = 0;
+	}
+
+	display_uuid_cache(scan_devices);
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/blockdev.c b/ap/app/busybox/src/util-linux/blockdev.c
new file mode 100644
index 0000000..e25e529
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/blockdev.c
@@ -0,0 +1,206 @@
+/*
+ * blockdev implementation for busybox
+ *
+ * Copyright (C) 2010 Sergey Naumov <sknaumov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//applet:IF_BLOCKDEV(APPLET(blockdev, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_BLOCKDEV) += blockdev.o
+
+//config:config BLOCKDEV
+//config:	bool "blockdev"
+//config:	default y
+//config:	help
+//config:	  Performs some ioctls with block devices.
+
+//usage:#define blockdev_trivial_usage
+//usage:	"OPTION BLOCKDEV"
+//usage:#define blockdev_full_usage "\n\n"
+//usage:       "	--setro		Set ro"
+//usage:     "\n	--setrw		Set rw"
+//usage:     "\n	--getro		Get ro"
+//usage:     "\n	--getss		Get sector size"
+//usage:     "\n	--getbsz	Get block size"
+//usage:     "\n	--setbsz BYTES	Set block size"
+//usage:     "\n	--getsz		Get device size in 512-byte sectors"
+/*//usage:     "\n	--getsize	Get device size in sectors (deprecated)"*/
+//usage:     "\n	--getsize64	Get device size in bytes"
+//usage:     "\n	--flushbufs	Flush buffers"
+//usage:     "\n	--rereadpt	Reread partition table"
+
+
+#include "libbb.h"
+#include <linux/fs.h>
+
+enum {
+	ARG_NONE   = 0,
+	ARG_INT    = 1,
+	ARG_ULONG  = 2,
+	/* Yes, BLKGETSIZE64 takes pointer to uint64_t, not ullong! */
+	ARG_U64    = 3,
+	ARG_MASK   = 3,
+
+	FL_USRARG   = 4, /* argument is provided by user */
+	FL_NORESULT = 8,
+	FL_SCALE512 = 16,
+};
+
+struct bdc {
+	uint32_t   ioc;                       /* ioctl code */
+	const char name[sizeof("flushbufs")]; /* "--setfoo" wothout "--" */
+	uint8_t    flags;
+	int8_t     argval;                    /* default argument value */
+};
+
+static const struct bdc bdcommands[] = {
+	{
+		.ioc = BLKROSET,
+		.name = "setro",
+		.flags = ARG_INT + FL_NORESULT,
+		.argval = 1,
+	},{
+		.ioc = BLKROSET,
+		.name = "setrw",
+		.flags = ARG_INT + FL_NORESULT,
+		.argval = 0,
+	},{
+		.ioc = BLKROGET,
+		.name = "getro",
+		.flags = ARG_INT,
+		.argval = -1,
+	},{
+		.ioc = BLKSSZGET,
+		.name = "getss",
+		.flags = ARG_INT,
+		.argval = -1,
+	},{
+		.ioc = BLKBSZGET,
+		.name = "getbsz",
+		.flags = ARG_INT,
+		.argval = -1,
+	},{
+		.ioc = BLKBSZSET,
+		.name = "setbsz",
+		.flags = ARG_INT + FL_NORESULT + FL_USRARG,
+		.argval = 0,
+	},{
+		.ioc = BLKGETSIZE64,
+		.name = "getsz",
+		.flags = ARG_U64 + FL_SCALE512,
+		.argval = -1,
+	},{
+		.ioc = BLKGETSIZE,
+		.name = "getsize",
+		.flags = ARG_ULONG,
+		.argval = -1,
+	},{
+		.ioc = BLKGETSIZE64,
+		.name = "getsize64",
+		.flags = ARG_U64,
+		.argval = -1,
+	},{
+		.ioc = BLKFLSBUF,
+		.name = "flushbufs",
+		.flags = ARG_NONE + FL_NORESULT,
+		.argval = 0,
+	},{
+		.ioc = BLKRRPART,
+		.name = "rereadpt",
+		.flags = ARG_NONE + FL_NORESULT,
+		.argval = 0,
+	}
+};
+
+static const struct bdc *find_cmd(const char *s)
+{
+	const struct bdc *bdcmd = bdcommands;
+	if (s[0] == '-' && s[1] == '-') {
+		s += 2;
+		do {
+			if (strcmp(s, bdcmd->name) == 0)
+				return bdcmd;
+			bdcmd++;
+		} while (bdcmd != bdcommands + ARRAY_SIZE(bdcommands));
+	}
+	bb_show_usage();
+}
+
+int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int blockdev_main(int argc UNUSED_PARAM, char **argv)
+{
+	const struct bdc *bdcmd;
+	int fd;
+	uint64_t u64;
+	union {
+		int i;
+		unsigned long lu;
+		uint64_t u64;
+	} ioctl_val_on_stack;
+
+	argv++;
+	if (!argv[0] || !argv[1]) /* must have at least 2 args */
+		bb_show_usage();
+
+	bdcmd = find_cmd(*argv);
+
+	u64 = (int)bdcmd->argval;
+	if (bdcmd->flags & FL_USRARG)
+		u64 = xatoi_positive(*++argv);
+
+	argv++;
+	if (!argv[0] || argv[1])
+		bb_show_usage();
+	fd = xopen(argv[0], O_RDONLY);
+
+	ioctl_val_on_stack.u64 = u64;
+#if BB_BIG_ENDIAN
+	/* Store data properly wrt data size.
+	 * (1) It's no-op for little-endian.
+	 * (2) it's no-op for 0 and -1. Only --setro uses arg != 0 and != -1,
+	 * and it is ARG_INT. --setbsz USER_VAL is also ARG_INT.
+	 * Thus, we don't need to handle ARG_ULONG.
+	 */
+	switch (bdcmd->flags & ARG_MASK) {
+	case ARG_INT:
+		ioctl_val_on_stack.i = (int)u64;
+		break;
+# if 0 /* unused */
+	case ARG_ULONG:
+		ioctl_val_on_stack.lu = (unsigned long)u64;
+		break;
+# endif
+	}
+#endif
+
+	if (ioctl(fd, bdcmd->ioc, &ioctl_val_on_stack.u64) == -1)
+		bb_simple_perror_msg_and_die(*argv);
+
+	/* Fetch it into register(s) */
+	u64 = ioctl_val_on_stack.u64;
+
+	if (bdcmd->flags & FL_SCALE512)
+		u64 >>= 9;
+
+	/* Zero- or one-extend the value if needed, then print */
+	switch (bdcmd->flags & (ARG_MASK+FL_NORESULT)) {
+	case ARG_INT:
+		/* Smaller code when we use long long
+		 * (gcc tail-merges printf call)
+		 */
+		printf("%lld\n", (long long)(int)u64);
+		break;
+	case ARG_ULONG:
+		u64 = (unsigned long)u64;
+		/* FALLTHROUGH */
+	case ARG_U64:
+		printf("%llu\n", (unsigned long long)u64);
+		break;
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(fd);
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/dmesg.c b/ap/app/busybox/src/util-linux/dmesg.c
new file mode 100644
index 0000000..81ba1c9
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/dmesg.c
@@ -0,0 +1,86 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *
+ * dmesg - display/control kernel ring buffer.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ * Copyright 2006 Bernhard Reutner-Fischer <rep.nop@aon.at>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define dmesg_trivial_usage
+//usage:       "[-c] [-n LEVEL] [-s SIZE]"
+//usage:#define dmesg_full_usage "\n\n"
+//usage:       "Print or control the kernel ring buffer\n"
+//usage:     "\n	-c		Clear ring buffer after printing"
+//usage:     "\n	-n LEVEL	Set console logging level"
+//usage:     "\n	-s SIZE		Buffer size"
+
+#include <sys/klog.h>
+#include "libbb.h"
+
+int dmesg_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dmesg_main(int argc UNUSED_PARAM, char **argv)
+{
+	int len, level;
+	char *buf;
+	unsigned opts;
+	enum {
+		OPT_c = 1 << 0,
+		OPT_s = 1 << 1,
+		OPT_n = 1 << 2
+	};
+
+	opt_complementary = "s+:n+"; /* numeric */
+	opts = getopt32(argv, "cs:n:", &len, &level);
+	if (opts & OPT_n) {
+		if (klogctl(8, NULL, (long) level))
+			bb_perror_msg_and_die("klogctl");
+		return EXIT_SUCCESS;
+	}
+
+	if (!(opts & OPT_s))
+		len = klogctl(10, NULL, 0); /* read ring buffer size */
+	if (len < 16*1024)
+		len = 16*1024;
+	if (len > 16*1024*1024)
+		len = 16*1024*1024;
+
+	buf = xmalloc(len);
+	len = klogctl(3 + (opts & OPT_c), buf, len); /* read ring buffer */
+	if (len < 0)
+		bb_perror_msg_and_die("klogctl");
+	if (len == 0)
+		return EXIT_SUCCESS;
+
+
+	if (ENABLE_FEATURE_DMESG_PRETTY) {
+		int last = '\n';
+		int in = 0;
+
+		/* Skip <[0-9]+> at the start of lines */
+		while (1) {
+			if (last == '\n' && buf[in] == '<') {
+				while (buf[in++] != '>' && in < len)
+					;
+			} else {
+				last = buf[in++];
+				putchar(last);
+			}
+			if (in >= len)
+				break;
+		}
+		/* Make sure we end with a newline */
+		if (last != '\n')
+			bb_putchar('\n');
+	} else {
+		full_write(STDOUT_FILENO, buf, len);
+		if (buf[len-1] != '\n')
+			bb_putchar('\n');
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP) free(buf);
+
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/fbset.c b/ap/app/busybox/src/util-linux/fbset.c
new file mode 100644
index 0000000..e9aacce
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/fbset.c
@@ -0,0 +1,535 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini fbset implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * This is a from-scratch implementation of fbset; but the de facto fbset
+ * implementation was a good reference. fbset (original) is released under
+ * the GPL, and is (c) 1995-1999 by:
+ *     Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be)
+ */
+
+//usage:#define fbset_trivial_usage
+//usage:       "[OPTIONS] [MODE]"
+//usage:#define fbset_full_usage "\n\n"
+//usage:       "Show and modify frame buffer settings"
+//usage:
+//usage:#define fbset_example_usage
+//usage:       "$ fbset\n"
+//usage:       "mode \"1024x768-76\"\n"
+//usage:       "	# D: 78.653 MHz, H: 59.949 kHz, V: 75.694 Hz\n"
+//usage:       "	geometry 1024 768 1024 768 16\n"
+//usage:       "	timings 12714 128 32 16 4 128 4\n"
+//usage:       "	accel false\n"
+//usage:       "	rgba 5/11,6/5,5/0,0/0\n"
+//usage:       "endmode\n"
+
+#include "libbb.h"
+
+#define DEFAULTFBDEV  FB_0
+#define DEFAULTFBMODE "/etc/fb.modes"
+
+/* Stuff stolen from the kernel's fb.h */
+#define FB_ACTIVATE_ALL 64
+enum {
+	FBIOGET_VSCREENINFO = 0x4600,
+	FBIOPUT_VSCREENINFO = 0x4601
+};
+
+struct fb_bitfield {
+	uint32_t offset;                /* beginning of bitfield */
+	uint32_t length;                /* length of bitfield */
+	uint32_t msb_right;             /* !=0: Most significant bit is right */
+};
+struct fb_var_screeninfo {
+	uint32_t xres;                  /* visible resolution */
+	uint32_t yres;
+	uint32_t xres_virtual;          /* virtual resolution */
+	uint32_t yres_virtual;
+	uint32_t xoffset;               /* offset from virtual to visible */
+	uint32_t yoffset;               /* resolution */
+
+	uint32_t bits_per_pixel;
+	uint32_t grayscale;             /* !=0 Graylevels instead of colors */
+
+	struct fb_bitfield red;         /* bitfield in fb mem if true color, */
+	struct fb_bitfield green;       /* else only length is significant */
+	struct fb_bitfield blue;
+	struct fb_bitfield transp;      /* transparency */
+
+	uint32_t nonstd;                /* !=0 Non standard pixel format */
+
+	uint32_t activate;              /* see FB_ACTIVATE_x */
+
+	uint32_t height;                /* height of picture in mm */
+	uint32_t width;                 /* width of picture in mm */
+
+	uint32_t accel_flags;           /* acceleration flags (hints) */
+
+	/* Timing: All values in pixclocks, except pixclock (of course) */
+	uint32_t pixclock;              /* pixel clock in ps (pico seconds) */
+	uint32_t left_margin;           /* time from sync to picture */
+	uint32_t right_margin;          /* time from picture to sync */
+	uint32_t upper_margin;          /* time from sync to picture */
+	uint32_t lower_margin;
+	uint32_t hsync_len;             /* length of horizontal sync */
+	uint32_t vsync_len;             /* length of vertical sync */
+	uint32_t sync;                  /* see FB_SYNC_x */
+	uint32_t vmode;                 /* see FB_VMODE_x */
+	uint32_t reserved[6];           /* Reserved for future compatibility */
+};
+
+static void copy_if_gt0(uint32_t *src, uint32_t *dst, unsigned cnt)
+{
+	do {
+		if ((int32_t) *src > 0)
+			*dst = *src;
+		src++;
+		dst++;
+	} while (--cnt);
+}
+
+static NOINLINE void copy_changed_values(
+		struct fb_var_screeninfo *base,
+		struct fb_var_screeninfo *set)
+{
+	//if ((int32_t) set->xres > 0) base->xres = set->xres;
+	//if ((int32_t) set->yres > 0) base->yres = set->yres;
+	//if ((int32_t) set->xres_virtual > 0)   base->xres_virtual = set->xres_virtual;
+	//if ((int32_t) set->yres_virtual > 0)   base->yres_virtual = set->yres_virtual;
+	copy_if_gt0(&set->xres, &base->xres, 4);
+
+	if ((int32_t) set->bits_per_pixel > 0) base->bits_per_pixel = set->bits_per_pixel;
+	//copy_if_gt0(&set->bits_per_pixel, &base->bits_per_pixel, 1);
+
+	//if ((int32_t) set->pixclock > 0)       base->pixclock = set->pixclock;
+	//if ((int32_t) set->left_margin > 0)    base->left_margin = set->left_margin;
+	//if ((int32_t) set->right_margin > 0)   base->right_margin = set->right_margin;
+	//if ((int32_t) set->upper_margin > 0)   base->upper_margin = set->upper_margin;
+	//if ((int32_t) set->lower_margin > 0)   base->lower_margin = set->lower_margin;
+	//if ((int32_t) set->hsync_len > 0) base->hsync_len = set->hsync_len;
+	//if ((int32_t) set->vsync_len > 0) base->vsync_len = set->vsync_len;
+	//if ((int32_t) set->sync > 0)  base->sync = set->sync;
+	//if ((int32_t) set->vmode > 0) base->vmode = set->vmode;
+	copy_if_gt0(&set->pixclock, &base->pixclock, 9);
+}
+
+
+enum {
+	CMD_FB = 1,
+	CMD_DB = 2,
+	CMD_GEOMETRY = 3,
+	CMD_TIMING = 4,
+	CMD_ACCEL = 5,
+	CMD_HSYNC = 6,
+	CMD_VSYNC = 7,
+	CMD_LACED = 8,
+	CMD_DOUBLE = 9,
+/*	CMD_XCOMPAT =     10, */
+	CMD_ALL = 11,
+	CMD_INFO = 12,
+	CMD_SHOW = 13,
+	CMD_CHANGE = 14,
+
+#if ENABLE_FEATURE_FBSET_FANCY
+	CMD_XRES = 100,
+	CMD_YRES = 101,
+	CMD_VXRES = 102,
+	CMD_VYRES = 103,
+	CMD_DEPTH = 104,
+	CMD_MATCH = 105,
+	CMD_PIXCLOCK = 106,
+	CMD_LEFT = 107,
+	CMD_RIGHT = 108,
+	CMD_UPPER = 109,
+	CMD_LOWER = 110,
+	CMD_HSLEN = 111,
+	CMD_VSLEN = 112,
+	CMD_CSYNC = 113,
+	CMD_GSYNC = 114,
+	CMD_EXTSYNC = 115,
+	CMD_BCAST = 116,
+	CMD_RGBA = 117,
+	CMD_STEP = 118,
+	CMD_MOVE = 119,
+#endif
+};
+
+static const struct cmdoptions_t {
+	const char name[9];
+	const unsigned char param_count;
+	const unsigned char code;
+} g_cmdoptions[] = {
+	/*"12345678" + NUL */
+	{ "fb"      , 1, CMD_FB       },
+	{ "db"      , 1, CMD_DB       },
+	{ "a"       , 0, CMD_ALL      },
+	{ "i"       , 0, CMD_INFO     },
+	{ "g"       , 5, CMD_GEOMETRY },
+	{ "t"       , 7, CMD_TIMING   },
+	{ "accel"   , 1, CMD_ACCEL    },
+	{ "hsync"   , 1, CMD_HSYNC    },
+	{ "vsync"   , 1, CMD_VSYNC    },
+	{ "laced"   , 1, CMD_LACED    },
+	{ "double"  , 1, CMD_DOUBLE   },
+	{ "show"    , 0, CMD_SHOW     },
+	{ "s"       , 0, CMD_SHOW     },
+#if ENABLE_FEATURE_FBSET_FANCY
+	{ "all"     , 0, CMD_ALL      },
+	{ "xres"    , 1, CMD_XRES     },
+	{ "yres"    , 1, CMD_YRES     },
+	{ "vxres"   , 1, CMD_VXRES    },
+	{ "vyres"   , 1, CMD_VYRES    },
+	{ "depth"   , 1, CMD_DEPTH    },
+	{ "match"   , 0, CMD_MATCH    },
+	{ "geometry", 5, CMD_GEOMETRY },
+	{ "pixclock", 1, CMD_PIXCLOCK },
+	{ "left"    , 1, CMD_LEFT     },
+	{ "right"   , 1, CMD_RIGHT    },
+	{ "upper"   , 1, CMD_UPPER    },
+	{ "lower"   , 1, CMD_LOWER    },
+	{ "hslen"   , 1, CMD_HSLEN    },
+	{ "vslen"   , 1, CMD_VSLEN    },
+	{ "timings" , 7, CMD_TIMING   },
+	{ "csync"   , 1, CMD_CSYNC    },
+	{ "gsync"   , 1, CMD_GSYNC    },
+	{ "extsync" , 1, CMD_EXTSYNC  },
+	{ "bcast"   , 1, CMD_BCAST    },
+	{ "rgba"    , 1, CMD_RGBA     },
+	{ "step"    , 1, CMD_STEP     },
+	{ "move"    , 1, CMD_MOVE     },
+#endif
+};
+
+/* taken from linux/fb.h */
+enum {
+	FB_SYNC_HOR_HIGH_ACT = 1,       /* horizontal sync high active */
+	FB_SYNC_VERT_HIGH_ACT = 2,      /* vertical sync high active */
+#if ENABLE_FEATURE_FBSET_READMODE
+	FB_VMODE_INTERLACED = 1,        /* interlaced */
+	FB_VMODE_DOUBLE = 2,            /* double scan */
+	FB_SYNC_EXT = 4,                /* external sync */
+	FB_SYNC_COMP_HIGH_ACT = 8,      /* composite sync high active */
+#endif
+};
+
+#if ENABLE_FEATURE_FBSET_READMODE
+static void ss(uint32_t *x, uint32_t flag, char *buf, const char *what)
+{
+	if (strcmp(buf, what) == 0)
+		*x &= ~flag;
+	else
+		*x |= flag;
+}
+
+/* Mode db file contains mode definitions like this:
+ * mode "800x600-48-lace"
+ *     # D: 36.00 MHz, H: 33.835 kHz, V: 96.39 Hz
+ *     geometry 800 600 800 600 8
+ *     timings 27778 56 80 79 11 128 12
+ *     laced true
+ *     hsync high
+ *     vsync high
+ * endmode
+ */
+static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
+					const char *mode)
+{
+	char *token[2], *p, *s;
+	parser_t *parser = config_open(fn);
+
+	while (config_read(parser, token, 2, 1, "# \t\r", PARSE_NORMAL)) {
+		if (strcmp(token[0], "mode") != 0 || !token[1])
+			continue;
+		p = strstr(token[1], mode);
+		if (!p)
+			continue;
+		s = p + strlen(mode);
+		//bb_info_msg("CHECK[%s][%s][%d]", mode, p-1, *s);
+		/* exact match? */
+		if (((!*s || isspace(*s)) && '"' != s[-1]) /* end-of-token */
+		 || ('"' == *s && '"' == p[-1]) /* ends with " but starts with " too! */
+		) {
+			//bb_info_msg("FOUND[%s][%s][%s][%d]", token[1], p, mode, isspace(*s));
+			break;
+		}
+	}
+
+	if (!token[0])
+		return 0;
+
+	while (config_read(parser, token, 2, 1, "# \t", PARSE_NORMAL)) {
+		int i;
+
+//bb_info_msg("???[%s][%s]", token[0], token[1]);
+		if (strcmp(token[0], "endmode") == 0) {
+//bb_info_msg("OK[%s]", mode);
+			return 1;
+		}
+		p = token[1];
+		i = index_in_strings(
+			"geometry\0timings\0interlaced\0double\0vsync\0hsync\0csync\0extsync\0rgba\0",
+			token[0]);
+		switch (i) {
+		case 0:
+			if (sizeof(int) == sizeof(base->xres)) {
+				sscanf(p, "%d %d %d %d %d",
+					&base->xres, &base->yres,
+					&base->xres_virtual, &base->yres_virtual,
+					&base->bits_per_pixel);
+			} else {
+				int base_xres, base_yres;
+				int base_xres_virtual, base_yres_virtual;
+				int base_bits_per_pixel;
+				sscanf(p, "%d %d %d %d %d",
+					&base_xres, &base_yres,
+					&base_xres_virtual, &base_yres_virtual,
+					&base_bits_per_pixel);
+				base->xres = base_xres;
+				base->yres = base_yres;
+				base->xres_virtual = base_xres_virtual;
+				base->yres_virtual = base_yres_virtual;
+				base->bits_per_pixel = base_bits_per_pixel;
+			}
+//bb_info_msg("GEO[%s]", p);
+			break;
+		case 1:
+			if (sizeof(int) == sizeof(base->xres)) {
+				sscanf(p, "%d %d %d %d %d %d %d",
+					&base->pixclock,
+					&base->left_margin, &base->right_margin,
+					&base->upper_margin, &base->lower_margin,
+					&base->hsync_len, &base->vsync_len);
+			} else {
+				int base_pixclock;
+				int base_left_margin, base_right_margin;
+				int base_upper_margin, base_lower_margin;
+				int base_hsync_len, base_vsync_len;
+				sscanf(p, "%d %d %d %d %d %d %d",
+					&base_pixclock,
+					&base_left_margin, &base_right_margin,
+					&base_upper_margin, &base_lower_margin,
+					&base_hsync_len, &base_vsync_len);
+				base->pixclock = base_pixclock;
+				base->left_margin = base_left_margin;
+				base->right_margin = base_right_margin;
+				base->upper_margin = base_upper_margin;
+				base->lower_margin = base_lower_margin;
+				base->hsync_len = base_hsync_len;
+				base->vsync_len = base_vsync_len;
+			}
+//bb_info_msg("TIM[%s]", p);
+			break;
+		case 2:
+		case 3: {
+			static const uint32_t syncs[] = {FB_VMODE_INTERLACED, FB_VMODE_DOUBLE};
+			ss(&base->vmode, syncs[i-2], p, "false");
+//bb_info_msg("VMODE[%s]", p);
+			break;
+		}
+		case 4:
+		case 5:
+		case 6: {
+			static const uint32_t syncs[] = {FB_SYNC_VERT_HIGH_ACT, FB_SYNC_HOR_HIGH_ACT, FB_SYNC_COMP_HIGH_ACT};
+			ss(&base->sync, syncs[i-4], p, "low");
+//bb_info_msg("SYNC[%s]", p);
+			break;
+		}
+		case 7:
+			ss(&base->sync, FB_SYNC_EXT, p, "false");
+//bb_info_msg("EXTSYNC[%s]", p);
+			break;
+		case 8: {
+			int red_offset, red_length;
+			int green_offset, green_length;
+			int blue_offset, blue_length;
+			int transp_offset, transp_length;
+
+			sscanf(p, "%d/%d,%d/%d,%d/%d,%d/%d",
+				&red_offset, &red_length,
+				&green_offset, &green_length,
+				&blue_offset, &blue_length,
+				&transp_offset, &transp_length);
+			base->red.offset = red_offset;
+			base->red.length = red_length;
+			base->red.msb_right = 0;
+			base->green.offset = green_offset;
+			base->green.length = green_length;
+			base->green.msb_right = 0;
+			base->blue.offset = blue_offset;
+			base->blue.length = blue_length;
+			base->blue.msb_right = 0;
+			base->transp.offset = transp_offset;
+			base->transp.length = transp_length;
+			base->transp.msb_right = 0;
+		}
+		}
+	}
+	return 0;
+}
+#endif
+
+static NOINLINE void showmode(struct fb_var_screeninfo *v)
+{
+	double drate = 0, hrate = 0, vrate = 0;
+
+	if (v->pixclock) {
+		drate = 1e12 / v->pixclock;
+		hrate = drate / (v->left_margin + v->xres + v->right_margin + v->hsync_len);
+		vrate = hrate / (v->upper_margin + v->yres + v->lower_margin + v->vsync_len);
+	}
+	printf("\nmode \"%ux%u-%u\"\n"
+#if ENABLE_FEATURE_FBSET_FANCY
+	"\t# D: %.3f MHz, H: %.3f kHz, V: %.3f Hz\n"
+#endif
+	"\tgeometry %u %u %u %u %u\n"
+	"\ttimings %u %u %u %u %u %u %u\n"
+	"\taccel %s\n"
+	"\trgba %u/%u,%u/%u,%u/%u,%u/%u\n"
+	"endmode\n\n",
+		v->xres, v->yres, (int) (vrate + 0.5),
+#if ENABLE_FEATURE_FBSET_FANCY
+		drate / 1e6, hrate / 1e3, vrate,
+#endif
+		v->xres, v->yres, v->xres_virtual, v->yres_virtual, v->bits_per_pixel,
+		v->pixclock, v->left_margin, v->right_margin, v->upper_margin, v->lower_margin,
+			v->hsync_len, v->vsync_len,
+		(v->accel_flags > 0 ? "true" : "false"),
+		v->red.length, v->red.offset, v->green.length, v->green.offset,
+			v->blue.length, v->blue.offset, v->transp.length, v->transp.offset);
+}
+
+int fbset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fbset_main(int argc, char **argv)
+{
+	enum {
+		OPT_CHANGE   = (1 << 0),
+		OPT_SHOW     = (1 << 1),
+		OPT_READMODE = (1 << 2),
+		OPT_ALL      = (1 << 3),
+	};
+	struct fb_var_screeninfo var_old, var_set;
+	int fh, i;
+	unsigned options = 0;
+
+	const char *fbdev = DEFAULTFBDEV;
+	const char *modefile = DEFAULTFBMODE;
+	char *thisarg;
+	char *mode = mode; /* for compiler */
+
+	memset(&var_set, 0xff, sizeof(var_set)); /* set all to -1 */
+
+	/* parse cmd args.... why do they have to make things so difficult? */
+	argv++;
+	argc--;
+	for (; argc > 0 && (thisarg = *argv) != NULL; argc--, argv++) {
+		if (thisarg[0] != '-') {
+			if (!ENABLE_FEATURE_FBSET_READMODE || argc != 1)
+				bb_show_usage();
+			mode = thisarg;
+			options |= OPT_READMODE;
+			goto contin;
+		}
+		for (i = 0; i < ARRAY_SIZE(g_cmdoptions); i++) {
+			if (strcmp(thisarg + 1, g_cmdoptions[i].name) != 0)
+				continue;
+			if (argc <= g_cmdoptions[i].param_count)
+				bb_show_usage();
+
+			switch (g_cmdoptions[i].code) {
+			case CMD_FB:
+				fbdev = argv[1];
+				break;
+			case CMD_DB:
+				modefile = argv[1];
+				break;
+			case CMD_ALL:
+				options |= OPT_ALL;
+				break;
+			case CMD_SHOW:
+				options |= OPT_SHOW;
+				break;
+			case CMD_GEOMETRY:
+				var_set.xres = xatou32(argv[1]);
+				var_set.yres = xatou32(argv[2]);
+				var_set.xres_virtual = xatou32(argv[3]);
+				var_set.yres_virtual = xatou32(argv[4]);
+				var_set.bits_per_pixel = xatou32(argv[5]);
+				break;
+			case CMD_TIMING:
+				var_set.pixclock = xatou32(argv[1]);
+				var_set.left_margin = xatou32(argv[2]);
+				var_set.right_margin = xatou32(argv[3]);
+				var_set.upper_margin = xatou32(argv[4]);
+				var_set.lower_margin = xatou32(argv[5]);
+				var_set.hsync_len = xatou32(argv[6]);
+				var_set.vsync_len = xatou32(argv[7]);
+				break;
+			case CMD_ACCEL:
+				break;
+			case CMD_HSYNC:
+				var_set.sync |= FB_SYNC_HOR_HIGH_ACT;
+				break;
+			case CMD_VSYNC:
+				var_set.sync |= FB_SYNC_VERT_HIGH_ACT;
+				break;
+#if ENABLE_FEATURE_FBSET_FANCY
+			case CMD_XRES:
+				var_set.xres = xatou32(argv[1]);
+				break;
+			case CMD_YRES:
+				var_set.yres = xatou32(argv[1]);
+				break;
+			case CMD_DEPTH:
+				var_set.bits_per_pixel = xatou32(argv[1]);
+				break;
+#endif
+			}
+			switch (g_cmdoptions[i].code) {
+			case CMD_FB:
+			case CMD_DB:
+			case CMD_ALL:
+			case CMD_SHOW:
+				break;
+			default:
+				/* other commands imply changes */
+				options |= OPT_CHANGE;
+			}
+			argc -= g_cmdoptions[i].param_count;
+			argv += g_cmdoptions[i].param_count;
+			goto contin;
+		}
+		bb_show_usage();
+ contin: ;
+	}
+
+	fh = xopen(fbdev, O_RDONLY);
+	xioctl(fh, FBIOGET_VSCREENINFO, &var_old);
+
+	if (options & OPT_READMODE) {
+#if ENABLE_FEATURE_FBSET_READMODE
+		if (!read_mode_db(&var_old, modefile, mode)) {
+			bb_error_msg_and_die("unknown video mode '%s'", mode);
+		}
+		options |= OPT_CHANGE;
+#endif
+	}
+
+	if (options & OPT_CHANGE) {
+		copy_changed_values(&var_old, &var_set);
+		if (options & OPT_ALL)
+			var_old.activate = FB_ACTIVATE_ALL;
+		xioctl(fh, FBIOPUT_VSCREENINFO, &var_old);
+	}
+
+	if (options == 0 || (options & OPT_SHOW))
+		showmode(&var_old);
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(fh);
+
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/fdformat.c b/ap/app/busybox/src/util-linux/fdformat.c
new file mode 100644
index 0000000..b3e918f
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/fdformat.c
@@ -0,0 +1,137 @@
+/* vi: set sw=4 ts=4: */
+/* fdformat.c  -  Low-level formats a floppy disk - Werner Almesberger
+ * 5 July 2003 -- modified for Busybox by Erik Andersen
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define fdformat_trivial_usage
+//usage:       "[-n] DEVICE"
+//usage:#define fdformat_full_usage "\n\n"
+//usage:       "Format floppy disk\n"
+//usage:     "\n	-n	Don't verify after format"
+
+#include "libbb.h"
+
+
+/* Stuff extracted from linux/fd.h */
+struct floppy_struct {
+	unsigned int	size,		/* nr of sectors total */
+			sect,		/* sectors per track */
+			head,		/* nr of heads */
+			track,		/* nr of tracks */
+			stretch;	/* !=0 means double track steps */
+#define FD_STRETCH 1
+#define FD_SWAPSIDES 2
+
+	unsigned char	gap,		/* gap1 size */
+
+			rate,		/* data rate. |= 0x40 for perpendicular */
+#define FD_2M 0x4
+#define FD_SIZECODEMASK 0x38
+#define FD_SIZECODE(floppy) (((((floppy)->rate&FD_SIZECODEMASK)>> 3)+ 2) %8)
+#define FD_SECTSIZE(floppy) ( (floppy)->rate & FD_2M ? \
+			     512 : 128 << FD_SIZECODE(floppy) )
+#define FD_PERP 0x40
+
+			spec1,		/* stepping rate, head unload time */
+			fmt_gap;	/* gap2 size */
+	const char	* name; /* used only for predefined formats */
+};
+struct format_descr {
+	unsigned int device,head,track;
+};
+#define FDFMTBEG _IO(2,0x47)
+#define FDFMTTRK _IOW(2,0x48, struct format_descr)
+#define FDFMTEND _IO(2,0x49)
+#define FDGETPRM _IOR(2, 0x04, struct floppy_struct)
+#define FD_FILL_BYTE 0xF6 /* format fill byte. */
+
+int fdformat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fdformat_main(int argc UNUSED_PARAM, char **argv)
+{
+	int fd, n, cyl, read_bytes, verify;
+	unsigned char *data;
+	struct stat st;
+	struct floppy_struct param;
+	struct format_descr descr;
+
+	opt_complementary = "=1"; /* must have 1 param */
+	verify = !getopt32(argv, "n");
+	argv += optind;
+
+	xstat(*argv, &st);
+	if (!S_ISBLK(st.st_mode)) {
+		bb_error_msg_and_die("%s: not a block device", *argv);
+		/* do not test major - perhaps this was an USB floppy */
+	}
+
+	/* O_RDWR for formatting and verifying */
+	fd = xopen(*argv, O_RDWR);
+
+	/* original message was: "Could not determine current format type" */
+	xioctl(fd, FDGETPRM, &param);
+
+	printf("%s-sided, %d tracks, %d sec/track. Total capacity %d kB\n",
+		(param.head == 2) ? "Double" : "Single",
+		param.track, param.sect, param.size >> 1);
+
+	/* FORMAT */
+	printf("Formatting... ");
+	xioctl(fd, FDFMTBEG, NULL);
+
+	/* n == track */
+	for (n = 0; n < param.track; n++) {
+		descr.head = 0;
+		descr.track = n;
+		xioctl(fd, FDFMTTRK, &descr);
+		printf("%3d\b\b\b", n);
+		if (param.head == 2) {
+			descr.head = 1;
+			xioctl(fd, FDFMTTRK, &descr);
+		}
+	}
+
+	xioctl(fd, FDFMTEND, NULL);
+	printf("done\n");
+
+	/* VERIFY */
+	if (verify) {
+		/* n == cyl_size */
+		n = param.sect*param.head*512;
+
+		data = xmalloc(n);
+		printf("Verifying... ");
+		for (cyl = 0; cyl < param.track; cyl++) {
+			printf("%3d\b\b\b", cyl);
+			read_bytes = safe_read(fd, data, n);
+			if (read_bytes != n) {
+				if (read_bytes < 0) {
+					bb_perror_msg(bb_msg_read_error);
+				}
+				bb_error_msg_and_die("problem reading cylinder %d, "
+					"expected %d, read %d", cyl, n, read_bytes);
+				// FIXME: maybe better seek & continue??
+			}
+			/* Check backwards so we don't need a counter */
+			while (--read_bytes >= 0) {
+				if (data[read_bytes] != FD_FILL_BYTE) {
+					printf("bad data in cyl %d\nContinuing... ", cyl);
+				}
+			}
+		}
+		/* There is no point in freeing blocks at the end of a program, because
+		all of the program's space is given back to the system when the process
+		terminates.*/
+
+		if (ENABLE_FEATURE_CLEAN_UP) free(data);
+
+		printf("done\n");
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP) close(fd);
+
+	/* Don't bother closing.  Exit does
+	 * that, so we can save a few bytes */
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/fdisk.c b/ap/app/busybox/src/util-linux/fdisk.c
new file mode 100644
index 0000000..39eb27b
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/fdisk.c
@@ -0,0 +1,3122 @@
+/* vi: set sw=4 ts=4: */
+/* fdisk.c -- Partition table manipulator for Linux.
+ *
+ * Copyright (C) 1992  A. V. Le Blanc (LeBlanc@mcc.ac.uk)
+ * Copyright (C) 2001,2002 Vladimir Oleynik <dzo@simtreas.ru> (initial bb port)
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* Looks like someone forgot to add this to config system */
+//usage:#ifndef ENABLE_FEATURE_FDISK_BLKSIZE
+//usage:# define ENABLE_FEATURE_FDISK_BLKSIZE 0
+//usage:# define IF_FEATURE_FDISK_BLKSIZE(a)
+//usage:#endif
+//usage:
+//usage:#define fdisk_trivial_usage
+//usage:       "[-ul" IF_FEATURE_FDISK_BLKSIZE("s") "] "
+//usage:       "[-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SSZ] DISK"
+//usage:#define fdisk_full_usage "\n\n"
+//usage:       "Change partition table\n"
+//usage:     "\n	-u		Start and End are in sectors (instead of cylinders)"
+//usage:     "\n	-l		Show partition table for each DISK, then exit"
+//usage:	IF_FEATURE_FDISK_BLKSIZE(
+//usage:     "\n	-s		Show partition sizes in kb for each DISK, then exit"
+//usage:	)
+//usage:     "\n	-b 2048		(for certain MO disks) use 2048-byte sectors"
+//usage:     "\n	-C CYLINDERS	Set number of cylinders/heads/sectors"
+//usage:     "\n	-H HEADS"
+//usage:     "\n	-S SECTORS"
+
+#ifndef _LARGEFILE64_SOURCE
+/* For lseek64 */
+# define _LARGEFILE64_SOURCE
+#endif
+#include <assert.h>             /* assert */
+#include <sys/mount.h>
+#if !defined(BLKSSZGET)
+# define BLKSSZGET _IO(0x12, 104)
+#endif
+#if !defined(BLKGETSIZE64)
+# define BLKGETSIZE64 _IOR(0x12,114,size_t)
+#endif
+#include "libbb.h"
+
+#if BB_LITTLE_ENDIAN
+# define inline_if_little_endian ALWAYS_INLINE
+#else
+# define inline_if_little_endian /* nothing */
+#endif
+
+
+/* Looks like someone forgot to add this to config system */
+#ifndef ENABLE_FEATURE_FDISK_BLKSIZE
+# define ENABLE_FEATURE_FDISK_BLKSIZE 0
+# define IF_FEATURE_FDISK_BLKSIZE(a)
+#endif
+
+#define DEFAULT_SECTOR_SIZE      512
+#define DEFAULT_SECTOR_SIZE_STR "512"
+#define MAX_SECTOR_SIZE         2048
+#define SECTOR_SIZE              512 /* still used in osf/sgi/sun code */
+#define MAXIMUM_PARTS             60
+
+#define ACTIVE_FLAG             0x80
+
+#define EXTENDED                0x05
+#define WIN98_EXTENDED          0x0f
+#define LINUX_PARTITION         0x81
+#define LINUX_SWAP              0x82
+#define LINUX_NATIVE            0x83
+#define LINUX_EXTENDED          0x85
+#define LINUX_LVM               0x8e
+#define LINUX_RAID              0xfd
+
+
+enum {
+	OPT_b = 1 << 0,
+	OPT_C = 1 << 1,
+	OPT_H = 1 << 2,
+	OPT_l = 1 << 3,
+	OPT_S = 1 << 4,
+	OPT_u = 1 << 5,
+	OPT_s = (1 << 6) * ENABLE_FEATURE_FDISK_BLKSIZE,
+};
+
+
+typedef unsigned long long ullong;
+/* Used for sector numbers. Partition formats we know
+ * do not support more than 2^32 sectors
+ */
+typedef uint32_t sector_t;
+#if UINT_MAX == 4294967295
+# define SECT_FMT ""
+#elif ULONG_MAX == 4294967295
+# define SECT_FMT "l"
+#else
+# error Cant detect sizeof(uint32_t)
+#endif
+
+struct hd_geometry {
+	unsigned char heads;
+	unsigned char sectors;
+	unsigned short cylinders;
+	unsigned long start;
+};
+
+#define HDIO_GETGEO     0x0301  /* get device geometry */
+
+static const char msg_building_new_label[] ALIGN1 =
+"Building a new %s. Changes will remain in memory only,\n"
+"until you decide to write them. After that the previous content\n"
+"won't be recoverable.\n\n";
+
+static const char msg_part_already_defined[] ALIGN1 =
+"Partition %u is already defined, delete it before re-adding\n";
+
+
+struct partition {
+	unsigned char boot_ind;         /* 0x80 - active */
+	unsigned char head;             /* starting head */
+	unsigned char sector;           /* starting sector */
+	unsigned char cyl;              /* starting cylinder */
+	unsigned char sys_ind;          /* what partition type */
+	unsigned char end_head;         /* end head */
+	unsigned char end_sector;       /* end sector */
+	unsigned char end_cyl;          /* end cylinder */
+	unsigned char start4[4];        /* starting sector counting from 0 */
+	unsigned char size4[4];         /* nr of sectors in partition */
+} PACKED;
+
+/*
+ * per partition table entry data
+ *
+ * The four primary partitions have the same sectorbuffer (MBRbuffer)
+ * and have NULL ext_pointer.
+ * Each logical partition table entry has two pointers, one for the
+ * partition and one link to the next one.
+ */
+struct pte {
+	struct partition *part_table;   /* points into sectorbuffer */
+	struct partition *ext_pointer;  /* points into sectorbuffer */
+	sector_t offset_from_dev_start; /* disk sector number */
+	char *sectorbuffer;             /* disk sector contents */
+#if ENABLE_FEATURE_FDISK_WRITABLE
+	char changed;                   /* boolean */
+#endif
+};
+
+#define unable_to_open "can't open '%s'"
+#define unable_to_read "can't read from %s"
+#define unable_to_seek "can't seek on %s"
+
+enum label_type {
+	LABEL_DOS, LABEL_SUN, LABEL_SGI, LABEL_AIX, LABEL_OSF, LABEL_GPT
+};
+
+#define LABEL_IS_DOS	(LABEL_DOS == current_label_type)
+
+#if ENABLE_FEATURE_SUN_LABEL
+#define LABEL_IS_SUN	(LABEL_SUN == current_label_type)
+#define STATIC_SUN static
+#else
+#define LABEL_IS_SUN	0
+#define STATIC_SUN extern
+#endif
+
+#if ENABLE_FEATURE_SGI_LABEL
+#define LABEL_IS_SGI	(LABEL_SGI == current_label_type)
+#define STATIC_SGI static
+#else
+#define LABEL_IS_SGI	0
+#define STATIC_SGI extern
+#endif
+
+#if ENABLE_FEATURE_AIX_LABEL
+#define LABEL_IS_AIX	(LABEL_AIX == current_label_type)
+#define STATIC_AIX static
+#else
+#define LABEL_IS_AIX	0
+#define STATIC_AIX extern
+#endif
+
+#if ENABLE_FEATURE_OSF_LABEL
+#define LABEL_IS_OSF	(LABEL_OSF == current_label_type)
+#define STATIC_OSF static
+#else
+#define LABEL_IS_OSF	0
+#define STATIC_OSF extern
+#endif
+
+#if ENABLE_FEATURE_GPT_LABEL
+#define LABEL_IS_GPT	(LABEL_GPT == current_label_type)
+#define STATIC_GPT static
+#else
+#define LABEL_IS_GPT	0
+#define STATIC_GPT extern
+#endif
+
+enum action { OPEN_MAIN, TRY_ONLY, CREATE_EMPTY_DOS, CREATE_EMPTY_SUN };
+
+static void update_units(void);
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void change_units(void);
+static void reread_partition_table(int leave);
+static void delete_partition(int i);
+static unsigned get_partition(int warn, unsigned max);
+static void list_types(const char *const *sys);
+static sector_t read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char *mesg);
+#endif
+static const char *partition_type(unsigned char type);
+static void get_geometry(void);
+static void read_pte(struct pte *pe, sector_t offset);
+#if ENABLE_FEATURE_SUN_LABEL || ENABLE_FEATURE_FDISK_WRITABLE
+static int get_boot(enum action what);
+#else
+static int get_boot(void);
+#endif
+
+#define PLURAL   0
+#define SINGULAR 1
+
+static sector_t get_start_sect(const struct partition *p);
+static sector_t get_nr_sects(const struct partition *p);
+
+/* DOS partition types */
+
+static const char *const i386_sys_types[] = {
+	"\x00" "Empty",
+	"\x01" "FAT12",
+	"\x04" "FAT16 <32M",
+	"\x05" "Extended",         /* DOS 3.3+ extended partition */
+	"\x06" "FAT16",            /* DOS 16-bit >=32M */
+	"\x07" "HPFS/NTFS",        /* OS/2 IFS, eg, HPFS or NTFS or QNX */
+	"\x0a" "OS/2 Boot Manager",/* OS/2 Boot Manager */
+	"\x0b" "Win95 FAT32",
+	"\x0c" "Win95 FAT32 (LBA)",/* LBA really is 'Extended Int 13h' */
+	"\x0e" "Win95 FAT16 (LBA)",
+	"\x0f" "Win95 Ext'd (LBA)",
+	"\x11" "Hidden FAT12",
+	"\x12" "Compaq diagnostics",
+	"\x14" "Hidden FAT16 <32M",
+	"\x16" "Hidden FAT16",
+	"\x17" "Hidden HPFS/NTFS",
+	"\x1b" "Hidden Win95 FAT32",
+	"\x1c" "Hidden W95 FAT32 (LBA)",
+	"\x1e" "Hidden W95 FAT16 (LBA)",
+	"\x3c" "Part.Magic recovery",
+	"\x41" "PPC PReP Boot",
+	"\x42" "SFS",
+	"\x63" "GNU HURD or SysV", /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+	"\x80" "Old Minix",        /* Minix 1.4a and earlier */
+	"\x81" "Minix / old Linux",/* Minix 1.4b and later */
+	"\x82" "Linux swap",       /* also Solaris */
+	"\x83" "Linux",
+	"\x84" "OS/2 hidden C: drive",
+	"\x85" "Linux extended",
+	"\x86" "NTFS volume set",
+	"\x87" "NTFS volume set",
+	"\x8e" "Linux LVM",
+	"\x9f" "BSD/OS",           /* BSDI */
+	"\xa0" "Thinkpad hibernation",
+	"\xa5" "FreeBSD",          /* various BSD flavours */
+	"\xa6" "OpenBSD",
+	"\xa8" "Darwin UFS",
+	"\xa9" "NetBSD",
+	"\xab" "Darwin boot",
+	"\xb7" "BSDI fs",
+	"\xb8" "BSDI swap",
+	"\xbe" "Solaris boot",
+	"\xeb" "BeOS fs",
+	"\xee" "EFI GPT",                    /* Intel EFI GUID Partition Table */
+	"\xef" "EFI (FAT-12/16/32)",         /* Intel EFI System Partition */
+	"\xf0" "Linux/PA-RISC boot",         /* Linux/PA-RISC boot loader */
+	"\xf2" "DOS secondary",              /* DOS 3.3+ secondary */
+	"\xfd" "Linux raid autodetect",      /* New (2.2.x) raid partition with
+						autodetect using persistent
+						superblock */
+#if 0 /* ENABLE_WEIRD_PARTITION_TYPES */
+	"\x02" "XENIX root",
+	"\x03" "XENIX usr",
+	"\x08" "AIX",              /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+	"\x09" "AIX bootable",     /* AIX data or Coherent */
+	"\x10" "OPUS",
+	"\x18" "AST SmartSleep",
+	"\x24" "NEC DOS",
+	"\x39" "Plan 9",
+	"\x40" "Venix 80286",
+	"\x4d" "QNX4.x",
+	"\x4e" "QNX4.x 2nd part",
+	"\x4f" "QNX4.x 3rd part",
+	"\x50" "OnTrack DM",
+	"\x51" "OnTrack DM6 Aux1", /* (or Novell) */
+	"\x52" "CP/M",             /* CP/M or Microport SysV/AT */
+	"\x53" "OnTrack DM6 Aux3",
+	"\x54" "OnTrackDM6",
+	"\x55" "EZ-Drive",
+	"\x56" "Golden Bow",
+	"\x5c" "Priam Edisk",
+	"\x61" "SpeedStor",
+	"\x64" "Novell Netware 286",
+	"\x65" "Novell Netware 386",
+	"\x70" "DiskSecure Multi-Boot",
+	"\x75" "PC/IX",
+	"\x93" "Amoeba",
+	"\x94" "Amoeba BBT",       /* (bad block table) */
+	"\xa7" "NeXTSTEP",
+	"\xbb" "Boot Wizard hidden",
+	"\xc1" "DRDOS/sec (FAT-12)",
+	"\xc4" "DRDOS/sec (FAT-16 < 32M)",
+	"\xc6" "DRDOS/sec (FAT-16)",
+	"\xc7" "Syrinx",
+	"\xda" "Non-FS data",
+	"\xdb" "CP/M / CTOS / ...",/* CP/M or Concurrent CP/M or
+	                              Concurrent DOS or CTOS */
+	"\xde" "Dell Utility",     /* Dell PowerEdge Server utilities */
+	"\xdf" "BootIt",           /* BootIt EMBRM */
+	"\xe1" "DOS access",       /* DOS access or SpeedStor 12-bit FAT
+	                              extended partition */
+	"\xe3" "DOS R/O",          /* DOS R/O or SpeedStor */
+	"\xe4" "SpeedStor",        /* SpeedStor 16-bit FAT extended
+	                              partition < 1024 cyl. */
+	"\xf1" "SpeedStor",
+	"\xf4" "SpeedStor",        /* SpeedStor large partition */
+	"\xfe" "LANstep",          /* SpeedStor >1024 cyl. or LANstep */
+	"\xff" "BBT",              /* Xenix Bad Block Table */
+#endif
+	NULL
+};
+
+enum {
+	dev_fd = 3                  /* the disk */
+};
+
+/* Globals */
+struct globals {
+	char *line_ptr;
+
+	const char *disk_device;
+	int g_partitions; // = 4;       /* maximum partition + 1 */
+	unsigned units_per_sector; // = 1;
+	unsigned sector_size; // = DEFAULT_SECTOR_SIZE;
+	unsigned user_set_sector_size;
+	unsigned sector_offset; // = 1;
+	unsigned g_heads, g_sectors, g_cylinders;
+	smallint /* enum label_type */ current_label_type;
+	smallint display_in_cyl_units; // = 1;
+#if ENABLE_FEATURE_OSF_LABEL
+	smallint possibly_osf_label;
+#endif
+
+	smallint listing;               /* no aborts for fdisk -l */
+	smallint dos_compatible_flag; // = 1;
+#if ENABLE_FEATURE_FDISK_WRITABLE
+	//int dos_changed;
+	smallint nowarn;                /* no warnings for fdisk -l/-s */
+#endif
+	int ext_index;                  /* the prime extended partition */
+	unsigned user_cylinders, user_heads, user_sectors;
+	unsigned pt_heads, pt_sectors;
+	unsigned kern_heads, kern_sectors;
+	sector_t extended_offset;       /* offset of link pointers */
+	sector_t total_number_of_sectors;
+
+	jmp_buf listingbuf;
+	char line_buffer[80];
+	char partname_buffer[80];
+	/* Raw disk label. For DOS-type partition tables the MBR,
+	 * with descriptions of the primary partitions. */
+	char MBRbuffer[MAX_SECTOR_SIZE];
+	/* Partition tables */
+	struct pte ptes[MAXIMUM_PARTS];
+};
+#define G (*ptr_to_globals)
+#define line_ptr             (G.line_ptr            )
+#define disk_device          (G.disk_device         )
+#define g_partitions         (G.g_partitions        )
+#define units_per_sector     (G.units_per_sector    )
+#define sector_size          (G.sector_size         )
+#define user_set_sector_size (G.user_set_sector_size)
+#define sector_offset        (G.sector_offset       )
+#define g_heads              (G.g_heads             )
+#define g_sectors            (G.g_sectors           )
+#define g_cylinders          (G.g_cylinders         )
+#define current_label_type   (G.current_label_type  )
+#define display_in_cyl_units (G.display_in_cyl_units)
+#define possibly_osf_label   (G.possibly_osf_label  )
+#define listing                 (G.listing                )
+#define dos_compatible_flag     (G.dos_compatible_flag    )
+#define nowarn                  (G.nowarn                 )
+#define ext_index               (G.ext_index              )
+#define user_cylinders          (G.user_cylinders         )
+#define user_heads              (G.user_heads             )
+#define user_sectors            (G.user_sectors           )
+#define pt_heads                (G.pt_heads               )
+#define pt_sectors              (G.pt_sectors             )
+#define kern_heads              (G.kern_heads             )
+#define kern_sectors            (G.kern_sectors           )
+#define extended_offset         (G.extended_offset        )
+#define total_number_of_sectors (G.total_number_of_sectors)
+#define listingbuf      (G.listingbuf     )
+#define line_buffer     (G.line_buffer    )
+#define partname_buffer (G.partname_buffer)
+#define MBRbuffer       (G.MBRbuffer      )
+#define ptes            (G.ptes           )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+	sector_size = DEFAULT_SECTOR_SIZE; \
+	sector_offset = 1; \
+	g_partitions = 4; \
+	display_in_cyl_units = 1; \
+	units_per_sector = 1; \
+	dos_compatible_flag = 1; \
+} while (0)
+
+
+/* TODO: move to libbb? */
+/* TODO: return unsigned long long, FEATURE_FDISK_BLKSIZE _can_ handle
+ * disks > 2^32 sectors
+ */
+static sector_t bb_BLKGETSIZE_sectors(int fd)
+{
+	uint64_t v64;
+	unsigned long longsectors;
+
+	if (ioctl(fd, BLKGETSIZE64, &v64) == 0) {
+		/* Got bytes, convert to 512 byte sectors */
+		v64 >>= 9;
+		if (v64 != (sector_t)v64) {
+ ret_trunc:
+			/* Not only DOS, but all other partition tables
+			 * we support can't record more than 32 bit
+			 * sector counts or offsets
+			 */
+			bb_error_msg("device has more than 2^32 sectors, can't use all of them");
+			v64 = (uint32_t)-1L;
+		}
+		return v64;
+	}
+	/* Needs temp of type long */
+	if (ioctl(fd, BLKGETSIZE, &longsectors)) {
+		/* Perhaps this is a disk image */
+		off_t sz = lseek(fd, 0, SEEK_END);
+		longsectors = 0;
+		if (sz > 0)
+			longsectors = (uoff_t)sz / sector_size;
+		lseek(fd, 0, SEEK_SET);
+	}
+	if (sizeof(long) > sizeof(sector_t)
+	 && longsectors != (sector_t)longsectors
+	) {
+		goto ret_trunc;
+	}
+	return longsectors;
+}
+
+
+#define IS_EXTENDED(i) \
+	((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED)
+
+#define cround(n)       (display_in_cyl_units ? ((n)/units_per_sector)+1 : (n))
+
+#define scround(x)      (((x)+units_per_sector-1)/units_per_sector)
+
+#define pt_offset(b, n) \
+	((struct partition *)((b) + 0x1be + (n) * sizeof(struct partition)))
+
+#define sector(s)       ((s) & 0x3f)
+
+#define cylinder(s, c)  ((c) | (((s) & 0xc0) << 2))
+
+#define hsc2sector(h,s,c) \
+	(sector(s) - 1 + sectors * ((h) + heads * cylinder(s,c)))
+
+static void
+close_dev_fd(void)
+{
+	/* Not really closing, but making sure it is open, and to harmless place */
+	xmove_fd(xopen(bb_dev_null, O_RDONLY), dev_fd);
+}
+
+/*
+ * Return partition name - uses static storage
+ */
+static const char *
+partname(const char *dev, int pno, int lth)
+{
+	const char *p;
+	int w, wp;
+	int bufsiz;
+	char *bufp;
+
+	bufp = partname_buffer;
+	bufsiz = sizeof(partname_buffer);
+
+	w = strlen(dev);
+	p = "";
+
+	if (isdigit(dev[w-1]))
+		p = "p";
+
+	/* 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";
+	}
+
+	wp = strlen(p);
+
+	if (lth) {
+		snprintf(bufp, bufsiz, "%*.*s%s%-2u",
+			lth-wp-2, w, dev, p, pno);
+	} else {
+		snprintf(bufp, bufsiz, "%.*s%s%-2u", w, dev, p, pno);
+	}
+	return bufp;
+}
+
+static ALWAYS_INLINE struct partition *
+get_part_table(int i)
+{
+	return ptes[i].part_table;
+}
+
+static const char *
+str_units(int n)
+{      /* n==1: use singular */
+	if (n == 1)
+		return display_in_cyl_units ? "cylinder" : "sector";
+	return display_in_cyl_units ? "cylinders" : "sectors";
+}
+
+static int
+valid_part_table_flag(const char *mbuffer)
+{
+	return (mbuffer[510] == 0x55 && (uint8_t)mbuffer[511] == 0xaa);
+}
+
+static void fdisk_fatal(const char *why)
+{
+	if (listing) {
+		close_dev_fd();
+		longjmp(listingbuf, 1);
+	}
+	bb_error_msg_and_die(why, disk_device);
+}
+
+static void
+seek_sector(sector_t secno)
+{
+#if ENABLE_FDISK_SUPPORT_LARGE_DISKS
+	off64_t off = (off64_t)secno * sector_size;
+	if (lseek64(dev_fd, off, SEEK_SET) == (off64_t) -1)
+		fdisk_fatal(unable_to_seek);
+#else
+	uint64_t off = (uint64_t)secno * sector_size;
+	if (off > MAXINT(off_t)
+	 || lseek(dev_fd, (off_t)off, SEEK_SET) == (off_t) -1
+	) {
+		fdisk_fatal(unable_to_seek);
+	}
+#endif
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+/* Read line; return 0 or first printable char */
+static int
+read_line(const char *prompt)
+{
+	int sz;
+
+	sz = read_line_input(NULL, prompt, line_buffer, sizeof(line_buffer), /*timeout*/ -1);
+	if (sz <= 0)
+		exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */
+
+	if (line_buffer[sz-1] == '\n')
+		line_buffer[--sz] = '\0';
+
+	line_ptr = line_buffer;
+	while (*line_ptr != '\0' && (unsigned char)*line_ptr <= ' ')
+		line_ptr++;
+	return *line_ptr;
+}
+
+static void
+set_all_unchanged(void)
+{
+	int i;
+
+	for (i = 0; i < MAXIMUM_PARTS; i++)
+		ptes[i].changed = 0;
+}
+
+static ALWAYS_INLINE void
+set_changed(int i)
+{
+	ptes[i].changed = 1;
+}
+
+static ALWAYS_INLINE void
+write_part_table_flag(char *b)
+{
+	b[510] = 0x55;
+	b[511] = 0xaa;
+}
+
+static char
+read_nonempty(const char *mesg)
+{
+	while (!read_line(mesg))
+		continue;
+	return *line_ptr;
+}
+
+static char
+read_maybe_empty(const char *mesg)
+{
+	if (!read_line(mesg)) {
+		line_ptr = line_buffer;
+		line_ptr[0] = '\n';
+		line_ptr[1] = '\0';
+	}
+	return line_ptr[0];
+}
+
+static int
+read_hex(const char *const *sys)
+{
+	unsigned long v;
+	while (1) {
+		read_nonempty("Hex code (type L to list codes): ");
+		if ((line_ptr[0] | 0x20) == 'l') {
+			list_types(sys);
+			continue;
+		}
+		v = bb_strtoul(line_ptr, NULL, 16);
+		if (v <= 0xff)
+			return v;
+	}
+}
+
+static void
+write_sector(sector_t secno, const void *buf)
+{
+	seek_sector(secno);
+	xwrite(dev_fd, buf, sector_size);
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+
+#include "fdisk_aix.c"
+
+struct sun_partition {
+	unsigned char info[128];   /* Informative text string */
+	unsigned char spare0[14];
+	struct sun_info {
+		unsigned char spare1;
+		unsigned char id;
+		unsigned char spare2;
+		unsigned char flags;
+	} infos[8];
+	unsigned char spare1[246]; /* Boot information etc. */
+	unsigned short rspeed;     /* Disk rotational speed */
+	unsigned short pcylcount;  /* Physical cylinder count */
+	unsigned short sparecyl;   /* extra sects per cylinder */
+	unsigned char spare2[4];   /* More magic... */
+	unsigned short ilfact;     /* Interleave factor */
+	unsigned short ncyl;       /* Data cylinder count */
+	unsigned short nacyl;      /* Alt. cylinder count */
+	unsigned short ntrks;      /* Tracks per cylinder */
+	unsigned short nsect;      /* Sectors per track */
+	unsigned char spare3[4];   /* Even more magic... */
+	struct sun_partinfo {
+		uint32_t start_cylinder;
+		uint32_t num_sectors;
+	} partitions[8];
+	unsigned short magic;      /* Magic number */
+	unsigned short csum;       /* Label xor'd checksum */
+} FIX_ALIASING;
+typedef struct sun_partition sun_partition;
+#define sunlabel ((sun_partition *)MBRbuffer)
+STATIC_OSF void bsd_select(void);
+STATIC_OSF void xbsd_print_disklabel(int);
+#include "fdisk_osf.c"
+
+STATIC_GPT void gpt_list_table(int xtra);
+#include "fdisk_gpt.c"
+
+#if ENABLE_FEATURE_SGI_LABEL || ENABLE_FEATURE_SUN_LABEL
+static uint16_t
+fdisk_swap16(uint16_t x)
+{
+	return (x << 8) | (x >> 8);
+}
+
+static uint32_t
+fdisk_swap32(uint32_t x)
+{
+	return (x << 24) |
+	       ((x & 0xFF00) << 8) |
+	       ((x & 0xFF0000) >> 8) |
+	       (x >> 24);
+}
+#endif
+
+STATIC_SGI const char *const sgi_sys_types[];
+STATIC_SGI unsigned sgi_get_num_sectors(int i);
+STATIC_SGI int sgi_get_sysid(int i);
+STATIC_SGI void sgi_delete_partition(int i);
+STATIC_SGI void sgi_change_sysid(int i, int sys);
+STATIC_SGI void sgi_list_table(int xtra);
+#if ENABLE_FEATURE_FDISK_ADVANCED
+STATIC_SGI void sgi_set_xcyl(void);
+#endif
+STATIC_SGI int verify_sgi(int verbose);
+STATIC_SGI void sgi_add_partition(int n, int sys);
+STATIC_SGI void sgi_set_swappartition(int i);
+STATIC_SGI const char *sgi_get_bootfile(void);
+STATIC_SGI void sgi_set_bootfile(const char* aFile);
+STATIC_SGI void create_sgiinfo(void);
+STATIC_SGI void sgi_write_table(void);
+STATIC_SGI void sgi_set_bootpartition(int i);
+#include "fdisk_sgi.c"
+
+STATIC_SUN const char *const sun_sys_types[];
+STATIC_SUN void sun_delete_partition(int i);
+STATIC_SUN void sun_change_sysid(int i, int sys);
+STATIC_SUN void sun_list_table(int xtra);
+STATIC_SUN void add_sun_partition(int n, int sys);
+#if ENABLE_FEATURE_FDISK_ADVANCED
+STATIC_SUN void sun_set_alt_cyl(void);
+STATIC_SUN void sun_set_ncyl(int cyl);
+STATIC_SUN void sun_set_xcyl(void);
+STATIC_SUN void sun_set_ilfact(void);
+STATIC_SUN void sun_set_rspeed(void);
+STATIC_SUN void sun_set_pcylcount(void);
+#endif
+STATIC_SUN void toggle_sunflags(int i, unsigned char mask);
+STATIC_SUN void verify_sun(void);
+STATIC_SUN void sun_write_table(void);
+#include "fdisk_sun.c"
+
+
+static inline_if_little_endian unsigned
+read4_little_endian(const unsigned char *cp)
+{
+	uint32_t v;
+	move_from_unaligned32(v, cp);
+	return SWAP_LE32(v);
+}
+
+static sector_t
+get_start_sect(const struct partition *p)
+{
+	return read4_little_endian(p->start4);
+}
+
+static sector_t
+get_nr_sects(const struct partition *p)
+{
+	return read4_little_endian(p->size4);
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+/* start_sect and nr_sects are stored little endian on all machines */
+/* moreover, they are not aligned correctly */
+static inline_if_little_endian void
+store4_little_endian(unsigned char *cp, unsigned val)
+{
+	uint32_t v = SWAP_LE32(val);
+	move_to_unaligned32(cp, v);
+}
+
+static void
+set_start_sect(struct partition *p, unsigned start_sect)
+{
+	store4_little_endian(p->start4, start_sect);
+}
+
+static void
+set_nr_sects(struct partition *p, unsigned nr_sects)
+{
+	store4_little_endian(p->size4, nr_sects);
+}
+#endif
+
+/* Allocate a buffer and read a partition table sector */
+static void
+read_pte(struct pte *pe, sector_t offset)
+{
+	pe->offset_from_dev_start = offset;
+	pe->sectorbuffer = xzalloc(sector_size);
+	seek_sector(offset);
+	/* xread would make us abort - bad for fdisk -l */
+	if (full_read(dev_fd, pe->sectorbuffer, sector_size) != sector_size)
+		fdisk_fatal(unable_to_read);
+#if ENABLE_FEATURE_FDISK_WRITABLE
+	pe->changed = 0;
+#endif
+	pe->part_table = pe->ext_pointer = NULL;
+}
+
+static sector_t
+get_partition_start_from_dev_start(const struct pte *pe)
+{
+	return pe->offset_from_dev_start + get_start_sect(pe->part_table);
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+/*
+ * Avoid warning about DOS partitions when no DOS partition was changed.
+ * Here a heuristic "is probably dos partition".
+ * We might also do the opposite and warn in all cases except
+ * for "is probably nondos partition".
+ */
+#ifdef UNUSED
+static int
+is_dos_partition(int t)
+{
+	return (t == 1 || t == 4 || t == 6 ||
+		t == 0x0b || t == 0x0c || t == 0x0e ||
+		t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 ||
+		t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 ||
+		t == 0xc1 || t == 0xc4 || t == 0xc6);
+}
+#endif
+
+static void
+menu(void)
+{
+	puts("Command Action");
+	if (LABEL_IS_SUN) {
+		puts("a\ttoggle a read only flag");           /* sun */
+		puts("b\tedit bsd disklabel");
+		puts("c\ttoggle the mountable flag");         /* sun */
+		puts("d\tdelete a partition");
+		puts("l\tlist known partition types");
+		puts("n\tadd a new partition");
+		puts("o\tcreate a new empty DOS partition table");
+		puts("p\tprint the partition table");
+		puts("q\tquit without saving changes");
+		puts("s\tcreate a new empty Sun disklabel");  /* sun */
+		puts("t\tchange a partition's system id");
+		puts("u\tchange display/entry units");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+#if ENABLE_FEATURE_FDISK_ADVANCED
+		puts("x\textra functionality (experts only)");
+#endif
+	} else if (LABEL_IS_SGI) {
+		puts("a\tselect bootable partition");    /* sgi flavour */
+		puts("b\tedit bootfile entry");          /* sgi */
+		puts("c\tselect sgi swap partition");    /* sgi flavour */
+		puts("d\tdelete a partition");
+		puts("l\tlist known partition types");
+		puts("n\tadd a new partition");
+		puts("o\tcreate a new empty DOS partition table");
+		puts("p\tprint the partition table");
+		puts("q\tquit without saving changes");
+		puts("s\tcreate a new empty Sun disklabel");  /* sun */
+		puts("t\tchange a partition's system id");
+		puts("u\tchange display/entry units");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+	} else if (LABEL_IS_AIX) {
+		puts("o\tcreate a new empty DOS partition table");
+		puts("q\tquit without saving changes");
+		puts("s\tcreate a new empty Sun disklabel");  /* sun */
+	} else if (LABEL_IS_GPT) {
+		puts("o\tcreate a new empty DOS partition table");
+		puts("p\tprint the partition table");
+		puts("q\tquit without saving changes");
+		puts("s\tcreate a new empty Sun disklabel");  /* sun */
+	} else {
+		puts("a\ttoggle a bootable flag");
+		puts("b\tedit bsd disklabel");
+		puts("c\ttoggle the dos compatibility flag");
+		puts("d\tdelete a partition");
+		puts("l\tlist known partition types");
+		puts("n\tadd a new partition");
+		puts("o\tcreate a new empty DOS partition table");
+		puts("p\tprint the partition table");
+		puts("q\tquit without saving changes");
+		puts("s\tcreate a new empty Sun disklabel");  /* sun */
+		puts("t\tchange a partition's system id");
+		puts("u\tchange display/entry units");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+#if ENABLE_FEATURE_FDISK_ADVANCED
+		puts("x\textra functionality (experts only)");
+#endif
+	}
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+static void
+xmenu(void)
+{
+	puts("Command Action");
+	if (LABEL_IS_SUN) {
+		puts("a\tchange number of alternate cylinders");      /*sun*/
+		puts("c\tchange number of cylinders");
+		puts("d\tprint the raw data in the partition table");
+		puts("e\tchange number of extra sectors per cylinder");/*sun*/
+		puts("h\tchange number of heads");
+		puts("i\tchange interleave factor");                  /*sun*/
+		puts("o\tchange rotation speed (rpm)");               /*sun*/
+		puts("p\tprint the partition table");
+		puts("q\tquit without saving changes");
+		puts("r\treturn to main menu");
+		puts("s\tchange number of sectors/track");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+		puts("y\tchange number of physical cylinders");       /*sun*/
+	} else if (LABEL_IS_SGI) {
+		puts("b\tmove beginning of data in a partition"); /* !sun */
+		puts("c\tchange number of cylinders");
+		puts("d\tprint the raw data in the partition table");
+		puts("e\tlist extended partitions");          /* !sun */
+		puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
+		puts("h\tchange number of heads");
+		puts("p\tprint the partition table");
+		puts("q\tquit without saving changes");
+		puts("r\treturn to main menu");
+		puts("s\tchange number of sectors/track");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+	} else if (LABEL_IS_AIX) {
+		puts("b\tmove beginning of data in a partition"); /* !sun */
+		puts("c\tchange number of cylinders");
+		puts("d\tprint the raw data in the partition table");
+		puts("e\tlist extended partitions");          /* !sun */
+		puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
+		puts("h\tchange number of heads");
+		puts("p\tprint the partition table");
+		puts("q\tquit without saving changes");
+		puts("r\treturn to main menu");
+		puts("s\tchange number of sectors/track");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+	} else {
+		puts("b\tmove beginning of data in a partition"); /* !sun */
+		puts("c\tchange number of cylinders");
+		puts("d\tprint the raw data in the partition table");
+		puts("e\tlist extended partitions");          /* !sun */
+		puts("f\tfix partition order");               /* !sun, !aix, !sgi */
+#if ENABLE_FEATURE_SGI_LABEL
+		puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
+#endif
+		puts("h\tchange number of heads");
+		puts("p\tprint the partition table");
+		puts("q\tquit without saving changes");
+		puts("r\treturn to main menu");
+		puts("s\tchange number of sectors/track");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+	}
+}
+#endif /* ADVANCED mode */
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static const char *const *
+get_sys_types(void)
+{
+	return (
+		LABEL_IS_SUN ? sun_sys_types :
+		LABEL_IS_SGI ? sgi_sys_types :
+		i386_sys_types);
+}
+#else
+#define get_sys_types() i386_sys_types
+#endif
+
+static const char *
+partition_type(unsigned char type)
+{
+	int i;
+	const char *const *types = get_sys_types();
+
+	for (i = 0; types[i]; i++)
+		if ((unsigned char)types[i][0] == type)
+			return types[i] + 1;
+
+	return "Unknown";
+}
+
+static int
+is_cleared_partition(const struct partition *p)
+{
+	/* We consider partition "cleared" only if it has only zeros */
+	const char *cp = (const char *)p;
+	int cnt = sizeof(*p);
+	char bits = 0;
+	while (--cnt >= 0)
+		bits |= *cp++;
+	return (bits == 0);
+}
+
+static void
+clear_partition(struct partition *p)
+{
+	if (p)
+		memset(p, 0, sizeof(*p));
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static int
+get_sysid(int i)
+{
+	return LABEL_IS_SUN ? sunlabel->infos[i].id :
+			(LABEL_IS_SGI ? sgi_get_sysid(i) :
+				ptes[i].part_table->sys_ind);
+}
+
+static void
+list_types(const char *const *sys)
+{
+	enum { COLS = 3 };
+
+	unsigned last[COLS];
+	unsigned done, next, size;
+	int i;
+
+	for (size = 0; sys[size]; size++)
+		continue;
+
+	done = 0;
+	for (i = COLS-1; i >= 0; i--) {
+		done += (size + i - done) / (i + 1);
+		last[COLS-1 - i] = done;
+	}
+
+	i = done = next = 0;
+	do {
+		printf("%c%2x %-22.22s", i ? ' ' : '\n',
+			(unsigned char)sys[next][0],
+			sys[next] + 1);
+		next = last[i++] + done;
+		if (i >= COLS || next >= last[i]) {
+			i = 0;
+			next = ++done;
+		}
+	} while (done < last[0]);
+	bb_putchar('\n');
+}
+
+#define set_hsc(h, s, c, sector) do \
+{ \
+	s = sector % g_sectors + 1;  \
+	sector /= g_sectors;         \
+	h = sector % g_heads;        \
+	sector /= g_heads;           \
+	c = sector & 0xff;           \
+	s |= (sector >> 2) & 0xc0;   \
+} while (0)
+
+static void set_hsc_start_end(struct partition *p, sector_t start, sector_t stop)
+{
+	if (dos_compatible_flag && (start / (g_sectors * g_heads) > 1023))
+		start = g_heads * g_sectors * 1024 - 1;
+	set_hsc(p->head, p->sector, p->cyl, start);
+
+	if (dos_compatible_flag && (stop / (g_sectors * g_heads) > 1023))
+		stop = g_heads * g_sectors * 1024 - 1;
+	set_hsc(p->end_head, p->end_sector, p->end_cyl, stop);
+}
+
+static void
+set_partition(int i, int doext, sector_t start, sector_t stop, int sysid)
+{
+	struct partition *p;
+	sector_t offset;
+
+	if (doext) {
+		p = ptes[i].ext_pointer;
+		offset = extended_offset;
+	} else {
+		p = ptes[i].part_table;
+		offset = ptes[i].offset_from_dev_start;
+	}
+	p->boot_ind = 0;
+	p->sys_ind = sysid;
+	set_start_sect(p, start - offset);
+	set_nr_sects(p, stop - start + 1);
+	set_hsc_start_end(p, start, stop);
+	ptes[i].changed = 1;
+}
+#endif
+
+static int
+warn_geometry(void)
+{
+	if (g_heads && g_sectors && g_cylinders)
+		return 0;
+
+	printf("Unknown value(s) for:");
+	if (!g_heads)
+		printf(" heads");
+	if (!g_sectors)
+		printf(" sectors");
+	if (!g_cylinders)
+		printf(" cylinders");
+	printf(
+#if ENABLE_FEATURE_FDISK_WRITABLE
+		" (settable in the extra functions menu)"
+#endif
+		"\n");
+	return 1;
+}
+
+static void
+update_units(void)
+{
+	int cyl_units = g_heads * g_sectors;
+
+	if (display_in_cyl_units && cyl_units)
+		units_per_sector = cyl_units;
+	else
+		units_per_sector = 1;   /* in sectors */
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+warn_cylinders(void)
+{
+	if (LABEL_IS_DOS && g_cylinders > 1024 && !nowarn)
+		printf("\n"
+"The number of cylinders for this disk is set to %u.\n"
+"There is nothing wrong with that, but this is larger than 1024,\n"
+"and could in certain setups cause problems with:\n"
+"1) software that runs at boot time (e.g., old versions of LILO)\n"
+"2) booting and partitioning software from other OSs\n"
+"   (e.g., DOS FDISK, OS/2 FDISK)\n",
+			g_cylinders);
+}
+#endif
+
+static void
+read_extended(int ext)
+{
+	int i;
+	struct pte *pex;
+	struct partition *p, *q;
+
+	ext_index = ext;
+	pex = &ptes[ext];
+	pex->ext_pointer = pex->part_table;
+
+	p = pex->part_table;
+	if (!get_start_sect(p)) {
+		printf("Bad offset in primary extended partition\n");
+		return;
+	}
+
+	while (IS_EXTENDED(p->sys_ind)) {
+		struct pte *pe = &ptes[g_partitions];
+
+		if (g_partitions >= 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 = &ptes[g_partitions - 1];
+#if ENABLE_FEATURE_FDISK_WRITABLE
+			printf("Warning: deleting partitions after %u\n",
+				g_partitions);
+			pre->changed = 1;
+#endif
+			clear_partition(pre->ext_pointer);
+			return;
+		}
+
+		read_pte(pe, extended_offset + get_start_sect(p));
+
+		if (!extended_offset)
+			extended_offset = get_start_sect(p);
+
+		q = p = pt_offset(pe->sectorbuffer, 0);
+		for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) {
+			if (IS_EXTENDED(p->sys_ind)) {
+				if (pe->ext_pointer)
+					printf("Warning: extra link "
+						"pointer in partition table"
+						" %u\n", g_partitions + 1);
+				else
+					pe->ext_pointer = p;
+			} else if (p->sys_ind) {
+				if (pe->part_table)
+					printf("Warning: ignoring extra "
+						  "data in partition table"
+						  " %u\n", g_partitions + 1);
+				else
+					pe->part_table = p;
+			}
+		}
+
+		/* very strange code here... */
+		if (!pe->part_table) {
+			if (q != pe->ext_pointer)
+				pe->part_table = q;
+			else
+				pe->part_table = q + 1;
+		}
+		if (!pe->ext_pointer) {
+			if (q != pe->part_table)
+				pe->ext_pointer = q;
+			else
+				pe->ext_pointer = q + 1;
+		}
+
+		p = pe->ext_pointer;
+		g_partitions++;
+	}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+	/* remove empty links */
+ remove:
+	for (i = 4; i < g_partitions; i++) {
+		struct pte *pe = &ptes[i];
+
+		if (!get_nr_sects(pe->part_table)
+		 && (g_partitions > 5 || ptes[4].part_table->sys_ind)
+		) {
+			printf("Omitting empty partition (%u)\n", i+1);
+			delete_partition(i);
+			goto remove;    /* numbering changed */
+		}
+	}
+#endif
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+create_doslabel(void)
+{
+	printf(msg_building_new_label, "DOS disklabel");
+
+	current_label_type = LABEL_DOS;
+#if ENABLE_FEATURE_OSF_LABEL
+	possibly_osf_label = 0;
+#endif
+	g_partitions = 4;
+
+	memset(&MBRbuffer[510 - 4*16], 0, 4*16);
+	write_part_table_flag(MBRbuffer);
+	extended_offset = 0;
+	set_all_unchanged();
+	set_changed(0);
+	get_boot(CREATE_EMPTY_DOS);
+}
+#endif
+
+static void
+get_sectorsize(void)
+{
+	if (!user_set_sector_size) {
+		int arg;
+		if (ioctl(dev_fd, BLKSSZGET, &arg) == 0)
+			sector_size = arg;
+		if (sector_size != DEFAULT_SECTOR_SIZE)
+			printf("Note: sector size is %u "
+				"(not " DEFAULT_SECTOR_SIZE_STR ")\n",
+				sector_size);
+	}
+}
+
+static void
+get_kernel_geometry(void)
+{
+	struct hd_geometry geometry;
+
+	if (!ioctl(dev_fd, HDIO_GETGEO, &geometry)) {
+		kern_heads = geometry.heads;
+		kern_sectors = geometry.sectors;
+		/* never use geometry.cylinders - it is truncated */
+	}
+}
+
+static void
+get_partition_table_geometry(void)
+{
+	const unsigned char *bufp = (const unsigned char *)MBRbuffer;
+	struct partition *p;
+	int i, h, s, hh, ss;
+	int first = 1;
+	int bad = 0;
+
+	if (!(valid_part_table_flag((char*)bufp)))
+		return;
+
+	hh = ss = 0;
+	for (i = 0; i < 4; i++) {
+		p = pt_offset(bufp, i);
+		if (p->sys_ind != 0) {
+			h = p->end_head + 1;
+			s = (p->end_sector & 077);
+			if (first) {
+				hh = h;
+				ss = s;
+				first = 0;
+			} else if (hh != h || ss != s)
+				bad = 1;
+		}
+	}
+
+	if (!first && !bad) {
+		pt_heads = hh;
+		pt_sectors = ss;
+	}
+}
+
+static void
+get_geometry(void)
+{
+	int sec_fac;
+
+	get_sectorsize();
+	sec_fac = sector_size / 512;
+#if ENABLE_FEATURE_SUN_LABEL
+	guess_device_type();
+#endif
+	g_heads = g_cylinders = g_sectors = 0;
+	kern_heads = kern_sectors = 0;
+	pt_heads = pt_sectors = 0;
+
+	get_kernel_geometry();
+	get_partition_table_geometry();
+
+	g_heads = user_heads ? user_heads :
+		pt_heads ? pt_heads :
+		kern_heads ? kern_heads : 255;
+	g_sectors = user_sectors ? user_sectors :
+		pt_sectors ? pt_sectors :
+		kern_sectors ? kern_sectors : 63;
+	total_number_of_sectors = bb_BLKGETSIZE_sectors(dev_fd);
+
+	sector_offset = 1;
+	if (dos_compatible_flag)
+		sector_offset = g_sectors;
+
+	g_cylinders = total_number_of_sectors / (g_heads * g_sectors * sec_fac);
+	if (!g_cylinders)
+		g_cylinders = user_cylinders;
+}
+
+/*
+ * Opens disk_device and optionally reads MBR.
+ *    If what == OPEN_MAIN:
+ *      Open device, read MBR.  Abort program on short read.  Create empty
+ *      disklabel if the on-disk structure is invalid (WRITABLE mode).
+ *    If what == TRY_ONLY:
+ *      Open device, read MBR.  Return an error if anything is out of place.
+ *      Do not create an empty disklabel.  This is used for the "list"
+ *      operations: "fdisk -l /dev/sda" and "fdisk -l" (all devices).
+ *    If what == CREATE_EMPTY_*:
+ *      This means that get_boot() was called recursively from create_*label().
+ *      Do not re-open the device; just set up the ptes array and print
+ *      geometry warnings.
+ *
+ * Returns:
+ *   -1: no 0xaa55 flag present (possibly entire disk BSD)
+ *    0: found or created label
+ *    1: I/O error
+ */
+#if ENABLE_FEATURE_SUN_LABEL || ENABLE_FEATURE_FDISK_WRITABLE
+static int get_boot(enum action what)
+#else
+static int get_boot(void)
+#define get_boot(what) get_boot()
+#endif
+{
+	int i, fd;
+
+	g_partitions = 4;
+	for (i = 0; i < 4; i++) {
+		struct pte *pe = &ptes[i];
+		pe->part_table = pt_offset(MBRbuffer, i);
+		pe->ext_pointer = NULL;
+		pe->offset_from_dev_start = 0;
+		pe->sectorbuffer = MBRbuffer;
+#if ENABLE_FEATURE_FDISK_WRITABLE
+		pe->changed = (what == CREATE_EMPTY_DOS);
+#endif
+	}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+// ALERT! highly idiotic design!
+// We end up here when we call get_boot() recursively
+// via get_boot() [table is bad] -> create_doslabel() -> get_boot(CREATE_EMPTY_DOS).
+// or get_boot() [table is bad] -> create_sunlabel() -> get_boot(CREATE_EMPTY_SUN).
+// (just factor out re-init of ptes[0,1,2,3] in a separate fn instead?)
+// So skip opening device _again_...
+	if (what == CREATE_EMPTY_DOS  IF_FEATURE_SUN_LABEL(|| what == CREATE_EMPTY_SUN))
+		goto created_table;
+
+	fd = open(disk_device, (option_mask32 & OPT_l) ? O_RDONLY : O_RDWR);
+
+	if (fd < 0) {
+		fd = open(disk_device, O_RDONLY);
+		if (fd < 0) {
+			if (what == TRY_ONLY)
+				return 1;
+			fdisk_fatal(unable_to_open);
+		}
+		printf("'%s' is opened for read only\n", disk_device);
+	}
+	xmove_fd(fd, dev_fd);
+	if (512 != full_read(dev_fd, MBRbuffer, 512)) {
+		if (what == TRY_ONLY) {
+			close_dev_fd();
+			return 1;
+		}
+		fdisk_fatal(unable_to_read);
+	}
+#else
+	fd = open(disk_device, O_RDONLY);
+	if (fd < 0)
+		return 1;
+	if (512 != full_read(fd, MBRbuffer, 512)) {
+		close(fd);
+		return 1;
+	}
+	xmove_fd(fd, dev_fd);
+#endif
+
+	get_geometry();
+	update_units();
+
+#if ENABLE_FEATURE_SUN_LABEL
+	if (check_sun_label())
+		return 0;
+#endif
+#if ENABLE_FEATURE_SGI_LABEL
+	if (check_sgi_label())
+		return 0;
+#endif
+#if ENABLE_FEATURE_AIX_LABEL
+	if (check_aix_label())
+		return 0;
+#endif
+#if ENABLE_FEATURE_GPT_LABEL
+	if (check_gpt_label())
+		return 0;
+#endif
+#if ENABLE_FEATURE_OSF_LABEL
+	if (check_osf_label()) {
+		possibly_osf_label = 1;
+		if (!valid_part_table_flag(MBRbuffer)) {
+			current_label_type = LABEL_OSF;
+			return 0;
+		}
+		printf("This disk has both DOS and BSD magic.\n"
+			 "Give the 'b' command to go to BSD mode.\n");
+	}
+#endif
+
+#if !ENABLE_FEATURE_FDISK_WRITABLE
+	if (!valid_part_table_flag(MBRbuffer))
+		return -1;
+#else
+	if (!valid_part_table_flag(MBRbuffer)) {
+		if (what == OPEN_MAIN) {
+			printf("Device contains neither a valid DOS "
+				  "partition table, nor Sun, SGI, OSF or GPT "
+				  "disklabel\n");
+#ifdef __sparc__
+			IF_FEATURE_SUN_LABEL(create_sunlabel();)
+#else
+			create_doslabel();
+#endif
+			return 0;
+		}
+		/* TRY_ONLY: */
+		return -1;
+	}
+ created_table:
+#endif /* FEATURE_FDISK_WRITABLE */
+
+
+	IF_FEATURE_FDISK_WRITABLE(warn_cylinders();)
+	warn_geometry();
+
+	for (i = 0; i < 4; i++) {
+		if (IS_EXTENDED(ptes[i].part_table->sys_ind)) {
+			if (g_partitions != 4)
+				printf("Ignoring extra extended "
+					"partition %u\n", i + 1);
+			else
+				read_extended(i);
+		}
+	}
+
+	for (i = 3; i < g_partitions; i++) {
+		struct pte *pe = &ptes[i];
+		if (!valid_part_table_flag(pe->sectorbuffer)) {
+			printf("Warning: invalid flag 0x%02x,0x%02x of partition "
+				"table %u will be corrected by w(rite)\n",
+				pe->sectorbuffer[510],
+				pe->sectorbuffer[511],
+				i + 1);
+			IF_FEATURE_FDISK_WRITABLE(pe->changed = 1;)
+		}
+	}
+
+	return 0;
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+/*
+ * Print the message MESG, then read an integer between LOW and HIGH (inclusive).
+ * If the user hits Enter, DFLT is returned.
+ * Answers like +10 are interpreted as offsets from BASE.
+ *
+ * There is no default if DFLT is not between LOW and HIGH.
+ */
+static sector_t
+read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char *mesg)
+{
+	sector_t value;
+	int default_ok = 1;
+	const char *fmt = "%s (%u-%u, default %u): ";
+
+	if (dflt < low || dflt > high) {
+		fmt = "%s (%u-%u): ";
+		default_ok = 0;
+	}
+
+	while (1) {
+		int use_default = default_ok;
+
+		/* ask question and read answer */
+		do {
+			printf(fmt, mesg, low, high, dflt);
+			read_maybe_empty("");
+		} while (*line_ptr != '\n' && !isdigit(*line_ptr)
+		 && *line_ptr != '-' && *line_ptr != '+');
+
+		if (*line_ptr == '+' || *line_ptr == '-') {
+			int minus = (*line_ptr == '-');
+			int absolute = 0;
+
+			value = atoi(line_ptr + 1);
+
+			/* (1) if 2nd char is digit, use_default = 0.
+			 * (2) move line_ptr to first non-digit. */
+			while (isdigit(*++line_ptr))
+				use_default = 0;
+
+			switch (*line_ptr) {
+			case 'c':
+			case 'C':
+				if (!display_in_cyl_units)
+					value *= g_heads * g_sectors;
+				break;
+			case 'K':
+				absolute = 1024;
+				break;
+			case 'k':
+				absolute = 1000;
+				break;
+			case 'm':
+			case 'M':
+				absolute = 1000000;
+				break;
+			case 'g':
+			case 'G':
+				absolute = 1000000000;
+				break;
+			default:
+				break;
+			}
+			if (absolute) {
+				ullong bytes;
+				unsigned long unit;
+
+				bytes = (ullong) value * absolute;
+				unit = sector_size * units_per_sector;
+				bytes += unit/2; /* round */
+				bytes /= unit;
+				value = bytes;
+			}
+			if (minus)
+				value = -value;
+			value += base;
+		} else {
+			value = atoi(line_ptr);
+			while (isdigit(*line_ptr)) {
+				line_ptr++;
+				use_default = 0;
+			}
+		}
+		if (use_default) {
+			value = dflt;
+			printf("Using default value %u\n", value);
+		}
+		if (value >= low && value <= high)
+			break;
+		printf("Value is out of range\n");
+	}
+	return value;
+}
+
+static unsigned
+get_partition(int warn, unsigned max)
+{
+	struct pte *pe;
+	unsigned i;
+
+	i = read_int(1, 0, max, 0, "Partition number") - 1;
+	pe = &ptes[i];
+
+	if (warn) {
+		if ((!LABEL_IS_SUN && !LABEL_IS_SGI && !pe->part_table->sys_ind)
+		 || (LABEL_IS_SUN && (!sunlabel->partitions[i].num_sectors || !sunlabel->infos[i].id))
+		 || (LABEL_IS_SGI && !sgi_get_num_sectors(i))
+		) {
+			printf("Warning: partition %u has empty type\n", i+1);
+		}
+	}
+	return i;
+}
+
+static int
+get_existing_partition(int warn, unsigned max)
+{
+	int pno = -1;
+	unsigned i;
+
+	for (i = 0; i < max; i++) {
+		struct pte *pe = &ptes[i];
+		struct partition *p = pe->part_table;
+
+		if (p && !is_cleared_partition(p)) {
+			if (pno >= 0)
+				goto not_unique;
+			pno = i;
+		}
+	}
+	if (pno >= 0) {
+		printf("Selected partition %u\n", pno+1);
+		return pno;
+	}
+	printf("No partition is defined yet!\n");
+	return -1;
+
+ not_unique:
+	return get_partition(warn, max);
+}
+
+static int
+get_nonexisting_partition(int warn, unsigned max)
+{
+	int pno = -1;
+	unsigned i;
+
+	for (i = 0; i < max; i++) {
+		struct pte *pe = &ptes[i];
+		struct partition *p = pe->part_table;
+
+		if (p && is_cleared_partition(p)) {
+			if (pno >= 0)
+				goto not_unique;
+			pno = i;
+		}
+	}
+	if (pno >= 0) {
+		printf("Selected partition %u\n", pno+1);
+		return pno;
+	}
+	printf("All primary partitions have been defined already!\n");
+	return -1;
+
+ not_unique:
+	return get_partition(warn, max);
+}
+
+
+static void
+change_units(void)
+{
+	display_in_cyl_units = !display_in_cyl_units;
+	update_units();
+	printf("Changing display/entry units to %s\n",
+		str_units(PLURAL));
+}
+
+static void
+toggle_active(int i)
+{
+	struct pte *pe = &ptes[i];
+	struct partition *p = pe->part_table;
+
+	if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
+		printf("WARNING: Partition %u is an extended partition\n", i + 1);
+	p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
+	pe->changed = 1;
+}
+
+static void
+toggle_dos_compatibility_flag(void)
+{
+	dos_compatible_flag = 1 - dos_compatible_flag;
+	if (dos_compatible_flag) {
+		sector_offset = g_sectors;
+		printf("DOS Compatibility flag is set\n");
+	} else {
+		sector_offset = 1;
+		printf("DOS Compatibility flag is not set\n");
+	}
+}
+
+static void
+delete_partition(int i)
+{
+	struct pte *pe = &ptes[i];
+	struct partition *p = pe->part_table;
+	struct partition *q = pe->ext_pointer;
+
+/* Note that for the fifth partition (i == 4) we don't actually
+ * decrement partitions.
+ */
+
+	if (warn_geometry())
+		return;         /* C/H/S not set */
+	pe->changed = 1;
+
+	if (LABEL_IS_SUN) {
+		sun_delete_partition(i);
+		return;
+	}
+	if (LABEL_IS_SGI) {
+		sgi_delete_partition(i);
+		return;
+	}
+
+	if (i < 4) {
+		if (IS_EXTENDED(p->sys_ind) && i == ext_index) {
+			g_partitions = 4;
+			ptes[ext_index].ext_pointer = NULL;
+			extended_offset = 0;
+		}
+		clear_partition(p);
+		return;
+	}
+
+	if (!q->sys_ind && i > 4) {
+		/* the last one in the chain - just delete */
+		--g_partitions;
+		--i;
+		clear_partition(ptes[i].ext_pointer);
+		ptes[i].changed = 1;
+	} else {
+		/* not the last one - further ones will be moved down */
+		if (i > 4) {
+			/* delete this link in the chain */
+			p = ptes[i-1].ext_pointer;
+			*p = *q;
+			set_start_sect(p, get_start_sect(q));
+			set_nr_sects(p, get_nr_sects(q));
+			ptes[i-1].changed = 1;
+		} else if (g_partitions > 5) {    /* 5 will be moved to 4 */
+			/* the first logical in a longer chain */
+			pe = &ptes[5];
+
+			if (pe->part_table) /* prevent SEGFAULT */
+				set_start_sect(pe->part_table,
+						get_partition_start_from_dev_start(pe) -
+						extended_offset);
+			pe->offset_from_dev_start = extended_offset;
+			pe->changed = 1;
+		}
+
+		if (g_partitions > 5) {
+			g_partitions--;
+			while (i < g_partitions) {
+				ptes[i] = ptes[i+1];
+				i++;
+			}
+		} else {
+			/* the only logical: clear only */
+			clear_partition(ptes[i].part_table);
+		}
+	}
+}
+
+static void
+change_sysid(void)
+{
+	int i, sys, origsys;
+	struct partition *p;
+
+	/* If sgi_label then don't use get_existing_partition,
+	   let the user select a partition, since get_existing_partition()
+	   only works for Linux like partition tables. */
+	if (!LABEL_IS_SGI) {
+		i = get_existing_partition(0, g_partitions);
+	} else {
+		i = get_partition(0, g_partitions);
+	}
+	if (i == -1)
+		return;
+	p = ptes[i].part_table;
+	origsys = sys = get_sysid(i);
+
+	/* if changing types T to 0 is allowed, then
+	   the reverse change must be allowed, too */
+	if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN && !get_nr_sects(p))	{
+		printf("Partition %u does not exist yet!\n", i + 1);
+		return;
+	}
+	while (1) {
+		sys = read_hex(get_sys_types());
+
+		if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN) {
+			printf("Type 0 means free space to many systems\n"
+				   "(but not to Linux). Having partitions of\n"
+				   "type 0 is probably unwise.\n");
+			/* break; */
+		}
+
+		if (!LABEL_IS_SUN && !LABEL_IS_SGI) {
+			if (IS_EXTENDED(sys) != IS_EXTENDED(p->sys_ind)) {
+				printf("You cannot change a partition into"
+					   " an extended one or vice versa\n");
+				break;
+			}
+		}
+
+		if (sys < 256) {
+#if ENABLE_FEATURE_SUN_LABEL
+			if (LABEL_IS_SUN && i == 2 && sys != SUN_WHOLE_DISK)
+				printf("Consider leaving partition 3 "
+					   "as Whole disk (5),\n"
+					   "as SunOS/Solaris expects it and "
+					   "even Linux likes it\n\n");
+#endif
+#if ENABLE_FEATURE_SGI_LABEL
+			if (LABEL_IS_SGI &&
+				(
+					(i == 10 && sys != SGI_ENTIRE_DISK) ||
+					(i == 8 && sys != 0)
+				)
+			) {
+				printf("Consider leaving partition 9 "
+					   "as volume header (0),\nand "
+					   "partition 11 as entire volume (6)"
+					   "as IRIX expects it\n\n");
+			}
+#endif
+			if (sys == origsys)
+				break;
+			if (LABEL_IS_SUN) {
+				sun_change_sysid(i, sys);
+			} else if (LABEL_IS_SGI) {
+				sgi_change_sysid(i, sys);
+			} else
+				p->sys_ind = sys;
+
+			printf("Changed system type of partition %u "
+				"to %x (%s)\n", i + 1, sys,
+				partition_type(sys));
+			ptes[i].changed = 1;
+			//if (is_dos_partition(origsys) || is_dos_partition(sys))
+			//	dos_changed = 1;
+			break;
+		}
+	}
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+
+/* check_consistency() and linear2chs() 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
+linear2chs(unsigned ls, unsigned *c, unsigned *h, unsigned *s)
+{
+	int spc = g_heads * g_sectors;
+
+	*c = ls / spc;
+	ls = ls % spc;
+	*h = ls / g_sectors;
+	*s = ls % g_sectors + 1;  /* sectors count from 1 */
+}
+
+static void
+check_consistency(const struct partition *p, int partition)
+{
+	unsigned pbc, pbh, pbs;          /* physical beginning c, h, s */
+	unsigned pec, peh, pes;          /* physical ending c, h, s */
+	unsigned lbc, lbh, lbs;          /* logical beginning c, h, s */
+	unsigned lec, leh, les;          /* logical ending c, h, s */
+
+	if (!g_heads || !g_sectors || (partition >= 4))
+		return;         /* do not check extended partitions */
+
+/* physical beginning c, h, s */
+	pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300);
+	pbh = p->head;
+	pbs = p->sector & 0x3f;
+
+/* physical ending c, h, s */
+	pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300);
+	peh = p->end_head;
+	pes = p->end_sector & 0x3f;
+
+/* compute logical beginning (c, h, s) */
+	linear2chs(get_start_sect(p), &lbc, &lbh, &lbs);
+
+/* compute logical ending (c, h, s) */
+	linear2chs(get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les);
+
+/* Same physical / logical beginning? */
+	if (g_cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
+		printf("Partition %u has different physical/logical "
+			"beginnings (non-Linux?):\n", partition + 1);
+		printf("     phys=(%u, %u, %u) ", pbc, pbh, pbs);
+		printf("logical=(%u, %u, %u)\n", lbc, lbh, lbs);
+	}
+
+/* Same physical / logical ending? */
+	if (g_cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
+		printf("Partition %u has different physical/logical "
+			"endings:\n", partition + 1);
+		printf("     phys=(%u, %u, %u) ", pec, peh, pes);
+		printf("logical=(%u, %u, %u)\n", lec, leh, les);
+	}
+
+/* Ending on cylinder boundary? */
+	if (peh != (g_heads - 1) || pes != g_sectors) {
+		printf("Partition %u does not end on cylinder boundary\n",
+			partition + 1);
+	}
+}
+
+static void
+list_disk_geometry(void)
+{
+	ullong bytes = ((ullong)total_number_of_sectors << 9);
+	long megabytes = bytes / 1000000;
+
+	if (megabytes < 10000)
+		printf("\nDisk %s: %lu MB, %llu bytes\n",
+			disk_device, megabytes, bytes);
+	else
+		printf("\nDisk %s: %lu.%lu GB, %llu bytes\n",
+			disk_device, megabytes/1000, (megabytes/100)%10, bytes);
+	printf("%u heads, %u sectors/track, %u cylinders",
+		   g_heads, g_sectors, g_cylinders);
+	if (units_per_sector == 1)
+		printf(", total %"SECT_FMT"u sectors",
+			total_number_of_sectors / (sector_size/512));
+	printf("\nUnits = %s of %u * %u = %u bytes\n\n",
+		str_units(PLURAL),
+		units_per_sector, sector_size, units_per_sector * sector_size);
+}
+
+/*
+ * 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(int *prev)
+{
+	const struct pte *pe;
+	const struct partition *p;
+	sector_t last_p_start_pos = 0, p_start_pos;
+	unsigned i, last_i = 0;
+
+	for (i = 0; i < g_partitions; i++) {
+		if (i == 4) {
+			last_i = 4;
+			last_p_start_pos = 0;
+		}
+		pe = &ptes[i];
+		p = pe->part_table;
+		if (p->sys_ind) {
+			p_start_pos = get_partition_start_from_dev_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;
+}
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+/*
+ * Fix the chain of logicals.
+ * extended_offset is unchanged, the set of sectors used is unchanged
+ * The chain is sorted so that sectors increase, and so that
+ * starting sectors increase.
+ *
+ * After this it may still be that cfdisk doesnt like the table.
+ * (This is because cfdisk considers expanded parts, from link to
+ * end of partition, and these may still overlap.)
+ * Now
+ *   sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
+ * may help.
+ */
+static void
+fix_chain_of_logicals(void)
+{
+	int j, oj, ojj, sj, sjj;
+	struct partition *pj,*pjj,tmp;
+
+	/* Stage 1: sort sectors but leave sector of part 4 */
+	/* (Its sector is the global extended_offset.) */
+ stage1:
+	for (j = 5; j < g_partitions - 1; j++) {
+		oj = ptes[j].offset_from_dev_start;
+		ojj = ptes[j+1].offset_from_dev_start;
+		if (oj > ojj) {
+			ptes[j].offset_from_dev_start = ojj;
+			ptes[j+1].offset_from_dev_start = oj;
+			pj = ptes[j].part_table;
+			set_start_sect(pj, get_start_sect(pj)+oj-ojj);
+			pjj = ptes[j+1].part_table;
+			set_start_sect(pjj, get_start_sect(pjj)+ojj-oj);
+			set_start_sect(ptes[j-1].ext_pointer,
+					   ojj-extended_offset);
+			set_start_sect(ptes[j].ext_pointer,
+					   oj-extended_offset);
+			goto stage1;
+		}
+	}
+
+	/* Stage 2: sort starting sectors */
+ stage2:
+	for (j = 4; j < g_partitions - 1; j++) {
+		pj = ptes[j].part_table;
+		pjj = ptes[j+1].part_table;
+		sj = get_start_sect(pj);
+		sjj = get_start_sect(pjj);
+		oj = ptes[j].offset_from_dev_start;
+		ojj = ptes[j+1].offset_from_dev_start;
+		if (oj+sj > ojj+sjj) {
+			tmp = *pj;
+			*pj = *pjj;
+			*pjj = tmp;
+			set_start_sect(pj, ojj+sjj-oj);
+			set_start_sect(pjj, oj+sj-ojj);
+			goto stage2;
+		}
+	}
+
+	/* Probably something was changed */
+	for (j = 4; j < g_partitions; j++)
+		ptes[j].changed = 1;
+}
+
+
+static void
+fix_partition_table_order(void)
+{
+	struct pte *pei, *pek;
+	int i,k;
+
+	if (!wrong_p_order(NULL)) {
+		printf("Ordering is already correct\n\n");
+		return;
+	}
+
+	while ((i = wrong_p_order(&k)) != 0 && i < 4) {
+		/* partition i should have come earlier, move it */
+		/* We have to move data in the MBR */
+		struct partition *pi, *pk, *pe, pbuf;
+		pei = &ptes[i];
+		pek = &ptes[k];
+
+		pe = pei->ext_pointer;
+		pei->ext_pointer = pek->ext_pointer;
+		pek->ext_pointer = pe;
+
+		pi = pei->part_table;
+		pk = pek->part_table;
+
+		memmove(&pbuf, pi, sizeof(struct partition));
+		memmove(pi, pk, sizeof(struct partition));
+		memmove(pk, &pbuf, sizeof(struct partition));
+
+		pei->changed = pek->changed = 1;
+	}
+
+	if (i)
+		fix_chain_of_logicals();
+
+	printf("Done.\n");
+}
+#endif
+
+static void
+list_table(int xtra)
+{
+	const struct partition *p;
+	int i, w;
+
+	if (LABEL_IS_SUN) {
+		sun_list_table(xtra);
+		return;
+	}
+	if (LABEL_IS_SGI) {
+		sgi_list_table(xtra);
+		return;
+	}
+	if (LABEL_IS_GPT) {
+		gpt_list_table(xtra);
+		return;
+	}
+
+	list_disk_geometry();
+
+	if (LABEL_IS_OSF) {
+		xbsd_print_disklabel(xtra);
+		return;
+	}
+
+	/* Heuristic: we list partition 3 of /dev/foo as /dev/foo3,
+	   but if the device name ends in a digit, say /dev/foo1,
+	   then the partition is called /dev/foo1p3. */
+	w = strlen(disk_device);
+	if (w && isdigit(disk_device[w-1]))
+		w++;
+	if (w < 5)
+		w = 5;
+
+	//            1 12345678901 12345678901 12345678901  12
+	printf("%*s Boot      Start         End      Blocks  Id System\n",
+		   w+1, "Device");
+
+	for (i = 0; i < g_partitions; i++) {
+		const struct pte *pe = &ptes[i];
+		sector_t psects;
+		sector_t pblocks;
+		unsigned podd;
+
+		p = pe->part_table;
+		if (!p || is_cleared_partition(p))
+			continue;
+
+		psects = get_nr_sects(p);
+		pblocks = psects;
+		podd = 0;
+
+		if (sector_size < 1024) {
+			pblocks /= (1024 / sector_size);
+			podd = psects % (1024 / sector_size);
+		}
+		if (sector_size > 1024)
+			pblocks *= (sector_size / 1024);
+
+		printf("%s  %c %11"SECT_FMT"u %11"SECT_FMT"u %11"SECT_FMT"u%c %2x %s\n",
+			partname(disk_device, i+1, w+2),
+			!p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG /* boot flag */
+				? '*' : '?',
+			cround(get_partition_start_from_dev_start(pe)),           /* start */
+			cround(get_partition_start_from_dev_start(pe) + psects    /* end */
+				- (psects ? 1 : 0)),
+			pblocks, podd ? '+' : ' ', /* odd flag on end */
+			p->sys_ind,                                     /* type id */
+			partition_type(p->sys_ind));                    /* type name */
+
+		check_consistency(p, i);
+	}
+
+	/* Is partition table in disk order? It need not be, but... */
+	/* partition table entries are not checked for correct order
+	 * if this is a sgi, sun or aix labeled disk... */
+	if (LABEL_IS_DOS && wrong_p_order(NULL)) {
+		/* FIXME */
+		printf("\nPartition table entries are not in disk order\n");
+	}
+}
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+static void
+x_list_table(int extend)
+{
+	const struct pte *pe;
+	const struct partition *p;
+	int i;
+
+	printf("\nDisk %s: %u heads, %u sectors, %u cylinders\n\n",
+		disk_device, g_heads, g_sectors, g_cylinders);
+	printf("Nr AF  Hd Sec  Cyl  Hd Sec  Cyl      Start       Size ID\n");
+	for (i = 0; i < g_partitions; i++) {
+		pe = &ptes[i];
+		p = (extend ? pe->ext_pointer : pe->part_table);
+		if (p != NULL) {
+			printf("%2u %02x%4u%4u%5u%4u%4u%5u%11"SECT_FMT"u%11"SECT_FMT"u %02x\n",
+				i + 1, p->boot_ind, p->head,
+				sector(p->sector),
+				cylinder(p->sector, p->cyl), p->end_head,
+				sector(p->end_sector),
+				cylinder(p->end_sector, p->end_cyl),
+				get_start_sect(p), get_nr_sects(p),
+				p->sys_ind);
+			if (p->sys_ind)
+				check_consistency(p, i);
+		}
+	}
+}
+#endif
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+fill_bounds(sector_t *first, sector_t *last)
+{
+	unsigned i;
+	const struct pte *pe = &ptes[0];
+	const struct partition *p;
+
+	for (i = 0; i < g_partitions; pe++,i++) {
+		p = pe->part_table;
+		if (!p->sys_ind || IS_EXTENDED(p->sys_ind)) {
+			first[i] = 0xffffffff;
+			last[i] = 0;
+		} else {
+			first[i] = get_partition_start_from_dev_start(pe);
+			last[i] = first[i] + get_nr_sects(p) - 1;
+		}
+	}
+}
+
+static void
+check(int n, unsigned h, unsigned s, unsigned c, sector_t start)
+{
+	sector_t total, real_s, real_c;
+
+	real_s = sector(s) - 1;
+	real_c = cylinder(s, c);
+	total = (real_c * g_sectors + real_s) * g_heads + h;
+	if (!total)
+		printf("Partition %u contains sector 0\n", n);
+	if (h >= g_heads)
+		printf("Partition %u: head %u greater than maximum %u\n",
+			n, h + 1, g_heads);
+	if (real_s >= g_sectors)
+		printf("Partition %u: sector %u greater than "
+			"maximum %u\n", n, s, g_sectors);
+	if (real_c >= g_cylinders)
+		printf("Partition %u: cylinder %"SECT_FMT"u greater than "
+			"maximum %u\n", n, real_c + 1, g_cylinders);
+	if (g_cylinders <= 1024 && start != total)
+		printf("Partition %u: previous sectors %"SECT_FMT"u disagrees with "
+			"total %"SECT_FMT"u\n", n, start, total);
+}
+
+static void
+verify(void)
+{
+	int i, j;
+	sector_t total = 1;
+	sector_t first[g_partitions], last[g_partitions];
+	struct partition *p;
+
+	if (warn_geometry())
+		return;
+
+	if (LABEL_IS_SUN) {
+		verify_sun();
+		return;
+	}
+	if (LABEL_IS_SGI) {
+		verify_sgi(1);
+		return;
+	}
+
+	fill_bounds(first, last);
+	for (i = 0; i < g_partitions; i++) {
+		struct pte *pe = &ptes[i];
+
+		p = pe->part_table;
+		if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) {
+			check_consistency(p, i);
+			if (get_partition_start_from_dev_start(pe) < first[i])
+				printf("Warning: bad start-of-data in "
+					"partition %u\n", i + 1);
+			check(i + 1, p->end_head, p->end_sector, p->end_cyl,
+				last[i]);
+			total += last[i] + 1 - first[i];
+			for (j = 0; j < i; j++) {
+				if ((first[i] >= first[j] && first[i] <= last[j])
+				 || ((last[i] <= last[j] && last[i] >= first[j]))) {
+					printf("Warning: partition %u overlaps "
+						"partition %u\n", j + 1, i + 1);
+					total += first[i] >= first[j] ?
+						first[i] : first[j];
+					total -= last[i] <= last[j] ?
+						last[i] : last[j];
+				}
+			}
+		}
+	}
+
+	if (extended_offset) {
+		struct pte *pex = &ptes[ext_index];
+		sector_t e_last = get_start_sect(pex->part_table) +
+			get_nr_sects(pex->part_table) - 1;
+
+		for (i = 4; i < g_partitions; i++) {
+			total++;
+			p = ptes[i].part_table;
+			if (!p->sys_ind) {
+				if (i != 4 || i + 1 < g_partitions)
+					printf("Warning: partition %u "
+						"is empty\n", i + 1);
+			} else if (first[i] < extended_offset || last[i] > e_last) {
+				printf("Logical partition %u not entirely in "
+					"partition %u\n", i + 1, ext_index + 1);
+			}
+		}
+	}
+
+	if (total > g_heads * g_sectors * g_cylinders)
+		printf("Total allocated sectors %u greater than the maximum "
+			"%u\n", total, g_heads * g_sectors * g_cylinders);
+	else {
+		total = g_heads * g_sectors * g_cylinders - total;
+		if (total != 0)
+			printf("%"SECT_FMT"u unallocated sectors\n", total);
+	}
+}
+
+static void
+add_partition(int n, int sys)
+{
+	char mesg[256];         /* 48 does not suffice in Japanese */
+	int i, num_read = 0;
+	struct partition *p = ptes[n].part_table;
+	struct partition *q = ptes[ext_index].part_table;
+	sector_t limit, temp;
+	sector_t start, stop = 0;
+	sector_t first[g_partitions], last[g_partitions];
+
+	if (p && p->sys_ind) {
+		printf(msg_part_already_defined, n + 1);
+		return;
+	}
+	fill_bounds(first, last);
+	if (n < 4) {
+		start = sector_offset;
+		if (display_in_cyl_units || !total_number_of_sectors)
+			limit = (sector_t) g_heads * g_sectors * g_cylinders - 1;
+		else
+			limit = total_number_of_sectors - 1;
+		if (extended_offset) {
+			first[ext_index] = extended_offset;
+			last[ext_index] = get_start_sect(q) +
+				get_nr_sects(q) - 1;
+		}
+	} else {
+		start = extended_offset + sector_offset;
+		limit = get_start_sect(q) + get_nr_sects(q) - 1;
+	}
+	if (display_in_cyl_units)
+		for (i = 0; i < g_partitions; i++)
+			first[i] = (cround(first[i]) - 1) * units_per_sector;
+
+	snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
+	do {
+		temp = start;
+		for (i = 0; i < g_partitions; i++) {
+			int lastplusoff;
+
+			if (start == ptes[i].offset_from_dev_start)
+				start += sector_offset;
+			lastplusoff = last[i] + ((n < 4) ? 0 : sector_offset);
+			if (start >= first[i] && start <= lastplusoff)
+				start = lastplusoff + 1;
+		}
+		if (start > limit)
+			break;
+		if (start >= temp+units_per_sector && num_read) {
+			printf("Sector %"SECT_FMT"u is already allocated\n", temp);
+			temp = start;
+			num_read = 0;
+		}
+		if (!num_read && start == temp) {
+			sector_t saved_start;
+
+			saved_start = start;
+			start = read_int(cround(saved_start), cround(saved_start), cround(limit), 0, mesg);
+			if (display_in_cyl_units) {
+				start = (start - 1) * units_per_sector;
+				if (start < saved_start)
+					start = saved_start;
+			}
+			num_read = 1;
+		}
+	} while (start != temp || !num_read);
+	if (n > 4) {                    /* NOT for fifth partition */
+		struct pte *pe = &ptes[n];
+
+		pe->offset_from_dev_start = start - sector_offset;
+		if (pe->offset_from_dev_start == extended_offset) { /* must be corrected */
+			pe->offset_from_dev_start++;
+			if (sector_offset == 1)
+				start++;
+		}
+	}
+
+	for (i = 0; i < g_partitions; i++) {
+		struct pte *pe = &ptes[i];
+
+		if (start < pe->offset_from_dev_start && limit >= pe->offset_from_dev_start)
+			limit = pe->offset_from_dev_start - 1;
+		if (start < first[i] && limit >= first[i])
+			limit = first[i] - 1;
+	}
+	if (start > limit) {
+		printf("No free sectors available\n");
+		if (n > 4)
+			g_partitions--;
+		return;
+	}
+	if (cround(start) == cround(limit)) {
+		stop = limit;
+	} else {
+		snprintf(mesg, sizeof(mesg),
+			 "Last %s or +size or +sizeM or +sizeK",
+			 str_units(SINGULAR));
+		stop = read_int(cround(start), cround(limit), cround(limit), cround(start), mesg);
+		if (display_in_cyl_units) {
+			stop = stop * units_per_sector - 1;
+			if (stop >limit)
+				stop = limit;
+		}
+	}
+
+	set_partition(n, 0, start, stop, sys);
+	if (n > 4)
+		set_partition(n - 1, 1, ptes[n].offset_from_dev_start, stop, EXTENDED);
+
+	if (IS_EXTENDED(sys)) {
+		struct pte *pe4 = &ptes[4];
+		struct pte *pen = &ptes[n];
+
+		ext_index = n;
+		pen->ext_pointer = p;
+		pe4->offset_from_dev_start = extended_offset = start;
+		pe4->sectorbuffer = xzalloc(sector_size);
+		pe4->part_table = pt_offset(pe4->sectorbuffer, 0);
+		pe4->ext_pointer = pe4->part_table + 1;
+		pe4->changed = 1;
+		g_partitions = 5;
+	}
+}
+
+static void
+add_logical(void)
+{
+	if (g_partitions > 5 || ptes[4].part_table->sys_ind) {
+		struct pte *pe = &ptes[g_partitions];
+
+		pe->sectorbuffer = xzalloc(sector_size);
+		pe->part_table = pt_offset(pe->sectorbuffer, 0);
+		pe->ext_pointer = pe->part_table + 1;
+		pe->offset_from_dev_start = 0;
+		pe->changed = 1;
+		g_partitions++;
+	}
+	add_partition(g_partitions - 1, LINUX_NATIVE);
+}
+
+static void
+new_partition(void)
+{
+	int i, free_primary = 0;
+
+	if (warn_geometry())
+		return;
+
+	if (LABEL_IS_SUN) {
+		add_sun_partition(get_partition(0, g_partitions), LINUX_NATIVE);
+		return;
+	}
+	if (LABEL_IS_SGI) {
+		sgi_add_partition(get_partition(0, g_partitions), LINUX_NATIVE);
+		return;
+	}
+	if (LABEL_IS_AIX) {
+		printf("Sorry - this fdisk cannot handle AIX disk labels.\n"
+"If you want to add DOS-type partitions, create a new empty DOS partition\n"
+"table first (use 'o'). This will destroy the present disk contents.\n");
+		return;
+	}
+
+	for (i = 0; i < 4; i++)
+		free_primary += !ptes[i].part_table->sys_ind;
+
+	if (!free_primary && g_partitions >= MAXIMUM_PARTS) {
+		printf("The maximum number of partitions has been created\n");
+		return;
+	}
+
+	if (!free_primary) {
+		if (extended_offset)
+			add_logical();
+		else
+			printf("You must delete some partition and add "
+				 "an extended partition first\n");
+	} else {
+		char c, line[80];
+		snprintf(line, sizeof(line),
+			"Command action\n"
+			"   %s\n"
+			"   p   primary partition (1-4)\n",
+			(extended_offset ?
+			"l   logical (5 or over)" : "e   extended"));
+		while (1) {
+			c = read_nonempty(line);
+			if ((c | 0x20) == 'p') {
+				i = get_nonexisting_partition(0, 4);
+				if (i >= 0)
+					add_partition(i, LINUX_NATIVE);
+				return;
+			}
+			if (c == 'l' && extended_offset) {
+				add_logical();
+				return;
+			}
+			if (c == 'e' && !extended_offset) {
+				i = get_nonexisting_partition(0, 4);
+				if (i >= 0)
+					add_partition(i, EXTENDED);
+				return;
+			}
+			printf("Invalid partition number "
+					 "for type '%c'\n", c);
+		}
+	}
+}
+
+static void
+reread_partition_table(int leave)
+{
+	int i;
+
+	printf("Calling ioctl() to re-read partition table\n");
+	sync();
+	/* Users with slow external USB disks on a 320MHz ARM system (year 2011)
+	 * report that sleep is needed, otherwise BLKRRPART may fail with -EIO:
+	 */
+	sleep(1);
+	i = ioctl_or_perror(dev_fd, BLKRRPART, NULL,
+			"WARNING: rereading partition table "
+			"failed, kernel still uses old table");
+#if 0
+	if (dos_changed)
+		printf(
+		"\nWARNING: If you have created or modified any DOS 6.x\n"
+		"partitions, please see the fdisk manual page for additional\n"
+		"information\n");
+#endif
+
+	if (leave) {
+		if (ENABLE_FEATURE_CLEAN_UP)
+			close_dev_fd();
+		exit(i != 0);
+	}
+}
+
+static void
+write_table(void)
+{
+	int i;
+
+	if (LABEL_IS_DOS) {
+		for (i = 0; i < 3; i++)
+			if (ptes[i].changed)
+				ptes[3].changed = 1;
+		for (i = 3; i < g_partitions; i++) {
+			struct pte *pe = &ptes[i];
+			if (pe->changed) {
+				write_part_table_flag(pe->sectorbuffer);
+				write_sector(pe->offset_from_dev_start, pe->sectorbuffer);
+			}
+		}
+	}
+	else if (LABEL_IS_SGI) {
+		/* no test on change? the printf below might be mistaken */
+		sgi_write_table();
+	}
+	else if (LABEL_IS_SUN) {
+		for (i = 0; i < 8; i++) {
+			if (ptes[i].changed) {
+				sun_write_table();
+				break;
+			}
+		}
+	}
+
+	printf("The partition table has been altered.\n");
+	reread_partition_table(1);
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+#define MAX_PER_LINE    16
+static void
+print_buffer(char *pbuffer)
+{
+	int i,l;
+
+	for (i = 0, l = 0; i < sector_size; i++, l++) {
+		if (l == 0)
+			printf("0x%03X:", i);
+		printf(" %02X", (unsigned char) pbuffer[i]);
+		if (l == MAX_PER_LINE - 1) {
+			bb_putchar('\n');
+			l = -1;
+		}
+	}
+	if (l > 0)
+		bb_putchar('\n');
+	bb_putchar('\n');
+}
+
+static void
+print_raw(void)
+{
+	int i;
+
+	printf("Device: %s\n", disk_device);
+	if (LABEL_IS_SGI || LABEL_IS_SUN)
+		print_buffer(MBRbuffer);
+	else {
+		for (i = 3; i < g_partitions; i++)
+			print_buffer(ptes[i].sectorbuffer);
+	}
+}
+
+static void
+move_begin(unsigned i)
+{
+	struct pte *pe = &ptes[i];
+	struct partition *p = pe->part_table;
+	sector_t new, first, nr_sects;
+
+	if (warn_geometry())
+		return;
+	nr_sects = get_nr_sects(p);
+	if (!p->sys_ind || !nr_sects || IS_EXTENDED(p->sys_ind)) {
+		printf("Partition %u has no data area\n", i + 1);
+		return;
+	}
+	first = get_partition_start_from_dev_start(pe); /* == pe->offset_from_dev_start + get_start_sect(p) */
+	new = read_int(0 /*was:first*/, first, first + nr_sects - 1, first, "New beginning of data");
+	if (new != first) {
+		sector_t new_relative = new - pe->offset_from_dev_start;
+		nr_sects += (get_start_sect(p) - new_relative);
+		set_start_sect(p, new_relative);
+		set_nr_sects(p, nr_sects);
+		read_nonempty("Recalculate C/H/S values? (Y/N): ");
+		if ((line_ptr[0] | 0x20) == 'y')
+			set_hsc_start_end(p, new, new + nr_sects - 1);
+		pe->changed = 1;
+	}
+}
+
+static void
+xselect(void)
+{
+	char c;
+
+	while (1) {
+		bb_putchar('\n');
+		c = 0x20 | read_nonempty("Expert command (m for help): ");
+		switch (c) {
+		case 'a':
+			if (LABEL_IS_SUN)
+				sun_set_alt_cyl();
+			break;
+		case 'b':
+			if (LABEL_IS_DOS)
+				move_begin(get_partition(0, g_partitions));
+			break;
+		case 'c':
+			user_cylinders = g_cylinders =
+				read_int(1, g_cylinders, 1048576, 0,
+					"Number of cylinders");
+			if (LABEL_IS_SUN)
+				sun_set_ncyl(g_cylinders);
+			if (LABEL_IS_DOS)
+				warn_cylinders();
+			break;
+		case 'd':
+			print_raw();
+			break;
+		case 'e':
+			if (LABEL_IS_SGI)
+				sgi_set_xcyl();
+			else if (LABEL_IS_SUN)
+				sun_set_xcyl();
+			else if (LABEL_IS_DOS)
+				x_list_table(1);
+			break;
+		case 'f':
+			if (LABEL_IS_DOS)
+				fix_partition_table_order();
+			break;
+		case 'g':
+#if ENABLE_FEATURE_SGI_LABEL
+			create_sgilabel();
+#endif
+			break;
+		case 'h':
+			user_heads = g_heads = read_int(1, g_heads, 256, 0, "Number of heads");
+			update_units();
+			break;
+		case 'i':
+			if (LABEL_IS_SUN)
+				sun_set_ilfact();
+			break;
+		case 'o':
+			if (LABEL_IS_SUN)
+				sun_set_rspeed();
+			break;
+		case 'p':
+			if (LABEL_IS_SUN)
+				list_table(1);
+			else
+				x_list_table(0);
+			break;
+		case 'q':
+			if (ENABLE_FEATURE_CLEAN_UP)
+				close_dev_fd();
+			bb_putchar('\n');
+			exit(EXIT_SUCCESS);
+		case 'r':
+			return;
+		case 's':
+			user_sectors = g_sectors = read_int(1, g_sectors, 63, 0, "Number of sectors");
+			if (dos_compatible_flag) {
+				sector_offset = g_sectors;
+				printf("Warning: setting sector offset for DOS "
+					"compatiblity\n");
+			}
+			update_units();
+			break;
+		case 'v':
+			verify();
+			break;
+		case 'w':
+			write_table();  /* does not return */
+			break;
+		case 'y':
+			if (LABEL_IS_SUN)
+				sun_set_pcylcount();
+			break;
+		default:
+			xmenu();
+		}
+	}
+}
+#endif /* ADVANCED mode */
+
+static int
+is_ide_cdrom_or_tape(const char *device)
+{
+	FILE *procf;
+	char buf[100];
+	struct stat statbuf;
+	int is_ide = 0;
+
+	/* No device was given explicitly, and we are trying some
+	   likely things.  But opening /dev/hdc may produce errors like
+	   "hdc: tray open or drive not ready"
+	   if it happens to be a CD-ROM drive. It even happens that
+	   the process hangs on the attempt to read a music CD.
+	   So try to be careful. This only works since 2.1.73. */
+
+	if (strncmp("/dev/hd", device, 7))
+		return 0;
+
+	snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5);
+	procf = fopen_for_read(buf);
+	if (procf != NULL && fgets(buf, sizeof(buf), procf))
+		is_ide = (!strncmp(buf, "cdrom", 5) ||
+			  !strncmp(buf, "tape", 4));
+	else
+		/* Now when this proc file does not exist, skip the
+		   device when it is read-only. */
+		if (stat(device, &statbuf) == 0)
+			is_ide = ((statbuf.st_mode & 0222) == 0);
+
+	if (procf)
+		fclose(procf);
+	return is_ide;
+}
+
+
+static void
+open_list_and_close(const char *device, int user_specified)
+{
+	int gb;
+
+	disk_device = device;
+	if (setjmp(listingbuf))
+		return;
+	if (!user_specified)
+		if (is_ide_cdrom_or_tape(device))
+			return;
+
+	/* Open disk_device, save file descriptor to dev_fd */
+	errno = 0;
+	gb = get_boot(TRY_ONLY);
+	if (gb > 0) {   /* I/O error */
+		/* Ignore other errors, since we try IDE
+		   and SCSI hard disks which may not be
+		   installed on the system. */
+		if (user_specified || errno == EACCES)
+			bb_perror_msg("can't open '%s'", device);
+		return;
+	}
+
+	if (gb < 0) { /* no DOS signature */
+		list_disk_geometry();
+		if (LABEL_IS_AIX)
+			goto ret;
+#if ENABLE_FEATURE_OSF_LABEL
+		if (bsd_trydev(device) < 0)
+#endif
+			printf("Disk %s doesn't contain a valid "
+				"partition table\n", device);
+	} else {
+		list_table(0);
+#if ENABLE_FEATURE_FDISK_WRITABLE
+		if (!LABEL_IS_SUN && g_partitions > 4) {
+			delete_partition(ext_index);
+		}
+#endif
+	}
+ ret:
+	close_dev_fd();
+}
+
+/* Is it a whole disk? The digit check is still useful
+   for Xen devices for example. */
+static int is_whole_disk(const char *disk)
+{
+	unsigned len;
+	int fd = open(disk, O_RDONLY);
+
+	if (fd != -1) {
+		struct hd_geometry geometry;
+		int err = ioctl(fd, HDIO_GETGEO, &geometry);
+		close(fd);
+		if (!err)
+			return (geometry.start == 0);
+	}
+
+	/* Treat "nameN" as a partition name, not whole disk */
+	/* note: mmcblk0 should work from the geometry check above */
+	len = strlen(disk);
+	if (len != 0 && isdigit(disk[len - 1]))
+		return 0;
+
+	return 1;
+}
+
+/* for fdisk -l: try all things in /proc/partitions
+   that look like a partition name (do not end in a digit) */
+static void
+list_devs_in_proc_partititons(void)
+{
+	FILE *procpt;
+	char line[100], ptname[100], devname[120];
+	int ma, mi, sz;
+
+	procpt = fopen_or_warn("/proc/partitions", "r");
+
+	while (fgets(line, sizeof(line), procpt)) {
+		if (sscanf(line, " %u %u %u %[^\n ]",
+				&ma, &mi, &sz, ptname) != 4)
+			continue;
+
+		sprintf(devname, "/dev/%s", ptname);
+		if (is_whole_disk(devname))
+			open_list_and_close(devname, 0);
+	}
+#if ENABLE_FEATURE_CLEAN_UP
+	fclose(procpt);
+#endif
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+unknown_command(int c)
+{
+	printf("%c: unknown command\n", c);
+}
+#endif
+
+int fdisk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fdisk_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opt;
+	/*
+	 *  fdisk -v
+	 *  fdisk -l [-b sectorsize] [-u] device ...
+	 *  fdisk -s [partition] ...
+	 *  fdisk [-b sectorsize] [-u] device
+	 *
+	 * Options -C, -H, -S set the geometry.
+	 */
+	INIT_G();
+
+	close_dev_fd(); /* needed: fd 3 must not stay closed */
+
+	opt_complementary = "b+:C+:H+:S+"; /* numeric params */
+	opt = getopt32(argv, "b:C:H:lS:u" IF_FEATURE_FDISK_BLKSIZE("s"),
+				&sector_size, &user_cylinders, &user_heads, &user_sectors);
+	argv += optind;
+	if (opt & OPT_b) {
+		/* Ugly: this sector size is really per device,
+		 * so cannot be combined with multiple disks,
+		 * and the same goes for the C/H/S options.
+		 */
+		if (sector_size < 512
+		 || sector_size > 0x10000
+		 || (sector_size & (sector_size-1)) /* not power of 2 */
+		) {
+			bb_show_usage();
+		}
+		sector_offset = 2;
+		user_set_sector_size = 1;
+	}
+	if (user_heads <= 0 || user_heads >= 256)
+		user_heads = 0;
+	if (user_sectors <= 0 || user_sectors >= 64)
+		user_sectors = 0;
+	if (opt & OPT_u)
+		display_in_cyl_units = 0; // -u
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+	if (opt & OPT_l) {
+		nowarn = 1;
+#endif
+		if (*argv) {
+			listing = 1;
+			do {
+				open_list_and_close(*argv, 1);
+			} while (*++argv);
+		} else {
+			/* we don't have device names, */
+			/* use /proc/partitions instead */
+			list_devs_in_proc_partititons();
+		}
+		return 0;
+#if ENABLE_FEATURE_FDISK_WRITABLE
+	}
+#endif
+
+#if ENABLE_FEATURE_FDISK_BLKSIZE
+	if (opt & OPT_s) {
+		int j;
+
+		nowarn = 1;
+		if (!argv[0])
+			bb_show_usage();
+		for (j = 0; argv[j]; j++) {
+			unsigned long long size;
+			fd = xopen(argv[j], O_RDONLY);
+			size = bb_BLKGETSIZE_sectors(fd) / 2;
+			close(fd);
+			if (argv[1])
+				printf("%llu\n", size);
+			else
+				printf("%s: %llu\n", argv[j], size);
+		}
+		return 0;
+	}
+#endif
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+	if (!argv[0] || argv[1])
+		bb_show_usage();
+
+	disk_device = argv[0];
+	get_boot(OPEN_MAIN);
+
+	if (LABEL_IS_OSF) {
+		/* OSF label, and no DOS label */
+		printf("Detected an OSF/1 disklabel on %s, entering "
+			"disklabel mode\n", disk_device);
+		bsd_select();
+		/*Why do we do this?  It seems to be counter-intuitive*/
+		current_label_type = LABEL_DOS;
+		/* If we return we may want to make an empty DOS label? */
+	}
+
+	while (1) {
+		int c;
+		bb_putchar('\n');
+		c = 0x20 | read_nonempty("Command (m for help): ");
+		switch (c) {
+		case 'a':
+			if (LABEL_IS_DOS)
+				toggle_active(get_partition(1, g_partitions));
+			else if (LABEL_IS_SUN)
+				toggle_sunflags(get_partition(1, g_partitions),
+						0x01);
+			else if (LABEL_IS_SGI)
+				sgi_set_bootpartition(
+					get_partition(1, g_partitions));
+			else
+				unknown_command(c);
+			break;
+		case 'b':
+			if (LABEL_IS_SGI) {
+				printf("\nThe current boot file is: %s\n",
+					sgi_get_bootfile());
+				if (read_maybe_empty("Please enter the name of the "
+						"new boot file: ") == '\n')
+					printf("Boot file unchanged\n");
+				else
+					sgi_set_bootfile(line_ptr);
+			}
+#if ENABLE_FEATURE_OSF_LABEL
+			else
+				bsd_select();
+#endif
+			break;
+		case 'c':
+			if (LABEL_IS_DOS)
+				toggle_dos_compatibility_flag();
+			else if (LABEL_IS_SUN)
+				toggle_sunflags(get_partition(1, g_partitions),
+						0x10);
+			else if (LABEL_IS_SGI)
+				sgi_set_swappartition(
+						get_partition(1, g_partitions));
+			else
+				unknown_command(c);
+			break;
+		case 'd':
+			{
+				int j;
+			/* If sgi_label then don't use get_existing_partition,
+			   let the user select a partition, since
+			   get_existing_partition() only works for Linux-like
+			   partition tables */
+				if (!LABEL_IS_SGI) {
+					j = get_existing_partition(1, g_partitions);
+				} else {
+					j = get_partition(1, g_partitions);
+				}
+				if (j >= 0)
+					delete_partition(j);
+			}
+			break;
+		case 'i':
+			if (LABEL_IS_SGI)
+				create_sgiinfo();
+			else
+				unknown_command(c);
+		case 'l':
+			list_types(get_sys_types());
+			break;
+		case 'm':
+			menu();
+			break;
+		case 'n':
+			new_partition();
+			break;
+		case 'o':
+			create_doslabel();
+			break;
+		case 'p':
+			list_table(0);
+			break;
+		case 'q':
+			if (ENABLE_FEATURE_CLEAN_UP)
+				close_dev_fd();
+			bb_putchar('\n');
+			return 0;
+		case 's':
+#if ENABLE_FEATURE_SUN_LABEL
+			create_sunlabel();
+#endif
+			break;
+		case 't':
+			change_sysid();
+			break;
+		case 'u':
+			change_units();
+			break;
+		case 'v':
+			verify();
+			break;
+		case 'w':
+			write_table();  /* does not return */
+			break;
+#if ENABLE_FEATURE_FDISK_ADVANCED
+		case 'x':
+			if (LABEL_IS_SGI) {
+				printf("\n\tSorry, no experts menu for SGI "
+					"partition tables available\n\n");
+			} else
+				xselect();
+			break;
+#endif
+		default:
+			unknown_command(c);
+			menu();
+		}
+	}
+	return 0;
+#endif /* FEATURE_FDISK_WRITABLE */
+}
diff --git a/ap/app/busybox/src/util-linux/fdisk_aix.c b/ap/app/busybox/src/util-linux/fdisk_aix.c
new file mode 100644
index 0000000..ee5df50
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/fdisk_aix.c
@@ -0,0 +1,74 @@
+#if ENABLE_FEATURE_AIX_LABEL
+/*
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+typedef struct {
+	unsigned int   magic;        /* expect AIX_LABEL_MAGIC */
+	unsigned int   fillbytes1[124];
+	unsigned int   physical_volume_id;
+	unsigned int   fillbytes2[124];
+} aix_partition;
+
+#define AIX_LABEL_MAGIC         0xc9c2d4c1
+#define AIX_LABEL_MAGIC_SWAPPED 0xc1d4c2c9
+#define AIX_INFO_MAGIC          0x00072959
+#define AIX_INFO_MAGIC_SWAPPED  0x59290700
+
+#define aixlabel ((aix_partition *)MBRbuffer)
+
+
+/*
+  Changes:
+  * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+  *     Internationalization
+  *
+  * 2003-03-20 Phillip Kesling <pkesling@sgi.com>
+  *      Some fixes
+*/
+
+static smallint aix_other_endian; /* bool */
+static smallint aix_volumes = 1; /* max 15 */
+
+/*
+ * only dealing with free blocks here
+ */
+
+static void
+aix_info(void)
+{
+	puts("\n"
+"There is a valid AIX label on this disk.\n"
+"Unfortunately Linux cannot handle these disks at the moment.\n"
+"Nevertheless some advice:\n"
+"1. fdisk will destroy its contents on write.\n"
+"2. Be sure that this disk is NOT a still vital part of a volume group.\n"
+"   (Otherwise you may erase the other disks as well, if unmirrored.)\n"
+"3. Before deleting this physical volume be sure to remove the disk\n"
+"   logically from your AIX machine. (Otherwise you become an AIXpert).\n"
+	);
+}
+
+static int
+check_aix_label(void)
+{
+	if (aixlabel->magic != AIX_LABEL_MAGIC
+	 && aixlabel->magic != AIX_LABEL_MAGIC_SWAPPED
+	) {
+		current_label_type = 0;
+		aix_other_endian = 0;
+		return 0;
+	}
+	aix_other_endian = (aixlabel->magic == AIX_LABEL_MAGIC_SWAPPED);
+	update_units();
+	current_label_type = LABEL_AIX;
+	g_partitions = 1016;
+	aix_volumes = 15;
+	aix_info();
+	/*aix_nolabel();*/              /* %% */
+	/*aix_label = 1;*/              /* %% */
+	return 1;
+}
+#endif  /* AIX_LABEL */
diff --git a/ap/app/busybox/src/util-linux/fdisk_gpt.c b/ap/app/busybox/src/util-linux/fdisk_gpt.c
new file mode 100644
index 0000000..d43d9c7
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/fdisk_gpt.c
@@ -0,0 +1,195 @@
+#if ENABLE_FEATURE_GPT_LABEL
+/*
+ * Copyright (C) 2010 Kevin Cernekee <cernekee@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#define GPT_MAGIC 0x5452415020494645ULL
+enum {
+	LEGACY_GPT_TYPE = 0xee,
+	GPT_MAX_PARTS   = 256,
+	GPT_MAX_PART_ENTRY_LEN = 4096,
+	GUID_LEN        = 16,
+};
+
+typedef struct {
+	uint64_t magic;
+	uint32_t revision;
+	uint32_t hdr_size;
+	uint32_t hdr_crc32;
+	uint32_t reserved;
+	uint64_t current_lba;
+	uint64_t backup_lba;
+	uint64_t first_usable_lba;
+	uint64_t last_usable_lba;
+	uint8_t  disk_guid[GUID_LEN];
+	uint64_t first_part_lba;
+	uint32_t n_parts;
+	uint32_t part_entry_len;
+	uint32_t part_array_crc32;
+} gpt_header;
+
+typedef struct {
+	uint8_t  type_guid[GUID_LEN];
+	uint8_t  part_guid[GUID_LEN];
+	uint64_t lba_start;
+	uint64_t lba_end;
+	uint64_t flags;
+	uint16_t name[36];
+} gpt_partition;
+
+static gpt_header *gpt_hdr;
+
+static char *part_array;
+static unsigned int n_parts;
+static unsigned int part_array_len;
+static unsigned int part_entry_len;
+
+static inline gpt_partition *
+gpt_part(int i)
+{
+	if (i >= n_parts) {
+		return NULL;
+	}
+	return (gpt_partition *)&part_array[i * part_entry_len];
+}
+
+static uint32_t
+gpt_crc32(void *buf, int len)
+{
+	return ~crc32_block_endian0(0xffffffff, buf, len, global_crc32_table);
+}
+
+static void
+gpt_print_guid(uint8_t *buf)
+{
+	printf(
+		"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+		buf[3], buf[2], buf[1], buf[0],
+		buf[5], buf[4],
+		buf[7], buf[6],
+		buf[8], buf[9],
+		buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
+}
+
+/* TODO: real unicode support */
+static void
+gpt_print_wide(uint16_t *s, int max_len)
+{
+	int i = 0;
+
+	while (i < max_len) {
+		if (*s == 0)
+			return;
+		fputc(*s, stdout);
+		s++;
+	}
+}
+
+static void
+gpt_list_table(int xtra UNUSED_PARAM)
+{
+	int i;
+	char numstr6[6];
+
+	numstr6[5] = '\0';
+
+	smart_ulltoa5(total_number_of_sectors, numstr6, " KMGTPEZY");
+	printf("Disk %s: %llu sectors, %s\n", disk_device,
+		(unsigned long long)total_number_of_sectors,
+		numstr6);
+	printf("Logical sector size: %u\n", sector_size);
+	printf("Disk identifier (GUID): ");
+	gpt_print_guid(gpt_hdr->disk_guid);
+	printf("\nPartition table holds up to %u entries\n",
+		(int)SWAP_LE32(gpt_hdr->n_parts));
+	printf("First usable sector is %llu, last usable sector is %llu\n\n",
+		(unsigned long long)SWAP_LE64(gpt_hdr->first_usable_lba),
+		(unsigned long long)SWAP_LE64(gpt_hdr->last_usable_lba));
+
+	printf("Number  Start (sector)    End (sector)  Size       Code  Name\n");
+	for (i = 0; i < n_parts; i++) {
+		gpt_partition *p = gpt_part(i);
+		if (p->lba_start) {
+			smart_ulltoa5(1 + SWAP_LE64(p->lba_end) - SWAP_LE64(p->lba_start),
+				numstr6, " KMGTPEZY");
+			printf("%4u %15llu %15llu %11s   %04x  ",
+				i + 1,
+				(unsigned long long)SWAP_LE64(p->lba_start),
+				(unsigned long long)SWAP_LE64(p->lba_end),
+				numstr6,
+				0x0700 /* FIXME */);
+			gpt_print_wide(p->name, 18);
+			printf("\n");
+		}
+	}
+}
+
+static int
+check_gpt_label(void)
+{
+	struct partition *first = pt_offset(MBRbuffer, 0);
+	struct pte pe;
+	uint32_t crc;
+
+	/* LBA 0 contains the legacy MBR */
+
+	if (!valid_part_table_flag(MBRbuffer)
+	 || first->sys_ind != LEGACY_GPT_TYPE
+	) {
+		current_label_type = 0;
+		return 0;
+	}
+
+	/* LBA 1 contains the GPT header */
+
+	read_pte(&pe, 1);
+	gpt_hdr = (void *)pe.sectorbuffer;
+
+	if (gpt_hdr->magic != SWAP_LE64(GPT_MAGIC)) {
+		current_label_type = 0;
+		return 0;
+	}
+
+	if (!global_crc32_table) {
+		global_crc32_table = crc32_filltable(NULL, 0);
+	}
+
+	crc = SWAP_LE32(gpt_hdr->hdr_crc32);
+	gpt_hdr->hdr_crc32 = 0;
+	if (gpt_crc32(gpt_hdr, SWAP_LE32(gpt_hdr->hdr_size)) != crc) {
+		/* FIXME: read the backup table */
+		puts("\nwarning: GPT header CRC is invalid\n");
+	}
+
+	n_parts = SWAP_LE32(gpt_hdr->n_parts);
+	part_entry_len = SWAP_LE32(gpt_hdr->part_entry_len);
+	if (n_parts > GPT_MAX_PARTS
+	 || part_entry_len > GPT_MAX_PART_ENTRY_LEN
+	 || SWAP_LE32(gpt_hdr->hdr_size) > sector_size
+	) {
+		puts("\nwarning: unable to parse GPT disklabel\n");
+		current_label_type = 0;
+		return 0;
+	}
+
+	part_array_len = n_parts * part_entry_len;
+	part_array = xmalloc(part_array_len);
+	seek_sector(SWAP_LE64(gpt_hdr->first_part_lba));
+	if (full_read(dev_fd, part_array, part_array_len) != part_array_len) {
+		fdisk_fatal(unable_to_read);
+	}
+
+	if (gpt_crc32(part_array, part_array_len) != gpt_hdr->part_array_crc32) {
+		/* FIXME: read the backup table */
+		puts("\nwarning: GPT array CRC is invalid\n");
+	}
+
+	puts("Found valid GPT with protective MBR; using GPT\n");
+
+	current_label_type = LABEL_GPT;
+	return 1;
+}
+
+#endif /* GPT_LABEL */
diff --git a/ap/app/busybox/src/util-linux/fdisk_osf.c b/ap/app/busybox/src/util-linux/fdisk_osf.c
new file mode 100644
index 0000000..ff16389
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/fdisk_osf.c
@@ -0,0 +1,1047 @@
+/*
+ * Copyright (c) 1987, 1988 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 acknowledgment:
+ *      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 ENABLE_FEATURE_OSF_LABEL
+
+#ifndef BSD_DISKMAGIC
+#define BSD_DISKMAGIC     ((uint32_t) 0x82564557)
+#endif
+
+#ifndef BSD_MAXPARTITIONS
+#define BSD_MAXPARTITIONS 16
+#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 xbsd_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 */
+#define NDDATA 5
+	uint32_t   d_drivedata[NDDATA];    /* drive-type specific information */
+#define NSPARE 5
+	uint32_t   d_spare[NSPARE];        /* 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 xbsd_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 */
+	} d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */
+};
+
+/* 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 */
+
+static const char *const xbsd_dktypenames[] = {
+	"unknown",
+	"SMD",
+	"MSCP",
+	"old DEC",
+	"SCSI",
+	"ESDI",
+	"ST506",
+	"HP-IB",
+	"HP-FL",
+	"type 9",
+	"floppy",
+	0
+};
+
+
+/*
+ * 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
+
+static const char *const xbsd_fstypes[] = {
+	"\x00" "unused",            /* BSD_FS_UNUSED  */
+	"\x01" "swap",              /* BSD_FS_SWAP    */
+	"\x02" "Version 6",         /* BSD_FS_V6      */
+	"\x03" "Version 7",         /* BSD_FS_V7      */
+	"\x04" "System V",          /* BSD_FS_SYSV    */
+	"\x05" "4.1BSD",            /* BSD_FS_V71K    */
+	"\x06" "Eighth Edition",    /* BSD_FS_V8      */
+	"\x07" "4.2BSD",            /* BSD_FS_BSDFFS  */
+#ifdef __alpha__
+	"\x08" "ext2",              /* BSD_FS_EXT2    */
+#else
+	"\x08" "MS-DOS",            /* BSD_FS_MSDOS   */
+#endif
+	"\x09" "4.4LFS",            /* BSD_FS_BSDLFS  */
+	"\x0a" "unknown",           /* BSD_FS_OTHER   */
+	"\x0b" "HPFS",              /* BSD_FS_HPFS    */
+	"\x0c" "ISO-9660",          /* BSD_FS_ISO9660 */
+	"\x0d" "boot",              /* BSD_FS_BOOT    */
+	"\x0e" "ADOS",              /* BSD_FS_ADOS    */
+	"\x0f" "HFS",               /* BSD_FS_HFS     */
+	"\x10" "AdvFS",             /* BSD_FS_ADVFS   */
+	NULL
+};
+
+
+/*
+ * 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 */
+
+/*
+   Changes:
+   19990319 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - i18n/nls
+
+   20000101 - David Huggins-Daines <dhuggins@linuxcare.com> - Better
+   support for OSF/1 disklabels on Alpha.
+   Also fixed unaligned accesses in alpha_bootblock_checksum()
+*/
+
+#define FREEBSD_PARTITION       0xa5
+#define NETBSD_PARTITION        0xa9
+
+static void xbsd_delete_part(void);
+static void xbsd_new_part(void);
+static void xbsd_write_disklabel(void);
+static int xbsd_create_disklabel(void);
+static void xbsd_edit_disklabel(void);
+static void xbsd_write_bootstrap(void);
+static void xbsd_change_fstype(void);
+static int xbsd_get_part_index(int max);
+static int xbsd_check_new_partition(int *i);
+static void xbsd_list_types(void);
+static uint16_t xbsd_dkcksum(struct xbsd_disklabel *lp);
+static int xbsd_initlabel(struct partition *p);
+static int xbsd_readlabel(struct partition *p);
+static int xbsd_writelabel(struct partition *p);
+
+#if defined(__alpha__)
+static void alpha_bootblock_checksum(char *boot);
+#endif
+
+#if !defined(__alpha__)
+static int xbsd_translate_fstype(int linux_type);
+static void xbsd_link_part(void);
+static struct partition *xbsd_part;
+static int xbsd_part_index;
+#endif
+
+
+/* Group big globals data and allocate it in one go */
+struct bsd_globals {
+/* We access this through a uint64_t * when checksumming */
+/* hopefully xmalloc gives us required alignment */
+	char disklabelbuffer[BSD_BBSIZE];
+	struct xbsd_disklabel xbsd_dlabel;
+};
+
+static struct bsd_globals *bsd_globals_ptr;
+
+#define disklabelbuffer (bsd_globals_ptr->disklabelbuffer)
+#define xbsd_dlabel     (bsd_globals_ptr->xbsd_dlabel)
+
+
+/* Code */
+
+#define bsd_cround(n) \
+	(display_in_cyl_units ? ((n)/xbsd_dlabel.d_secpercyl) + 1 : (n))
+
+/*
+ * Test whether the whole disk has BSD disk label magic.
+ *
+ * Note: often reformatting with DOS-type label leaves the BSD magic,
+ * so this does not mean that there is a BSD disk label.
+ */
+static int
+check_osf_label(void)
+{
+	if (xbsd_readlabel(NULL) == 0)
+		return 0;
+	return 1;
+}
+
+static int
+bsd_trydev(const char * dev)
+{
+	if (xbsd_readlabel(NULL) == 0)
+		return -1;
+	printf("\nBSD label for device: %s\n", dev);
+	xbsd_print_disklabel(0);
+	return 0;
+}
+
+static void
+bsd_menu(void)
+{
+	puts("Command Action");
+	puts("d\tdelete a BSD partition");
+	puts("e\tedit drive data");
+	puts("i\tinstall bootstrap");
+	puts("l\tlist known filesystem types");
+	puts("n\tadd a new BSD partition");
+	puts("p\tprint BSD partition table");
+	puts("q\tquit without saving changes");
+	puts("r\treturn to main menu");
+	puts("s\tshow complete disklabel");
+	puts("t\tchange a partition's filesystem id");
+	puts("u\tchange units (cylinders/sectors)");
+	puts("w\twrite disklabel to disk");
+#if !defined(__alpha__)
+	puts("x\tlink BSD partition to non-BSD partition");
+#endif
+}
+
+#if !defined(__alpha__)
+static int
+hidden(int type)
+{
+	return type ^ 0x10;
+}
+
+static int
+is_bsd_partition_type(int type)
+{
+	return (type == FREEBSD_PARTITION ||
+		type == hidden(FREEBSD_PARTITION) ||
+		type == NETBSD_PARTITION ||
+		type == hidden(NETBSD_PARTITION));
+}
+#endif
+
+static void
+bsd_select(void)
+{
+#if !defined(__alpha__)
+	int t, ss;
+	struct partition *p;
+
+	for (t = 0; t < 4; t++) {
+		p = get_part_table(t);
+		if (p && is_bsd_partition_type(p->sys_ind)) {
+			xbsd_part = p;
+			xbsd_part_index = t;
+			ss = get_start_sect(xbsd_part);
+			if (ss == 0) {
+				printf("Partition %s has invalid starting sector 0\n",
+					partname(disk_device, t+1, 0));
+				return;
+			}
+				printf("Reading disklabel of %s at sector %u\n",
+					partname(disk_device, t+1, 0), ss + BSD_LABELSECTOR);
+			if (xbsd_readlabel(xbsd_part) == 0)
+				if (xbsd_create_disklabel() == 0)
+					return;
+				break;
+		}
+	}
+
+	if (t == 4) {
+		printf("There is no *BSD partition on %s\n", disk_device);
+		return;
+	}
+
+#elif defined(__alpha__)
+
+	if (xbsd_readlabel(NULL) == 0)
+		if (xbsd_create_disklabel() == 0)
+			exit(EXIT_SUCCESS);
+
+#endif
+
+	while (1) {
+		bb_putchar('\n');
+		switch (tolower(read_nonempty("BSD disklabel command (m for help): "))) {
+		case 'd':
+			xbsd_delete_part();
+			break;
+		case 'e':
+			xbsd_edit_disklabel();
+			break;
+		case 'i':
+			xbsd_write_bootstrap();
+			break;
+		case 'l':
+			xbsd_list_types();
+			break;
+		case 'n':
+			xbsd_new_part();
+			break;
+		case 'p':
+			xbsd_print_disklabel(0);
+			break;
+		case 'q':
+			if (ENABLE_FEATURE_CLEAN_UP)
+				close_dev_fd();
+			exit(EXIT_SUCCESS);
+		case 'r':
+			return;
+		case 's':
+			xbsd_print_disklabel(1);
+			break;
+		case 't':
+			xbsd_change_fstype();
+			break;
+		case 'u':
+			change_units();
+			break;
+		case 'w':
+			xbsd_write_disklabel();
+			break;
+#if !defined(__alpha__)
+		case 'x':
+			xbsd_link_part();
+			break;
+#endif
+		default:
+			bsd_menu();
+			break;
+		}
+	}
+}
+
+static void
+xbsd_delete_part(void)
+{
+	int i;
+
+	i = xbsd_get_part_index(xbsd_dlabel.d_npartitions);
+	xbsd_dlabel.d_partitions[i].p_size   = 0;
+	xbsd_dlabel.d_partitions[i].p_offset = 0;
+	xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED;
+	if (xbsd_dlabel.d_npartitions == i + 1)
+		while (xbsd_dlabel.d_partitions[xbsd_dlabel.d_npartitions-1].p_size == 0)
+			xbsd_dlabel.d_npartitions--;
+}
+
+static void
+xbsd_new_part(void)
+{
+	off_t begin, end;
+	char mesg[256];
+	int i;
+
+	if (!xbsd_check_new_partition(&i))
+		return;
+
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__hppa__)
+	begin = get_start_sect(xbsd_part);
+	end = begin + get_nr_sects(xbsd_part) - 1;
+#else
+	begin = 0;
+	end = xbsd_dlabel.d_secperunit - 1;
+#endif
+
+	snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
+	begin = read_int(bsd_cround(begin), bsd_cround(begin), bsd_cround(end),
+		0, mesg);
+
+	if (display_in_cyl_units)
+		begin = (begin - 1) * xbsd_dlabel.d_secpercyl;
+
+	snprintf(mesg, sizeof(mesg), "Last %s or +size or +sizeM or +sizeK",
+		str_units(SINGULAR));
+	end = read_int(bsd_cround(begin), bsd_cround(end), bsd_cround(end),
+		bsd_cround(begin), mesg);
+
+	if (display_in_cyl_units)
+		end = end * xbsd_dlabel.d_secpercyl - 1;
+
+	xbsd_dlabel.d_partitions[i].p_size   = end - begin + 1;
+	xbsd_dlabel.d_partitions[i].p_offset = begin;
+	xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED;
+}
+
+static void
+xbsd_print_disklabel(int show_all)
+{
+	struct xbsd_disklabel *lp = &xbsd_dlabel;
+	struct xbsd_partition *pp;
+	int i, j;
+
+	if (show_all) {
+		static const int d_masks[] = { BSD_D_REMOVABLE, BSD_D_ECC, BSD_D_BADSECT };
+
+#if defined(__alpha__)
+		printf("# %s:\n", disk_device);
+#else
+		printf("# %s:\n", partname(disk_device, xbsd_part_index+1, 0));
+#endif
+		if ((unsigned) lp->d_type < ARRAY_SIZE(xbsd_dktypenames)-1)
+			printf("type: %s\n", xbsd_dktypenames[lp->d_type]);
+		else
+			printf("type: %u\n", lp->d_type);
+		printf("disk: %.*s\n", (int) sizeof(lp->d_typename), lp->d_typename);
+		printf("label: %.*s\n", (int) sizeof(lp->d_packname), lp->d_packname);
+		printf("flags: ");
+		print_flags_separated(d_masks, "removable\0""ecc\0""badsect\0", lp->d_flags, " ");
+		bb_putchar('\n');
+		/* On various machines the fields of *lp are short/int/long */
+		/* In order to avoid problems, we cast them all to long. */
+		printf("bytes/sector: %lu\n", (long) lp->d_secsize);
+		printf("sectors/track: %lu\n", (long) lp->d_nsectors);
+		printf("tracks/cylinder: %lu\n", (long) lp->d_ntracks);
+		printf("sectors/cylinder: %lu\n", (long) lp->d_secpercyl);
+		printf("cylinders: %lu\n", (long) lp->d_ncylinders);
+		printf("rpm: %u\n", lp->d_rpm);
+		printf("interleave: %u\n", lp->d_interleave);
+		printf("trackskew: %u\n", lp->d_trackskew);
+		printf("cylinderskew: %u\n", lp->d_cylskew);
+		printf("headswitch: %lu\t\t# milliseconds\n",
+			(long) lp->d_headswitch);
+		printf("track-to-track seek: %lu\t# milliseconds\n",
+			(long) lp->d_trkseek);
+		printf("drivedata: ");
+		for (i = NDDATA - 1; i >= 0; i--)
+			if (lp->d_drivedata[i])
+				break;
+		if (i < 0)
+			i = 0;
+		for (j = 0; j <= i; j++)
+			printf("%lu ", (long) lp->d_drivedata[j]);
+	}
+	printf("\n%u partitions:\n", lp->d_npartitions);
+	printf("#       start       end      size     fstype   [fsize bsize   cpg]\n");
+	pp = lp->d_partitions;
+	for (i = 0; i < lp->d_npartitions; i++, pp++) {
+		if (pp->p_size) {
+			if (display_in_cyl_units && lp->d_secpercyl) {
+				printf("  %c: %8lu%c %8lu%c %8lu%c  ",
+					'a' + i,
+					(unsigned long) pp->p_offset / lp->d_secpercyl + 1,
+					(pp->p_offset % lp->d_secpercyl) ? '*' : ' ',
+					(unsigned long) (pp->p_offset + pp->p_size + lp->d_secpercyl - 1) / lp->d_secpercyl,
+					((pp->p_offset + pp->p_size) % lp->d_secpercyl) ? '*' : ' ',
+					(long) pp->p_size / lp->d_secpercyl,
+					(pp->p_size % lp->d_secpercyl) ? '*' : ' '
+				);
+			} else {
+				printf("  %c: %8lu  %8lu  %8lu   ",
+					'a' + i,
+					(long) pp->p_offset,
+					(long) pp->p_offset + pp->p_size - 1,
+					(long) pp->p_size
+				);
+			}
+
+			if ((unsigned) pp->p_fstype < ARRAY_SIZE(xbsd_fstypes)-1)
+				printf("%8.8s", xbsd_fstypes[pp->p_fstype]);
+			else
+				printf("%8x", pp->p_fstype);
+
+			switch (pp->p_fstype) {
+			case BSD_FS_UNUSED:
+				printf("    %5lu %5lu %5.5s ",
+					(long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, "");
+				break;
+			case BSD_FS_BSDFFS:
+				printf("    %5lu %5lu %5u ",
+					(long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, pp->p_cpg);
+				break;
+			default:
+				printf("%22.22s", "");
+				break;
+			}
+			bb_putchar('\n');
+		}
+	}
+}
+
+static void
+xbsd_write_disklabel(void)
+{
+#if defined(__alpha__)
+	printf("Writing disklabel to %s\n", disk_device);
+	xbsd_writelabel(NULL);
+#else
+	printf("Writing disklabel to %s\n",
+		partname(disk_device, xbsd_part_index + 1, 0));
+	xbsd_writelabel(xbsd_part);
+#endif
+	reread_partition_table(0);      /* no exit yet */
+}
+
+static int
+xbsd_create_disklabel(void)
+{
+	char c;
+
+#if defined(__alpha__)
+	printf("%s contains no disklabel\n", disk_device);
+#else
+	printf("%s contains no disklabel\n",
+		partname(disk_device, xbsd_part_index + 1, 0));
+#endif
+
+	while (1) {
+		c = read_nonempty("Do you want to create a disklabel? (y/n) ");
+		if ((c|0x20) == 'y') {
+			if (xbsd_initlabel(
+#if defined(__alpha__) || defined(__powerpc__) || defined(__hppa__) || \
+	defined(__s390__) || defined(__s390x__)
+				NULL
+#else
+				xbsd_part
+#endif
+			) == 1) {
+				xbsd_print_disklabel(1);
+				return 1;
+			}
+			return 0;
+		}
+		if ((c|0x20) == 'n')
+			return 0;
+	}
+}
+
+static int
+edit_int(int def, const char *mesg)
+{
+	mesg = xasprintf("%s (%u): ", mesg, def);
+	do {
+		if (!read_line(mesg))
+			goto ret;
+	} while (!isdigit(*line_ptr));
+	def = atoi(line_ptr);
+ ret:
+	free((char*)mesg);
+	return def;
+}
+
+static void
+xbsd_edit_disklabel(void)
+{
+	struct xbsd_disklabel *d;
+
+	d = &xbsd_dlabel;
+
+#if defined(__alpha__) || defined(__ia64__)
+	d->d_secsize    = edit_int(d->d_secsize     , "bytes/sector");
+	d->d_nsectors   = edit_int(d->d_nsectors    , "sectors/track");
+	d->d_ntracks    = edit_int(d->d_ntracks     , "tracks/cylinder");
+	d->d_ncylinders = edit_int(d->d_ncylinders  , "cylinders");
+#endif
+
+	/* d->d_secpercyl can be != d->d_nsectors * d->d_ntracks */
+	while (1) {
+		d->d_secpercyl = edit_int(d->d_nsectors * d->d_ntracks,
+				"sectors/cylinder");
+		if (d->d_secpercyl <= d->d_nsectors * d->d_ntracks)
+			break;
+
+		printf("Must be <= sectors/track * tracks/cylinder (default)\n");
+	}
+	d->d_rpm        = edit_int(d->d_rpm       , "rpm");
+	d->d_interleave = edit_int(d->d_interleave, "interleave");
+	d->d_trackskew  = edit_int(d->d_trackskew , "trackskew");
+	d->d_cylskew    = edit_int(d->d_cylskew   , "cylinderskew");
+	d->d_headswitch = edit_int(d->d_headswitch, "headswitch");
+	d->d_trkseek    = edit_int(d->d_trkseek   , "track-to-track seek");
+
+	d->d_secperunit = d->d_secpercyl * d->d_ncylinders;
+}
+
+static int
+xbsd_get_bootstrap(char *path, void *ptr, int size)
+{
+	int fdb;
+
+	fdb = open_or_warn(path, O_RDONLY);
+	if (fdb < 0) {
+		return 0;
+	}
+	if (full_read(fdb, ptr, size) < 0) {
+		bb_simple_perror_msg(path);
+		close(fdb);
+		return 0;
+	}
+	printf(" ... %s\n", path);
+	close(fdb);
+	return 1;
+}
+
+static void
+sync_disks(void)
+{
+	printf("Syncing disks\n");
+	sync();
+	/* sleep(4); What? */
+}
+
+static void
+xbsd_write_bootstrap(void)
+{
+	char path[MAXPATHLEN];
+	const char *bootdir = BSD_LINUX_BOOTDIR;
+	const char *dkbasename;
+	struct xbsd_disklabel dl;
+	char *d, *p, *e;
+	int sector;
+
+	if (xbsd_dlabel.d_type == BSD_DTYPE_SCSI)
+		dkbasename = "sd";
+	else
+		dkbasename = "wd";
+
+	snprintf(path, sizeof(path), "Bootstrap: %sboot -> boot%s (%s): ",
+		dkbasename, dkbasename, dkbasename);
+	if (read_line(path)) {
+		dkbasename = line_ptr;
+	}
+	snprintf(path, sizeof(path), "%s/%sboot", bootdir, dkbasename);
+	if (!xbsd_get_bootstrap(path, disklabelbuffer, (int) xbsd_dlabel.d_secsize))
+		return;
+
+/* We need a backup of the disklabel (xbsd_dlabel might have changed). */
+	d = &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE];
+	memmove(&dl, d, sizeof(struct xbsd_disklabel));
+
+/* The disklabel will be overwritten by 0's from bootxx anyway */
+	memset(d, 0, sizeof(struct xbsd_disklabel));
+
+	snprintf(path, sizeof(path), "%s/boot%s", bootdir, dkbasename);
+	if (!xbsd_get_bootstrap(path, &disklabelbuffer[xbsd_dlabel.d_secsize],
+			(int) xbsd_dlabel.d_bbsize - xbsd_dlabel.d_secsize))
+		return;
+
+	e = d + sizeof(struct xbsd_disklabel);
+	for (p = d; p < e; p++)
+		if (*p) {
+			printf("Bootstrap overlaps with disk label!\n");
+			exit(EXIT_FAILURE);
+		}
+
+	memmove(d, &dl, sizeof(struct xbsd_disklabel));
+
+#if defined(__powerpc__) || defined(__hppa__)
+	sector = 0;
+#elif defined(__alpha__)
+	sector = 0;
+	alpha_bootblock_checksum(disklabelbuffer);
+#else
+	sector = get_start_sect(xbsd_part);
+#endif
+
+	seek_sector(sector);
+	xwrite(dev_fd, disklabelbuffer, BSD_BBSIZE);
+
+#if defined(__alpha__)
+	printf("Bootstrap installed on %s\n", disk_device);
+#else
+	printf("Bootstrap installed on %s\n",
+		partname(disk_device, xbsd_part_index+1, 0));
+#endif
+
+	sync_disks();
+}
+
+static void
+xbsd_change_fstype(void)
+{
+	int i;
+
+	i = xbsd_get_part_index(xbsd_dlabel.d_npartitions);
+	xbsd_dlabel.d_partitions[i].p_fstype = read_hex(xbsd_fstypes);
+}
+
+static int
+xbsd_get_part_index(int max)
+{
+	char prompt[sizeof("Partition (a-%c): ") + 16];
+	char l;
+
+	snprintf(prompt, sizeof(prompt), "Partition (a-%c): ", 'a' + max - 1);
+	do
+		l = tolower(read_nonempty(prompt));
+	while (l < 'a' || l > 'a' + max - 1);
+	return l - 'a';
+}
+
+static int
+xbsd_check_new_partition(int *i)
+{
+	/* room for more? various BSD flavours have different maxima */
+	if (xbsd_dlabel.d_npartitions == BSD_MAXPARTITIONS) {
+		int t;
+
+		for (t = 0; t < BSD_MAXPARTITIONS; t++)
+			if (xbsd_dlabel.d_partitions[t].p_size == 0)
+				break;
+
+		if (t == BSD_MAXPARTITIONS) {
+			printf("The maximum number of partitions has been created\n");
+			return 0;
+		}
+	}
+
+	*i = xbsd_get_part_index(BSD_MAXPARTITIONS);
+
+	if (*i >= xbsd_dlabel.d_npartitions)
+		xbsd_dlabel.d_npartitions = (*i) + 1;
+
+	if (xbsd_dlabel.d_partitions[*i].p_size != 0) {
+		printf("This partition already exists\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+static void
+xbsd_list_types(void)
+{
+	list_types(xbsd_fstypes);
+}
+
+static uint16_t
+xbsd_dkcksum(struct xbsd_disklabel *lp)
+{
+	uint16_t *start, *end;
+	uint16_t sum = 0;
+
+	start = (uint16_t *) lp;
+	end = (uint16_t *) &lp->d_partitions[lp->d_npartitions];
+	while (start < end)
+		sum ^= *start++;
+	return sum;
+}
+
+static int
+xbsd_initlabel(struct partition *p)
+{
+	struct xbsd_disklabel *d = &xbsd_dlabel;
+	struct xbsd_partition *pp;
+
+	get_geometry();
+	memset(d, 0, sizeof(struct xbsd_disklabel));
+
+	d->d_magic = BSD_DISKMAGIC;
+
+	if (strncmp(disk_device, "/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 = SECTOR_SIZE;           /* bytes/sector  */
+	d->d_nsectors = g_sectors;            /* sectors/track */
+	d->d_ntracks = g_heads;               /* tracks/cylinder (heads) */
+	d->d_ncylinders = g_cylinders;
+	d->d_secpercyl  = g_sectors * g_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 !defined(__alpha__)
+	d->d_npartitions = 4;
+	pp = &d->d_partitions[2]; /* Partition C should be NetBSD partition */
+
+	pp->p_offset = get_start_sect(p);
+	pp->p_size   = get_nr_sects(p);
+	pp->p_fstype = BSD_FS_UNUSED;
+	pp = &d->d_partitions[3]; /* Partition D should be 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;
+#endif
+
+	return 1;
+}
+
+/*
+ * Read a xbsd_disklabel from sector 0 or from the starting sector of p.
+ * If it has the right magic, return 1.
+ */
+static int
+xbsd_readlabel(struct partition *p)
+{
+	struct xbsd_disklabel *d;
+	int t, sector;
+
+	if (!bsd_globals_ptr)
+		bsd_globals_ptr = xzalloc(sizeof(*bsd_globals_ptr));
+
+	d = &xbsd_dlabel;
+
+	/* p is used only to get the starting sector */
+#if !defined(__alpha__)
+	sector = (p ? get_start_sect(p) : 0);
+#else
+	sector = 0;
+#endif
+
+	seek_sector(sector);
+	if (BSD_BBSIZE != full_read(dev_fd, disklabelbuffer, BSD_BBSIZE))
+		fdisk_fatal(unable_to_read);
+
+	memmove(d, &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET],
+			sizeof(struct xbsd_disklabel));
+
+	if (d->d_magic != BSD_DISKMAGIC || d->d_magic2 != BSD_DISKMAGIC)
+		return 0;
+
+	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)
+		printf("Warning: too many partitions (%u, maximum is %u)\n",
+			d->d_npartitions, BSD_MAXPARTITIONS);
+	return 1;
+}
+
+static int
+xbsd_writelabel(struct partition *p)
+{
+	struct xbsd_disklabel *d = &xbsd_dlabel;
+	unsigned int sector;
+
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__hppa__)
+	sector = get_start_sect(p) + BSD_LABELSECTOR;
+#else
+	(void)p; /* silence warning */
+	sector = BSD_LABELSECTOR;
+#endif
+
+	d->d_checksum = 0;
+	d->d_checksum = xbsd_dkcksum(d);
+
+	/* This is necessary if we want to write the bootstrap later,
+	   otherwise we'd write the old disklabel with the bootstrap.
+	*/
+	memmove(&disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET],
+		d, sizeof(struct xbsd_disklabel));
+
+#if defined(__alpha__) && BSD_LABELSECTOR == 0
+	alpha_bootblock_checksum(disklabelbuffer);
+	seek_sector(0);
+	xwrite(dev_fd, disklabelbuffer, BSD_BBSIZE);
+#else
+	seek_sector(sector);
+	lseek(dev_fd, BSD_LABELOFFSET, SEEK_CUR);
+	xwrite(dev_fd, d, sizeof(*d));
+#endif
+	sync_disks();
+	return 1;
+}
+
+
+#if !defined(__alpha__)
+static int
+xbsd_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          */
+	case 0xf2: /* DOS secondary    */
+		return BSD_FS_MSDOS;
+	case 0x07: /* OS/2 HPFS        */
+		return BSD_FS_HPFS;
+	default:
+		return BSD_FS_OTHER;
+	}
+}
+
+static void
+xbsd_link_part(void)
+{
+	int k, i;
+	struct partition *p;
+
+	k = get_partition(1, g_partitions);
+
+	if (!xbsd_check_new_partition(&i))
+		return;
+
+	p = get_part_table(k);
+
+	xbsd_dlabel.d_partitions[i].p_size   = get_nr_sects(p);
+	xbsd_dlabel.d_partitions[i].p_offset = get_start_sect(p);
+	xbsd_dlabel.d_partitions[i].p_fstype = xbsd_translate_fstype(p->sys_ind);
+}
+#endif
+
+#if defined(__alpha__)
+static void
+alpha_bootblock_checksum(char *boot)
+{
+	uint64_t *dp, sum;
+	int i;
+
+	dp = (uint64_t *)boot;
+	sum = 0;
+	for (i = 0; i < 63; i++)
+		sum += dp[i];
+	dp[63] = sum;
+}
+#endif /* __alpha__ */
+
+/* Undefine 'global' tricks */
+#undef disklabelbuffer
+#undef xbsd_dlabel
+
+#endif /* OSF_LABEL */
diff --git a/ap/app/busybox/src/util-linux/fdisk_sgi.c b/ap/app/busybox/src/util-linux/fdisk_sgi.c
new file mode 100644
index 0000000..785fc66
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/fdisk_sgi.c
@@ -0,0 +1,886 @@
+/*
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#if ENABLE_FEATURE_SGI_LABEL
+
+#define SGI_DEBUG 0
+
+#define SGI_VOLHDR      0x00
+/* 1 and 2 were used for drive types no longer supported by SGI */
+#define SGI_SWAP        0x03
+/* 4 and 5 were for filesystem types SGI haven't ever supported on MIPS CPUs */
+#define SGI_VOLUME      0x06
+#define SGI_EFS         0x07
+#define SGI_LVOL        0x08
+#define SGI_RLVOL       0x09
+#define SGI_XFS         0x0a
+#define SGI_XFSLOG      0x0b
+#define SGI_XLV         0x0c
+#define SGI_XVM         0x0d
+#define SGI_ENTIRE_DISK SGI_VOLUME
+
+struct device_parameter { /* 48 bytes */
+	unsigned char  skew;
+	unsigned char  gap1;
+	unsigned char  gap2;
+	unsigned char  sparecyl;
+	unsigned short pcylcount;
+	unsigned short head_vol0;
+	unsigned short ntrks;   /* tracks in cyl 0 or vol 0 */
+	unsigned char  cmd_tag_queue_depth;
+	unsigned char  unused0;
+	unsigned short unused1;
+	unsigned short nsect;   /* sectors/tracks in cyl 0 or vol 0 */
+	unsigned short bytes;
+	unsigned short ilfact;
+	unsigned int   flags;   /* controller flags */
+	unsigned int   datarate;
+	unsigned int   retries_on_error;
+	unsigned int   ms_per_word;
+	unsigned short xylogics_gap1;
+	unsigned short xylogics_syncdelay;
+	unsigned short xylogics_readdelay;
+	unsigned short xylogics_gap2;
+	unsigned short xylogics_readgate;
+	unsigned short xylogics_writecont;
+};
+
+/*
+ * controller flags
+ */
+#define SECTOR_SLIP     0x01
+#define SECTOR_FWD      0x02
+#define TRACK_FWD       0x04
+#define TRACK_MULTIVOL  0x08
+#define IGNORE_ERRORS   0x10
+#define RESEEK          0x20
+#define ENABLE_CMDTAGQ  0x40
+
+typedef struct {
+	unsigned int   magic;            /* expect SGI_LABEL_MAGIC */
+	unsigned short boot_part;        /* active boot partition */
+	unsigned short swap_part;        /* active swap partition */
+	unsigned char  boot_file[16];    /* name of the bootfile */
+	struct device_parameter devparam;       /*  1 * 48 bytes */
+	struct volume_directory {               /* 15 * 16 bytes */
+		unsigned char vol_file_name[8]; /* a character array */
+		unsigned int  vol_file_start;   /* number of logical block */
+		unsigned int  vol_file_size;    /* number of bytes */
+	} directory[15];
+	struct sgi_partinfo {                   /* 16 * 12 bytes */
+		unsigned int num_sectors;       /* number of blocks */
+		unsigned int start_sector;      /* must be cylinder aligned */
+		unsigned int id;
+	} partitions[16];
+	unsigned int   csum;
+	unsigned int   fillbytes;
+} sgi_partition;
+
+typedef struct {
+	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];
+} sgiinfo;
+
+#define SGI_LABEL_MAGIC         0x0be5a941
+#define SGI_LABEL_MAGIC_SWAPPED 0x41a9e50b
+#define SGI_INFO_MAGIC          0x00072959
+#define SGI_INFO_MAGIC_SWAPPED  0x59290700
+
+#define SGI_SSWAP16(x) (sgi_other_endian ? fdisk_swap16(x) : (uint16_t)(x))
+#define SGI_SSWAP32(x) (sgi_other_endian ? fdisk_swap32(x) : (uint32_t)(x))
+
+#define sgilabel ((sgi_partition *)MBRbuffer)
+#define sgiparam (sgilabel->devparam)
+
+/*
+ *
+ * fdisksgilabel.c
+ *
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ *      This file may be modified and redistributed under
+ *      the terms of the GNU Public License.
+ *
+ * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *      Internationalization
+ */
+
+
+static smallint sgi_other_endian; /* bool */
+static smallint sgi_volumes = 1; /* max 15 */
+
+/*
+ * only dealing with free blocks here
+ */
+
+typedef struct {
+	unsigned int first;
+	unsigned int last;
+} freeblocks;
+static freeblocks freelist[17]; /* 16 partitions can produce 17 vacant slots */
+
+static void
+setfreelist(int i, unsigned int f, unsigned int l)
+{
+	freelist[i].first = f;
+	freelist[i].last = l;
+}
+
+static void
+add2freelist(unsigned int f, unsigned int l)
+{
+	int i;
+	for (i = 0; i < 17; i++)
+		if (freelist[i].last == 0)
+			break;
+	setfreelist(i, f, l);
+}
+
+static void
+clearfreelist(void)
+{
+	int i;
+
+	for (i = 0; i < 17; i++)
+		setfreelist(i, 0, 0);
+}
+
+static unsigned int
+isinfreelist(unsigned int b)
+{
+	int i;
+
+	for (i = 0; i < 17; i++)
+		if (freelist[i].first <= b && freelist[i].last >= b)
+			return freelist[i].last;
+	return 0;
+}
+	/* return last vacant block of this stride (never 0). */
+	/* the '>=' is not quite correct, but simplifies the code */
+/*
+ * end of free blocks section
+ */
+
+static const char *const sgi_sys_types[] = {
+/* SGI_VOLHDR   */	"\x00" "SGI volhdr"  ,
+/* 0x01         */	"\x01" "SGI trkrepl" ,
+/* 0x02         */	"\x02" "SGI secrepl" ,
+/* SGI_SWAP     */	"\x03" "SGI raw"     ,
+/* 0x04         */	"\x04" "SGI bsd"     ,
+/* 0x05         */	"\x05" "SGI sysv"    ,
+/* SGI_ENTIRE_DISK  */	"\x06" "SGI volume"  ,
+/* SGI_EFS      */	"\x07" "SGI efs"     ,
+/* 0x08         */	"\x08" "SGI lvol"    ,
+/* 0x09         */	"\x09" "SGI rlvol"   ,
+/* SGI_XFS      */	"\x0a" "SGI xfs"     ,
+/* SGI_XFSLOG   */	"\x0b" "SGI xfslog"  ,
+/* SGI_XLV      */	"\x0c" "SGI xlv"     ,
+/* SGI_XVM      */	"\x0d" "SGI xvm"     ,
+/* LINUX_SWAP   */	"\x82" "Linux swap"  ,
+/* LINUX_NATIVE */	"\x83" "Linux native",
+/* LINUX_LVM    */	"\x8d" "Linux LVM"   ,
+/* LINUX_RAID   */	"\xfd" "Linux RAID"  ,
+			NULL
+};
+
+
+static int
+sgi_get_nsect(void)
+{
+	return SGI_SSWAP16(sgilabel->devparam.nsect);
+}
+
+static int
+sgi_get_ntrks(void)
+{
+	return SGI_SSWAP16(sgilabel->devparam.ntrks);
+}
+
+static unsigned int
+two_s_complement_32bit_sum(unsigned int* base, int size /* in bytes */)
+{
+	int i = 0;
+	unsigned int sum = 0;
+
+	size /= sizeof(unsigned int);
+	for (i = 0; i < size; i++)
+		sum -= SGI_SSWAP32(base[i]);
+	return sum;
+}
+
+void BUG_bad_sgi_partition_size(void);
+
+static int
+check_sgi_label(void)
+{
+	if (sizeof(sgi_partition) > 512) {
+		/* According to MIPS Computer Systems, Inc the label
+		 * must not contain more than 512 bytes */
+		BUG_bad_sgi_partition_size();
+	}
+
+	if (sgilabel->magic != SGI_LABEL_MAGIC
+	 && sgilabel->magic != SGI_LABEL_MAGIC_SWAPPED
+	) {
+		current_label_type = LABEL_DOS;
+		return 0;
+	}
+
+	sgi_other_endian = (sgilabel->magic == SGI_LABEL_MAGIC_SWAPPED);
+	/*
+	 * test for correct checksum
+	 */
+	if (two_s_complement_32bit_sum((unsigned int*)sgilabel,
+				sizeof(*sgilabel))) {
+		printf("Detected sgi disklabel with wrong checksum\n");
+	}
+	update_units();
+	current_label_type = LABEL_SGI;
+	g_partitions = 16;
+	sgi_volumes = 15;
+	return 1;
+}
+
+static unsigned int
+sgi_get_start_sector(int i)
+{
+	return SGI_SSWAP32(sgilabel->partitions[i].start_sector);
+}
+
+static unsigned int
+sgi_get_num_sectors(int i)
+{
+	return SGI_SSWAP32(sgilabel->partitions[i].num_sectors);
+}
+
+static int
+sgi_get_sysid(int i)
+{
+	return SGI_SSWAP32(sgilabel->partitions[i].id);
+}
+
+static int
+sgi_get_bootpartition(void)
+{
+	return SGI_SSWAP16(sgilabel->boot_part);
+}
+
+static int
+sgi_get_swappartition(void)
+{
+	return SGI_SSWAP16(sgilabel->swap_part);
+}
+
+static void
+sgi_list_table(int xtra)
+{
+	int i, w, wd;
+	int kpi = 0;                /* kernel partition ID */
+
+	if (xtra) {
+		printf("\nDisk %s (SGI disk label): %u heads, %u sectors\n"
+			"%u cylinders, %u physical cylinders\n"
+			"%u extra sects/cyl, interleave %u:1\n"
+			"%s\n"
+			"Units = %s of %u * 512 bytes\n\n",
+			disk_device, g_heads, g_sectors, g_cylinders,
+			SGI_SSWAP16(sgiparam.pcylcount),
+			SGI_SSWAP16(sgiparam.sparecyl),
+			SGI_SSWAP16(sgiparam.ilfact),
+			(char *)sgilabel,
+			str_units(PLURAL), units_per_sector);
+	} else {
+		printf("\nDisk %s (SGI disk label): "
+			"%u heads, %u sectors, %u cylinders\n"
+			"Units = %s of %u * 512 bytes\n\n",
+			disk_device, g_heads, g_sectors, g_cylinders,
+			str_units(PLURAL), units_per_sector );
+	}
+
+	w = strlen(disk_device);
+	wd = sizeof("Device") - 1;
+	if (w < wd)
+	w = wd;
+
+	printf("----- partitions -----\n"
+		"Pt# %*s  Info     Start       End   Sectors  Id  System\n",
+		w + 2, "Device");
+	for (i = 0; i < g_partitions; i++) {
+		if (sgi_get_num_sectors(i) || SGI_DEBUG) {
+			uint32_t start = sgi_get_start_sector(i);
+			uint32_t len = sgi_get_num_sectors(i);
+			kpi++;              /* only count nonempty partitions */
+			printf(
+			"%2u: %s %4s %9lu %9lu %9lu  %2x  %s\n",
+/* fdisk part number */	i+1,
+/* device */            partname(disk_device, kpi, w+3),
+/* flags */             (sgi_get_swappartition() == i) ? "swap" :
+/* flags */             (sgi_get_bootpartition() == i) ? "boot" : "    ",
+/* start */             (long) scround(start),
+/* end */               (long) scround(start+len)-1,
+/* no odd flag on end */(long) len,
+/* type id */           sgi_get_sysid(i),
+/* type name */         partition_type(sgi_get_sysid(i)));
+		}
+	}
+	printf("----- Bootinfo -----\nBootfile: %s\n"
+		"----- Directory Entries -----\n",
+		sgilabel->boot_file);
+	for (i = 0; i < sgi_volumes; i++) {
+		if (sgilabel->directory[i].vol_file_size) {
+			uint32_t start = SGI_SSWAP32(sgilabel->directory[i].vol_file_start);
+			uint32_t len = SGI_SSWAP32(sgilabel->directory[i].vol_file_size);
+			unsigned char *name = sgilabel->directory[i].vol_file_name;
+
+			printf("%2u: %-10s sector%5u size%8u\n",
+				i, (char*)name, (unsigned int) start, (unsigned int) len);
+		}
+	}
+}
+
+static void
+sgi_set_bootpartition(int i)
+{
+	sgilabel->boot_part = SGI_SSWAP16(((short)i));
+}
+
+static unsigned int
+sgi_get_lastblock(void)
+{
+	return g_heads * g_sectors * g_cylinders;
+}
+
+static void
+sgi_set_swappartition(int i)
+{
+	sgilabel->swap_part = SGI_SSWAP16(((short)i));
+}
+
+static int
+sgi_check_bootfile(const char* aFile)
+{
+	if (strlen(aFile) < 3) /* "/a\n" is minimum */ {
+		printf("\nInvalid Bootfile!\n"
+			"\tThe bootfile must be an absolute non-zero pathname,\n"
+			"\te.g. \"/unix\" or \"/unix.save\".\n");
+		return 0;
+	}
+	if (strlen(aFile) > 16) {
+		printf("\nName of Bootfile too long (>16 bytes)\n");
+		return 0;
+	}
+	if (aFile[0] != '/') {
+		printf("\nBootfile must have a fully qualified pathname\n");
+		return 0;
+	}
+	if (strncmp(aFile, (char*)sgilabel->boot_file, 16)) {
+		printf("\nBe aware, that the bootfile is not checked for existence.\n"
+			 "\tSGI's default is \"/unix\" and for backup \"/unix.save\".\n");
+		/* filename is correct and did change */
+		return 1;
+	}
+	return 0;   /* filename did not change */
+}
+
+static const char *
+sgi_get_bootfile(void)
+{
+	return (char*)sgilabel->boot_file;
+}
+
+static void
+sgi_set_bootfile(const char* aFile)
+{
+	int i = 0;
+
+	if (sgi_check_bootfile(aFile)) {
+		while (i < 16) {
+			if ((aFile[i] != '\n')  /* in principle caught again by next line */
+			 && (strlen(aFile) > i))
+				sgilabel->boot_file[i] = aFile[i];
+			else
+				sgilabel->boot_file[i] = 0;
+			i++;
+		}
+		printf("\n\tBootfile is changed to \"%s\"\n", sgilabel->boot_file);
+	}
+}
+
+static void
+create_sgiinfo(void)
+{
+	/* I keep SGI's habit to write the sgilabel to the second block */
+	sgilabel->directory[0].vol_file_start = SGI_SSWAP32(2);
+	sgilabel->directory[0].vol_file_size = SGI_SSWAP32(sizeof(sgiinfo));
+	strncpy((char*)sgilabel->directory[0].vol_file_name, "sgilabel", 8);
+}
+
+static sgiinfo *fill_sgiinfo(void);
+
+static void
+sgi_write_table(void)
+{
+	sgilabel->csum = 0;
+	sgilabel->csum = SGI_SSWAP32(two_s_complement_32bit_sum(
+			(unsigned int*)sgilabel, sizeof(*sgilabel)));
+	assert(two_s_complement_32bit_sum(
+		(unsigned int*)sgilabel, sizeof(*sgilabel)) == 0);
+
+	write_sector(0, sgilabel);
+	if (!strncmp((char*)sgilabel->directory[0].vol_file_name, "sgilabel", 8)) {
+		/*
+		 * keep this habit of first writing the "sgilabel".
+		 * I never tested whether it works without (AN 981002).
+		 */
+		sgiinfo *info = fill_sgiinfo();
+		int infostartblock = SGI_SSWAP32(sgilabel->directory[0].vol_file_start);
+		write_sector(infostartblock, info);
+		free(info);
+	}
+}
+
+static int
+compare_start(int *x, int *y)
+{
+	/*
+	 * sort according to start sectors
+	 * and prefers largest partition:
+	 * entry zero is entire disk entry
+	 */
+	unsigned int i = *x;
+	unsigned int j = *y;
+	unsigned int a = sgi_get_start_sector(i);
+	unsigned int b = sgi_get_start_sector(j);
+	unsigned int c = sgi_get_num_sectors(i);
+	unsigned int d = sgi_get_num_sectors(j);
+
+	if (a == b)
+		return (d > c) ? 1 : (d == c) ? 0 : -1;
+	return (a > b) ? 1 : -1;
+}
+
+
+static int
+verify_sgi(int verbose)
+{
+	int Index[16];      /* 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();
+
+	clearfreelist();
+	for (i = 0; i < 16; i++) {
+		if (sgi_get_num_sectors(i) != 0) {
+			Index[sortcount++] = i;
+			if (sgi_get_sysid(i) == SGI_ENTIRE_DISK) {
+				if (entire++ == 1) {
+					if (verbose)
+						printf("More than one entire disk entry present\n");
+				}
+			}
+		}
+	}
+	if (sortcount == 0) {
+		if (verbose)
+			printf("No partitions defined\n");
+		return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1;
+	}
+	qsort(Index, sortcount, sizeof(Index[0]), (void*)compare_start);
+	if (sgi_get_sysid(Index[0]) == SGI_ENTIRE_DISK) {
+		if ((Index[0] != 10) && verbose)
+			printf("IRIX likes when Partition 11 covers the entire disk\n");
+		if ((sgi_get_start_sector(Index[0]) != 0) && verbose)
+			printf("The entire disk partition should start "
+				"at block 0,\n"
+				"not at diskblock %u\n",
+				sgi_get_start_sector(Index[0]));
+		if (SGI_DEBUG)      /* I do not understand how some disks fulfil it */
+			if ((sgi_get_num_sectors(Index[0]) != lastblock) && verbose)
+				printf("The entire disk partition is only %u diskblock large,\n"
+					"but the disk is %u diskblocks long\n",
+					sgi_get_num_sectors(Index[0]), lastblock);
+			lastblock = sgi_get_num_sectors(Index[0]);
+	} else {
+		if (verbose)
+			printf("One Partition (#11) should cover the entire disk\n");
+		if (SGI_DEBUG > 2)
+			printf("sysid=%u\tpartition=%u\n",
+				sgi_get_sysid(Index[0]), Index[0]+1);
+	}
+	for (i = 1, start = 0; i < sortcount; i++) {
+		int cylsize = sgi_get_nsect() * sgi_get_ntrks();
+
+		if ((sgi_get_start_sector(Index[i]) % cylsize) != 0) {
+			if (SGI_DEBUG)      /* I do not understand how some disks fulfil it */
+				if (verbose)
+					printf("Partition %u does not start on cylinder boundary\n",
+						Index[i]+1);
+		}
+		if (sgi_get_num_sectors(Index[i]) % cylsize != 0) {
+			if (SGI_DEBUG)      /* I do not understand how some disks fulfil it */
+				if (verbose)
+					printf("Partition %u does not end on cylinder boundary\n",
+						Index[i]+1);
+		}
+		/* We cannot handle several "entire disk" entries. */
+		if (sgi_get_sysid(Index[i]) == SGI_ENTIRE_DISK) continue;
+		if (start > sgi_get_start_sector(Index[i])) {
+			if (verbose)
+				printf("Partitions %u and %u overlap by %u sectors\n",
+					Index[i-1]+1, Index[i]+1,
+					start - sgi_get_start_sector(Index[i]));
+			if (gap > 0) gap = -gap;
+			if (gap == 0) gap = -1;
+		}
+		if (start < sgi_get_start_sector(Index[i])) {
+			if (verbose)
+				printf("Unused gap of %u sectors - sectors %u-%u\n",
+					sgi_get_start_sector(Index[i]) - start,
+					start, sgi_get_start_sector(Index[i])-1);
+			gap += sgi_get_start_sector(Index[i]) - start;
+			add2freelist(start, sgi_get_start_sector(Index[i]));
+		}
+		start = sgi_get_start_sector(Index[i])
+			   + sgi_get_num_sectors(Index[i]);
+		if (SGI_DEBUG > 1) {
+			if (verbose)
+				printf("%2u:%12u\t%12u\t%12u\n", Index[i],
+					sgi_get_start_sector(Index[i]),
+					sgi_get_num_sectors(Index[i]),
+					sgi_get_sysid(Index[i]));
+		}
+	}
+	if (start < lastblock) {
+		if (verbose)
+			printf("Unused gap of %u sectors - sectors %u-%u\n",
+				lastblock - start, start, lastblock-1);
+		gap += lastblock - start;
+		add2freelist(start, lastblock);
+	}
+	/*
+	 * Done with arithmetics
+	 * Go for details now
+	 */
+	if (verbose) {
+		if (!sgi_get_num_sectors(sgi_get_bootpartition())) {
+			printf("\nThe boot partition does not exist\n");
+		}
+		if (!sgi_get_num_sectors(sgi_get_swappartition())) {
+			printf("\nThe swap partition does not exist\n");
+		} else {
+			if ((sgi_get_sysid(sgi_get_swappartition()) != SGI_SWAP)
+			 && (sgi_get_sysid(sgi_get_swappartition()) != LINUX_SWAP))
+				printf("\nThe swap partition has no swap type\n");
+		}
+		if (sgi_check_bootfile("/unix"))
+			printf("\tYou have chosen an unusual boot file name\n");
+	}
+	return (gap > 0) ? 1 : (gap == 0) ? 0 : -1;
+}
+
+static int
+sgi_gaps(void)
+{
+	/*
+	 * 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_sgi(0);
+}
+
+static void
+sgi_change_sysid(int i, int sys)
+{
+	if (sgi_get_num_sectors(i) == 0) { /* caught already before, ... */
+		printf("Sorry you may change the Tag of non-empty partitions\n");
+		return;
+	}
+	if ((sys != SGI_ENTIRE_DISK) && (sys != SGI_VOLHDR)
+	 && (sgi_get_start_sector(i) < 1)
+	) {
+		read_maybe_empty(
+			"It is highly recommended that the partition at offset 0\n"
+			"is of type \"SGI volhdr\", the IRIX system will rely on it to\n"
+			"retrieve from its directory standalone tools like sash and fx.\n"
+			"Only the \"SGI volume\" entire disk section may violate this.\n"
+			"Type YES if you are sure about tagging this partition differently.\n");
+		if (strcmp(line_ptr, "YES\n") != 0)
+			return;
+	}
+	sgilabel->partitions[i].id = SGI_SSWAP32(sys);
+}
+
+/* returns partition index of first entry marked as entire disk */
+static int
+sgi_entire(void)
+{
+	int i;
+
+	for (i = 0; i < 16; i++)
+		if (sgi_get_sysid(i) == SGI_VOLUME)
+			return i;
+	return -1;
+}
+
+static void
+sgi_set_partition(int i, unsigned int start, unsigned int length, int sys)
+{
+	sgilabel->partitions[i].id = SGI_SSWAP32(sys);
+	sgilabel->partitions[i].num_sectors = SGI_SSWAP32(length);
+	sgilabel->partitions[i].start_sector = SGI_SSWAP32(start);
+	set_changed(i);
+	if (sgi_gaps() < 0)     /* rebuild freelist */
+		printf("Partition overlap detected\n");
+}
+
+static void
+sgi_set_entire(void)
+{
+	int n;
+
+	for (n = 10; n < g_partitions; n++) {
+		if (!sgi_get_num_sectors(n) ) {
+			sgi_set_partition(n, 0, sgi_get_lastblock(), SGI_VOLUME);
+			break;
+		}
+	}
+}
+
+static void
+sgi_set_volhdr(void)
+{
+	int n;
+
+	for (n = 8; n < g_partitions; n++) {
+	if (!sgi_get_num_sectors(n)) {
+		/*
+		 * 5 cylinders is an arbitrary value I like
+		 * IRIX 5.3 stored files in the volume header
+		 * (like sash, symmon, fx, ide) with ca. 3200
+		 * sectors.
+		 */
+		if (g_heads * g_sectors * 5 < sgi_get_lastblock())
+			sgi_set_partition(n, 0, g_heads * g_sectors * 5, SGI_VOLHDR);
+			break;
+		}
+	}
+}
+
+static void
+sgi_delete_partition(int i)
+{
+	sgi_set_partition(i, 0, 0, 0);
+}
+
+static void
+sgi_add_partition(int n, int sys)
+{
+	char mesg[256];
+	unsigned int first = 0, last = 0;
+
+	if (n == 10) {
+		sys = SGI_VOLUME;
+	} else if (n == 8) {
+		sys = 0;
+	}
+	if (sgi_get_num_sectors(n)) {
+		printf(msg_part_already_defined, n + 1);
+		return;
+	}
+	if ((sgi_entire() == -1) && (sys != SGI_VOLUME)) {
+		printf("Attempting to generate entire disk entry automatically\n");
+		sgi_set_entire();
+		sgi_set_volhdr();
+	}
+	if ((sgi_gaps() == 0) && (sys != SGI_VOLUME)) {
+		printf("The entire disk is already covered with partitions\n");
+		return;
+	}
+	if (sgi_gaps() < 0) {
+		printf("You got a partition overlap on the disk. Fix it first!\n");
+		return;
+	}
+	snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
+	while (1) {
+		if (sys == SGI_VOLUME) {
+			last = sgi_get_lastblock();
+			first = read_int(0, 0, last-1, 0, mesg);
+			if (first != 0) {
+				printf("It is highly recommended that eleventh partition\n"
+						"covers the entire disk and is of type 'SGI volume'\n");
+			}
+		} else {
+			first = freelist[0].first;
+			last  = freelist[0].last;
+			first = read_int(scround(first), scround(first), scround(last)-1,
+				0, mesg);
+		}
+		if (display_in_cyl_units)
+			first *= units_per_sector;
+		else
+			first = first; /* align to cylinder if you know how ... */
+		if (!last )
+			last = isinfreelist(first);
+		if (last != 0)
+			break;
+		printf("You will get a partition overlap on the disk. "
+				"Fix it first!\n");
+	}
+	snprintf(mesg, sizeof(mesg), " Last %s", str_units(SINGULAR));
+	last = read_int(scround(first), scround(last)-1, scround(last)-1,
+			scround(first), mesg)+1;
+	if (display_in_cyl_units)
+		last *= units_per_sector;
+	else
+		last = last; /* align to cylinder if You know how ... */
+	if ( (sys == SGI_VOLUME) && (first != 0 || last != sgi_get_lastblock() ) )
+		printf("It is highly recommended that eleventh partition\n"
+			"covers the entire disk and is of type 'SGI volume'\n");
+	sgi_set_partition(n, first, last-first, sys);
+}
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+static void
+create_sgilabel(void)
+{
+	struct hd_geometry geometry;
+	struct {
+		unsigned int start;
+		unsigned int nsect;
+		int sysid;
+	} old[4];
+	int i = 0;
+	long longsectors;               /* the number of sectors on the device */
+	int res;                        /* the result from the ioctl */
+	int sec_fac;                    /* the sector factor */
+
+	sec_fac = sector_size / 512;    /* determine the sector factor */
+
+	printf(msg_building_new_label, "SGI disklabel");
+
+	sgi_other_endian = BB_LITTLE_ENDIAN;
+	res = ioctl(dev_fd, BLKGETSIZE, &longsectors);
+	if (!ioctl(dev_fd, HDIO_GETGEO, &geometry)) {
+		g_heads = geometry.heads;
+		g_sectors = geometry.sectors;
+		if (res == 0) {
+			/* the get device size ioctl was successful */
+			g_cylinders = longsectors / (g_heads * g_sectors);
+			g_cylinders /= sec_fac;
+		} else {
+			/* otherwise print error and use truncated version */
+			g_cylinders = geometry.cylinders;
+			printf(
+"Warning: BLKGETSIZE ioctl failed on %s.  Using geometry cylinder value of %u.\n"
+"This value may be truncated for devices > 33.8 GB.\n", disk_device, g_cylinders);
+		}
+	}
+	for (i = 0; i < 4; i++) {
+		old[i].sysid = 0;
+		if (valid_part_table_flag(MBRbuffer)) {
+			if (get_part_table(i)->sys_ind) {
+				old[i].sysid = get_part_table(i)->sys_ind;
+				old[i].start = get_start_sect(get_part_table(i));
+				old[i].nsect = get_nr_sects(get_part_table(i));
+				printf("Trying to keep parameters of partition %u\n", i);
+				if (SGI_DEBUG)
+					printf("ID=%02x\tSTART=%u\tLENGTH=%u\n",
+				old[i].sysid, old[i].start, old[i].nsect);
+			}
+		}
+	}
+
+	memset(MBRbuffer, 0, sizeof(MBRbuffer));
+	/* fields with '//' are already zeroed out by memset above */
+
+	sgilabel->magic = SGI_SSWAP32(SGI_LABEL_MAGIC);
+	//sgilabel->boot_part = SGI_SSWAP16(0);
+	sgilabel->swap_part = SGI_SSWAP16(1);
+
+	//memset(sgilabel->boot_file, 0, 16);
+	strcpy((char*)sgilabel->boot_file, "/unix"); /* sizeof(sgilabel->boot_file) == 16 > 6 */
+
+	//sgilabel->devparam.skew                     = (0);
+	//sgilabel->devparam.gap1                     = (0);
+	//sgilabel->devparam.gap2                     = (0);
+	//sgilabel->devparam.sparecyl                 = (0);
+	sgilabel->devparam.pcylcount                = SGI_SSWAP16(geometry.cylinders);
+	//sgilabel->devparam.head_vol0                = SGI_SSWAP16(0);
+	/* tracks/cylinder (heads) */
+	sgilabel->devparam.ntrks                    = SGI_SSWAP16(geometry.heads);
+	//sgilabel->devparam.cmd_tag_queue_depth      = (0);
+	//sgilabel->devparam.unused0                  = (0);
+	//sgilabel->devparam.unused1                  = SGI_SSWAP16(0);
+	/* sectors/track */
+	sgilabel->devparam.nsect                    = SGI_SSWAP16(geometry.sectors);
+	sgilabel->devparam.bytes                    = SGI_SSWAP16(512);
+	sgilabel->devparam.ilfact                   = SGI_SSWAP16(1);
+	sgilabel->devparam.flags                    = SGI_SSWAP32(TRACK_FWD|
+							IGNORE_ERRORS|RESEEK);
+	//sgilabel->devparam.datarate                 = SGI_SSWAP32(0);
+	sgilabel->devparam.retries_on_error         = SGI_SSWAP32(1);
+	//sgilabel->devparam.ms_per_word              = SGI_SSWAP32(0);
+	//sgilabel->devparam.xylogics_gap1            = SGI_SSWAP16(0);
+	//sgilabel->devparam.xylogics_syncdelay       = SGI_SSWAP16(0);
+	//sgilabel->devparam.xylogics_readdelay       = SGI_SSWAP16(0);
+	//sgilabel->devparam.xylogics_gap2            = SGI_SSWAP16(0);
+	//sgilabel->devparam.xylogics_readgate        = SGI_SSWAP16(0);
+	//sgilabel->devparam.xylogics_writecont       = SGI_SSWAP16(0);
+	//memset( &(sgilabel->directory), 0, sizeof(struct volume_directory)*15 );
+	//memset( &(sgilabel->partitions), 0, sizeof(struct sgi_partinfo)*16 );
+	current_label_type = LABEL_SGI;
+	g_partitions = 16;
+	sgi_volumes = 15;
+	sgi_set_entire();
+	sgi_set_volhdr();
+	for (i = 0; i < 4; i++) {
+		if (old[i].sysid) {
+			sgi_set_partition(i, old[i].start, old[i].nsect, old[i].sysid);
+		}
+	}
+}
+
+static void
+sgi_set_xcyl(void)
+{
+	/* do nothing in the beginning */
+}
+#endif /* FEATURE_FDISK_ADVANCED */
+
+/* _____________________________________________________________
+ */
+
+static sgiinfo *
+fill_sgiinfo(void)
+{
+	sgiinfo *info = xzalloc(sizeof(sgiinfo));
+
+	info->magic = SGI_SSWAP32(SGI_INFO_MAGIC);
+	info->b1 = SGI_SSWAP32(-1);
+	info->b2 = SGI_SSWAP16(-1);
+	info->b3 = SGI_SSWAP16(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 = SGI_SSWAP16(18*256 +16 );
+	strcpy( (char*)info->installer, "Sfx version 5.3, Oct 18, 1994" );
+	return info;
+}
+#endif /* SGI_LABEL */
diff --git a/ap/app/busybox/src/util-linux/fdisk_sun.c b/ap/app/busybox/src/util-linux/fdisk_sun.c
new file mode 100644
index 0000000..e7fcc06
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/fdisk_sun.c
@@ -0,0 +1,729 @@
+/*
+ * fdisk_sun.c
+ *
+ * I think this is mostly, or entirely, due to
+ *      Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996
+ *
+ * Merged with fdisk for other architectures, aeb, June 1998.
+ *
+ * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *      Internationalization
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#if ENABLE_FEATURE_SUN_LABEL
+
+#define SUNOS_SWAP 3
+#define SUN_WHOLE_DISK 5
+
+#define SUN_LABEL_MAGIC          0xDABE
+#define SUN_LABEL_MAGIC_SWAPPED  0xBEDA
+#define SUN_SSWAP16(x) (sun_other_endian ? fdisk_swap16(x) : (uint16_t)(x))
+#define SUN_SSWAP32(x) (sun_other_endian ? fdisk_swap32(x) : (uint32_t)(x))
+
+/* Copied from linux/major.h */
+#define FLOPPY_MAJOR    2
+
+#define SCSI_IOCTL_GET_IDLUN 0x5382
+
+static smallint sun_other_endian;
+static smallint scsi_disk;
+static smallint floppy;
+
+#ifndef IDE0_MAJOR
+#define IDE0_MAJOR 3
+#endif
+#ifndef IDE1_MAJOR
+#define IDE1_MAJOR 22
+#endif
+
+static void
+guess_device_type(void)
+{
+	struct stat bootstat;
+
+	if (fstat(dev_fd, &bootstat) < 0) {
+		scsi_disk = 0;
+		floppy = 0;
+	} else if (S_ISBLK(bootstat.st_mode)
+		&& (major(bootstat.st_rdev) == IDE0_MAJOR ||
+		    major(bootstat.st_rdev) == IDE1_MAJOR)) {
+		scsi_disk = 0;
+		floppy = 0;
+	} else if (S_ISBLK(bootstat.st_mode)
+		&& major(bootstat.st_rdev) == FLOPPY_MAJOR) {
+		scsi_disk = 0;
+		floppy = 1;
+	} else {
+		scsi_disk = 1;
+		floppy = 0;
+	}
+}
+
+static const char *const sun_sys_types[] = {
+	"\x00" "Empty"       , /* 0            */
+	"\x01" "Boot"        , /* 1            */
+	"\x02" "SunOS root"  , /* 2            */
+	"\x03" "SunOS swap"  , /* SUNOS_SWAP   */
+	"\x04" "SunOS usr"   , /* 4            */
+	"\x05" "Whole disk"  , /* SUN_WHOLE_DISK   */
+	"\x06" "SunOS stand" , /* 6            */
+	"\x07" "SunOS var"   , /* 7            */
+	"\x08" "SunOS home"  , /* 8            */
+	"\x82" "Linux swap"  , /* LINUX_SWAP   */
+	"\x83" "Linux native", /* LINUX_NATIVE */
+	"\x8e" "Linux LVM"   , /* 0x8e         */
+/* New (2.2.x) raid partition with autodetect using persistent superblock */
+	"\xfd" "Linux raid autodetect", /* 0xfd         */
+	NULL
+};
+
+
+static void
+set_sun_partition(int i, unsigned start, unsigned stop, int sysid)
+{
+	sunlabel->infos[i].id = sysid;
+	sunlabel->partitions[i].start_cylinder =
+		SUN_SSWAP32(start / (g_heads * g_sectors));
+	sunlabel->partitions[i].num_sectors =
+		SUN_SSWAP32(stop - start);
+	set_changed(i);
+}
+
+static int
+check_sun_label(void)
+{
+	unsigned short *ush;
+	int csum;
+
+	if (sunlabel->magic != SUN_LABEL_MAGIC
+	 && sunlabel->magic != SUN_LABEL_MAGIC_SWAPPED
+	) {
+		current_label_type = LABEL_DOS;
+		sun_other_endian = 0;
+		return 0;
+	}
+	sun_other_endian = (sunlabel->magic == SUN_LABEL_MAGIC_SWAPPED);
+	ush = ((unsigned short *) (sunlabel + 1)) - 1;
+	for (csum = 0; ush >= (unsigned short *)sunlabel;) csum ^= *ush--;
+	if (csum) {
+		printf("Detected sun disklabel with wrong checksum.\n"
+"Probably you'll have to set all the values,\n"
+"e.g. heads, sectors, cylinders and partitions\n"
+"or force a fresh label (s command in main menu)\n");
+	} else {
+		g_heads = SUN_SSWAP16(sunlabel->ntrks);
+		g_cylinders = SUN_SSWAP16(sunlabel->ncyl);
+		g_sectors = SUN_SSWAP16(sunlabel->nsect);
+	}
+	update_units();
+	current_label_type = LABEL_SUN;
+	g_partitions = 8;
+	return 1;
+}
+
+static const struct sun_predefined_drives {
+	const char *vendor;
+	const char *model;
+	unsigned short sparecyl;
+	unsigned short ncyl;
+	unsigned short nacyl;
+	unsigned short pcylcount;
+	unsigned short ntrks;
+	unsigned short nsect;
+	unsigned short rspeed;
+} sun_drives[] = {
+	{ "Quantum","ProDrive 80S",1,832,2,834,6,34,3662},
+	{ "Quantum","ProDrive 105S",1,974,2,1019,6,35,3662},
+	{ "CDC","Wren IV 94171-344",3,1545,2,1549,9,46,3600},
+	{ "IBM","DPES-31080",0,4901,2,4903,4,108,5400},
+	{ "IBM","DORS-32160",0,1015,2,1017,67,62,5400},
+	{ "IBM","DNES-318350",0,11199,2,11474,10,320,7200},
+	{ "SEAGATE","ST34371",0,3880,2,3882,16,135,7228},
+	{ "","SUN0104",1,974,2,1019,6,35,3662},
+	{ "","SUN0207",4,1254,2,1272,9,36,3600},
+	{ "","SUN0327",3,1545,2,1549,9,46,3600},
+	{ "","SUN0340",0,1538,2,1544,6,72,4200},
+	{ "","SUN0424",2,1151,2,2500,9,80,4400},
+	{ "","SUN0535",0,1866,2,2500,7,80,5400},
+	{ "","SUN0669",5,1614,2,1632,15,54,3600},
+	{ "","SUN1.0G",5,1703,2,1931,15,80,3597},
+	{ "","SUN1.05",0,2036,2,2038,14,72,5400},
+	{ "","SUN1.3G",6,1965,2,3500,17,80,5400},
+	{ "","SUN2.1G",0,2733,2,3500,19,80,5400},
+	{ "IOMEGA","Jaz",0,1019,2,1021,64,32,5394},
+};
+
+static const struct sun_predefined_drives *
+sun_autoconfigure_scsi(void)
+{
+	const struct sun_predefined_drives *p = NULL;
+
+#ifdef SCSI_IOCTL_GET_IDLUN
+	unsigned int id[2];
+	char buffer[2048];
+	char buffer2[2048];
+	FILE *pfd;
+	char *vendor;
+	char *model;
+	char *q;
+	int i;
+
+	if (ioctl(dev_fd, SCSI_IOCTL_GET_IDLUN, &id))
+		return NULL;
+
+	sprintf(buffer,
+		"Host: scsi%u Channel: %02u Id: %02u Lun: %02u\n",
+		/* This is very wrong (works only if you have one HBA),
+		   but I haven't found a way how to get hostno
+		   from the current kernel */
+		0,
+		(id[0]>>16) & 0xff,
+		id[0] & 0xff,
+		(id[0]>>8) & 0xff
+	);
+	pfd = fopen_for_read("/proc/scsi/scsi");
+	if (!pfd) {
+		return NULL;
+	}
+	while (fgets(buffer2, 2048, pfd)) {
+		if (strcmp(buffer, buffer2))
+			continue;
+		if (!fgets(buffer2, 2048, pfd))
+			break;
+		q = strstr(buffer2, "Vendor: ");
+		if (!q)
+			break;
+		q += 8;
+		vendor = q;
+		q = strstr(q, " ");
+		*q++ = '\0';   /* truncate vendor name */
+		q = strstr(q, "Model: ");
+		if (!q)
+			break;
+		*q = '\0';
+		q += 7;
+		model = q;
+		q = strstr(q, " Rev: ");
+		if (!q)
+			break;
+		*q = '\0';
+		for (i = 0; i < ARRAY_SIZE(sun_drives); i++) {
+			if (*sun_drives[i].vendor && strcasecmp(sun_drives[i].vendor, vendor))
+				continue;
+			if (!strstr(model, sun_drives[i].model))
+				continue;
+			printf("Autoconfigure found a %s%s%s\n",
+					sun_drives[i].vendor,
+					(*sun_drives[i].vendor) ? " " : "",
+					sun_drives[i].model);
+			p = sun_drives + i;
+			break;
+		}
+		break;
+	}
+	fclose(pfd);
+#endif
+	return p;
+}
+
+static void
+create_sunlabel(void)
+{
+	struct hd_geometry geometry;
+	unsigned ndiv;
+	unsigned char c;
+	const struct sun_predefined_drives *p = NULL;
+
+	printf(msg_building_new_label, "sun disklabel");
+
+	sun_other_endian = BB_LITTLE_ENDIAN;
+	memset(MBRbuffer, 0, sizeof(MBRbuffer));
+	sunlabel->magic = SUN_SSWAP16(SUN_LABEL_MAGIC);
+	if (!floppy) {
+		unsigned i;
+		puts("Drive type\n"
+		 "   ?   auto configure\n"
+		 "   0   custom (with hardware detected defaults)");
+		for (i = 0; i < ARRAY_SIZE(sun_drives); i++) {
+			printf("   %c   %s%s%s\n",
+				i + 'a', sun_drives[i].vendor,
+				(*sun_drives[i].vendor) ? " " : "",
+				sun_drives[i].model);
+		}
+		while (1) {
+			c = read_nonempty("Select type (? for auto, 0 for custom): ");
+			if (c == '0') {
+				break;
+			}
+			if (c >= 'a' && c < 'a' + ARRAY_SIZE(sun_drives)) {
+				p = sun_drives + c - 'a';
+				break;
+			}
+			if (c >= 'A' && c < 'A' + ARRAY_SIZE(sun_drives)) {
+				p = sun_drives + c - 'A';
+				break;
+			}
+			if (c == '?' && scsi_disk) {
+				p = sun_autoconfigure_scsi();
+				if (p)
+					break;
+				printf("Autoconfigure failed\n");
+			}
+		}
+	}
+	if (!p || floppy) {
+		if (!ioctl(dev_fd, HDIO_GETGEO, &geometry)) {
+			g_heads = geometry.heads;
+			g_sectors = geometry.sectors;
+			g_cylinders = geometry.cylinders;
+		} else {
+			g_heads = 0;
+			g_sectors = 0;
+			g_cylinders = 0;
+		}
+		if (floppy) {
+			sunlabel->nacyl = 0;
+			sunlabel->pcylcount = SUN_SSWAP16(g_cylinders);
+			sunlabel->rspeed = SUN_SSWAP16(300);
+			sunlabel->ilfact = SUN_SSWAP16(1);
+			sunlabel->sparecyl = 0;
+		} else {
+			g_heads = read_int(1, g_heads, 1024, 0, "Heads");
+			g_sectors = read_int(1, g_sectors, 1024, 0, "Sectors/track");
+		if (g_cylinders)
+			g_cylinders = read_int(1, g_cylinders - 2, 65535, 0, "Cylinders");
+		else
+			g_cylinders = read_int(1, 0, 65535, 0, "Cylinders");
+			sunlabel->nacyl = SUN_SSWAP16(read_int(0, 2, 65535, 0, "Alternate cylinders"));
+			sunlabel->pcylcount = SUN_SSWAP16(read_int(0, g_cylinders + SUN_SSWAP16(sunlabel->nacyl), 65535, 0, "Physical cylinders"));
+			sunlabel->rspeed = SUN_SSWAP16(read_int(1, 5400, 100000, 0, "Rotation speed (rpm)"));
+			sunlabel->ilfact = SUN_SSWAP16(read_int(1, 1, 32, 0, "Interleave factor"));
+			sunlabel->sparecyl = SUN_SSWAP16(read_int(0, 0, g_sectors, 0, "Extra sectors per cylinder"));
+		}
+	} else {
+		sunlabel->sparecyl = SUN_SSWAP16(p->sparecyl);
+		sunlabel->ncyl = SUN_SSWAP16(p->ncyl);
+		sunlabel->nacyl = SUN_SSWAP16(p->nacyl);
+		sunlabel->pcylcount = SUN_SSWAP16(p->pcylcount);
+		sunlabel->ntrks = SUN_SSWAP16(p->ntrks);
+		sunlabel->nsect = SUN_SSWAP16(p->nsect);
+		sunlabel->rspeed = SUN_SSWAP16(p->rspeed);
+		sunlabel->ilfact = SUN_SSWAP16(1);
+		g_cylinders = p->ncyl;
+		g_heads = p->ntrks;
+		g_sectors = p->nsect;
+		puts("You may change all the disk params from the x menu");
+	}
+
+	snprintf((char *)(sunlabel->info), sizeof(sunlabel->info),
+		"%s%s%s cyl %u alt %u hd %u sec %u",
+		p ? p->vendor : "", (p && *p->vendor) ? " " : "",
+		p ? p->model : (floppy ? "3,5\" floppy" : "Linux custom"),
+		g_cylinders, SUN_SSWAP16(sunlabel->nacyl), g_heads, g_sectors);
+
+	sunlabel->ntrks = SUN_SSWAP16(g_heads);
+	sunlabel->nsect = SUN_SSWAP16(g_sectors);
+	sunlabel->ncyl = SUN_SSWAP16(g_cylinders);
+	if (floppy)
+		set_sun_partition(0, 0, g_cylinders * g_heads * g_sectors, LINUX_NATIVE);
+	else {
+		if (g_cylinders * g_heads * g_sectors >= 150 * 2048) {
+			ndiv = g_cylinders - (50 * 2048 / (g_heads * g_sectors)); /* 50M swap */
+		} else
+			ndiv = g_cylinders * 2 / 3;
+		set_sun_partition(0, 0, ndiv * g_heads * g_sectors, LINUX_NATIVE);
+		set_sun_partition(1, ndiv * g_heads * g_sectors, g_cylinders * g_heads * g_sectors, LINUX_SWAP);
+		sunlabel->infos[1].flags |= 0x01; /* Not mountable */
+	}
+	set_sun_partition(2, 0, g_cylinders * g_heads * g_sectors, SUN_WHOLE_DISK);
+	{
+		unsigned short *ush = (unsigned short *)sunlabel;
+		unsigned short csum = 0;
+		while (ush < (unsigned short *)(&sunlabel->csum))
+			csum ^= *ush++;
+		sunlabel->csum = csum;
+	}
+
+	set_all_unchanged();
+	set_changed(0);
+	get_boot(CREATE_EMPTY_SUN);
+}
+
+static void
+toggle_sunflags(int i, unsigned char mask)
+{
+	if (sunlabel->infos[i].flags & mask)
+		sunlabel->infos[i].flags &= ~mask;
+	else
+		sunlabel->infos[i].flags |= mask;
+	set_changed(i);
+}
+
+static void
+fetch_sun(unsigned *starts, unsigned *lens, unsigned *start, unsigned *stop)
+{
+	int i, continuous = 1;
+
+	*start = 0;
+	*stop = g_cylinders * g_heads * g_sectors;
+	for (i = 0; i < g_partitions; i++) {
+		if (sunlabel->partitions[i].num_sectors
+		 && sunlabel->infos[i].id
+		 && sunlabel->infos[i].id != SUN_WHOLE_DISK) {
+			starts[i] = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * g_heads * g_sectors;
+			lens[i] = SUN_SSWAP32(sunlabel->partitions[i].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;
+		}
+	}
+}
+
+static unsigned *verify_sun_starts;
+
+static int
+verify_sun_cmp(int *a, int *b)
+{
+	if (*a == -1) return 1;
+	if (*b == -1) return -1;
+	if (verify_sun_starts[*a] > verify_sun_starts[*b]) return 1;
+	return -1;
+}
+
+static void
+verify_sun(void)
+{
+	unsigned starts[8], lens[8], start, stop;
+	int i,j,k,starto,endo;
+	int array[8];
+
+	verify_sun_starts = starts;
+	fetch_sun(starts, lens, &start, &stop);
+	for (k = 0; k < 7; k++) {
+		for (i = 0; i < 8; i++) {
+			if (k && (lens[i] % (g_heads * g_sectors))) {
+				printf("Partition %u doesn't end on cylinder boundary\n", 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];
+								printf("Partition %u overlaps with others in "
+									"sectors %u-%u\n", i+1, starto, endo);
+							}
+						}
+					}
+			}
+		}
+	}
+	for (i = 0; i < 8; i++) {
+		if (lens[i])
+			array[i] = i;
+		else
+			array[i] = -1;
+	}
+	qsort(array, ARRAY_SIZE(array), sizeof(array[0]),
+		(int (*)(const void *,const void *)) verify_sun_cmp);
+	if (array[0] == -1) {
+		printf("No partitions defined\n");
+		return;
+	}
+	stop = g_cylinders * g_heads * g_sectors;
+	if (starts[array[0]])
+		printf("Unused gap - sectors %u-%u\n", 0, starts[array[0]]);
+	for (i = 0; i < 7 && array[i+1] != -1; i++) {
+		printf("Unused gap - sectors %u-%u\n", starts[array[i]]+lens[array[i]], starts[array[i+1]]);
+	}
+	start = starts[array[i]] + lens[array[i]];
+	if (start < stop)
+		printf("Unused gap - sectors %u-%u\n", start, stop);
+}
+
+static void
+add_sun_partition(int n, int sys)
+{
+	unsigned start, stop, stop2;
+	unsigned starts[8], lens[8];
+	int whole_disk = 0;
+
+	char mesg[256];
+	int i, first, last;
+
+	if (sunlabel->partitions[n].num_sectors && sunlabel->infos[n].id) {
+		printf(msg_part_already_defined, n + 1);
+		return;
+	}
+
+	fetch_sun(starts, lens, &start, &stop);
+	if (stop <= start) {
+		if (n == 2)
+			whole_disk = 1;
+		else {
+			printf("Other partitions already cover the whole disk.\n"
+				"Delete/shrink them before retry.\n");
+			return;
+		}
+	}
+	snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
+	while (1) {
+		if (whole_disk)
+			first = read_int(0, 0, 0, 0, mesg);
+		else
+			first = read_int(scround(start), scround(stop)+1,
+					 scround(stop), 0, mesg);
+		if (display_in_cyl_units)
+			first *= units_per_sector;
+		else
+			/* Starting sector has to be properly aligned */
+			first = (first + g_heads * g_sectors - 1) / (g_heads * g_sectors);
+		if (n == 2 && first != 0)
+			printf("\
+It is highly recommended that the third partition covers the whole disk\n\
+and is of type 'Whole disk'\n");
+		/* 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. */
+		for (i = 0; i < g_partitions; i++)
+			if (lens[i] && starts[i] <= first && starts[i] + lens[i] > first)
+				break;
+		if (i < g_partitions && !whole_disk) {
+			if (n == 2 && !first) {
+				whole_disk = 1;
+				break;
+			}
+			printf("Sector %u is already allocated\n", first);
+		} else
+			break;
+	}
+	stop = g_cylinders * g_heads * g_sectors;
+	stop2 = stop;
+	for (i = 0; i < g_partitions; i++) {
+		if (starts[i] > first && starts[i] < stop)
+			stop = starts[i];
+	}
+	snprintf(mesg, sizeof(mesg),
+		"Last %s or +size or +sizeM or +sizeK",
+		str_units(SINGULAR));
+	if (whole_disk)
+		last = read_int(scround(stop2), scround(stop2), scround(stop2),
+				0, mesg);
+	else if (n == 2 && !first)
+		last = read_int(scround(first), scround(stop2), scround(stop2),
+				scround(first), mesg);
+	else
+		last = read_int(scround(first), scround(stop), scround(stop),
+				scround(first), mesg);
+	if (display_in_cyl_units)
+		last *= units_per_sector;
+	if (n == 2 && !first) {
+		if (last >= stop2) {
+			whole_disk = 1;
+			last = stop2;
+		} else if (last > stop) {
+			printf(
+"You haven't covered the whole disk with the 3rd partition,\n"
+"but your value %u %s covers some other partition.\n"
+"Your entry has been changed to %u %s\n",
+				scround(last), str_units(SINGULAR),
+				scround(stop), str_units(SINGULAR));
+			last = stop;
+		}
+	} else if (!whole_disk && last > stop)
+		last = stop;
+
+	if (whole_disk)
+		sys = SUN_WHOLE_DISK;
+	set_sun_partition(n, first, last, sys);
+}
+
+static void
+sun_delete_partition(int i)
+{
+	unsigned int nsec;
+
+	if (i == 2
+	 && sunlabel->infos[i].id == SUN_WHOLE_DISK
+	 && !sunlabel->partitions[i].start_cylinder
+	 && (nsec = SUN_SSWAP32(sunlabel->partitions[i].num_sectors)) == g_heads * g_sectors * g_cylinders)
+		printf("If you want to maintain SunOS/Solaris compatibility, "
+			"consider leaving this\n"
+			"partition as Whole disk (5), starting at 0, with %u "
+			"sectors\n", nsec);
+	sunlabel->infos[i].id = 0;
+	sunlabel->partitions[i].num_sectors = 0;
+}
+
+static void
+sun_change_sysid(int i, int sys)
+{
+	if (sys == LINUX_SWAP && !sunlabel->partitions[i].start_cylinder) {
+		read_maybe_empty(
+			"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"
+			"Type YES if you're very sure you would like that partition\n"
+			"tagged with 82 (Linux swap): ");
+		if (strcmp (line_ptr, "YES\n"))
+			return;
+	}
+	switch (sys) {
+	case SUNOS_SWAP:
+	case LINUX_SWAP:
+		/* swaps are not mountable by default */
+		sunlabel->infos[i].flags |= 0x01;
+		break;
+	default:
+		/* assume other types are mountable;
+		   user can change it anyway */
+		sunlabel->infos[i].flags &= ~0x01;
+		break;
+	}
+	sunlabel->infos[i].id = sys;
+}
+
+static void
+sun_list_table(int xtra)
+{
+	int i, w;
+
+	w = strlen(disk_device);
+	if (xtra)
+		printf(
+		"\nDisk %s (Sun disk label): %u heads, %u sectors, %u rpm\n"
+		"%u cylinders, %u alternate cylinders, %u physical cylinders\n"
+		"%u extra sects/cyl, interleave %u:1\n"
+		"%s\n"
+		"Units = %s of %u * 512 bytes\n\n",
+			disk_device, g_heads, g_sectors, SUN_SSWAP16(sunlabel->rspeed),
+			g_cylinders, SUN_SSWAP16(sunlabel->nacyl),
+			SUN_SSWAP16(sunlabel->pcylcount),
+			SUN_SSWAP16(sunlabel->sparecyl),
+			SUN_SSWAP16(sunlabel->ilfact),
+			(char *)sunlabel,
+			str_units(PLURAL), units_per_sector);
+	else
+		printf(
+	"\nDisk %s (Sun disk label): %u heads, %u sectors, %u cylinders\n"
+	"Units = %s of %u * 512 bytes\n\n",
+			disk_device, g_heads, g_sectors, g_cylinders,
+			str_units(PLURAL), units_per_sector);
+
+	printf("%*s Flag    Start       End    Blocks   Id  System\n",
+		w + 1, "Device");
+	for (i = 0; i < g_partitions; i++) {
+		if (sunlabel->partitions[i].num_sectors) {
+			uint32_t start = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * g_heads * g_sectors;
+			uint32_t len = SUN_SSWAP32(sunlabel->partitions[i].num_sectors);
+			printf("%s %c%c %9lu %9lu %9lu%c  %2x  %s\n",
+				partname(disk_device, i+1, w),                  /* device */
+				(sunlabel->infos[i].flags & 0x01) ? 'u' : ' ',  /* flags */
+				(sunlabel->infos[i].flags & 0x10) ? 'r' : ' ',
+				(long) scround(start),                          /* start */
+				(long) scround(start+len),                      /* end */
+				(long) len / 2, len & 1 ? '+' : ' ',            /* odd flag on end */
+				sunlabel->infos[i].id,                          /* type id */
+				partition_type(sunlabel->infos[i].id));         /* type name */
+		}
+	}
+}
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+
+static void
+sun_set_alt_cyl(void)
+{
+	sunlabel->nacyl =
+		SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->nacyl), 65535, 0,
+				"Number of alternate cylinders"));
+}
+
+static void
+sun_set_ncyl(int cyl)
+{
+	sunlabel->ncyl = SUN_SSWAP16(cyl);
+}
+
+static void
+sun_set_xcyl(void)
+{
+	sunlabel->sparecyl =
+		SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->sparecyl), g_sectors, 0,
+				"Extra sectors per cylinder"));
+}
+
+static void
+sun_set_ilfact(void)
+{
+	sunlabel->ilfact =
+		SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->ilfact), 32, 0,
+				"Interleave factor"));
+}
+
+static void
+sun_set_rspeed(void)
+{
+	sunlabel->rspeed =
+		SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->rspeed), 100000, 0,
+				"Rotation speed (rpm)"));
+}
+
+static void
+sun_set_pcylcount(void)
+{
+	sunlabel->pcylcount =
+		SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->pcylcount), 65535, 0,
+				"Number of physical cylinders"));
+}
+#endif /* FEATURE_FDISK_ADVANCED */
+
+static void
+sun_write_table(void)
+{
+	unsigned short *ush = (unsigned short *)sunlabel;
+	unsigned short csum = 0;
+
+	while (ush < (unsigned short *)(&sunlabel->csum))
+		csum ^= *ush++;
+	sunlabel->csum = csum;
+	write_sector(0, sunlabel);
+}
+#endif /* SUN_LABEL */
diff --git a/ap/app/busybox/src/util-linux/findfs.c b/ap/app/busybox/src/util-linux/findfs.c
new file mode 100644
index 0000000..49e8979
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/findfs.c
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Support functions for mounting devices by label/uuid
+ *
+ * Copyright (C) 2006 by Jason Schoon <floydpink@gmail.com>
+ * Some portions cribbed from e2fsprogs, util-linux, dosfstools
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define findfs_trivial_usage
+//usage:       "LABEL=label or UUID=uuid"
+//usage:#define findfs_full_usage "\n\n"
+//usage:       "Find a filesystem device based on a label or UUID"
+//usage:
+//usage:#define findfs_example_usage
+//usage:       "$ findfs LABEL=MyDevice"
+
+#include "libbb.h"
+#include "volume_id.h"
+
+int findfs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int findfs_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *dev = *++argv;
+
+	if (!dev)
+		bb_show_usage();
+
+	if (strncmp(dev, "/dev/", 5) == 0) {
+		/* Just pass any /dev/xxx name right through.
+		 * This might aid in some scripts being able
+		 * to call this unconditionally */
+		dev = NULL;
+	} else {
+		/* Otherwise, handle LABEL=xxx and UUID=xxx,
+		 * fail on anything else */
+		if (!resolve_mount_spec(argv))
+			bb_show_usage();
+	}
+
+	if (*argv != dev) {
+		puts(*argv);
+		return 0;
+	}
+	return 1;
+}
diff --git a/ap/app/busybox/src/util-linux/flock.c b/ap/app/busybox/src/util-linux/flock.c
new file mode 100644
index 0000000..05a747f
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/flock.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 Timo Teras <timo.teras@iki.fi>
+ *
+ * This is free software, licensed under the GNU General Public License v2.
+ */
+
+//usage:#define flock_trivial_usage
+//usage:       "[-sxun] FD|{FILE [-c] PROG ARGS}"
+//usage:#define flock_full_usage "\n\n"
+//usage:       "[Un]lock file descriptor, or lock FILE, run PROG\n"
+//usage:     "\n	-s	Shared lock"
+//usage:     "\n	-x	Exclusive lock (default)"
+//usage:     "\n	-u	Unlock FD"
+//usage:     "\n	-n	Fail rather than wait"
+
+#include <sys/file.h>
+#include "libbb.h"
+
+int flock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int flock_main(int argc UNUSED_PARAM, char **argv)
+{
+	int mode, opt, fd;
+	enum {
+		OPT_s = (1 << 0),
+		OPT_x = (1 << 1),
+		OPT_n = (1 << 2),
+		OPT_u = (1 << 3),
+		OPT_c = (1 << 4),
+	};
+
+#if ENABLE_LONG_OPTS
+	static const char getopt_longopts[] ALIGN1 =
+		"shared\0"      No_argument       "s"
+		"exclusive\0"   No_argument       "x"
+		"unlock\0"      No_argument       "u"
+		"nonblock\0"    No_argument       "n"
+		;
+	applet_long_options = getopt_longopts;
+#endif
+	opt_complementary = "-1";
+
+	opt = getopt32(argv, "+sxnu");
+	argv += optind;
+
+	if (argv[1]) {
+		fd = open(argv[0], O_RDONLY|O_NOCTTY|O_CREAT, 0666);
+		if (fd < 0 && errno == EISDIR)
+			fd = open(argv[0], O_RDONLY|O_NOCTTY);
+		if (fd < 0)
+			bb_perror_msg_and_die("can't open '%s'", argv[0]);
+		//TODO? close_on_exec_on(fd);
+	} else {
+		fd = xatoi_positive(argv[0]);
+	}
+	argv++;
+
+	/* If it is "flock FILE -c PROG", then -c isn't caught by getopt32:
+	 * we use "+" in order to support "flock -opt FILE PROG -with-opts",
+	 * we need to remove -c by hand.
+	 * TODO: in upstream, -c 'PROG ARGS' means "run sh -c 'PROG ARGS'"
+	 */
+	if (argv[0]
+	 && argv[0][0] == '-'
+	 && (  (argv[0][1] == 'c' && !argv[0][2])
+	    || (ENABLE_LONG_OPTS && strcmp(argv[0] + 1, "-command") == 0)
+	    )
+	) {
+		argv++;
+	}
+
+	if (OPT_s == LOCK_SH && OPT_x == LOCK_EX && OPT_n == LOCK_NB && OPT_u == LOCK_UN) {
+		/* With suitably matched constants, mode setting is much simpler */
+		mode = opt & (LOCK_SH + LOCK_EX + LOCK_NB + LOCK_UN);
+		if (!(mode & ~LOCK_NB))
+			mode |= LOCK_EX;
+	} else {
+		if (opt & OPT_u)
+			mode = LOCK_UN;
+		else if (opt & OPT_s)
+			mode = LOCK_SH;
+		else
+			mode = LOCK_EX;
+		if (opt & OPT_n)
+			mode |= LOCK_NB;
+	}
+
+	if (flock(fd, mode) != 0) {
+		if (errno == EWOULDBLOCK)
+			return EXIT_FAILURE;
+		bb_perror_nomsg_and_die();
+	}
+
+	if (argv[0])
+		return spawn_and_wait(argv);
+
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/freeramdisk.c b/ap/app/busybox/src/util-linux/freeramdisk.c
new file mode 100644
index 0000000..a89ae1a
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/freeramdisk.c
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * freeramdisk and fdflush implementations for busybox
+ *
+ * Copyright (C) 2000 and written by Emanuele Caratti <wiz@iol.it>
+ * Adjusted a bit by Erik Andersen <andersen@codepoet.org>
+ * Unified with fdflush by Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define freeramdisk_trivial_usage
+//usage:       "DEVICE"
+//usage:#define freeramdisk_full_usage "\n\n"
+//usage:       "Free all memory used by the specified ramdisk"
+//usage:
+//usage:#define freeramdisk_example_usage
+//usage:       "$ freeramdisk /dev/ram2\n"
+//usage:
+//usage:#define fdflush_trivial_usage
+//usage:       "DEVICE"
+//usage:#define fdflush_full_usage "\n\n"
+//usage:       "Force floppy disk drive to detect disk change"
+
+#include <sys/mount.h>
+#include "libbb.h"
+
+/* From <linux/fd.h> */
+#define FDFLUSH  _IO(2,0x4b)
+
+int freeramdisk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int freeramdisk_main(int argc UNUSED_PARAM, char **argv)
+{
+	int fd;
+
+	fd = xopen(single_argv(argv), O_RDWR);
+
+	// Act like freeramdisk, fdflush, or both depending on configuration.
+	ioctl_or_perror_and_die(fd, (ENABLE_FREERAMDISK && applet_name[1] == 'r')
+			|| !ENABLE_FDFLUSH ? BLKFLSBUF : FDFLUSH, NULL, "%s", argv[1]);
+
+	if (ENABLE_FEATURE_CLEAN_UP) close(fd);
+
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/fsck_minix.c b/ap/app/busybox/src/util-linux/fsck_minix.c
new file mode 100644
index 0000000..c1d1b2c
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/fsck_minix.c
@@ -0,0 +1,1315 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fsck.c - a file system consistency checker for Linux.
+ *
+ * (C) 1991, 1992 Linus Torvalds.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/*
+ * 09.11.91  -  made the first rudimentary functions
+ *
+ * 10.11.91  -  updated, does checking, no repairs yet.
+ *		Sent out to the mailing-list for testing.
+ *
+ * 14.11.91  -  Testing seems to have gone well. Added some
+ *		correction-code, and changed some functions.
+ *
+ * 15.11.91  -  More correction code. Hopefully it notices most
+ *		cases now, and tries to do something about them.
+ *
+ * 16.11.91  -  More corrections (thanks to Mika Jalava). Most
+ *		things seem to work now. Yeah, sure.
+ *
+ * 19.04.92  -  Had to start over again from this old version, as a
+ *		kernel bug ate my enhanced fsck in february.
+ *
+ * 28.02.93  -  added support for different directory entry sizes..
+ *
+ * Sat Mar  6 18:59:42 1993, faith@cs.unc.edu: Output namelen with
+ *                           superblock information
+ *
+ * Sat Oct  9 11:17:11 1993, faith@cs.unc.edu: make exit status conform
+ *                           to that required by fsutil
+ *
+ * Mon Jan  3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu)
+ *                            Added support for file system valid flag.  Also
+ *                            added program_version variable and output of
+ *                            program name and version number when program
+ *                            is executed.
+ *
+ * 30.10.94  - added support for v2 filesystem
+ *             (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
+ *
+ * 10.12.94  - added test to prevent checking of mounted fs adapted
+ *             from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
+ *             program.  (Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * 01.07.96  - Fixed the v2 fs stuff to use the right #defines and such
+ *             for modern libcs (janl@math.uio.no, Nicolai Langfeldt)
+ *
+ * 02.07.96  - Added C bit fiddling routines from rmk@ecs.soton.ac.uk
+ *             (Russell King).  He made them for ARM.  It would seem
+ *             that the ARM is powerful enough to do this in C whereas
+ *             i386 and m64k must use assembly to get it fast >:-)
+ *             This should make minix fsck system-independent.
+ *             (janl@math.uio.no, Nicolai Langfeldt)
+ *
+ * 04.11.96  - Added minor fixes from Andreas Schwab to avoid compiler
+ *             warnings.  Added mc68k bitops from
+ *             Joerg Dorchain <dorchain@mpi-sb.mpg.de>.
+ *
+ * 06.11.96  - Added v2 code submitted by Joerg Dorchain, but written by
+ *             Andreas Schwab.
+ *
+ * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+ *
+ *
+ * I've had no time to add comments - hopefully the function names
+ * are comments enough. As with all file system checkers, this assumes
+ * the file system is quiescent - don't use it on a mounted device
+ * unless you can be sure nobody is writing to it (and remember that the
+ * kernel can write to it when it searches for files).
+ *
+ * Usage: fsck [-larvsm] device
+ *	-l for a listing of all the filenames
+ *	-a for automatic repairs (not implemented)
+ *	-r for repairs (interactive) (not implemented)
+ *	-v for verbose (tells how many files)
+ *	-s for superblock info
+ *	-m for minix-like "mode not cleared" warnings
+ *	-f force filesystem check even if filesystem marked as valid
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ */
+
+//usage:#define fsck_minix_trivial_usage
+//usage:       "[-larvsmf] BLOCKDEV"
+//usage:#define fsck_minix_full_usage "\n\n"
+//usage:       "Check MINIX filesystem\n"
+//usage:     "\n	-l	List all filenames"
+//usage:     "\n	-r	Perform interactive repairs"
+//usage:     "\n	-a	Perform automatic repairs"
+//usage:     "\n	-v	Verbose"
+//usage:     "\n	-s	Output superblock information"
+//usage:     "\n	-m	Show \"mode not cleared\" warnings"
+//usage:     "\n	-f	Force file system check"
+
+#include <mntent.h>
+#include "libbb.h"
+#include "minix.h"
+
+#ifndef BLKGETSIZE
+#define BLKGETSIZE _IO(0x12,96)    /* return device size */
+#endif
+
+struct BUG_bad_inode_size {
+	char BUG_bad_inode1_size[(INODE_SIZE1 * MINIX1_INODES_PER_BLOCK != BLOCK_SIZE) ? -1 : 1];
+#if ENABLE_FEATURE_MINIX2
+	char BUG_bad_inode2_size[(INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) ? -1 : 1];
+#endif
+};
+
+enum {
+#ifdef UNUSED
+	MINIX1_LINK_MAX = 250,
+	MINIX2_LINK_MAX = 65530,
+	MINIX_I_MAP_SLOTS = 8,
+	MINIX_Z_MAP_SLOTS = 64,
+	MINIX_V1 = 0x0001,      /* original minix fs */
+	MINIX_V2 = 0x0002,      /* minix V2 fs */
+#endif
+	MINIX_NAME_MAX = 255,         /* # chars in a file name */
+};
+
+
+#if !ENABLE_FEATURE_MINIX2
+enum { version2 = 0 };
+#endif
+
+enum { MAX_DEPTH = 32 };
+
+enum { dev_fd = 3 };
+
+struct globals {
+#if ENABLE_FEATURE_MINIX2
+	smallint version2;
+#endif
+	smallint changed;  /* is filesystem modified? */
+	smallint errors_uncorrected;  /* flag if some error was not corrected */
+	smallint termios_set;
+	smallint dirsize;
+	smallint namelen;
+	const char *device_name;
+	int directory, regular, blockdev, chardev, links, symlinks, total;
+	char *inode_buffer;
+
+	char *inode_map;
+	char *zone_map;
+
+	unsigned char *inode_count;
+	unsigned char *zone_count;
+
+	/* File-name data */
+	int name_depth;
+	char *name_component[MAX_DEPTH+1];
+
+	/* Bigger stuff */
+	struct termios sv_termios;
+	char superblock_buffer[BLOCK_SIZE];
+	char add_zone_ind_blk[BLOCK_SIZE];
+	char add_zone_dind_blk[BLOCK_SIZE];
+	IF_FEATURE_MINIX2(char add_zone_tind_blk[BLOCK_SIZE];)
+	char check_file_blk[BLOCK_SIZE];
+
+	/* File-name data */
+	char current_name[MAX_DEPTH * MINIX_NAME_MAX];
+};
+#define G (*ptr_to_globals)
+#if ENABLE_FEATURE_MINIX2
+#define version2           (G.version2           )
+#endif
+#define changed            (G.changed            )
+#define errors_uncorrected (G.errors_uncorrected )
+#define termios_set        (G.termios_set        )
+#define dirsize            (G.dirsize            )
+#define namelen            (G.namelen            )
+#define device_name        (G.device_name        )
+#define directory          (G.directory          )
+#define regular            (G.regular            )
+#define blockdev           (G.blockdev           )
+#define chardev            (G.chardev            )
+#define links              (G.links              )
+#define symlinks           (G.symlinks           )
+#define total              (G.total              )
+#define inode_buffer       (G.inode_buffer       )
+#define inode_map          (G.inode_map          )
+#define zone_map           (G.zone_map           )
+#define inode_count        (G.inode_count        )
+#define zone_count         (G.zone_count         )
+#define name_depth         (G.name_depth         )
+#define name_component     (G.name_component     )
+#define sv_termios         (G.sv_termios         )
+#define superblock_buffer  (G.superblock_buffer )
+#define add_zone_ind_blk   (G.add_zone_ind_blk   )
+#define add_zone_dind_blk  (G.add_zone_dind_blk  )
+#define add_zone_tind_blk  (G.add_zone_tind_blk  )
+#define check_file_blk     (G.check_file_blk     )
+#define current_name       (G.current_name       )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+	dirsize = 16; \
+	namelen = 14; \
+	current_name[0] = '/'; \
+	/*current_name[1] = '\0';*/ \
+	name_component[0] = &current_name[0]; \
+} while (0)
+
+
+#define OPTION_STR "larvsmf"
+enum {
+	OPT_l = (1 << 0),
+	OPT_a = (1 << 1),
+	OPT_r = (1 << 2),
+	OPT_v = (1 << 3),
+	OPT_s = (1 << 4),
+	OPT_w = (1 << 5),
+	OPT_f = (1 << 6),
+};
+#define OPT_list      (option_mask32 & OPT_l)
+#define OPT_automatic (option_mask32 & OPT_a)
+#define OPT_repair    (option_mask32 & OPT_r)
+#define OPT_verbose   (option_mask32 & OPT_v)
+#define OPT_show      (option_mask32 & OPT_s)
+#define OPT_warn_mode (option_mask32 & OPT_w)
+#define OPT_force     (option_mask32 & OPT_f)
+/* non-automatic repairs requested? */
+#define OPT_manual    ((option_mask32 & (OPT_a|OPT_r)) == OPT_r)
+
+
+#define Inode1 (((struct minix1_inode *) inode_buffer)-1)
+#define Inode2 (((struct minix2_inode *) inode_buffer)-1)
+
+#define Super (*(struct minix_superblock *)(superblock_buffer))
+
+#if ENABLE_FEATURE_MINIX2
+# define ZONES    ((unsigned)(version2 ? Super.s_zones : Super.s_nzones))
+#else
+# define ZONES    ((unsigned)(Super.s_nzones))
+#endif
+#define INODES    ((unsigned)Super.s_ninodes)
+#define IMAPS     ((unsigned)Super.s_imap_blocks)
+#define ZMAPS     ((unsigned)Super.s_zmap_blocks)
+#define FIRSTZONE ((unsigned)Super.s_firstdatazone)
+#define ZONESIZE  ((unsigned)Super.s_log_zone_size)
+#define MAXSIZE   ((unsigned)Super.s_max_size)
+#define MAGIC     (Super.s_magic)
+
+/* gcc likes this more (code is smaller) than macro variant */
+static ALWAYS_INLINE unsigned div_roundup(unsigned size, unsigned n)
+{
+	return (size + n-1) / n;
+}
+
+#if !ENABLE_FEATURE_MINIX2
+#define INODE_BLOCKS            div_roundup(INODES, MINIX1_INODES_PER_BLOCK)
+#else
+#define INODE_BLOCKS            div_roundup(INODES, \
+                                (version2 ? MINIX2_INODES_PER_BLOCK : MINIX1_INODES_PER_BLOCK))
+#endif
+
+#define INODE_BUFFER_SIZE       (INODE_BLOCKS * BLOCK_SIZE)
+#define NORM_FIRSTZONE          (2 + IMAPS + ZMAPS + INODE_BLOCKS)
+
+/* Before you ask "where they come from?": */
+/* setbit/clrbit are supplied by sys/param.h */
+
+static int minix_bit(const char *a, unsigned i)
+{
+	return (a[i >> 3] & (1<<(i & 7)));
+}
+
+static void minix_setbit(char *a, unsigned i)
+{
+	setbit(a, i);
+	changed = 1;
+}
+static void minix_clrbit(char *a, unsigned i)
+{
+	clrbit(a, i);
+	changed = 1;
+}
+
+/* Note: do not assume 0/1, it is 0/nonzero */
+#define zone_in_use(x)  (minix_bit(zone_map,(x)-FIRSTZONE+1))
+#define inode_in_use(x) (minix_bit(inode_map,(x)))
+
+#define mark_inode(x)   (minix_setbit(inode_map,(x)))
+#define unmark_inode(x) (minix_clrbit(inode_map,(x)))
+
+#define mark_zone(x)    (minix_setbit(zone_map,(x)-FIRSTZONE+1))
+#define unmark_zone(x)  (minix_clrbit(zone_map,(x)-FIRSTZONE+1))
+
+
+static void recursive_check(unsigned ino);
+#if ENABLE_FEATURE_MINIX2
+static void recursive_check2(unsigned ino);
+#endif
+
+static void die(const char *str) NORETURN;
+static void die(const char *str)
+{
+	if (termios_set)
+		tcsetattr_stdin_TCSANOW(&sv_termios);
+	bb_error_msg_and_die("%s", str);
+}
+
+static void push_filename(const char *name)
+{
+	//  /dir/dir/dir/file
+	//  ^   ^   ^
+	// [0] [1] [2] <-name_component[i]
+	if (name_depth < MAX_DEPTH) {
+		int len;
+		char *p = name_component[name_depth];
+		*p++ = '/';
+		len = sprintf(p, "%.*s", namelen, name);
+		name_component[name_depth + 1] = p + len;
+	}
+	name_depth++;
+}
+
+static void pop_filename(void)
+{
+	name_depth--;
+	if (name_depth < MAX_DEPTH) {
+		*name_component[name_depth] = '\0';
+		if (!name_depth) {
+			current_name[0] = '/';
+			current_name[1] = '\0';
+		}
+	}
+}
+
+static int ask(const char *string, int def)
+{
+	int c;
+
+	if (!OPT_repair) {
+		bb_putchar('\n');
+		errors_uncorrected = 1;
+		return 0;
+	}
+	if (OPT_automatic) {
+		bb_putchar('\n');
+		if (!def)
+			errors_uncorrected = 1;
+		return def;
+	}
+	printf(def ? "%s (y/n)? " : "%s (n/y)? ", string);
+	for (;;) {
+		fflush_all();
+		c = getchar();
+		if (c == EOF) {
+			if (!def)
+				errors_uncorrected = 1;
+			return def;
+		}
+		if (c == '\n')
+			break;
+		c |= 0x20; /* tolower */
+		if (c == 'y') {
+			def = 1;
+			break;
+		}
+		if (c == 'n') {
+			def = 0;
+			break;
+		}
+	}
+	if (def)
+		printf("y\n");
+	else {
+		printf("n\n");
+		errors_uncorrected = 1;
+	}
+	return def;
+}
+
+/*
+ * Make certain that we aren't checking a filesystem that is on a
+ * mounted partition.  Code adapted from e2fsck, Copyright (C) 1993,
+ * 1994 Theodore Ts'o.  Also licensed under GPL.
+ */
+static void check_mount(void)
+{
+	if (find_mount_point(device_name, 0)) {
+		int cont;
+#if ENABLE_FEATURE_MTAB_SUPPORT
+		/*
+		 * If the root is mounted read-only, then /etc/mtab is
+		 * probably not correct; so we won't issue a warning based on
+		 * it.
+		 */
+		int fd = open(bb_path_mtab_file, O_RDWR);
+
+		if (fd < 0 && errno == EROFS)
+			return;
+		close(fd);
+#endif
+		printf("%s is mounted. ", device_name);
+		cont = 0;
+		if (isatty(0) && isatty(1))
+			cont = ask("Do you really want to continue", 0);
+		if (!cont) {
+			printf("Check aborted\n");
+			exit(EXIT_SUCCESS);
+		}
+	}
+}
+
+/*
+ * check_zone_nr checks to see that *nr is a valid zone nr. If it
+ * isn't, it will possibly be repaired. Check_zone_nr sets *corrected
+ * if an error was corrected, and returns the zone (0 for no zone
+ * or a bad zone-number).
+ */
+static int check_zone_nr2(uint32_t *nr, smallint *corrected)
+{
+	const char *msg;
+	if (!*nr)
+		return 0;
+	if (*nr < FIRSTZONE)
+		msg = "< FIRSTZONE";
+	else if (*nr >= ZONES)
+		msg = ">= ZONES";
+	else
+		return *nr;
+	printf("Zone nr %s in file '%s'. ", msg, current_name);
+	if (ask("Remove block", 1)) {
+		*nr = 0;
+		*corrected = 1;
+	}
+	return 0;
+}
+
+static int check_zone_nr(uint16_t *nr, smallint *corrected)
+{
+	uint32_t nr32 = *nr;
+	int r = check_zone_nr2(&nr32, corrected);
+	*nr = (uint16_t)nr32;
+	return r;
+}
+
+/*
+ * read-block reads block nr into the buffer at addr.
+ */
+static void read_block(unsigned nr, void *addr)
+{
+	if (!nr) {
+		memset(addr, 0, BLOCK_SIZE);
+		return;
+	}
+	xlseek(dev_fd, BLOCK_SIZE * nr, SEEK_SET);
+	if (BLOCK_SIZE != full_read(dev_fd, addr, BLOCK_SIZE)) {
+		printf("%s: bad block %u in file '%s'\n",
+				bb_msg_read_error, nr, current_name);
+		errors_uncorrected = 1;
+		memset(addr, 0, BLOCK_SIZE);
+	}
+}
+
+/*
+ * write_block writes block nr to disk.
+ */
+static void write_block(unsigned nr, void *addr)
+{
+	if (!nr)
+		return;
+	if (nr < FIRSTZONE || nr >= ZONES) {
+		printf("Internal error: trying to write bad block\n"
+			   "Write request ignored\n");
+		errors_uncorrected = 1;
+		return;
+	}
+	xlseek(dev_fd, BLOCK_SIZE * nr, SEEK_SET);
+	if (BLOCK_SIZE != full_write(dev_fd, addr, BLOCK_SIZE)) {
+		printf("%s: bad block %u in file '%s'\n",
+				bb_msg_write_error, nr, current_name);
+		errors_uncorrected = 1;
+	}
+}
+
+/*
+ * map_block calculates the absolute block nr of a block in a file.
+ * It sets 'changed' if the inode has needed changing, and re-writes
+ * any indirect blocks with errors.
+ */
+static int map_block(struct minix1_inode *inode, unsigned blknr)
+{
+	uint16_t ind[BLOCK_SIZE >> 1];
+	int block, result;
+	smallint blk_chg;
+
+	if (blknr < 7)
+		return check_zone_nr(inode->i_zone + blknr, &changed);
+	blknr -= 7;
+	if (blknr < 512) {
+		block = check_zone_nr(inode->i_zone + 7, &changed);
+		goto common;
+	}
+	blknr -= 512;
+	block = check_zone_nr(inode->i_zone + 8, &changed);
+	read_block(block, ind); /* double indirect */
+	blk_chg = 0;
+	result = check_zone_nr(&ind[blknr / 512], &blk_chg);
+	if (blk_chg)
+		write_block(block, ind);
+	block = result;
+ common:
+	read_block(block, ind);
+	blk_chg = 0;
+	result = check_zone_nr(&ind[blknr % 512], &blk_chg);
+	if (blk_chg)
+		write_block(block, ind);
+	return result;
+}
+
+#if ENABLE_FEATURE_MINIX2
+static int map_block2(struct minix2_inode *inode, unsigned blknr)
+{
+	uint32_t ind[BLOCK_SIZE >> 2];
+	int block, result;
+	smallint blk_chg;
+
+	if (blknr < 7)
+		return check_zone_nr2(inode->i_zone + blknr, &changed);
+	blknr -= 7;
+	if (blknr < 256) {
+		block = check_zone_nr2(inode->i_zone + 7, &changed);
+		goto common2;
+	}
+	blknr -= 256;
+	if (blknr < 256 * 256) {
+		block = check_zone_nr2(inode->i_zone + 8, &changed);
+		goto common1;
+	}
+	blknr -= 256 * 256;
+	block = check_zone_nr2(inode->i_zone + 9, &changed);
+	read_block(block, ind); /* triple indirect */
+	blk_chg = 0;
+	result = check_zone_nr2(&ind[blknr / (256 * 256)], &blk_chg);
+	if (blk_chg)
+		write_block(block, ind);
+	block = result;
+ common1:
+	read_block(block, ind); /* double indirect */
+	blk_chg = 0;
+	result = check_zone_nr2(&ind[(blknr / 256) % 256], &blk_chg);
+	if (blk_chg)
+		write_block(block, ind);
+	block = result;
+ common2:
+	read_block(block, ind);
+	blk_chg = 0;
+	result = check_zone_nr2(&ind[blknr % 256], &blk_chg);
+	if (blk_chg)
+		write_block(block, ind);
+	return result;
+}
+#endif
+
+static void write_superblock(void)
+{
+	/*
+	 * Set the state of the filesystem based on whether or not there
+	 * are uncorrected errors.  The filesystem valid flag is
+	 * unconditionally set if we get this far.
+	 */
+	Super.s_state |= MINIX_VALID_FS | MINIX_ERROR_FS;
+	if (!errors_uncorrected)
+		Super.s_state &= ~MINIX_ERROR_FS;
+
+	xlseek(dev_fd, BLOCK_SIZE, SEEK_SET);
+	if (BLOCK_SIZE != full_write(dev_fd, superblock_buffer, BLOCK_SIZE))
+		die("can't write superblock");
+}
+
+static void write_tables(void)
+{
+	write_superblock();
+
+	if (IMAPS * BLOCK_SIZE != write(dev_fd, inode_map, IMAPS * BLOCK_SIZE))
+		die("can't write inode map");
+	if (ZMAPS * BLOCK_SIZE != write(dev_fd, zone_map, ZMAPS * BLOCK_SIZE))
+		die("can't write zone map");
+	if (INODE_BUFFER_SIZE != write(dev_fd, inode_buffer, INODE_BUFFER_SIZE))
+		die("can't write inodes");
+}
+
+static void get_dirsize(void)
+{
+	int block;
+	char blk[BLOCK_SIZE];
+	int size;
+
+#if ENABLE_FEATURE_MINIX2
+	if (version2)
+		block = Inode2[MINIX_ROOT_INO].i_zone[0];
+	else
+#endif
+		block = Inode1[MINIX_ROOT_INO].i_zone[0];
+	read_block(block, blk);
+	for (size = 16; size < BLOCK_SIZE; size <<= 1) {
+		if (strcmp(blk + size + 2, "..") == 0) {
+			dirsize = size;
+			namelen = size - 2;
+			return;
+		}
+	}
+	/* use defaults */
+}
+
+static void read_superblock(void)
+{
+	xlseek(dev_fd, BLOCK_SIZE, SEEK_SET);
+	if (BLOCK_SIZE != full_read(dev_fd, superblock_buffer, BLOCK_SIZE))
+		die("can't read superblock");
+	/* already initialized to:
+	namelen = 14;
+	dirsize = 16;
+	version2 = 0;
+	*/
+	if (MAGIC == MINIX1_SUPER_MAGIC) {
+	} else if (MAGIC == MINIX1_SUPER_MAGIC2) {
+		namelen = 30;
+		dirsize = 32;
+#if ENABLE_FEATURE_MINIX2
+	} else if (MAGIC == MINIX2_SUPER_MAGIC) {
+		version2 = 1;
+	} else if (MAGIC == MINIX2_SUPER_MAGIC2) {
+		namelen = 30;
+		dirsize = 32;
+		version2 = 1;
+#endif
+	} else
+		die("bad magic number in superblock");
+	if (ZONESIZE != 0 || BLOCK_SIZE != 1024)
+		die("only 1k blocks/zones supported");
+	if (IMAPS * BLOCK_SIZE * 8 < INODES + 1)
+		die("bad s_imap_blocks field in superblock");
+	if (ZMAPS * BLOCK_SIZE * 8 < ZONES - FIRSTZONE + 1)
+		die("bad s_zmap_blocks field in superblock");
+}
+
+static void read_tables(void)
+{
+	inode_map = xzalloc(IMAPS * BLOCK_SIZE);
+	zone_map = xzalloc(ZMAPS * BLOCK_SIZE);
+	inode_buffer = xmalloc(INODE_BUFFER_SIZE);
+	inode_count = xmalloc(INODES + 1);
+	zone_count = xmalloc(ZONES);
+	if (IMAPS * BLOCK_SIZE != read(dev_fd, inode_map, IMAPS * BLOCK_SIZE))
+		die("can't read inode map");
+	if (ZMAPS * BLOCK_SIZE != read(dev_fd, zone_map, ZMAPS * BLOCK_SIZE))
+		die("can't read zone map");
+	if (INODE_BUFFER_SIZE != read(dev_fd, inode_buffer, INODE_BUFFER_SIZE))
+		die("can't read inodes");
+	if (NORM_FIRSTZONE != FIRSTZONE) {
+		printf("warning: firstzone!=norm_firstzone\n");
+		errors_uncorrected = 1;
+	}
+	get_dirsize();
+	if (OPT_show) {
+		printf("%u inodes\n"
+			"%u blocks\n"
+			"Firstdatazone=%u (%u)\n"
+			"Zonesize=%u\n"
+			"Maxsize=%u\n"
+			"Filesystem state=%u\n"
+			"namelen=%u\n\n",
+			INODES,
+			ZONES,
+			FIRSTZONE, NORM_FIRSTZONE,
+			BLOCK_SIZE << ZONESIZE,
+			MAXSIZE,
+			Super.s_state,
+			namelen);
+	}
+}
+
+static void get_inode_common(unsigned nr, uint16_t i_mode)
+{
+	total++;
+	if (!inode_count[nr]) {
+		if (!inode_in_use(nr)) {
+			printf("Inode %d is marked as 'unused', but it is used "
+					"for file '%s'\n", nr, current_name);
+			if (OPT_repair) {
+				if (ask("Mark as 'in use'", 1))
+					mark_inode(nr);
+				else
+					errors_uncorrected = 1;
+			}
+		}
+		if (S_ISDIR(i_mode))
+			directory++;
+		else if (S_ISREG(i_mode))
+			regular++;
+		else if (S_ISCHR(i_mode))
+			chardev++;
+		else if (S_ISBLK(i_mode))
+			blockdev++;
+		else if (S_ISLNK(i_mode))
+			symlinks++;
+		else if (S_ISSOCK(i_mode));
+		else if (S_ISFIFO(i_mode));
+		else {
+			printf("%s has mode %05o\n", current_name, i_mode);
+		}
+	} else
+		links++;
+	if (!++inode_count[nr]) {
+		printf("Warning: inode count too big\n");
+		inode_count[nr]--;
+		errors_uncorrected = 1;
+	}
+}
+
+static struct minix1_inode *get_inode(unsigned nr)
+{
+	struct minix1_inode *inode;
+
+	if (!nr || nr > INODES)
+		return NULL;
+	inode = Inode1 + nr;
+	get_inode_common(nr, inode->i_mode);
+	return inode;
+}
+
+#if ENABLE_FEATURE_MINIX2
+static struct minix2_inode *get_inode2(unsigned nr)
+{
+	struct minix2_inode *inode;
+
+	if (!nr || nr > INODES)
+		return NULL;
+	inode = Inode2 + nr;
+	get_inode_common(nr, inode->i_mode);
+	return inode;
+}
+#endif
+
+static void check_root(void)
+{
+	struct minix1_inode *inode = Inode1 + MINIX_ROOT_INO;
+
+	if (!inode || !S_ISDIR(inode->i_mode))
+		die("root inode isn't a directory");
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check_root2(void)
+{
+	struct minix2_inode *inode = Inode2 + MINIX_ROOT_INO;
+
+	if (!inode || !S_ISDIR(inode->i_mode))
+		die("root inode isn't a directory");
+}
+#else
+void check_root2(void);
+#endif
+
+static int add_zone_common(int block, smallint *corrected)
+{
+	if (!block)
+		return 0;
+	if (zone_count[block]) {
+		printf("Already used block is reused in file '%s'. ",
+				current_name);
+		if (ask("Clear", 1)) {
+			block = 0;
+			*corrected = 1;
+			return -1; /* "please zero out *znr" */
+		}
+	}
+	if (!zone_in_use(block)) {
+		printf("Block %d in file '%s' is marked as 'unused'. ",
+				block, current_name);
+		if (ask("Correct", 1))
+			mark_zone(block);
+	}
+	if (!++zone_count[block])
+		zone_count[block]--;
+	return block;
+}
+
+static int add_zone(uint16_t *znr, smallint *corrected)
+{
+	int block;
+
+	block = check_zone_nr(znr, corrected);
+	block = add_zone_common(block, corrected);
+	if (block == -1) {
+		*znr = 0;
+		block = 0;
+	}
+	return block;
+}
+
+#if ENABLE_FEATURE_MINIX2
+static int add_zone2(uint32_t *znr, smallint *corrected)
+{
+	int block;
+
+	block = check_zone_nr2(znr, corrected);
+	block = add_zone_common(block, corrected);
+	if (block == -1) {
+		*znr = 0;
+		block = 0;
+	}
+	return block;
+}
+#endif
+
+static void add_zone_ind(uint16_t *znr, smallint *corrected)
+{
+	int i;
+	int block;
+	smallint chg_blk = 0;
+
+	block = add_zone(znr, corrected);
+	if (!block)
+		return;
+	read_block(block, add_zone_ind_blk);
+	for (i = 0; i < (BLOCK_SIZE >> 1); i++)
+		add_zone(i + (uint16_t *) add_zone_ind_blk, &chg_blk);
+	if (chg_blk)
+		write_block(block, add_zone_ind_blk);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void add_zone_ind2(uint32_t *znr, smallint *corrected)
+{
+	int i;
+	int block;
+	smallint chg_blk = 0;
+
+	block = add_zone2(znr, corrected);
+	if (!block)
+		return;
+	read_block(block, add_zone_ind_blk);
+	for (i = 0; i < BLOCK_SIZE >> 2; i++)
+		add_zone2(i + (uint32_t *) add_zone_ind_blk, &chg_blk);
+	if (chg_blk)
+		write_block(block, add_zone_ind_blk);
+}
+#endif
+
+static void add_zone_dind(uint16_t *znr, smallint *corrected)
+{
+	int i;
+	int block;
+	smallint chg_blk = 0;
+
+	block = add_zone(znr, corrected);
+	if (!block)
+		return;
+	read_block(block, add_zone_dind_blk);
+	for (i = 0; i < (BLOCK_SIZE >> 1); i++)
+		add_zone_ind(i + (uint16_t *) add_zone_dind_blk, &chg_blk);
+	if (chg_blk)
+		write_block(block, add_zone_dind_blk);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void add_zone_dind2(uint32_t *znr, smallint *corrected)
+{
+	int i;
+	int block;
+	smallint chg_blk = 0;
+
+	block = add_zone2(znr, corrected);
+	if (!block)
+		return;
+	read_block(block, add_zone_dind_blk);
+	for (i = 0; i < BLOCK_SIZE >> 2; i++)
+		add_zone_ind2(i + (uint32_t *) add_zone_dind_blk, &chg_blk);
+	if (chg_blk)
+		write_block(block, add_zone_dind_blk);
+}
+
+static void add_zone_tind2(uint32_t *znr, smallint *corrected)
+{
+	int i;
+	int block;
+	smallint chg_blk = 0;
+
+	block = add_zone2(znr, corrected);
+	if (!block)
+		return;
+	read_block(block, add_zone_tind_blk);
+	for (i = 0; i < BLOCK_SIZE >> 2; i++)
+		add_zone_dind2(i + (uint32_t *) add_zone_tind_blk, &chg_blk);
+	if (chg_blk)
+		write_block(block, add_zone_tind_blk);
+}
+#endif
+
+static void check_zones(unsigned i)
+{
+	struct minix1_inode *inode;
+
+	if (!i || i > INODES)
+		return;
+	if (inode_count[i] > 1)		/* have we counted this file already? */
+		return;
+	inode = Inode1 + i;
+	if (!S_ISDIR(inode->i_mode)
+	 && !S_ISREG(inode->i_mode)
+	 && !S_ISLNK(inode->i_mode)
+	) {
+		return;
+	}
+	for (i = 0; i < 7; i++)
+		add_zone(i + inode->i_zone, &changed);
+	add_zone_ind(7 + inode->i_zone, &changed);
+	add_zone_dind(8 + inode->i_zone, &changed);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check_zones2(unsigned i)
+{
+	struct minix2_inode *inode;
+
+	if (!i || i > INODES)
+		return;
+	if (inode_count[i] > 1)		/* have we counted this file already? */
+		return;
+	inode = Inode2 + i;
+	if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)
+		&& !S_ISLNK(inode->i_mode))
+		return;
+	for (i = 0; i < 7; i++)
+		add_zone2(i + inode->i_zone, &changed);
+	add_zone_ind2(7 + inode->i_zone, &changed);
+	add_zone_dind2(8 + inode->i_zone, &changed);
+	add_zone_tind2(9 + inode->i_zone, &changed);
+}
+#endif
+
+static void check_file(struct minix1_inode *dir, unsigned offset)
+{
+	struct minix1_inode *inode;
+	int ino;
+	char *name;
+	int block;
+
+	block = map_block(dir, offset / BLOCK_SIZE);
+	read_block(block, check_file_blk);
+	name = check_file_blk + (offset % BLOCK_SIZE) + 2;
+	ino = *(uint16_t *) (name - 2);
+	if (ino > INODES) {
+		printf("%s contains a bad inode number for file '%.*s'. ",
+				current_name, namelen, name);
+		if (ask("Remove", 1)) {
+			*(uint16_t *) (name - 2) = 0;
+			write_block(block, check_file_blk);
+		}
+		ino = 0;
+	}
+	push_filename(name);
+	inode = get_inode(ino);
+	pop_filename();
+	if (!offset) {
+		if (inode && LONE_CHAR(name, '.'))
+			return;
+		printf("%s: bad directory: '.' isn't first\n", current_name);
+		errors_uncorrected = 1;
+	}
+	if (offset == dirsize) {
+		if (inode && strcmp("..", name) == 0)
+			return;
+		printf("%s: bad directory: '..' isn't second\n", current_name);
+		errors_uncorrected = 1;
+	}
+	if (!inode)
+		return;
+	push_filename(name);
+	if (OPT_list) {
+		if (OPT_verbose)
+			printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
+		printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : "");
+	}
+	check_zones(ino);
+	if (inode && S_ISDIR(inode->i_mode))
+		recursive_check(ino);
+	pop_filename();
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check_file2(struct minix2_inode *dir, unsigned offset)
+{
+	struct minix2_inode *inode;
+	int ino;
+	char *name;
+	int block;
+
+	block = map_block2(dir, offset / BLOCK_SIZE);
+	read_block(block, check_file_blk);
+	name = check_file_blk + (offset % BLOCK_SIZE) + 2;
+	ino = *(uint16_t *) (name - 2);
+	if (ino > INODES) {
+		printf("%s contains a bad inode number for file '%.*s'. ",
+				current_name, namelen, name);
+		if (ask("Remove", 1)) {
+			*(uint16_t *) (name - 2) = 0;
+			write_block(block, check_file_blk);
+		}
+		ino = 0;
+	}
+	push_filename(name);
+	inode = get_inode2(ino);
+	pop_filename();
+	if (!offset) {
+		if (inode && LONE_CHAR(name, '.'))
+			return;
+		printf("%s: bad directory: '.' isn't first\n", current_name);
+		errors_uncorrected = 1;
+	}
+	if (offset == dirsize) {
+		if (inode && strcmp("..", name) == 0)
+			return;
+		printf("%s: bad directory: '..' isn't second\n", current_name);
+		errors_uncorrected = 1;
+	}
+	if (!inode)
+		return;
+	push_filename(name);
+	if (OPT_list) {
+		if (OPT_verbose)
+			printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
+		printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : "");
+	}
+	check_zones2(ino);
+	if (inode && S_ISDIR(inode->i_mode))
+		recursive_check2(ino);
+	pop_filename();
+}
+#endif
+
+static void recursive_check(unsigned ino)
+{
+	struct minix1_inode *dir;
+	unsigned offset;
+
+	dir = Inode1 + ino;
+	if (!S_ISDIR(dir->i_mode))
+		die("internal error");
+	if (dir->i_size < 2 * dirsize) {
+		printf("%s: bad directory: size<32", current_name);
+		errors_uncorrected = 1;
+	}
+	for (offset = 0; offset < dir->i_size; offset += dirsize)
+		check_file(dir, offset);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void recursive_check2(unsigned ino)
+{
+	struct minix2_inode *dir;
+	unsigned offset;
+
+	dir = Inode2 + ino;
+	if (!S_ISDIR(dir->i_mode))
+		die("internal error");
+	if (dir->i_size < 2 * dirsize) {
+		printf("%s: bad directory: size<32", current_name);
+		errors_uncorrected = 1;
+	}
+	for (offset = 0; offset < dir->i_size; offset += dirsize)
+		check_file2(dir, offset);
+}
+#endif
+
+static int bad_zone(int i)
+{
+	char buffer[BLOCK_SIZE];
+
+	xlseek(dev_fd, BLOCK_SIZE * i, SEEK_SET);
+	return (BLOCK_SIZE != full_read(dev_fd, buffer, BLOCK_SIZE));
+}
+
+static void check_counts(void)
+{
+	int i;
+
+	for (i = 1; i <= INODES; i++) {
+		if (OPT_warn_mode && Inode1[i].i_mode && !inode_in_use(i)) {
+			printf("Inode %d has non-zero mode. ", i);
+			if (ask("Clear", 1)) {
+				Inode1[i].i_mode = 0;
+				changed = 1;
+			}
+		}
+		if (!inode_count[i]) {
+			if (!inode_in_use(i))
+				continue;
+			printf("Unused inode %d is marked as 'used' in the bitmap. ", i);
+			if (ask("Clear", 1))
+				unmark_inode(i);
+			continue;
+		}
+		if (!inode_in_use(i)) {
+			printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i);
+			if (ask("Set", 1))
+				mark_inode(i);
+		}
+		if (Inode1[i].i_nlinks != inode_count[i]) {
+			printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ",
+				i, Inode1[i].i_mode, Inode1[i].i_nlinks,
+				inode_count[i]);
+			if (ask("Set i_nlinks to count", 1)) {
+				Inode1[i].i_nlinks = inode_count[i];
+				changed = 1;
+			}
+		}
+	}
+	for (i = FIRSTZONE; i < ZONES; i++) {
+		if ((zone_in_use(i) != 0) == zone_count[i])
+			continue;
+		if (!zone_count[i]) {
+			if (bad_zone(i))
+				continue;
+			printf("Zone %d is marked 'in use', but no file uses it. ", i);
+			if (ask("Unmark", 1))
+				unmark_zone(i);
+			continue;
+		}
+		printf("Zone %d: %sin use, counted=%d\n",
+			i, zone_in_use(i) ? "" : "not ", zone_count[i]);
+	}
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check_counts2(void)
+{
+	int i;
+
+	for (i = 1; i <= INODES; i++) {
+		if (OPT_warn_mode && Inode2[i].i_mode && !inode_in_use(i)) {
+			printf("Inode %d has non-zero mode. ", i);
+			if (ask("Clear", 1)) {
+				Inode2[i].i_mode = 0;
+				changed = 1;
+			}
+		}
+		if (!inode_count[i]) {
+			if (!inode_in_use(i))
+				continue;
+			printf("Unused inode %d is marked as 'used' in the bitmap. ", i);
+			if (ask("Clear", 1))
+				unmark_inode(i);
+			continue;
+		}
+		if (!inode_in_use(i)) {
+			printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i);
+			if (ask("Set", 1))
+				mark_inode(i);
+		}
+		if (Inode2[i].i_nlinks != inode_count[i]) {
+			printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ",
+				i, Inode2[i].i_mode, Inode2[i].i_nlinks,
+				inode_count[i]);
+			if (ask("Set i_nlinks to count", 1)) {
+				Inode2[i].i_nlinks = inode_count[i];
+				changed = 1;
+			}
+		}
+	}
+	for (i = FIRSTZONE; i < ZONES; i++) {
+		if ((zone_in_use(i) != 0) == zone_count[i])
+			continue;
+		if (!zone_count[i]) {
+			if (bad_zone(i))
+				continue;
+			printf("Zone %d is marked 'in use', but no file uses it. ", i);
+			if (ask("Unmark", 1))
+				unmark_zone(i);
+			continue;
+		}
+		printf("Zone %d: %sin use, counted=%d\n",
+			i, zone_in_use(i) ? "" : "not ", zone_count[i]);
+	}
+}
+#endif
+
+static void check(void)
+{
+	memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
+	memset(zone_count, 0, ZONES * sizeof(*zone_count));
+	check_zones(MINIX_ROOT_INO);
+	recursive_check(MINIX_ROOT_INO);
+	check_counts();
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check2(void)
+{
+	memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
+	memset(zone_count, 0, ZONES * sizeof(*zone_count));
+	check_zones2(MINIX_ROOT_INO);
+	recursive_check2(MINIX_ROOT_INO);
+	check_counts2();
+}
+#else
+void check2(void);
+#endif
+
+int fsck_minix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fsck_minix_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct termios tmp;
+	int retcode = 0;
+
+	xfunc_error_retval = 8;
+
+	INIT_G();
+
+	opt_complementary = "=1:ar"; /* one argument; -a assumes -r */
+	getopt32(argv, OPTION_STR);
+	argv += optind;
+	device_name = argv[0];
+
+	check_mount();  /* trying to check a mounted filesystem? */
+	if (OPT_manual) {
+		if (!isatty(0) || !isatty(1))
+			die("need terminal for interactive repairs");
+	}
+	xmove_fd(xopen(device_name, OPT_repair ? O_RDWR : O_RDONLY), dev_fd);
+
+	/*sync(); paranoia? */
+	read_superblock();
+
+	/*
+	 * Determine whether or not we should continue with the checking.
+	 * This is based on the status of the filesystem valid and error
+	 * flags and whether or not the -f switch was specified on the
+	 * command line.
+	 */
+	printf("%s: %s\n", applet_name, bb_banner);
+
+	if (!(Super.s_state & MINIX_ERROR_FS)
+	 && (Super.s_state & MINIX_VALID_FS) && !OPT_force
+	) {
+		if (OPT_repair)
+			printf("%s is clean, check is skipped\n", device_name);
+		return 0;
+	} else if (OPT_force)
+		printf("Forcing filesystem check on %s\n", device_name);
+	else if (OPT_repair)
+		printf("Filesystem on %s is dirty, needs checking\n",
+			device_name);
+
+	read_tables();
+
+	if (OPT_manual) {
+		tcgetattr(0, &sv_termios);
+		tmp = sv_termios;
+		tmp.c_lflag &= ~(ICANON | ECHO);
+		tcsetattr_stdin_TCSANOW(&tmp);
+		termios_set = 1;
+	}
+
+	if (version2) {
+		check_root2();
+		check2();
+	} else {
+		check_root();
+		check();
+	}
+
+	if (OPT_verbose) {
+		int i, free_cnt;
+
+		for (i = 1, free_cnt = 0; i <= INODES; i++)
+			if (!inode_in_use(i))
+				free_cnt++;
+		printf("\n%6u inodes used (%u%%)\n", (INODES - free_cnt),
+			100 * (INODES - free_cnt) / INODES);
+		for (i = FIRSTZONE, free_cnt = 0; i < ZONES; i++)
+			if (!zone_in_use(i))
+				free_cnt++;
+		printf("%6u zones used (%u%%)\n\n"
+			"%6u regular files\n"
+			"%6u directories\n"
+			"%6u character device files\n"
+			"%6u block device files\n"
+			"%6u links\n"
+			"%6u symbolic links\n"
+			"------\n"
+			"%6u files\n",
+			(ZONES - free_cnt), 100 * (ZONES - free_cnt) / ZONES,
+			regular, directory, chardev, blockdev,
+			links - 2 * directory + 1, symlinks,
+			total - 2 * directory + 1);
+	}
+	if (changed) {
+		write_tables();
+		printf("FILE SYSTEM HAS BEEN CHANGED\n");
+		sync();
+	} else if (OPT_repair)
+		write_superblock();
+
+	if (OPT_manual)
+		tcsetattr_stdin_TCSANOW(&sv_termios);
+
+	if (changed)
+		retcode += 3;
+	if (errors_uncorrected)
+		retcode += 4;
+	return retcode;
+}
diff --git a/ap/app/busybox/src/util-linux/getopt.c b/ap/app/busybox/src/util-linux/getopt.c
new file mode 100644
index 0000000..1ae0c59
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/getopt.c
@@ -0,0 +1,422 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * getopt.c - Enhanced implementation of BSD getopt(1)
+ *   Copyright (c) 1997, 1998, 1999, 2000  Frodo Looijaard <frodol@dds.nl>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/*
+ * Version 1.0-b4: Tue Sep 23 1997. First public release.
+ * Version 1.0: Wed Nov 19 1997.
+ *   Bumped up the version number to 1.0
+ *   Fixed minor typo (CSH instead of TCSH)
+ * Version 1.0.1: Tue Jun 3 1998
+ *   Fixed sizeof instead of strlen bug
+ *   Bumped up the version number to 1.0.1
+ * Version 1.0.2: Thu Jun 11 1998 (not present)
+ *   Fixed gcc-2.8.1 warnings
+ *   Fixed --version/-V option (not present)
+ * Version 1.0.5: Tue Jun 22 1999
+ *   Make -u option work (not present)
+ * Version 1.0.6: Tue Jun 27 2000
+ *   No important changes
+ * Version 1.1.0: Tue Jun 30 2000
+ *   Added NLS support (partly written by Arkadiusz Mickiewicz
+ *     <misiek@misiek.eu.org>)
+ * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org>
+ *  Removed --version/-V and --help/-h
+ *  Removed parse_error(), using bb_error_msg() from Busybox instead
+ *  Replaced our_malloc with xmalloc and our_realloc with xrealloc
+ *
+ */
+
+//usage:#define getopt_trivial_usage
+//usage:       "[OPTIONS] [--] OPTSTRING PARAMS"
+//usage:#define getopt_full_usage "\n\n"
+//usage:	IF_LONG_OPTS(
+//usage:       "	-a,--alternative		Allow long options starting with single -"
+//usage:     "\n	-l,--longoptions=LOPT[,...]	Long options to be recognized"
+//usage:     "\n	-n,--name=PROGNAME		The name under which errors are reported"
+//usage:     "\n	-o,--options=OPTSTRING		Short options to be recognized"
+//usage:     "\n	-q,--quiet			Disable error reporting by getopt(3)"
+//usage:     "\n	-Q,--quiet-output		No normal output"
+//usage:     "\n	-s,--shell=SHELL		Set shell quoting conventions"
+//usage:     "\n	-T,--test			Test for getopt(1) version"
+//usage:     "\n	-u,--unquoted			Don't quote the output"
+//usage:	)
+//usage:	IF_NOT_LONG_OPTS(
+//usage:       "	-a		Allow long options starting with single -"
+//usage:     "\n	-l LOPT[,...]	Long options to be recognized"
+//usage:     "\n	-n PROGNAME	The name under which errors are reported"
+//usage:     "\n	-o OPTSTRING	Short options to be recognized"
+//usage:     "\n	-q		Disable error reporting by getopt(3)"
+//usage:     "\n	-Q		No normal output"
+//usage:     "\n	-s SHELL	Set shell quoting conventions"
+//usage:     "\n	-T		Test for getopt(1) version"
+//usage:     "\n	-u		Don't quote the output"
+//usage:	)
+//usage:     "\n"
+//usage:     "\nExample:"
+//usage:     "\n"
+//usage:     "\nO=`getopt -l bb: -- ab:c:: \"$@\"` || exit 1"
+//usage:     "\neval set -- \"$O\""
+//usage:     "\nwhile true; do"
+//usage:     "\n	case \"$1\" in"
+//usage:     "\n	-a)	echo A; shift;;"
+//usage:     "\n	-b|--bb) echo \"B:'$2'\"; shift 2;;"
+//usage:     "\n	-c)	case \"$2\" in"
+//usage:     "\n		\"\")	echo C; shift 2;;"
+//usage:     "\n		*)	echo \"C:'$2'\"; shift 2;;"
+//usage:     "\n		esac;;"
+//usage:     "\n	--)	shift; break;;"
+//usage:     "\n	*)	echo Error; exit 1;;"
+//usage:     "\n	esac"
+//usage:     "\ndone"
+//usage:
+//usage:#define getopt_example_usage
+//usage:       "$ cat getopt.test\n"
+//usage:       "#!/bin/sh\n"
+//usage:       "GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \\\n"
+//usage:       "       -n 'example.busybox' -- \"$@\"`\n"
+//usage:       "if [ $? != 0 ]; then exit 1; fi\n"
+//usage:       "eval set -- \"$GETOPT\"\n"
+//usage:       "while true; do\n"
+//usage:       " case $1 in\n"
+//usage:       "   -a|--a-long) echo \"Option a\"; shift;;\n"
+//usage:       "   -b|--b-long) echo \"Option b, argument '$2'\"; shift 2;;\n"
+//usage:       "   -c|--c-long)\n"
+//usage:       "     case \"$2\" in\n"
+//usage:       "       \"\") echo \"Option c, no argument\"; shift 2;;\n"
+//usage:       "       *)  echo \"Option c, argument '$2'\"; shift 2;;\n"
+//usage:       "     esac;;\n"
+//usage:       "   --) shift; break;;\n"
+//usage:       "   *) echo \"Internal error!\"; exit 1;;\n"
+//usage:       " esac\n"
+//usage:       "done\n"
+
+#if ENABLE_FEATURE_GETOPT_LONG
+# include <getopt.h>
+#endif
+#include "libbb.h"
+
+/* NON_OPT is the code that is returned when a non-option is found in '+'
+   mode */
+enum {
+	NON_OPT = 1,
+#if ENABLE_FEATURE_GETOPT_LONG
+/* LONG_OPT is the code that is returned when a long option is found. */
+	LONG_OPT = 2
+#endif
+};
+
+/* For finding activated option flags. Must match getopt32 call! */
+enum {
+	OPT_o	= 0x1,	// -o
+	OPT_n	= 0x2,	// -n
+	OPT_q	= 0x4,	// -q
+	OPT_Q	= 0x8,	// -Q
+	OPT_s	= 0x10,	// -s
+	OPT_T	= 0x20,	// -T
+	OPT_u	= 0x40,	// -u
+#if ENABLE_FEATURE_GETOPT_LONG
+	OPT_a	= 0x80,	// -a
+	OPT_l	= 0x100, // -l
+#endif
+	SHELL_IS_TCSH = 0x8000, /* hijack this bit for other purposes */
+};
+
+/* 0 is getopt_long, 1 is getopt_long_only */
+#define alternative  (option_mask32 & OPT_a)
+
+#define quiet_errors (option_mask32 & OPT_q)
+#define quiet_output (option_mask32 & OPT_Q)
+#define quote        (!(option_mask32 & OPT_u))
+#define shell_TCSH   (option_mask32 & SHELL_IS_TCSH)
+
+/*
+ * This function 'normalizes' a single argument: it puts single quotes around
+ * it and escapes other special characters. If quote is false, it just
+ * returns its argument.
+ * Bash only needs special treatment for single quotes; tcsh also recognizes
+ * exclamation marks within single quotes, and nukes whitespace.
+ * This function returns a pointer to a buffer that is overwritten by
+ * each call.
+ */
+static const char *normalize(const char *arg)
+{
+	char *bufptr;
+#if ENABLE_FEATURE_CLEAN_UP
+	static char *BUFFER = NULL;
+	free(BUFFER);
+#else
+	char *BUFFER;
+#endif
+
+	if (!quote) { /* Just copy arg */
+		BUFFER = xstrdup(arg);
+		return BUFFER;
+	}
+
+	/* Each character in arg may take up to four characters in the result:
+	   For a quote we need a closing quote, a backslash, a quote and an
+	   opening quote! We need also the global opening and closing quote,
+	   and one extra character for '\0'. */
+	BUFFER = xmalloc(strlen(arg)*4 + 3);
+
+	bufptr = BUFFER;
+	*bufptr ++= '\'';
+
+	while (*arg) {
+		if (*arg == '\'') {
+			/* Quote: replace it with: '\'' */
+			*bufptr ++= '\'';
+			*bufptr ++= '\\';
+			*bufptr ++= '\'';
+			*bufptr ++= '\'';
+		} else if (shell_TCSH && *arg == '!') {
+			/* Exclamation mark: replace it with: \! */
+			*bufptr ++= '\'';
+			*bufptr ++= '\\';
+			*bufptr ++= '!';
+			*bufptr ++= '\'';
+		} else if (shell_TCSH && *arg == '\n') {
+			/* Newline: replace it with: \n */
+			*bufptr ++= '\\';
+			*bufptr ++= 'n';
+		} else if (shell_TCSH && isspace(*arg)) {
+			/* Non-newline whitespace: replace it with \<ws> */
+			*bufptr ++= '\'';
+			*bufptr ++= '\\';
+			*bufptr ++= *arg;
+			*bufptr ++= '\'';
+		} else
+			/* Just copy */
+			*bufptr ++= *arg;
+		arg++;
+	}
+	*bufptr ++= '\'';
+	*bufptr ++= '\0';
+	return BUFFER;
+}
+
+/*
+ * Generate the output. argv[0] is the program name (used for reporting errors).
+ * argv[1..] contains the options to be parsed. argc must be the number of
+ * elements in argv (ie. 1 if there are no options, only the program name),
+ * optstr must contain the short options, and longopts the long options.
+ * Other settings are found in global variables.
+ */
+#if !ENABLE_FEATURE_GETOPT_LONG
+#define generate_output(argv,argc,optstr,longopts) \
+	generate_output(argv,argc,optstr)
+#endif
+static int generate_output(char **argv, int argc, const char *optstr, const struct option *longopts)
+{
+	int exit_code = 0; /* We assume everything will be OK */
+	int opt;
+#if ENABLE_FEATURE_GETOPT_LONG
+	int longindex;
+#endif
+	const char *charptr;
+
+	if (quiet_errors) /* No error reporting from getopt(3) */
+		opterr = 0;
+
+	/* We used it already in main() in getopt32(),
+	 * we *must* reset getopt(3): */
+#ifdef __GLIBC__
+	optind = 0;
+#else /* BSD style */
+	optind = 1;
+	/* optreset = 1; */
+#endif
+
+	while (1) {
+		opt =
+#if ENABLE_FEATURE_GETOPT_LONG
+			alternative ?
+			getopt_long_only(argc, argv, optstr, longopts, &longindex) :
+			getopt_long(argc, argv, optstr, longopts, &longindex);
+#else
+			getopt(argc, argv, optstr);
+#endif
+		if (opt == -1)
+			break;
+		if (opt == '?' || opt == ':' )
+			exit_code = 1;
+		else if (!quiet_output) {
+#if ENABLE_FEATURE_GETOPT_LONG
+			if (opt == LONG_OPT) {
+				printf(" --%s", longopts[longindex].name);
+				if (longopts[longindex].has_arg)
+					printf(" %s",
+						normalize(optarg ? optarg : ""));
+			} else
+#endif
+			if (opt == NON_OPT)
+				printf(" %s", normalize(optarg));
+			else {
+				printf(" -%c", opt);
+				charptr = strchr(optstr, opt);
+				if (charptr != NULL && *++charptr == ':')
+					printf(" %s",
+						normalize(optarg ? optarg : ""));
+			}
+		}
+	}
+
+	if (!quiet_output) {
+		printf(" --");
+		while (optind < argc)
+			printf(" %s", normalize(argv[optind++]));
+		bb_putchar('\n');
+	}
+	return exit_code;
+}
+
+#if ENABLE_FEATURE_GETOPT_LONG
+/*
+ * Register several long options. options is a string of long options,
+ * separated by commas or whitespace.
+ * This nukes options!
+ */
+static struct option *add_long_options(struct option *long_options, char *options)
+{
+	int long_nr = 0;
+	int arg_opt, tlen;
+	char *tokptr = strtok(options, ", \t\n");
+
+	if (long_options)
+		while (long_options[long_nr].name)
+			long_nr++;
+
+	while (tokptr) {
+		arg_opt = no_argument;
+		tlen = strlen(tokptr);
+		if (tlen) {
+			tlen--;
+			if (tokptr[tlen] == ':') {
+				arg_opt = required_argument;
+				if (tlen && tokptr[tlen-1] == ':') {
+					tlen--;
+					arg_opt = optional_argument;
+				}
+				tokptr[tlen] = '\0';
+				if (tlen == 0)
+					bb_error_msg_and_die("empty long option specified");
+			}
+			long_options = xrealloc_vector(long_options, 4, long_nr);
+			long_options[long_nr].has_arg = arg_opt;
+			/*long_options[long_nr].flag = NULL; - xrealloc_vector did it */
+			long_options[long_nr].val = LONG_OPT;
+			long_options[long_nr].name = xstrdup(tokptr);
+			long_nr++;
+			/*memset(&long_options[long_nr], 0, sizeof(long_options[0])); - xrealloc_vector did it */
+		}
+		tokptr = strtok(NULL, ", \t\n");
+	}
+	return long_options;
+}
+#endif
+
+static void set_shell(const char *new_shell)
+{
+	if (!strcmp(new_shell, "bash") || !strcmp(new_shell, "sh"))
+		return;
+	if (!strcmp(new_shell, "tcsh") || !strcmp(new_shell, "csh"))
+		option_mask32 |= SHELL_IS_TCSH;
+	else
+		bb_error_msg("unknown shell '%s', assuming bash", new_shell);
+}
+
+
+/* Exit codes:
+ *   0) No errors, successful operation.
+ *   1) getopt(3) returned an error.
+ *   2) A problem with parameter parsing for getopt(1).
+ *   3) Internal error, out of memory
+ *   4) Returned for -T
+ */
+
+#if ENABLE_FEATURE_GETOPT_LONG
+static const char getopt_longopts[] ALIGN1 =
+	"options\0"      Required_argument "o"
+	"longoptions\0"  Required_argument "l"
+	"quiet\0"        No_argument       "q"
+	"quiet-output\0" No_argument       "Q"
+	"shell\0"        Required_argument "s"
+	"test\0"         No_argument       "T"
+	"unquoted\0"     No_argument       "u"
+	"alternative\0"  No_argument       "a"
+	"name\0"         Required_argument "n"
+	;
+#endif
+
+int getopt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int getopt_main(int argc, char **argv)
+{
+	int n;
+	char *optstr = NULL;
+	char *name = NULL;
+	unsigned opt;
+	const char *compatible;
+	char *s_arg;
+#if ENABLE_FEATURE_GETOPT_LONG
+	struct option *long_options = NULL;
+	llist_t *l_arg = NULL;
+#endif
+
+	compatible = getenv("GETOPT_COMPATIBLE"); /* used as yes/no flag */
+
+	if (!argv[1]) {
+		if (compatible) {
+			/* For some reason, the original getopt gave no error
+			 * when there were no arguments. */
+			printf(" --\n");
+			return 0;
+		}
+		bb_error_msg_and_die("missing optstring argument");
+	}
+
+	if (argv[1][0] != '-' || compatible) {
+		char *s = argv[1];
+
+		option_mask32 |= OPT_u; /* quoting off */
+		s = xstrdup(s + strspn(s, "-+"));
+		argv[1] = argv[0];
+		return generate_output(argv+1, argc-1, s, long_options);
+	}
+
+#if !ENABLE_FEATURE_GETOPT_LONG
+	opt = getopt32(argv, "+o:n:qQs:Tu", &optstr, &name, &s_arg);
+#else
+	applet_long_options = getopt_longopts;
+	opt_complementary = "l::";
+	opt = getopt32(argv, "+o:n:qQs:Tual:",
+					&optstr, &name, &s_arg, &l_arg);
+	/* Effectuate the read options for the applet itself */
+	while (l_arg) {
+		long_options = add_long_options(long_options, llist_pop(&l_arg));
+	}
+#endif
+
+	if (opt & OPT_s) {
+		set_shell(s_arg);
+	}
+
+	if (opt & OPT_T) {
+		return 4;
+	}
+
+	/* All options controlling the applet have now been parsed */
+	n = optind - 1;
+	if (!optstr) {
+		optstr = argv[++n];
+		if (!optstr)
+			bb_error_msg_and_die("missing optstring argument");
+	}
+
+	argv[n] = name ? name : argv[0];
+	return generate_output(argv + n, argc - n, optstr, long_options);
+}
diff --git a/ap/app/busybox/src/util-linux/hexdump.c b/ap/app/busybox/src/util-linux/hexdump.c
new file mode 100644
index 0000000..9a312f9
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/hexdump.c
@@ -0,0 +1,174 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * hexdump implementation for busybox
+ * Based on code from util-linux v 2.11l
+ *
+ * Copyright (c) 1989
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define hexdump_trivial_usage
+//usage:       "[-bcCdefnosvx" IF_FEATURE_HEXDUMP_REVERSE("R") "] [FILE]..."
+//usage:#define hexdump_full_usage "\n\n"
+//usage:       "Display FILEs (or stdin) in a user specified format\n"
+//usage:     "\n	-b		One-byte octal display"
+//usage:     "\n	-c		One-byte character display"
+//usage:     "\n	-C		Canonical hex+ASCII, 16 bytes per line"
+//usage:     "\n	-d		Two-byte decimal display"
+//usage:     "\n	-e FORMAT_STRING"
+//usage:     "\n	-f FORMAT_FILE"
+//usage:     "\n	-n LENGTH	Interpret only LENGTH bytes of input"
+//usage:     "\n	-o		Two-byte octal display"
+//usage:     "\n	-s OFFSET	Skip OFFSET bytes"
+//usage:     "\n	-v		Display all input data"
+//usage:     "\n	-x		Two-byte hexadecimal display"
+//usage:	IF_FEATURE_HEXDUMP_REVERSE(
+//usage:     "\n	-R		Reverse of 'hexdump -Cv'")
+//usage:
+//usage:#define hd_trivial_usage
+//usage:       "FILE..."
+//usage:#define hd_full_usage "\n\n"
+//usage:       "hd is an alias for hexdump -C"
+
+#include "libbb.h"
+#include "dump.h"
+
+/* This is a NOEXEC applet. Be very careful! */
+
+static void bb_dump_addfile(dumper_t *dumper, char *name)
+{
+	char *p;
+	FILE *fp;
+	char *buf;
+
+	fp = xfopen_for_read(name);
+	while ((buf = xmalloc_fgetline(fp)) != NULL) {
+		p = skip_whitespace(buf);
+		if (*p && (*p != '#')) {
+			bb_dump_add(dumper, p);
+		}
+		free(buf);
+	}
+	fclose(fp);
+}
+
+static const char *const add_strings[] = {
+	"\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"",   /* b */
+	"\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"",   /* c */
+	"\"%07.7_ax \" 8/2 \"  %05u \" \"\\n\"",  /* d */
+	"\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"",   /* o */
+	"\"%07.7_ax \" 8/2 \"   %04x \" \"\\n\"", /* x */
+};
+
+static const char add_first[] ALIGN1 = "\"%07.7_Ax\n\"";
+
+static const char hexdump_opts[] ALIGN1 = "bcdoxCe:f:n:s:v" IF_FEATURE_HEXDUMP_REVERSE("R");
+
+static const struct suffix_mult suffixes[] = {
+	{ "b", 512 },
+	{ "k", 1024 },
+	{ "m", 1024*1024 },
+	{ "", 0 }
+};
+
+int hexdump_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int hexdump_main(int argc, char **argv)
+{
+	dumper_t *dumper = alloc_dumper();
+	const char *p;
+	int ch;
+#if ENABLE_FEATURE_HEXDUMP_REVERSE
+	FILE *fp;
+	smallint rdump = 0;
+#endif
+
+	if (ENABLE_HD && !applet_name[2]) { /* we are "hd" */
+		ch = 'C';
+		goto hd_applet;
+	}
+
+	/* We cannot use getopt32: in hexdump options are cumulative.
+	 * E.g. "hexdump -C -C file" should dump each line twice */
+	while ((ch = getopt(argc, argv, hexdump_opts)) > 0) {
+		p = strchr(hexdump_opts, ch);
+		if (!p)
+			bb_show_usage();
+		if ((p - hexdump_opts) < 5) {
+			bb_dump_add(dumper, add_first);
+			bb_dump_add(dumper, add_strings[(int)(p - hexdump_opts)]);
+		}
+		/* Save a little bit of space below by omitting the 'else's. */
+		if (ch == 'C') {
+ hd_applet:
+			bb_dump_add(dumper, "\"%08.8_Ax\n\"");
+			bb_dump_add(dumper, "\"%08.8_ax  \" 8/1 \"%02x \" \"  \" 8/1 \"%02x \" ");
+			bb_dump_add(dumper, "\"  |\" 16/1 \"%_p\" \"|\\n\"");
+		}
+		if (ch == 'e') {
+			bb_dump_add(dumper, optarg);
+		} /* else */
+		if (ch == 'f') {
+			bb_dump_addfile(dumper, optarg);
+		} /* else */
+		if (ch == 'n') {
+			dumper->dump_length = xatoi_positive(optarg);
+		} /* else */
+		if (ch == 's') { /* compat: -s accepts hex numbers too */
+			dumper->dump_skip = xstrtoul_range_sfx(optarg, /*base:*/ 0, /*lo:*/ 0, /*hi:*/ LONG_MAX, suffixes);
+		} /* else */
+		if (ch == 'v') {
+			dumper->dump_vflag = ALL;
+		}
+#if ENABLE_FEATURE_HEXDUMP_REVERSE
+		if (ch == 'R') {
+			rdump = 1;
+		}
+#endif
+	}
+
+	if (!dumper->fshead) {
+		bb_dump_add(dumper, add_first);
+		bb_dump_add(dumper, "\"%07.7_ax \" 8/2 \"%04x \" \"\\n\"");
+	}
+
+	argv += optind;
+
+#if !ENABLE_FEATURE_HEXDUMP_REVERSE
+	return bb_dump_dump(dumper, argv);
+#else
+	if (!rdump) {
+		return bb_dump_dump(dumper, argv);
+	}
+
+	/* -R: reverse of 'hexdump -Cv' */
+	fp = stdin;
+	if (!*argv) {
+		argv--;
+		goto jump_in;
+	}
+
+	do {
+		char *buf;
+		fp = xfopen_for_read(*argv);
+ jump_in:
+		while ((buf = xmalloc_fgetline(fp)) != NULL) {
+			p = buf;
+			while (1) {
+				/* skip address or previous byte */
+				while (isxdigit(*p)) p++;
+				while (*p == ' ') p++;
+				/* '|' char will break the line */
+				if (!isxdigit(*p) || sscanf(p, "%x ", &ch) != 1)
+					break;
+				putchar(ch);
+			}
+			free(buf);
+		}
+		fclose(fp);
+	} while (*++argv);
+
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+#endif
+}
diff --git a/ap/app/busybox/src/util-linux/hwclock.c b/ap/app/busybox/src/util-linux/hwclock.c
new file mode 100644
index 0000000..379eeb2
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/hwclock.c
@@ -0,0 +1,328 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini hwclock implementation for busybox
+ *
+ * Copyright (C) 2002 Robert Griebl <griebl@gmx.de>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+*/
+
+#include "libbb.h"
+/* After libbb.h, since it needs sys/types.h on some systems */
+#include <sys/utsname.h>
+#include "rtc_.h"
+
+/* diff code is disabled: it's not sys/hw clock diff, it's some useless
+ * "time between hwclock was started and we saw CMOS tick" quantity.
+ * It's useless since hwclock is started at a random moment,
+ * thus the quantity is also random, useless. Showing 0.000000 does not
+ * deprive us from any useful info.
+ *
+ * SHOW_HWCLOCK_DIFF code in this file shows the difference between system
+ * and hw clock. It is useful, but not compatible with standard hwclock.
+ * Thus disabled.
+ */
+#define SHOW_HWCLOCK_DIFF 0
+
+
+#if !SHOW_HWCLOCK_DIFF
+# define read_rtc(pp_rtcname, sys_tv, utc) read_rtc(pp_rtcname, utc)
+#endif
+static time_t read_rtc(const char **pp_rtcname, struct timeval *sys_tv, int utc)
+{
+	struct tm tm_time;
+	int fd;
+
+	fd = rtc_xopen(pp_rtcname, O_RDONLY);
+
+	rtc_read_tm(&tm_time, fd);
+
+#if SHOW_HWCLOCK_DIFF
+	{
+		int before = tm_time.tm_sec;
+		while (1) {
+			rtc_read_tm(&tm_time, fd);
+			gettimeofday(sys_tv, NULL);
+			if (before != (int)tm_time.tm_sec)
+				break;
+		}
+	}
+#endif
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(fd);
+
+	return rtc_tm2time(&tm_time, utc);
+}
+
+static void show_clock(const char **pp_rtcname, int utc)
+{
+#if SHOW_HWCLOCK_DIFF
+	struct timeval sys_tv;
+#endif
+	time_t t = read_rtc(pp_rtcname, &sys_tv, utc);
+
+#if ENABLE_LOCALE_SUPPORT
+	/* Standard hwclock uses locale-specific output format */
+	char cp[64];
+	struct tm *ptm = localtime(&t);
+	strftime(cp, sizeof(cp), "%c", ptm);
+#else
+	char *cp = ctime(&t);
+	strchrnul(cp, '\n')[0] = '\0';
+#endif
+
+#if !SHOW_HWCLOCK_DIFF
+	printf("%s  0.000000 seconds\n", cp);
+#else
+	{
+		long diff = sys_tv.tv_sec - t;
+		if (diff < 0 /*&& tv.tv_usec != 0*/) {
+			/* Why we need diff++? */
+			/* diff >= 0 is ok: | diff < 0, can't just use tv.tv_usec: */
+			/*   45.520820      |   43.520820 */
+			/* - 44.000000      | - 45.000000 */
+			/* =  1.520820      | = -1.479180, not -2.520820! */
+			diff++;
+			/* Should be 1000000 - tv.tv_usec, but then we must check tv.tv_usec != 0 */
+			sys_tv.tv_usec = 999999 - sys_tv.tv_usec;
+		}
+		printf("%s  %ld.%06lu seconds\n", cp, diff, (unsigned long)sys_tv.tv_usec);
+	}
+#endif
+}
+
+static void to_sys_clock(const char **pp_rtcname, int utc)
+{
+	struct timeval tv;
+	struct timezone tz;
+
+	tz.tz_minuteswest = timezone/60 - 60*daylight;
+	tz.tz_dsttime = 0;
+
+	tv.tv_sec = read_rtc(pp_rtcname, NULL, utc);
+	tv.tv_usec = 0;
+	if (settimeofday(&tv, &tz))
+		bb_perror_msg_and_die("settimeofday");
+}
+
+static void from_sys_clock(const char **pp_rtcname, int utc)
+{
+#if 1
+	struct timeval tv;
+	struct tm tm_time;
+	int rtc;
+
+	rtc = rtc_xopen(pp_rtcname, O_WRONLY);
+	gettimeofday(&tv, NULL);
+	/* Prepare tm_time */
+	if (sizeof(time_t) == sizeof(tv.tv_sec)) {
+		if (utc)
+			gmtime_r((time_t*)&tv.tv_sec, &tm_time);
+		else
+			localtime_r((time_t*)&tv.tv_sec, &tm_time);
+	} else {
+		time_t t = tv.tv_sec;
+		if (utc)
+			gmtime_r(&t, &tm_time);
+		else
+			localtime_r(&t, &tm_time);
+	}
+#else
+/* Bloated code which tries to set hw clock with better precision.
+ * On x86, even though code does set hw clock within <1ms of exact
+ * whole seconds, apparently hw clock (at least on some machines)
+ * doesn't reset internal fractional seconds to 0,
+ * making all this a pointless excercise.
+ */
+	/* If we see that we are N usec away from whole second,
+	 * we'll sleep for N-ADJ usecs. ADJ corrects for the fact
+	 * that CPU is not infinitely fast.
+	 * On infinitely fast CPU, next wakeup would be
+	 * on (exactly_next_whole_second - ADJ). On real CPUs,
+	 * this difference between current time and whole second
+	 * is less than ADJ (assuming system isn't heavily loaded).
+	 */
+	/* Small value of 256us gives very precise sync for 2+ GHz CPUs.
+	 * Slower CPUs will fail to sync and will go to bigger
+	 * ADJ values. qemu-emulated armv4tl with ~100 MHz
+	 * performance ends up using ADJ ~= 4*1024 and it takes
+	 * 2+ secs (2 tries with successively larger ADJ)
+	 * to sync. Even straced one on the same qemu (very slow)
+	 * takes only 4 tries.
+	 */
+#define TWEAK_USEC 256
+	unsigned adj = TWEAK_USEC;
+	struct tm tm_time;
+	struct timeval tv;
+	int rtc = rtc_xopen(pp_rtcname, O_WRONLY);
+
+	/* Try to catch the moment when whole second is close */
+	while (1) {
+		unsigned rem_usec;
+		time_t t;
+
+		gettimeofday(&tv, NULL);
+
+		t = tv.tv_sec;
+		rem_usec = 1000000 - tv.tv_usec;
+		if (rem_usec < adj) {
+			/* Close enough */
+ small_rem:
+			t++;
+		}
+
+		/* Prepare tm_time from t */
+		if (utc)
+			gmtime_r(&t, &tm_time); /* may read /etc/xxx (it takes time) */
+		else
+			localtime_r(&t, &tm_time); /* same */
+
+		if (adj >= 32*1024) {
+			break; /* 32 ms diff and still no luck?? give up trying to sync */
+		}
+
+		/* gmtime/localtime took some time, re-get cur time */
+		gettimeofday(&tv, NULL);
+
+		if (tv.tv_sec < t /* we are still in old second */
+		 || (tv.tv_sec == t && tv.tv_usec < adj) /* not too far into next second */
+		) {
+			break; /* good, we are in sync! */
+		}
+
+		rem_usec = 1000000 - tv.tv_usec;
+		if (rem_usec < adj) {
+			t = tv.tv_sec;
+			goto small_rem; /* already close to next sec, don't sleep */
+		}
+
+		/* Try to sync up by sleeping */
+		usleep(rem_usec - adj);
+
+		/* Jump to 1ms diff, then increase fast (x2): EVERY loop
+		 * takes ~1 sec, people won't like slowly converging code here!
+		 */
+	//bb_error_msg("adj:%d tv.tv_usec:%d", adj, (int)tv.tv_usec);
+		if (adj < 512)
+			adj = 512;
+		/* ... and if last "overshoot" does not look insanely big,
+		 * just use it as adj increment. This makes convergence faster.
+		 */
+		if (tv.tv_usec < adj * 8) {
+			adj += tv.tv_usec;
+			continue;
+		}
+		adj *= 2;
+	}
+	/* Debug aid to find "optimal" TWEAK_USEC with nearly exact sync.
+	 * Look for a value which makes tv_usec close to 999999 or 0.
+	 * For 2.20GHz Intel Core 2: optimal TWEAK_USEC ~= 200
+	 */
+	//bb_error_msg("tv.tv_usec:%d", (int)tv.tv_usec);
+#endif
+
+	tm_time.tm_isdst = 0;
+	xioctl(rtc, RTC_SET_TIME, &tm_time);
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(rtc);
+}
+
+/*
+ * At system boot, kernel may set system time from RTC,
+ * but it knows nothing about timezones. If RTC is in local time,
+ * then system time is wrong - it is offset by timezone.
+ * This option corrects system time if RTC is in local time,
+ * and (always) sets in-kernel timezone.
+ *
+ * This is an alternate option to --hctosys that does not read the
+ * hardware clock.
+ */
+static void set_system_clock_timezone(int utc)
+{
+	struct timeval tv;
+	struct tm *broken;
+	struct timezone tz;
+
+	gettimeofday(&tv, NULL);
+	broken = localtime(&tv.tv_sec);
+	tz.tz_minuteswest = timezone / 60;
+	if (broken->tm_isdst)
+		tz.tz_minuteswest -= 60;
+	tz.tz_dsttime = 0;
+	gettimeofday(&tv, NULL);
+	if (!utc)
+		tv.tv_sec += tz.tz_minuteswest * 60;
+	if (settimeofday(&tv, &tz))
+		bb_perror_msg_and_die("settimeofday");
+}
+
+//usage:#define hwclock_trivial_usage
+//usage:	IF_FEATURE_HWCLOCK_LONG_OPTIONS(
+//usage:       "[-r|--show] [-s|--hctosys] [-w|--systohc] [-t|--systz]"
+//usage:       " [-l|--localtime] [-u|--utc]"
+//usage:       " [-f|--rtc FILE]"
+//usage:	)
+//usage:	IF_NOT_FEATURE_HWCLOCK_LONG_OPTIONS(
+//usage:       "[-r] [-s] [-w] [-t] [-l] [-u] [-f FILE]"
+//usage:	)
+//usage:#define hwclock_full_usage "\n\n"
+//usage:       "Query and set hardware clock (RTC)\n"
+//usage:     "\n	-r	Show hardware clock time"
+//usage:     "\n	-s	Set system time from hardware clock"
+//usage:     "\n	-w	Set hardware clock from system time"
+//usage:     "\n	-t	Set in-kernel timezone, correct system time"
+//usage:     "\n		if hardware clock is in local time"
+//usage:     "\n	-u	Assume hardware clock is kept in UTC"
+//usage:     "\n	-l	Assume hardware clock is kept in local time"
+//usage:     "\n	-f FILE	Use specified device (e.g. /dev/rtc2)"
+
+#define HWCLOCK_OPT_LOCALTIME   0x01
+#define HWCLOCK_OPT_UTC         0x02
+#define HWCLOCK_OPT_SHOW        0x04
+#define HWCLOCK_OPT_HCTOSYS     0x08
+#define HWCLOCK_OPT_SYSTOHC     0x10
+#define HWCLOCK_OPT_SYSTZ       0x20
+#define HWCLOCK_OPT_RTCFILE     0x40
+
+int hwclock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int hwclock_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *rtcname = NULL;
+	unsigned opt;
+	int utc;
+
+#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
+	static const char hwclock_longopts[] ALIGN1 =
+		"localtime\0" No_argument "l" /* short opt is non-standard */
+		"utc\0"       No_argument "u"
+		"show\0"      No_argument "r"
+		"hctosys\0"   No_argument "s"
+		"systohc\0"   No_argument "w"
+		"systz\0"     No_argument "t" /* short opt is non-standard */
+		"rtc\0"       Required_argument "f"
+		;
+	applet_long_options = hwclock_longopts;
+#endif
+	opt_complementary = "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l";
+	opt = getopt32(argv, "lurswtf:", &rtcname);
+
+	/* If -u or -l wasn't given check if we are using utc */
+	if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
+		utc = (opt & HWCLOCK_OPT_UTC);
+	else
+		utc = rtc_adjtime_is_utc();
+
+	if (opt & HWCLOCK_OPT_HCTOSYS)
+		to_sys_clock(&rtcname, utc);
+	else if (opt & HWCLOCK_OPT_SYSTOHC)
+		from_sys_clock(&rtcname, utc);
+	else if (opt & HWCLOCK_OPT_SYSTZ)
+		set_system_clock_timezone(utc);
+	else
+		/* default HWCLOCK_OPT_SHOW */
+		show_clock(&rtcname, utc);
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/ipcrm.c b/ap/app/busybox/src/util-linux/ipcrm.c
new file mode 100644
index 0000000..888f70e
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/ipcrm.c
@@ -0,0 +1,227 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ipcrm.c - utility to allow removal of IPC objects and data structures.
+ *
+ * 01 Sept 2004 - Rodney Radford <rradford@mindspring.com>
+ * Adapted for busybox from util-linux-2.12a.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define ipcrm_trivial_usage
+//usage:       "[-MQS key] [-mqs id]"
+//usage:#define ipcrm_full_usage "\n\n"
+//usage:       "Upper-case options MQS remove an object by shmkey value.\n"
+//usage:       "Lower-case options remove an object by shmid value.\n"
+//usage:     "\n	-mM	Remove memory segment after last detach"
+//usage:     "\n	-qQ	Remove message queue"
+//usage:     "\n	-sS	Remove semaphore"
+
+#include "libbb.h"
+
+/* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */
+/* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/msg.h>
+#include <sys/sem.h>
+
+#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
+/* union semun is defined by including <sys/sem.h> */
+#else
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+	int val;
+	struct semid_ds *buf;
+	unsigned short *array;
+	struct seminfo *__buf;
+};
+#endif
+
+#define IPCRM_LEGACY 1
+
+
+#if IPCRM_LEGACY
+
+typedef enum type_id {
+	SHM,
+	SEM,
+	MSG
+} type_id;
+
+static int remove_ids(type_id type, char **argv)
+{
+	unsigned long id;
+	int nb_errors = 0;
+	union semun arg;
+
+	arg.val = 0;
+
+	while (argv[0]) {
+		id = bb_strtoul(argv[0], NULL, 10);
+		if (errno || id > INT_MAX) {
+			bb_error_msg("invalid id: %s", argv[0]);
+			nb_errors++;
+		} else {
+			int ret = 0;
+			if (type == SEM)
+				ret = semctl(id, 0, IPC_RMID, arg);
+			else if (type == MSG)
+				ret = msgctl(id, IPC_RMID, NULL);
+			else if (type ==  SHM)
+				ret = shmctl(id, IPC_RMID, NULL);
+
+			if (ret) {
+				bb_perror_msg("can't remove id %s", argv[0]);
+				nb_errors++;
+			}
+		}
+		argv++;
+	}
+
+	return nb_errors;
+}
+#endif /* IPCRM_LEGACY */
+
+
+int ipcrm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ipcrm_main(int argc, char **argv)
+{
+	int c;
+	int error = 0;
+
+	/* if the command is executed without parameters, do nothing */
+	if (argc == 1)
+		return 0;
+#if IPCRM_LEGACY
+	/* check to see if the command is being invoked in the old way if so
+	   then run the old code. Valid commands are msg, shm, sem. */
+	{
+		type_id what = 0; /* silence gcc */
+		char w;
+
+		w = argv[1][0];
+		if ( ((w == 'm' && argv[1][1] == 's' && argv[1][2] == 'g')
+		       || (argv[1][0] == 's'
+		           && ((w = argv[1][1]) == 'h' || w == 'e')
+		           && argv[1][2] == 'm')
+		     ) && argv[1][3] == '\0'
+		) {
+			if (argc < 3)
+				bb_show_usage();
+
+			if (w == 'h')
+				what = SHM;
+			else if (w == 'm')
+				what = MSG;
+			else if (w == 'e')
+				what = SEM;
+
+			if (remove_ids(what, &argv[2]))
+				fflush_stdout_and_exit(EXIT_FAILURE);
+			printf("resource(s) deleted\n");
+			return 0;
+		}
+	}
+#endif /* IPCRM_LEGACY */
+
+	/* process new syntax to conform with SYSV ipcrm */
+	while ((c = getopt(argc, argv, "q:m:s:Q:M:S:h?")) != -1) {
+		int result;
+		int id = 0;
+		int iskey = isupper(c);
+
+		/* needed to delete semaphores */
+		union semun arg;
+
+		arg.val = 0;
+
+		if ((c == '?') || (c == 'h')) {
+			bb_show_usage();
+		}
+
+		/* we don't need case information any more */
+		c = tolower(c);
+
+		/* make sure the option is in range: allowed are q, m, s */
+		if (c != 'q' && c != 'm' && c != 's') {
+			bb_show_usage();
+		}
+
+		if (iskey) {
+			/* keys are in hex or decimal */
+			key_t key = xstrtoul(optarg, 0);
+
+			if (key == IPC_PRIVATE) {
+				error++;
+				bb_error_msg("illegal key (%s)", optarg);
+				continue;
+			}
+
+			/* convert key to id */
+			id = ((c == 'q') ? msgget(key, 0) :
+				(c == 'm') ? shmget(key, 0, 0) : semget(key, 0, 0));
+
+			if (id < 0) {
+				const char *errmsg;
+
+				error++;
+				switch (errno) {
+				case EACCES:
+					errmsg = "permission denied for";
+					break;
+				case EIDRM:
+					errmsg = "already removed";
+					break;
+				case ENOENT:
+					errmsg = "invalid";
+					break;
+				default:
+					errmsg = "unknown error in";
+					break;
+				}
+				bb_error_msg("%s %s (%s)", errmsg, "key", optarg);
+				continue;
+			}
+		} else {
+			/* ids are in decimal */
+			id = xatoul(optarg);
+		}
+
+		result = ((c == 'q') ? msgctl(id, IPC_RMID, NULL) :
+				(c == 'm') ? shmctl(id, IPC_RMID, NULL) :
+				semctl(id, 0, IPC_RMID, arg));
+
+		if (result) {
+			const char *errmsg;
+			const char *const what = iskey ? "key" : "id";
+
+			error++;
+			switch (errno) {
+			case EACCES:
+			case EPERM:
+				errmsg = "permission denied for";
+				break;
+			case EINVAL:
+				errmsg = "invalid";
+				break;
+			case EIDRM:
+				errmsg = "already removed";
+				break;
+			default:
+				errmsg = "unknown error in";
+				break;
+			}
+			bb_error_msg("%s %s (%s)", errmsg, what, optarg);
+			continue;
+		}
+	}
+
+	/* print usage if we still have some arguments left over */
+	if (optind != argc) {
+		bb_show_usage();
+	}
+
+	/* exit value reflects the number of errors encountered */
+	return error;
+}
diff --git a/ap/app/busybox/src/util-linux/ipcs.c b/ap/app/busybox/src/util-linux/ipcs.c
new file mode 100644
index 0000000..2668caf
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/ipcs.c
@@ -0,0 +1,637 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ipcs.c -- provides information on allocated ipc resources.
+ *
+ * 01 Sept 2004 - Rodney Radford <rradford@mindspring.com>
+ * Adapted for busybox from util-linux-2.12a.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define ipcs_trivial_usage
+//usage:       "[[-smq] -i shmid] | [[-asmq] [-tcplu]]"
+//usage:#define ipcs_full_usage "\n\n"
+//usage:       "	-i	Show specific resource"
+//usage:     "\nResource specification:"
+//usage:     "\n	-m	Shared memory segments"
+//usage:     "\n	-q	Message queues"
+//usage:     "\n	-s	Semaphore arrays"
+//usage:     "\n	-a	All (default)"
+//usage:     "\nOutput format:"
+//usage:     "\n	-t	Time"
+//usage:     "\n	-c	Creator"
+//usage:     "\n	-p	Pid"
+//usage:     "\n	-l	Limits"
+//usage:     "\n	-u	Summary"
+
+/* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */
+/* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */
+/* X/OPEN tells us to use <sys/{types,ipc,shm}.h> for shmctl() */
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/msg.h>
+#include <sys/shm.h>
+
+#include "libbb.h"
+
+/*-------------------------------------------------------------------*/
+/* SHM_DEST and SHM_LOCKED are defined in kernel headers,
+   but inside #ifdef __KERNEL__ ... #endif */
+#ifndef SHM_DEST
+/* shm_mode upper byte flags */
+#define SHM_DEST        01000	/* segment will be destroyed on last detach */
+#define SHM_LOCKED      02000	/* segment will not be swapped */
+#endif
+
+/* For older kernels the same holds for the defines below */
+#ifndef MSG_STAT
+#define MSG_STAT	11
+#define MSG_INFO	12
+#endif
+
+#ifndef SHM_STAT
+#define SHM_STAT        13
+#define SHM_INFO        14
+struct shm_info {
+	int used_ids;
+	unsigned long shm_tot;		/* total allocated shm */
+	unsigned long shm_rss;		/* total resident shm */
+	unsigned long shm_swp;		/* total swapped shm */
+	unsigned long swap_attempts;
+	unsigned long swap_successes;
+};
+#endif
+
+#ifndef SEM_STAT
+#define SEM_STAT	18
+#define SEM_INFO	19
+#endif
+
+/* Some versions of libc only define IPC_INFO when __USE_GNU is defined. */
+#ifndef IPC_INFO
+#define IPC_INFO        3
+#endif
+/*-------------------------------------------------------------------*/
+
+/* The last arg of semctl is a union semun, but where is it defined?
+   X/OPEN tells us to define it ourselves, but until recently
+   Linux include files would also define it. */
+#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
+/* union semun is defined by including <sys/sem.h> */
+#else
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+	int val;
+	struct semid_ds *buf;
+	unsigned short *array;
+	struct seminfo *__buf;
+};
+#endif
+
+/* X/OPEN (Jan 1987) does not define fields key, seq in struct ipc_perm;
+   libc 4/5 does not mention struct ipc_term at all, but includes
+   <linux/ipc.h>, which defines a struct ipc_perm with such fields.
+   glibc-1.09 has no support for sysv ipc.
+   glibc 2 uses __key, __seq */
+#if defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ > 1
+#define KEY __key
+#else
+#define KEY key
+#endif
+
+#define LIMITS 1
+#define STATUS 2
+#define CREATOR 3
+#define TIME 4
+#define PID 5
+
+static char format;
+
+static void print_perms(int id, struct ipc_perm *ipcp)
+{
+	struct passwd *pw;
+	struct group *gr;
+
+	printf("%-10d %-10o", id, ipcp->mode & 0777);
+
+	pw = getpwuid(ipcp->cuid);
+	if (pw)	printf(" %-10s", pw->pw_name);
+	else	printf(" %-10d", ipcp->cuid);
+	gr = getgrgid(ipcp->cgid);
+	if (gr)	printf(" %-10s", gr->gr_name);
+	else	printf(" %-10d", ipcp->cgid);
+
+	pw = getpwuid(ipcp->uid);
+	if (pw)	printf(" %-10s", pw->pw_name);
+	else	printf(" %-10d", ipcp->uid);
+	gr = getgrgid(ipcp->gid);
+	if (gr)	printf(" %-10s\n", gr->gr_name);
+	else	printf(" %-10d\n", ipcp->gid);
+}
+
+
+static NOINLINE void do_shm(void)
+{
+	int maxid, shmid, id;
+	struct shmid_ds shmseg;
+	struct shm_info shm_info;
+	struct shminfo shminfo;
+	struct ipc_perm *ipcp = &shmseg.shm_perm;
+	struct passwd *pw;
+
+	maxid = shmctl(0, SHM_INFO, (struct shmid_ds *) (void *) &shm_info);
+	if (maxid < 0) {
+		printf("kernel not configured for %s\n", "shared memory");
+		return;
+	}
+
+	switch (format) {
+	case LIMITS:
+		printf("------ Shared Memory %s --------\n", "Limits");
+		if ((shmctl(0, IPC_INFO, (struct shmid_ds *) (void *) &shminfo)) < 0)
+			return;
+		/* glibc 2.1.3 and all earlier libc's have ints as fields
+		 * of struct shminfo; glibc 2.1.91 has unsigned long; ach */
+		printf("max number of segments = %lu\n"
+				"max seg size (kbytes) = %lu\n"
+				"max total shared memory (pages) = %lu\n"
+				"min seg size (bytes) = %lu\n",
+				(unsigned long) shminfo.shmmni,
+				(unsigned long) (shminfo.shmmax >> 10),
+				(unsigned long) shminfo.shmall,
+				(unsigned long) shminfo.shmmin);
+		return;
+
+	case STATUS:
+		printf("------ Shared Memory %s --------\n", "Status");
+		printf("segments allocated %d\n"
+				"pages allocated %ld\n"
+				"pages resident  %ld\n"
+				"pages swapped   %ld\n"
+				"Swap performance: %ld attempts\t%ld successes\n",
+				shm_info.used_ids,
+				shm_info.shm_tot,
+				shm_info.shm_rss,
+				shm_info.shm_swp,
+				shm_info.swap_attempts, shm_info.swap_successes);
+		return;
+
+	case CREATOR:
+		printf("------ Shared Memory %s --------\n", "Segment Creators/Owners");
+		printf("%-10s %-10s %-10s %-10s %-10s %-10s\n",
+				"shmid", "perms", "cuid", "cgid", "uid", "gid");
+		break;
+
+	case TIME:
+		printf("------ Shared Memory %s --------\n", "Attach/Detach/Change Times");
+		printf("%-10s %-10s %-20s %-20s %-20s\n",
+				"shmid", "owner", "attached", "detached", "changed");
+		break;
+
+	case PID:
+		printf("------ Shared Memory %s --------\n", "Creator/Last-op");
+		printf("%-10s %-10s %-10s %-10s\n",
+				"shmid", "owner", "cpid", "lpid");
+		break;
+
+	default:
+		printf("------ Shared Memory %s --------\n", "Segments");
+		printf("%-10s %-10s %-10s %-10s %-10s %-10s %-12s\n",
+				"key", "shmid", "owner", "perms", "bytes", "nattch",
+				"status");
+		break;
+	}
+
+	for (id = 0; id <= maxid; id++) {
+		shmid = shmctl(id, SHM_STAT, &shmseg);
+		if (shmid < 0)
+			continue;
+		if (format == CREATOR) {
+			print_perms(shmid, ipcp);
+			continue;
+		}
+		pw = getpwuid(ipcp->uid);
+		switch (format) {
+		case TIME:
+			if (pw)
+				printf("%-10d %-10.10s", shmid, pw->pw_name);
+			else
+				printf("%-10d %-10d", shmid, ipcp->uid);
+			/* ctime uses static buffer: use separate calls */
+			printf(" %-20.16s", shmseg.shm_atime
+					? ctime(&shmseg.shm_atime) + 4 : "Not set");
+			printf(" %-20.16s", shmseg.shm_dtime
+					? ctime(&shmseg.shm_dtime) + 4 : "Not set");
+			printf(" %-20.16s\n", shmseg.shm_ctime
+					? ctime(&shmseg.shm_ctime) + 4 : "Not set");
+			break;
+		case PID:
+			if (pw)
+				printf("%-10d %-10.10s", shmid, pw->pw_name);
+			else
+				printf("%-10d %-10d", shmid, ipcp->uid);
+			printf(" %-10d %-10d\n", shmseg.shm_cpid, shmseg.shm_lpid);
+			break;
+
+		default:
+			printf("0x%08x ", ipcp->KEY);
+			if (pw)
+				printf("%-10d %-10.10s", shmid, pw->pw_name);
+			else
+				printf("%-10d %-10d", shmid, ipcp->uid);
+			printf(" %-10o %-10lu %-10ld %-6s %-6s\n", ipcp->mode & 0777,
+					/*
+					 * earlier: int, Austin has size_t
+					 */
+					(unsigned long) shmseg.shm_segsz,
+					/*
+					 * glibc-2.1.3 and earlier has unsigned short;
+					 * Austin has shmatt_t
+					 */
+					(long) shmseg.shm_nattch,
+					ipcp->mode & SHM_DEST ? "dest" : " ",
+					ipcp->mode & SHM_LOCKED ? "locked" : " ");
+			break;
+		}
+	}
+}
+
+
+static NOINLINE void do_sem(void)
+{
+	int maxid, semid, id;
+	struct semid_ds semary;
+	struct seminfo seminfo;
+	struct ipc_perm *ipcp = &semary.sem_perm;
+	struct passwd *pw;
+	union semun arg;
+
+	arg.array = (unsigned short *) (void *) &seminfo;
+	maxid = semctl(0, 0, SEM_INFO, arg);
+	if (maxid < 0) {
+		printf("kernel not configured for %s\n", "semaphores");
+		return;
+	}
+
+	switch (format) {
+	case LIMITS:
+		printf("------ Semaphore %s --------\n", "Limits");
+		arg.array = (unsigned short *) (void *) &seminfo;	/* damn union */
+		if ((semctl(0, 0, IPC_INFO, arg)) < 0)
+			return;
+		printf("max number of arrays = %d\n"
+				"max semaphores per array = %d\n"
+				"max semaphores system wide = %d\n"
+				"max ops per semop call = %d\n"
+				"semaphore max value = %d\n",
+				seminfo.semmni,
+				seminfo.semmsl,
+				seminfo.semmns, seminfo.semopm, seminfo.semvmx);
+		return;
+
+	case STATUS:
+		printf("------ Semaphore %s --------\n", "Status");
+		printf("used arrays = %d\n"
+				"allocated semaphores = %d\n",
+				seminfo.semusz, seminfo.semaem);
+		return;
+
+	case CREATOR:
+		printf("------ Semaphore %s --------\n", "Arrays Creators/Owners");
+		printf("%-10s %-10s %-10s %-10s %-10s %-10s\n",
+				"semid", "perms", "cuid", "cgid", "uid", "gid");
+		break;
+
+	case TIME:
+		printf("------ Shared Memory %s --------\n", "Operation/Change Times");
+		printf("%-8s %-10s %-26.24s %-26.24s\n",
+				"shmid", "owner", "last-op", "last-changed");
+		break;
+
+	case PID:
+		break;
+
+	default:
+		printf("------ Semaphore %s --------\n", "Arrays");
+		printf("%-10s %-10s %-10s %-10s %-10s\n",
+				"key", "semid", "owner", "perms", "nsems");
+		break;
+	}
+
+	for (id = 0; id <= maxid; id++) {
+		arg.buf = (struct semid_ds *) &semary;
+		semid = semctl(id, 0, SEM_STAT, arg);
+		if (semid < 0)
+			continue;
+		if (format == CREATOR) {
+			print_perms(semid, ipcp);
+			continue;
+		}
+		pw = getpwuid(ipcp->uid);
+		switch (format) {
+		case TIME:
+			if (pw)
+				printf("%-8d %-10.10s", semid, pw->pw_name);
+			else
+				printf("%-8d %-10d", semid, ipcp->uid);
+			/* ctime uses static buffer: use separate calls */
+			printf("  %-26.24s", semary.sem_otime
+					? ctime(&semary.sem_otime) : "Not set");
+			printf(" %-26.24s\n", semary.sem_ctime
+					? ctime(&semary.sem_ctime) : "Not set");
+			break;
+		case PID:
+			break;
+
+		default:
+			printf("0x%08x ", ipcp->KEY);
+			if (pw)
+				printf("%-10d %-10.9s", semid, pw->pw_name);
+			else
+				printf("%-10d %-9d", semid, ipcp->uid);
+			printf(" %-10o %-10ld\n", ipcp->mode & 0777,
+					/*
+					 * glibc-2.1.3 and earlier has unsigned short;
+					 * glibc-2.1.91 has variation between
+					 * unsigned short and unsigned long
+					 * Austin prescribes unsigned short.
+					 */
+					(long) semary.sem_nsems);
+			break;
+		}
+	}
+}
+
+
+static NOINLINE void do_msg(void)
+{
+	int maxid, msqid, id;
+	struct msqid_ds msgque;
+	struct msginfo msginfo;
+	struct ipc_perm *ipcp = &msgque.msg_perm;
+	struct passwd *pw;
+
+	maxid = msgctl(0, MSG_INFO, (struct msqid_ds *) (void *) &msginfo);
+	if (maxid < 0) {
+		printf("kernel not configured for %s\n", "message queues");
+		return;
+	}
+
+	switch (format) {
+	case LIMITS:
+		if ((msgctl(0, IPC_INFO, (struct msqid_ds *) (void *) &msginfo)) < 0)
+			return;
+		printf("------ Message%s --------\n", "s: Limits");
+		printf("max queues system wide = %d\n"
+				"max size of message (bytes) = %d\n"
+				"default max size of queue (bytes) = %d\n",
+				msginfo.msgmni, msginfo.msgmax, msginfo.msgmnb);
+		return;
+
+	case STATUS:
+		printf("------ Message%s --------\n", "s: Status");
+		printf("allocated queues = %d\n"
+				"used headers = %d\n"
+				"used space = %d bytes\n",
+				msginfo.msgpool, msginfo.msgmap, msginfo.msgtql);
+		return;
+
+	case CREATOR:
+		printf("------ Message%s --------\n", " Queues: Creators/Owners");
+		printf("%-10s %-10s %-10s %-10s %-10s %-10s\n",
+				"msqid", "perms", "cuid", "cgid", "uid", "gid");
+		break;
+
+	case TIME:
+		printf("------ Message%s --------\n", " Queues Send/Recv/Change Times");
+		printf("%-8s %-10s %-20s %-20s %-20s\n",
+				"msqid", "owner", "send", "recv", "change");
+		break;
+
+	case PID:
+		printf("------ Message%s --------\n", " Queues PIDs");
+		printf("%-10s %-10s %-10s %-10s\n",
+				"msqid", "owner", "lspid", "lrpid");
+		break;
+
+	default:
+		printf("------ Message%s --------\n", " Queues");
+		printf("%-10s %-10s %-10s %-10s %-12s %-12s\n",
+				"key", "msqid", "owner", "perms", "used-bytes", "messages");
+		break;
+	}
+
+	for (id = 0; id <= maxid; id++) {
+		msqid = msgctl(id, MSG_STAT, &msgque);
+		if (msqid < 0)
+			continue;
+		if (format == CREATOR) {
+			print_perms(msqid, ipcp);
+			continue;
+		}
+		pw = getpwuid(ipcp->uid);
+		switch (format) {
+		case TIME:
+			if (pw)
+				printf("%-8d %-10.10s", msqid, pw->pw_name);
+			else
+				printf("%-8d %-10d", msqid, ipcp->uid);
+			printf(" %-20.16s", msgque.msg_stime
+					? ctime(&msgque.msg_stime) + 4 : "Not set");
+			printf(" %-20.16s", msgque.msg_rtime
+					? ctime(&msgque.msg_rtime) + 4 : "Not set");
+			printf(" %-20.16s\n", msgque.msg_ctime
+					? ctime(&msgque.msg_ctime) + 4 : "Not set");
+			break;
+		case PID:
+			if (pw)
+				printf("%-8d %-10.10s", msqid, pw->pw_name);
+			else
+				printf("%-8d %-10d", msqid, ipcp->uid);
+			printf("  %5d     %5d\n", msgque.msg_lspid, msgque.msg_lrpid);
+			break;
+
+		default:
+			printf("0x%08x ", ipcp->KEY);
+			if (pw)
+				printf("%-10d %-10.10s", msqid, pw->pw_name);
+			else
+				printf("%-10d %-10d", msqid, ipcp->uid);
+			printf(" %-10o %-12ld %-12ld\n", ipcp->mode & 0777,
+					/*
+					 * glibc-2.1.3 and earlier has unsigned short;
+					 * glibc-2.1.91 has variation between
+					 * unsigned short, unsigned long
+					 * Austin has msgqnum_t
+					 */
+					(long) msgque.msg_cbytes, (long) msgque.msg_qnum);
+			break;
+		}
+	}
+}
+
+
+static void print_shm(int shmid)
+{
+	struct shmid_ds shmds;
+	struct ipc_perm *ipcp = &shmds.shm_perm;
+
+	if (shmctl(shmid, IPC_STAT, &shmds) == -1) {
+		bb_perror_msg("shmctl");
+		return;
+	}
+
+	printf("\nShared memory Segment shmid=%d\n"
+			"uid=%d\tgid=%d\tcuid=%d\tcgid=%d\n"
+			"mode=%#o\taccess_perms=%#o\n"
+			"bytes=%ld\tlpid=%d\tcpid=%d\tnattch=%ld\n",
+			shmid,
+			ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid,
+			ipcp->mode, ipcp->mode & 0777,
+			(long) shmds.shm_segsz, shmds.shm_lpid, shmds.shm_cpid,
+			(long) shmds.shm_nattch);
+	printf("att_time=%-26.24s\n",
+			shmds.shm_atime ? ctime(&shmds.shm_atime) : "Not set");
+	printf("det_time=%-26.24s\n",
+			shmds.shm_dtime ? ctime(&shmds.shm_dtime) : "Not set");
+	printf("change_time=%-26.24s\n\n", ctime(&shmds.shm_ctime));
+}
+
+
+static void print_msg(int msqid)
+{
+	struct msqid_ds buf;
+	struct ipc_perm *ipcp = &buf.msg_perm;
+
+	if (msgctl(msqid, IPC_STAT, &buf) == -1) {
+		bb_perror_msg("msgctl");
+		return;
+	}
+
+	printf("\nMessage Queue msqid=%d\n"
+			"uid=%d\tgid=%d\tcuid=%d\tcgid=%d\tmode=%#o\n"
+			"cbytes=%ld\tqbytes=%ld\tqnum=%ld\tlspid=%d\tlrpid=%d\n",
+			msqid, ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, ipcp->mode,
+			/*
+			 * glibc-2.1.3 and earlier has unsigned short;
+			 * glibc-2.1.91 has variation between
+			 * unsigned short, unsigned long
+			 * Austin has msgqnum_t (for msg_qbytes)
+			 */
+			(long) buf.msg_cbytes, (long) buf.msg_qbytes,
+			(long) buf.msg_qnum, buf.msg_lspid, buf.msg_lrpid);
+
+	printf("send_time=%-26.24s\n",
+			buf.msg_stime ? ctime(&buf.msg_stime) : "Not set");
+	printf("rcv_time=%-26.24s\n",
+			buf.msg_rtime ? ctime(&buf.msg_rtime) : "Not set");
+	printf("change_time=%-26.24s\n\n",
+			buf.msg_ctime ? ctime(&buf.msg_ctime) : "Not set");
+}
+
+static void print_sem(int semid)
+{
+	struct semid_ds semds;
+	struct ipc_perm *ipcp = &semds.sem_perm;
+	union semun arg;
+	unsigned int i;
+
+	arg.buf = &semds;
+	if (semctl(semid, 0, IPC_STAT, arg)) {
+		bb_perror_msg("semctl");
+		return;
+	}
+
+	printf("\nSemaphore Array semid=%d\n"
+			"uid=%d\t gid=%d\t cuid=%d\t cgid=%d\n"
+			"mode=%#o, access_perms=%#o\n"
+			"nsems = %ld\n"
+			"otime = %-26.24s\n",
+			semid,
+			ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid,
+			ipcp->mode, ipcp->mode & 0777,
+			(long) semds.sem_nsems,
+			semds.sem_otime ? ctime(&semds.sem_otime) : "Not set");
+	printf("ctime = %-26.24s\n"
+			"%-10s %-10s %-10s %-10s %-10s\n",
+			ctime(&semds.sem_ctime),
+			"semnum", "value", "ncount", "zcount", "pid");
+
+	arg.val = 0;
+	for (i = 0; i < semds.sem_nsems; i++) {
+		int val, ncnt, zcnt, pid;
+
+		val = semctl(semid, i, GETVAL, arg);
+		ncnt = semctl(semid, i, GETNCNT, arg);
+		zcnt = semctl(semid, i, GETZCNT, arg);
+		pid = semctl(semid, i, GETPID, arg);
+		if (val < 0 || ncnt < 0 || zcnt < 0 || pid < 0) {
+			bb_perror_msg_and_die("semctl");
+		}
+		printf("%-10d %-10d %-10d %-10d %-10d\n", i, val, ncnt, zcnt, pid);
+	}
+	bb_putchar('\n');
+}
+
+int ipcs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ipcs_main(int argc UNUSED_PARAM, char **argv)
+{
+	int id = 0;
+	unsigned flags = 0;
+	unsigned opt;
+	char *opt_i;
+#define flag_print	(1<<0)
+#define flag_msg	(1<<1)
+#define flag_sem	(1<<2)
+#define flag_shm	(1<<3)
+
+	opt = getopt32(argv, "i:aqsmtcplu", &opt_i);
+	if (opt & 0x1) { // -i
+		id = xatoi(opt_i);
+		flags |= flag_print;
+	}
+	if (opt & 0x2) flags |= flag_msg | flag_sem | flag_shm; // -a
+	if (opt & 0x4) flags |= flag_msg; // -q
+	if (opt & 0x8) flags |= flag_sem; // -s
+	if (opt & 0x10) flags |= flag_shm; // -m
+	if (opt & 0x20) format = TIME; // -t
+	if (opt & 0x40) format = CREATOR; // -c
+	if (opt & 0x80) format = PID; // -p
+	if (opt & 0x100) format = LIMITS; // -l
+	if (opt & 0x200) format = STATUS; // -u
+
+	if (flags & flag_print) {
+		if (flags & flag_shm) {
+			print_shm(id);
+			fflush_stdout_and_exit(EXIT_SUCCESS);
+		}
+		if (flags & flag_sem) {
+			print_sem(id);
+			fflush_stdout_and_exit(EXIT_SUCCESS);
+		}
+		if (flags & flag_msg) {
+			print_msg(id);
+			fflush_stdout_and_exit(EXIT_SUCCESS);
+		}
+		bb_show_usage();
+	}
+
+	if (!(flags & (flag_shm | flag_msg | flag_sem)))
+		flags |= flag_msg | flag_shm | flag_sem;
+	bb_putchar('\n');
+
+	if (flags & flag_shm) {
+		do_shm();
+		bb_putchar('\n');
+	}
+	if (flags & flag_sem) {
+		do_sem();
+		bb_putchar('\n');
+	}
+	if (flags & flag_msg) {
+		do_msg();
+		bb_putchar('\n');
+	}
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/ap/app/busybox/src/util-linux/losetup.c b/ap/app/busybox/src/util-linux/losetup.c
new file mode 100644
index 0000000..21108d0
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/losetup.c
@@ -0,0 +1,107 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini losetup implementation for busybox
+ *
+ * Copyright (C) 2002  Matt Kraai.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define losetup_trivial_usage
+//usage:       "[-r] [-o OFS] LOOPDEV FILE - associate loop devices\n"
+//usage:       "	losetup -d LOOPDEV - disassociate\n"
+//usage:       "	losetup [-f] - show"
+//usage:#define losetup_full_usage "\n\n"
+//usage:       "	-o OFS	Start OFS bytes into FILE"
+//usage:     "\n	-r	Read-only"
+//usage:     "\n	-f	Show first free loop device"
+//usage:
+//usage:#define losetup_notes_usage
+//usage:       "No arguments will display all current associations.\n"
+//usage:       "One argument (losetup /dev/loop1) will display the current association\n"
+//usage:       "(if any), or disassociate it (with -d). The display shows the offset\n"
+//usage:       "and filename of the file the loop device is currently bound to.\n\n"
+//usage:       "Two arguments (losetup /dev/loop1 file.img) create a new association,\n"
+//usage:       "with an optional offset (-o 12345). Encryption is not yet supported.\n"
+//usage:       "losetup -f will show the first loop free loop device\n\n"
+
+#include "libbb.h"
+
+int losetup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int losetup_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opt;
+	int n;
+	char *opt_o;
+	unsigned long long offset = 0;
+	enum {
+		OPT_d = (1 << 0),
+		OPT_o = (1 << 1),
+		OPT_f = (1 << 2),
+		OPT_r = (1 << 3), /* must be last */
+	};
+
+	/* max 2 args, -d,-o,-f opts are mutually exclusive */
+	opt_complementary = "?2:d--of:o--df:f--do";
+	opt = getopt32(argv, "do:fr", &opt_o);
+	argv += optind;
+
+	if (opt == OPT_o)
+		offset = xatoull(opt_o);
+
+	if (opt == OPT_d) {
+		/* -d BLOCKDEV */
+		if (!argv[0] || argv[1])
+			bb_show_usage();
+		if (del_loop(argv[0]))
+			bb_simple_perror_msg_and_die(argv[0]);
+		return EXIT_SUCCESS;
+	}
+
+	if (argv[0]) {
+		char *s;
+
+		if (opt == OPT_f) /* -f should not have arguments */
+			bb_show_usage();
+
+		if (argv[1]) {
+			/* [-r] [-o OFS] BLOCKDEV FILE */
+			if (set_loop(&argv[0], argv[1], offset, (opt / OPT_r)) < 0)
+				bb_simple_perror_msg_and_die(argv[0]);
+			return EXIT_SUCCESS;
+		}
+		/* [-r] [-o OFS] BLOCKDEV */
+		s = query_loop(argv[0]);
+		if (!s)
+			bb_simple_perror_msg_and_die(argv[0]);
+		printf("%s: %s\n", argv[0], s);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(s);
+		return EXIT_SUCCESS;
+	}
+
+	/* [-r] [-o OFS|-f] with no params */
+	n = 0;
+	while (1) {
+		char *s;
+		char dev[LOOP_NAMESIZE];
+
+		sprintf(dev, LOOP_FORMAT, n);
+		s = query_loop(dev);
+		n++;
+		if (!s) {
+			if (n > 9 && errno && errno != ENXIO)
+				return EXIT_SUCCESS;
+			if (opt == OPT_f) {
+				puts(dev);
+				return EXIT_SUCCESS;
+			}
+		} else {
+			if (opt != OPT_f)
+				printf("%s: %s\n", dev, s);
+			if (ENABLE_FEATURE_CLEAN_UP)
+				free(s);
+		}
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/lspci.c b/ap/app/busybox/src/util-linux/lspci.c
new file mode 100644
index 0000000..514678a
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/lspci.c
@@ -0,0 +1,112 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * lspci implementation for busybox
+ *
+ * Copyright (C) 2009  Malek Degachi <malek-degachi@laposte.net>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define lspci_trivial_usage
+//usage:       "[-mk]"
+//usage:#define lspci_full_usage "\n\n"
+//usage:       "List all PCI devices"
+//usage:     "\n"
+//usage:     "\n	-m	Parsable output"
+//usage:     "\n	-k	Show driver"
+
+#include "libbb.h"
+
+enum {
+	OPT_m = (1 << 0),
+	OPT_k = (1 << 1),
+};
+
+/*
+ * PCI_SLOT_NAME PCI_CLASS: PCI_VID:PCI_DID [PCI_SUBSYS_VID:PCI_SUBSYS_DID] [DRIVER]
+ */
+static int FAST_FUNC fileAction(
+		const char *fileName,
+		struct stat *statbuf UNUSED_PARAM,
+		void *userData UNUSED_PARAM,
+		int depth UNUSED_PARAM)
+{
+	parser_t *parser;
+	char *tokens[3];
+	char *pci_slot_name = NULL, *driver = NULL;
+	int pci_class = 0, pci_vid = 0, pci_did = 0;
+	int pci_subsys_vid = 0, pci_subsys_did = 0;
+
+	char *uevent_filename = concat_path_file(fileName, "/uevent");
+	parser = config_open2(uevent_filename, fopen_for_read);
+	free(uevent_filename);
+
+	while (config_read(parser, tokens, 3, 2, "\0:=", PARSE_NORMAL)) {
+		if (strcmp(tokens[0], "DRIVER") == 0) {
+			driver = xstrdup(tokens[1]);
+			continue;
+		}
+
+		if (strcmp(tokens[0], "PCI_CLASS") == 0) {
+			pci_class = xstrtou(tokens[1], 16)>>8;
+			continue;
+		}
+
+		if (strcmp(tokens[0], "PCI_ID") == 0) {
+			pci_vid = xstrtou(tokens[1], 16);
+			pci_did = xstrtou(tokens[2], 16);
+			continue;
+		}
+
+		if (strcmp(tokens[0], "PCI_SUBSYS_ID") == 0) {
+			pci_subsys_vid = xstrtou(tokens[1], 16);
+			pci_subsys_did = xstrtou(tokens[2], 16);
+			continue;
+		}
+
+		if (strcmp(tokens[0], "PCI_SLOT_NAME") == 0) {
+			pci_slot_name = xstrdup(tokens[2]);
+			continue;
+		}
+	}
+	config_close(parser);
+
+
+	if (option_mask32 & OPT_m) {
+		printf("%s \"Class %04x\" \"%04x\" \"%04x\" \"%04x\" \"%04x\"",
+			pci_slot_name, pci_class, pci_vid, pci_did,
+			pci_subsys_vid, pci_subsys_did);
+	} else {
+		printf("%s Class %04x: %04x:%04x",
+			pci_slot_name, pci_class, pci_vid, pci_did);
+	}
+
+	if ((option_mask32 & OPT_k) && driver) {
+		if (option_mask32 & OPT_m) {
+			printf(" \"%s\"", driver);
+		} else {
+			printf(" %s", driver);
+		}
+	}
+	bb_putchar('\n');
+
+	free(driver);
+	free(pci_slot_name);
+
+	return TRUE;
+}
+
+int lspci_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int lspci_main(int argc UNUSED_PARAM, char **argv)
+{
+	getopt32(argv, "m" /*non-compat:*/ "k" /*ignored:*/ "nv");
+
+	recursive_action("/sys/bus/pci/devices",
+			ACTION_RECURSE,
+			fileAction,
+			NULL, /* dirAction */
+			NULL, /* userData */
+			0 /* depth */);
+
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/lsusb.c b/ap/app/busybox/src/util-linux/lsusb.c
new file mode 100644
index 0000000..540f21e
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/lsusb.c
@@ -0,0 +1,75 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * lsusb implementation for busybox
+ *
+ * Copyright (C) 2009  Malek Degachi <malek-degachi@laposte.net>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define lsusb_trivial_usage NOUSAGE_STR
+//usage:#define lsusb_full_usage ""
+
+#include "libbb.h"
+
+static int FAST_FUNC fileAction(
+		const char *fileName,
+		struct stat *statbuf UNUSED_PARAM,
+		void *userData UNUSED_PARAM,
+		int depth UNUSED_PARAM)
+{
+	parser_t *parser;
+	char *tokens[4];
+	char *busnum = NULL, *devnum = NULL;
+	int product_vid = 0, product_did = 0;
+	char *uevent_filename = concat_path_file(fileName, "/uevent");
+
+	parser = config_open2(uevent_filename, fopen_for_read);
+	free(uevent_filename);
+
+	while (config_read(parser, tokens, 4, 2, "\\/=", PARSE_NORMAL)) {
+		if ((parser->lineno == 1) && strcmp(tokens[0], "DEVTYPE") == 0) {
+			break;
+		}
+
+		if (strcmp(tokens[0], "PRODUCT") == 0) {
+			product_vid = xstrtou(tokens[1], 16);
+			product_did = xstrtou(tokens[2], 16);
+			continue;
+		}
+
+		if (strcmp(tokens[0], "BUSNUM") == 0) {
+			busnum = xstrdup(tokens[1]);
+			continue;
+		}
+
+		if (strcmp(tokens[0], "DEVNUM") == 0) {
+			devnum = xstrdup(tokens[1]);
+			continue;
+		}
+	}
+	config_close(parser);
+
+	if (busnum) {
+		printf("Bus %s Device %s: ID %04x:%04x\n", busnum, devnum, product_vid, product_did);
+		free(busnum);
+		free(devnum);
+	}
+
+	return TRUE;
+}
+
+int lsusb_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int lsusb_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	/* no options, no getopt */
+
+	recursive_action("/sys/bus/usb/devices",
+			ACTION_RECURSE,
+			fileAction,
+			NULL, /* dirAction */
+			NULL, /* userData */
+			0 /* depth */);
+
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/mdev.c b/ap/app/busybox/src/util-linux/mdev.c
new file mode 100644
index 0000000..75de14f
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/mdev.c
@@ -0,0 +1,955 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mdev - Mini udev for busybox
+ *
+ * Copyright 2005 Rob Landley <rob@landley.net>
+ * Copyright 2005 Frank Sorenson <frank@tuxrocks.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//config:config MDEV
+//config:	bool "mdev"
+//config:	default y
+//config:	select PLATFORM_LINUX
+//config:	help
+//config:	  mdev is a mini-udev implementation for dynamically creating device
+//config:	  nodes in the /dev directory.
+//config:
+//config:	  For more information, please see docs/mdev.txt
+//config:
+//config:config FEATURE_MDEV_CONF
+//config:	bool "Support /etc/mdev.conf"
+//config:	default y
+//config:	depends on MDEV
+//config:	help
+//config:	  Add support for the mdev config file to control ownership and
+//config:	  permissions of the device nodes.
+//config:
+//config:	  For more information, please see docs/mdev.txt
+//config:
+//config:config FEATURE_MDEV_RENAME
+//config:	bool "Support subdirs/symlinks"
+//config:	default y
+//config:	depends on FEATURE_MDEV_CONF
+//config:	help
+//config:	  Add support for renaming devices and creating symlinks.
+//config:
+//config:	  For more information, please see docs/mdev.txt
+//config:
+//config:config FEATURE_MDEV_RENAME_REGEXP
+//config:	bool "Support regular expressions substitutions when renaming device"
+//config:	default y
+//config:	depends on FEATURE_MDEV_RENAME
+//config:	help
+//config:	  Add support for regular expressions substitutions when renaming
+//config:	  device.
+//config:
+//config:config FEATURE_MDEV_EXEC
+//config:	bool "Support command execution at device addition/removal"
+//config:	default y
+//config:	depends on FEATURE_MDEV_CONF
+//config:	help
+//config:	  This adds support for an optional field to /etc/mdev.conf for
+//config:	  executing commands when devices are created/removed.
+//config:
+//config:	  For more information, please see docs/mdev.txt
+//config:
+//config:config FEATURE_MDEV_LOAD_FIRMWARE
+//config:	bool "Support loading of firmwares"
+//config:	default y
+//config:	depends on MDEV
+//config:	help
+//config:	  Some devices need to load firmware before they can be usable.
+//config:
+//config:	  These devices will request userspace look up the files in
+//config:	  /lib/firmware/ and if it exists, send it to the kernel for
+//config:	  loading into the hardware.
+
+//applet:IF_MDEV(APPLET(mdev, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MDEV) += mdev.o
+
+//usage:#define mdev_trivial_usage
+//usage:       "[-s]"
+//usage:#define mdev_full_usage "\n\n"
+//usage:       "mdev -s is to be run during boot to scan /sys and populate /dev.\n"
+//usage:       "\n"
+//usage:       "Bare mdev is a kernel hotplug helper. To activate it:\n"
+//usage:       "	echo /sbin/mdev >/proc/sys/kernel/hotplug\n"
+//usage:	IF_FEATURE_MDEV_CONF(
+//usage:       "\n"
+//usage:       "It uses /etc/mdev.conf with lines\n"
+//usage:       "	[-]DEVNAME UID:GID PERM"
+//usage:			IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]")
+//usage:			IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]")
+//usage:       "\n"
+//usage:       "where DEVNAME is device name regex, @major,minor[-minor2], or\n"
+//usage:       "environment variable regex. A common use of the latter is\n"
+//usage:       "to load modules for hotplugged devices:\n"
+//usage:       "	$MODALIAS=.* 0:0 660 @modprobe \"$MODALIAS\"\n"
+//usage:	)
+//usage:       "\n"
+//usage:       "If /dev/mdev.seq file exists, mdev will wait for its value\n"
+//usage:       "to match $SEQNUM variable. This prevents plug/unplug races.\n"
+//usage:       "To activate this feature, create empty /dev/mdev.seq at boot.\n"
+//usage:       "\n"
+//usage:       "If /dev/mdev.log file exists, debug log will be appended to it."
+
+#include "libbb.h"
+#include "xregex.h"
+
+/* "mdev -s" scans /sys/class/xxx, looking for directories which have dev
+ * file (it is of the form "M:m\n"). Example: /sys/class/tty/tty0/dev
+ * contains "4:0\n". Directory name is taken as device name, path component
+ * directly after /sys/class/ as subsystem. In this example, "tty0" and "tty".
+ * Then mdev creates the /dev/device_name node.
+ * If /sys/class/.../dev file does not exist, mdev still may act
+ * on this device: see "@|$|*command args..." parameter in config file.
+ *
+ * mdev w/o parameters is called as hotplug helper. It takes device
+ * and subsystem names from $DEVPATH and $SUBSYSTEM, extracts
+ * maj,min from "/sys/$DEVPATH/dev" and also examines
+ * $ACTION ("add"/"delete") and $FIRMWARE.
+ *
+ * If action is "add", mdev creates /dev/device_name similarly to mdev -s.
+ * (todo: explain "delete" and $FIRMWARE)
+ *
+ * If /etc/mdev.conf exists, it may modify /dev/device_name's properties.
+ *
+ * Leading minus in 1st field means "don't stop on this line", otherwise
+ * search is stopped after the matching line is encountered.
+ *
+ * $envvar=regex format is useful for loading modules for hot-plugged devices
+ * which do not have driver loaded yet. In this case /sys/class/.../dev
+ * does not exist, but $MODALIAS is set to needed module's name
+ * (actually, an alias to it) by kernel. This rule instructs mdev
+ * to load the module and exit:
+ *    $MODALIAS=.* 0:0 660 @modprobe "$MODALIAS"
+ * The kernel will generate another hotplug event when /sys/class/.../dev
+ * file appears.
+ *
+ * When line matches, the device node is created, chmod'ed and chown'ed,
+ * moved to path, and if >path, a symlink to moved node is created,
+ * all this if /sys/class/.../dev exists.
+ *    Examples:
+ *    =loop/      - moves to /dev/loop
+ *    >disk/sda%1 - moves to /dev/disk/sdaN, makes /dev/sdaN a symlink
+ *
+ * Then "command args..." is executed (via sh -c 'command args...').
+ * @:execute on creation, $:on deletion, *:on both.
+ * This happens regardless of /sys/class/.../dev existence.
+ */
+
+/* Kernel's hotplug environment constantly changes.
+ * Here are new cases I observed on 3.1.0:
+ *
+ * Case with $DEVNAME and $DEVICE, not just $DEVPATH:
+ * ACTION=add
+ * BUSNUM=001
+ * DEVICE=/proc/bus/usb/001/003
+ * DEVNAME=bus/usb/001/003
+ * DEVNUM=003
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5
+ * DEVTYPE=usb_device
+ * MAJOR=189
+ * MINOR=2
+ * PRODUCT=18d1/4e12/227
+ * SUBSYSTEM=usb
+ * TYPE=0/0/0
+ *
+ * Case with $DEVICE, but no $DEVNAME - apparenty, usb iface notification?
+ * "Please load me a module" thing?
+ * ACTION=add
+ * DEVICE=/proc/bus/usb/001/003
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0
+ * DEVTYPE=usb_interface
+ * INTERFACE=8/6/80
+ * MODALIAS=usb:v18D1p4E12d0227dc00dsc00dp00ic08isc06ip50
+ * PRODUCT=18d1/4e12/227
+ * SUBSYSTEM=usb
+ * TYPE=0/0/0
+ *
+ * ACTION=add
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5
+ * DEVTYPE=scsi_host
+ * SUBSYSTEM=scsi
+ *
+ * ACTION=add
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/scsi_host/host5
+ * SUBSYSTEM=scsi_host
+ *
+ * ACTION=add
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0
+ * DEVTYPE=scsi_target
+ * SUBSYSTEM=scsi
+ *
+ * Case with strange $MODALIAS:
+ * ACTION=add
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0
+ * DEVTYPE=scsi_device
+ * MODALIAS=scsi:t-0x00
+ * SUBSYSTEM=scsi
+ *
+ * ACTION=add
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/scsi_disk/5:0:0:0
+ * SUBSYSTEM=scsi_disk
+ *
+ * ACTION=add
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/scsi_device/5:0:0:0
+ * SUBSYSTEM=scsi_device
+ *
+ * Case with explicit $MAJOR/$MINOR (no need to read /sys/$DEVPATH/dev?):
+ * ACTION=add
+ * DEVNAME=bsg/5:0:0:0
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/bsg/5:0:0:0
+ * MAJOR=253
+ * MINOR=1
+ * SUBSYSTEM=bsg
+ *
+ * ACTION=add
+ * DEVPATH=/devices/virtual/bdi/8:16
+ * SUBSYSTEM=bdi
+ *
+ * ACTION=add
+ * DEVNAME=sdb
+ * DEVPATH=/block/sdb
+ * DEVTYPE=disk
+ * MAJOR=8
+ * MINOR=16
+ * SUBSYSTEM=block
+ *
+ * Case with ACTION=change:
+ * ACTION=change
+ * DEVNAME=sdb
+ * DEVPATH=/block/sdb
+ * DEVTYPE=disk
+ * DISK_MEDIA_CHANGE=1
+ * MAJOR=8
+ * MINOR=16
+ * SUBSYSTEM=block
+ */
+
+static const char keywords[] ALIGN1 = "add\0remove\0change\0";
+enum { OP_add, OP_remove };
+
+struct rule {
+	bool keep_matching;
+	bool regex_compiled;
+	mode_t mode;
+	int maj, min0, min1;
+	struct bb_uidgid_t ugid;
+	char *envvar;
+	char *ren_mov;
+	IF_FEATURE_MDEV_EXEC(char *r_cmd;)
+	regex_t match;
+};
+
+struct globals {
+	int root_major, root_minor;
+	smallint verbose;
+	char *subsystem;
+#if ENABLE_FEATURE_MDEV_CONF
+	const char *filename;
+	parser_t *parser;
+	struct rule **rule_vec;
+	unsigned rule_idx;
+#endif
+	struct rule cur_rule;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { \
+	IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.maj = -1;) \
+	IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.mode = 0660;) \
+} while (0)
+
+
+/* Prevent infinite loops in /sys symlinks */
+#define MAX_SYSFS_DEPTH 3
+
+/* We use additional 64+ bytes in make_device() */
+#define SCRATCH_SIZE 80
+
+#if 0
+# define dbg(...) bb_error_msg(__VA_ARGS__)
+#else
+# define dbg(...) ((void)0)
+#endif
+
+
+#if ENABLE_FEATURE_MDEV_CONF
+
+static void make_default_cur_rule(void)
+{
+	memset(&G.cur_rule, 0, sizeof(G.cur_rule));
+	G.cur_rule.maj = -1; /* "not a @major,minor rule" */
+	G.cur_rule.mode = 0660;
+}
+
+static void clean_up_cur_rule(void)
+{
+	free(G.cur_rule.envvar);
+	if (G.cur_rule.regex_compiled)
+		regfree(&G.cur_rule.match);
+	free(G.cur_rule.ren_mov);
+	IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);)
+	make_default_cur_rule();
+}
+
+static void parse_next_rule(void)
+{
+	/* Note: on entry, G.cur_rule is set to default */
+	while (1) {
+		char *tokens[4];
+		char *val;
+
+		/* No PARSE_EOL_COMMENTS, because command may contain '#' chars */
+		if (!config_read(G.parser, tokens, 4, 3, "# \t", PARSE_NORMAL & ~PARSE_EOL_COMMENTS))
+			break;
+
+		/* Fields: [-]regex uid:gid mode [alias] [cmd] */
+		dbg("token1:'%s'", tokens[1]);
+
+		/* 1st field */
+		val = tokens[0];
+		G.cur_rule.keep_matching = ('-' == val[0]);
+		val += G.cur_rule.keep_matching; /* swallow leading dash */
+		if (val[0] == '@') {
+			/* @major,minor[-minor2] */
+			/* (useful when name is ambiguous:
+			 * "/sys/class/usb/lp0" and
+			 * "/sys/class/printer/lp0")
+			 */
+			int sc = sscanf(val, "@%u,%u-%u", &G.cur_rule.maj, &G.cur_rule.min0, &G.cur_rule.min1);
+			if (sc < 2 || G.cur_rule.maj < 0) {
+				bb_error_msg("bad @maj,min on line %d", G.parser->lineno);
+				goto next_rule;
+			}
+			if (sc == 2)
+				G.cur_rule.min1 = G.cur_rule.min0;
+		} else {
+			if (val[0] == '$') {
+				char *eq = strchr(++val, '=');
+				if (!eq) {
+					bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno);
+					goto next_rule;
+				}
+				G.cur_rule.envvar = xstrndup(val, eq - val);
+				val = eq + 1;
+			}
+			xregcomp(&G.cur_rule.match, val, REG_EXTENDED);
+			G.cur_rule.regex_compiled = 1;
+		}
+
+		/* 2nd field: uid:gid - device ownership */
+		if (get_uidgid(&G.cur_rule.ugid, tokens[1], /*allow_numeric:*/ 1) == 0) {
+			bb_error_msg("unknown user/group '%s' on line %d", tokens[1], G.parser->lineno);
+			goto next_rule;
+		}
+
+		/* 3rd field: mode - device permissions */
+		bb_parse_mode(tokens[2], &G.cur_rule.mode);
+
+		/* 4th field (opt): ">|=alias" or "!" to not create the node */
+		val = tokens[3];
+		if (ENABLE_FEATURE_MDEV_RENAME && val && strchr(">=!", val[0])) {
+			char *s = skip_non_whitespace(val);
+			G.cur_rule.ren_mov = xstrndup(val, s - val);
+			val = skip_whitespace(s);
+		}
+
+		if (ENABLE_FEATURE_MDEV_EXEC && val && val[0]) {
+			const char *s = "$@*";
+			const char *s2 = strchr(s, val[0]);
+			if (!s2) {
+				bb_error_msg("bad line %u", G.parser->lineno);
+				goto next_rule;
+			}
+			IF_FEATURE_MDEV_EXEC(G.cur_rule.r_cmd = xstrdup(val);)
+		}
+
+		return;
+ next_rule:
+		clean_up_cur_rule();
+	} /* while (config_read) */
+
+	dbg("config_close(G.parser)");
+	config_close(G.parser);
+	G.parser = NULL;
+
+	return;
+}
+
+/* If mdev -s, we remember rules in G.rule_vec[].
+ * Otherwise, there is no point in doing it, and we just
+ * save only one parsed rule in G.cur_rule.
+ */
+static const struct rule *next_rule(void)
+{
+	struct rule *rule;
+
+	/* Open conf file if we didn't do it yet */
+	if (!G.parser && G.filename) {
+		dbg("config_open('%s')", G.filename);
+		G.parser = config_open2(G.filename, fopen_for_read);
+		G.filename = NULL;
+	}
+
+	if (G.rule_vec) {
+		/* mdev -s */
+		/* Do we have rule parsed already? */
+		if (G.rule_vec[G.rule_idx]) {
+			dbg("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
+			return G.rule_vec[G.rule_idx++];
+		}
+		make_default_cur_rule();
+	} else {
+		/* not mdev -s */
+		clean_up_cur_rule();
+	}
+
+	/* Parse one more rule if file isn't fully read */
+	rule = &G.cur_rule;
+	if (G.parser) {
+		parse_next_rule();
+		if (G.rule_vec) { /* mdev -s */
+			rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule));
+			G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx);
+			G.rule_vec[G.rule_idx++] = rule;
+			dbg("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
+		}
+	}
+
+	return rule;
+}
+
+#else
+
+# define next_rule() (&G.cur_rule)
+
+#endif
+
+static void mkdir_recursive(char *name)
+{
+	/* if name has many levels ("dir1/dir2"),
+	 * bb_make_directory() will create dir1 according to umask,
+	 * not according to its "mode" parameter.
+	 * Since we run with umask=0, need to temporarily switch it.
+	 */
+	umask(022); /* "dir1" (if any) will be 0755 too */
+	bb_make_directory(name, 0755, FILEUTILS_RECUR);
+	umask(0);
+}
+
+/* Builds an alias path.
+ * This function potentionally reallocates the alias parameter.
+ * Only used for ENABLE_FEATURE_MDEV_RENAME
+ */
+static char *build_alias(char *alias, const char *device_name)
+{
+	char *dest;
+
+	/* ">bar/": rename to bar/device_name */
+	/* ">bar[/]baz": rename to bar[/]baz */
+	dest = strrchr(alias, '/');
+	if (dest) { /* ">bar/[baz]" ? */
+		*dest = '\0'; /* mkdir bar */
+		mkdir_recursive(alias);
+		*dest = '/';
+		if (dest[1] == '\0') { /* ">bar/" => ">bar/device_name" */
+			dest = alias;
+			alias = concat_path_file(alias, device_name);
+			free(dest);
+		}
+	}
+
+	return alias;
+}
+
+/* mknod in /dev based on a path like "/sys/block/hda/hda1"
+ * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes
+ * after NUL, but we promise to not mangle (IOW: to restore if needed)
+ * path string.
+ * NB2: "mdev -s" may call us many times, do not leak memory/fds!
+ *
+ * device_name = $DEVNAME (may be NULL)
+ * path        = /sys/$DEVPATH
+ */
+static void make_device(char *device_name, char *path, int operation)
+{
+	int major, minor, type, len;
+
+	if (G.verbose)
+		bb_error_msg("device: %s, %s", device_name, path);
+
+	/* Try to read major/minor string.  Note that the kernel puts \n after
+	 * the data, so we don't need to worry about null terminating the string
+	 * because sscanf() will stop at the first nondigit, which \n is.
+	 * We also depend on path having writeable space after it.
+	 */
+	major = -1;
+	if (operation == OP_add) {
+		char *dev_maj_min = path + strlen(path);
+
+		strcpy(dev_maj_min, "/dev");
+		len = open_read_close(path, dev_maj_min + 1, 64);
+		*dev_maj_min = '\0';
+		if (len < 1) {
+			if (!ENABLE_FEATURE_MDEV_EXEC)
+				return;
+			/* no "dev" file, but we can still run scripts
+			 * based on device name */
+		} else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) == 2) {
+			if (G.verbose)
+				bb_error_msg("maj,min: %u,%u", major, minor);
+		} else {
+			major = -1;
+		}
+	}
+	/* else: for delete, -1 still deletes the node, but < -1 suppresses that */
+
+	/* Determine device name, type, major and minor */
+	if (!device_name)
+		device_name = (char*) bb_basename(path);
+	/* http://kernel.org/doc/pending/hotplug.txt says that only
+	 * "/sys/block/..." is for block devices. "/sys/bus" etc is not.
+	 * But since 2.6.25 block devices are also in /sys/class/block.
+	 * We use strstr("/block/") to forestall future surprises.
+	 */
+	type = S_IFCHR;
+	if (strstr(path, "/block/") || (G.subsystem && strncmp(G.subsystem, "block", 5) == 0))
+		type = S_IFBLK;
+
+#if ENABLE_FEATURE_MDEV_CONF
+	G.rule_idx = 0; /* restart from the beginning (think mdev -s) */
+#endif
+	for (;;) {
+		const char *str_to_match;
+		regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP];
+		char *command;
+		char *alias;
+		char aliaslink = aliaslink; /* for compiler */
+		char *node_name;
+		const struct rule *rule;
+
+		str_to_match = device_name;
+
+		rule = next_rule();
+
+#if ENABLE_FEATURE_MDEV_CONF
+		if (rule->maj >= 0) {  /* @maj,min rule */
+			if (major != rule->maj)
+				continue;
+			if (minor < rule->min0 || minor > rule->min1)
+				continue;
+			memset(off, 0, sizeof(off));
+			goto rule_matches;
+		}
+		if (rule->envvar) { /* $envvar=regex rule */
+			str_to_match = getenv(rule->envvar);
+			dbg("getenv('%s'):'%s'", rule->envvar, str_to_match);
+			if (!str_to_match)
+				continue;
+		}
+		/* else: str_to_match = device_name */
+
+		if (rule->regex_compiled) {
+			int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0);
+			dbg("regex_match for '%s':%d", str_to_match, regex_match);
+			//bb_error_msg("matches:");
+			//for (int i = 0; i < ARRAY_SIZE(off); i++) {
+			//	if (off[i].rm_so < 0) continue;
+			//	bb_error_msg("match %d: '%.*s'\n", i,
+			//		(int)(off[i].rm_eo - off[i].rm_so),
+			//		device_name + off[i].rm_so);
+			//}
+
+			if (regex_match != 0
+			/* regexec returns whole pattern as "range" 0 */
+			 || off[0].rm_so != 0
+			 || (int)off[0].rm_eo != (int)strlen(str_to_match)
+			) {
+				continue; /* this rule doesn't match */
+			}
+		}
+		/* else: it's final implicit "match-all" rule */
+ rule_matches:
+#endif
+		dbg("rule matched");
+
+		/* Build alias name */
+		alias = NULL;
+		if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) {
+			aliaslink = rule->ren_mov[0];
+			if (aliaslink == '!') {
+				/* "!": suppress node creation/deletion */
+				major = -2;
+			}
+			else if (aliaslink == '>' || aliaslink == '=') {
+				if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) {
+					char *s;
+					char *p;
+					unsigned n;
+
+					/* substitute %1..9 with off[1..9], if any */
+					n = 0;
+					s = rule->ren_mov;
+					while (*s)
+						if (*s++ == '%')
+							n++;
+
+					p = alias = xzalloc(strlen(rule->ren_mov) + n * strlen(str_to_match));
+					s = rule->ren_mov + 1;
+					while (*s) {
+						*p = *s;
+						if ('%' == *s) {
+							unsigned i = (s[1] - '0');
+							if (i <= 9 && off[i].rm_so >= 0) {
+								n = off[i].rm_eo - off[i].rm_so;
+								strncpy(p, str_to_match + off[i].rm_so, n);
+								p += n - 1;
+								s++;
+							}
+						}
+						p++;
+						s++;
+					}
+				} else {
+					alias = xstrdup(rule->ren_mov + 1);
+				}
+			}
+		}
+		dbg("alias:'%s'", alias);
+
+		command = NULL;
+		IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;)
+		if (command) {
+			const char *s = "$@*";
+			const char *s2 = strchr(s, command[0]);
+
+			/* Are we running this command now?
+			 * Run $cmd on delete, @cmd on create, *cmd on both
+			 */
+			if (s2 - s != (operation == OP_remove) || *s2 == '*') {
+				/* We are here if: '*',
+				 * or: '@' and delete = 0,
+				 * or: '$' and delete = 1
+				 */
+				command++;
+			} else {
+				command = NULL;
+			}
+		}
+		dbg("command:'%s'", command);
+
+		/* "Execute" the line we found */
+		node_name = device_name;
+		if (ENABLE_FEATURE_MDEV_RENAME && alias) {
+			node_name = alias = build_alias(alias, device_name);
+			dbg("alias2:'%s'", alias);
+		}
+
+		if (operation == OP_add && major >= 0) {
+			char *slash = strrchr(node_name, '/');
+			if (slash) {
+				*slash = '\0';
+				mkdir_recursive(node_name);
+				*slash = '/';
+			}
+			if (G.verbose)
+				bb_error_msg("mknod: %s (%d,%d) %o", node_name, major, minor, rule->mode | type);
+			if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
+				bb_perror_msg("can't create '%s'", node_name);
+			if (ENABLE_FEATURE_MDEV_CONF) {
+				chmod(node_name, rule->mode);
+				chown(node_name, rule->ugid.uid, rule->ugid.gid);
+			}
+			if (major == G.root_major && minor == G.root_minor)
+				symlink(node_name, "root");
+			if (ENABLE_FEATURE_MDEV_RENAME && alias) {
+				if (aliaslink == '>') {
+//TODO: on devtmpfs, device_name already exists and symlink() fails.
+//End result is that instead of symlink, we have two nodes.
+//What should be done?
+					if (G.verbose)
+						bb_error_msg("symlink: %s", device_name);
+					symlink(node_name, device_name);
+				}
+			}
+		}
+
+		if (ENABLE_FEATURE_MDEV_EXEC && command) {
+			/* setenv will leak memory, use putenv/unsetenv/free */
+			char *s = xasprintf("%s=%s", "MDEV", node_name);
+			char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
+			putenv(s);
+			putenv(s1);
+			if (G.verbose)
+				bb_error_msg("running: %s", command);
+			if (system(command) == -1)
+				bb_perror_msg("can't run '%s'", command);
+			bb_unsetenv_and_free(s1);
+			bb_unsetenv_and_free(s);
+		}
+
+		if (operation == OP_remove && major >= -1) {
+			if (ENABLE_FEATURE_MDEV_RENAME && alias) {
+				if (aliaslink == '>') {
+					if (G.verbose)
+						bb_error_msg("unlink: %s", device_name);
+					unlink(device_name);
+				}
+			}
+			if (G.verbose)
+				bb_error_msg("unlink: %s", node_name);
+			unlink(node_name);
+		}
+
+		if (ENABLE_FEATURE_MDEV_RENAME)
+			free(alias);
+
+		/* We found matching line.
+		 * Stop unless it was prefixed with '-'
+		 */
+		if (!ENABLE_FEATURE_MDEV_CONF || !rule->keep_matching)
+			break;
+	} /* for (;;) */
+}
+
+/* File callback for /sys/ traversal */
+static int FAST_FUNC fileAction(const char *fileName,
+		struct stat *statbuf UNUSED_PARAM,
+		void *userData,
+		int depth UNUSED_PARAM)
+{
+	size_t len = strlen(fileName) - 4; /* can't underflow */
+	char *scratch = userData;
+
+	/* len check is for paranoid reasons */
+	if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX)
+		return FALSE;
+
+	strcpy(scratch, fileName);
+	scratch[len] = '\0';
+	make_device(/*DEVNAME:*/ NULL, scratch, OP_add);
+
+	return TRUE;
+}
+
+/* Directory callback for /sys/ traversal */
+static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
+		struct stat *statbuf UNUSED_PARAM,
+		void *userData UNUSED_PARAM,
+		int depth)
+{
+	/* Extract device subsystem -- the name of the directory
+	 * under /sys/class/ */
+	if (1 == depth) {
+		free(G.subsystem);
+		G.subsystem = strrchr(fileName, '/');
+		if (G.subsystem)
+			G.subsystem = xstrdup(G.subsystem + 1);
+	}
+
+	return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
+}
+
+/* For the full gory details, see linux/Documentation/firmware_class/README
+ *
+ * Firmware loading works like this:
+ * - kernel sets FIRMWARE env var
+ * - userspace checks /lib/firmware/$FIRMWARE
+ * - userspace waits for /sys/$DEVPATH/loading to appear
+ * - userspace writes "1" to /sys/$DEVPATH/loading
+ * - userspace copies /lib/firmware/$FIRMWARE into /sys/$DEVPATH/data
+ * - userspace writes "0" (worked) or "-1" (failed) to /sys/$DEVPATH/loading
+ * - kernel loads firmware into device
+ */
+static void load_firmware(const char *firmware, const char *sysfs_path)
+{
+	int cnt;
+	int firmware_fd, loading_fd;
+
+	/* check for /lib/firmware/$FIRMWARE */
+	xchdir("/lib/firmware");
+	firmware_fd = open(firmware, O_RDONLY); /* can fail */
+
+	/* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */
+	xchdir(sysfs_path);
+	for (cnt = 0; cnt < 30; ++cnt) {
+		loading_fd = open("loading", O_WRONLY);
+		if (loading_fd >= 0)
+			goto loading;
+		sleep(1);
+	}
+	goto out;
+
+ loading:
+	cnt = 0;
+	if (firmware_fd >= 0) {
+		int data_fd;
+
+		/* tell kernel we're loading by "echo 1 > /sys/$DEVPATH/loading" */
+		if (full_write(loading_fd, "1", 1) != 1)
+			goto out;
+
+		/* load firmware into /sys/$DEVPATH/data */
+		data_fd = open("data", O_WRONLY);
+		if (data_fd < 0)
+			goto out;
+		cnt = bb_copyfd_eof(firmware_fd, data_fd);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			close(data_fd);
+	}
+
+	/* Tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading"
+	 * Note: we emit -1 also if firmware file wasn't found.
+	 * There are cases when otherwise kernel would wait for minutes
+	 * before timing out.
+	 */
+	if (cnt > 0)
+		full_write(loading_fd, "0", 1);
+	else
+		full_write(loading_fd, "-1", 2);
+
+ out:
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		close(firmware_fd);
+		close(loading_fd);
+	}
+}
+
+int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mdev_main(int argc UNUSED_PARAM, char **argv)
+{
+	RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
+
+	INIT_G();
+
+#if ENABLE_FEATURE_MDEV_CONF
+	G.filename = "/etc/mdev.conf";
+#endif
+
+	/* We can be called as hotplug helper */
+	/* Kernel cannot provide suitable stdio fds for us, do it ourself */
+	bb_sanitize_stdio();
+
+	/* Force the configuration file settings exactly */
+	umask(0);
+
+	xchdir("/dev");
+
+	if (argv[1] && strcmp(argv[1], "-s") == 0) {
+		/* Scan:
+		 * mdev -s
+		 */
+		struct stat st;
+
+#if ENABLE_FEATURE_MDEV_CONF
+		/* Same as xrealloc_vector(NULL, 4, 0): */
+		G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec));
+#endif
+		xstat("/", &st);
+		G.root_major = major(st.st_dev);
+		G.root_minor = minor(st.st_dev);
+
+		/* ACTION_FOLLOWLINKS is needed since in newer kernels
+		 * /sys/block/loop* (for example) are symlinks to dirs,
+		 * not real directories.
+		 * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs,
+		 * but we can't enforce that on users)
+		 */
+		if (access("/sys/class/block", F_OK) != 0) {
+			/* Scan obsolete /sys/block only if /sys/class/block
+			 * doesn't exist. Otherwise we'll have dupes.
+			 * Also, do not complain if it doesn't exist.
+			 * Some people configure kernel to have no blockdevs.
+			 */
+			recursive_action("/sys/block",
+				ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET,
+				fileAction, dirAction, temp, 0);
+		}
+		recursive_action("/sys/class",
+			ACTION_RECURSE | ACTION_FOLLOWLINKS,
+			fileAction, dirAction, temp, 0);
+	} else {
+		char *fw;
+		char *seq;
+		char *action;
+		char *env_devname;
+		char *env_devpath;
+		smalluint op;
+
+		/* Hotplug:
+		 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
+		 * ACTION can be "add" or "remove"
+		 * DEVPATH is like "/block/sda" or "/class/input/mice"
+		 */
+		action = getenv("ACTION");
+		op = index_in_strings(keywords, action);
+		env_devname = getenv("DEVNAME"); /* can be NULL */
+		env_devpath = getenv("DEVPATH");
+		G.subsystem = getenv("SUBSYSTEM");
+		if (!action || !env_devpath /*|| !G.subsystem*/)
+			bb_show_usage();
+		fw = getenv("FIRMWARE");
+		/* If it exists, does /dev/mdev.seq match $SEQNUM?
+		 * If it does not match, earlier mdev is running
+		 * in parallel, and we need to wait */
+		seq = getenv("SEQNUM");
+		if (seq) {
+			int timeout = 2000 / 32; /* 2000 msec */
+			do {
+				int seqlen;
+				char seqbuf[sizeof(int)*3 + 2];
+
+				seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf) - 1);
+				if (seqlen < 0) {
+					seq = NULL;
+					break;
+				}
+				seqbuf[seqlen] = '\0';
+				if (seqbuf[0] == '\n' /* seed file? */
+				 || strcmp(seq, seqbuf) == 0 /* correct idx? */
+				) {
+					break;
+				}
+				usleep(32*1000);
+			} while (--timeout);
+		}
+
+		{
+			int logfd = open("/dev/mdev.log", O_WRONLY | O_APPEND);
+			if (logfd >= 0) {
+				xmove_fd(logfd, STDERR_FILENO);
+				G.verbose = 1;
+				bb_error_msg("seq: %s action: %s", seq, action);
+			}
+		}
+
+		snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
+		if (op == OP_remove) {
+			/* Ignoring "remove firmware". It was reported
+			 * to happen and to cause erroneous deletion
+			 * of device nodes. */
+			if (!fw)
+				make_device(env_devname, temp, op);
+		}
+		else if (op == OP_add) {
+			make_device(env_devname, temp, op);
+			if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
+				if (fw)
+					load_firmware(fw, temp);
+			}
+		}
+
+		if (seq) {
+			xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));
+		}
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		RELEASE_CONFIG_BUFFER(temp);
+
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/minix.h b/ap/app/busybox/src/util-linux/minix.h
new file mode 100644
index 0000000..e0fbcf7
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/minix.h
@@ -0,0 +1,98 @@
+/*
+ * This is the original minix inode layout on disk.
+ * Note the 8-bit gid and atime and ctime.
+ */
+struct minix1_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];
+};
+
+/*
+ * The new minix inode has all the time entries, as well as
+ * long block numbers and a third indirect block (7+1+1+1
+ * instead of 7+1+1). Also, some previously 8-bit values are
+ * now 16-bit. The inode is now 64 bytes instead of 32.
+ */
+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];
+};
+
+/*
+ * minix superblock data on disk
+ */
+struct minix_superblock {
+	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;
+};
+
+struct minix_dir_entry {
+	uint16_t inode;
+	char name[];
+};
+
+/* Believe it or not, but mount.h has this one #defined */
+#undef BLOCK_SIZE
+
+enum {
+	BLOCK_SIZE              = 1024,
+	BITS_PER_BLOCK          = BLOCK_SIZE << 3,
+
+	MINIX_ROOT_INO          = 1,
+	MINIX_BAD_INO           = 2,
+
+	MINIX1_SUPER_MAGIC      = 0x137F,       /* original minix fs */
+	MINIX1_SUPER_MAGIC2     = 0x138F,       /* minix fs, 30 char names */
+	MINIX2_SUPER_MAGIC      = 0x2468,       /* minix V2 fs */
+	MINIX2_SUPER_MAGIC2     = 0x2478,       /* minix V2 fs, 30 char names */
+	MINIX_VALID_FS          = 0x0001,       /* clean fs */
+	MINIX_ERROR_FS          = 0x0002,       /* fs has errors */
+
+	INODE_SIZE1             = sizeof(struct minix1_inode),
+	INODE_SIZE2             = sizeof(struct minix2_inode),
+	MINIX1_INODES_PER_BLOCK = BLOCK_SIZE / sizeof(struct minix1_inode),
+	MINIX2_INODES_PER_BLOCK = BLOCK_SIZE / sizeof(struct minix2_inode),
+};
+
+/*
+Basic test script for regressions in mkfs/fsck.
+Copies current dir into image (typically bbox build tree).
+
+#!/bin/sh
+tmpdir=/tmp/minixtest-$$
+tmpimg=/tmp/minix-img-$$
+
+mkdir $tmpdir
+dd if=/dev/zero of=$tmpimg bs=1M count=20 || exit
+./busybox mkfs.minix $tmpimg || exit
+mount -o loop $tmpimg $tmpdir || exit
+cp -a "$PWD" $tmpdir
+umount $tmpdir || exit
+./busybox fsck.minix -vfm $tmpimg || exit
+echo "Continue?"
+read junk
+./busybox fsck.minix -vfml $tmpimg || exit
+rmdir $tmpdir
+rm $tmpimg
+
+*/
diff --git a/ap/app/busybox/src/util-linux/mkfs_ext2.c b/ap/app/busybox/src/util-linux/mkfs_ext2.c
new file mode 100644
index 0000000..3258d7e
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/mkfs_ext2.c
@@ -0,0 +1,696 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfs_ext2: utility to create EXT2 filesystem
+ * inspired by genext2fs
+ *
+ * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define mkfs_ext2_trivial_usage
+//usage:       "[-Fn] "
+/* //usage:    "[-c|-l filename] " */
+//usage:       "[-b BLK_SIZE] "
+/* //usage:    "[-f fragment-size] [-g blocks-per-group] " */
+//usage:       "[-i INODE_RATIO] [-I INODE_SIZE] "
+/* //usage:    "[-j] [-J journal-options] [-N number-of-inodes] " */
+//usage:       "[-m RESERVED_PERCENT] "
+/* //usage:    "[-o creator-os] [-O feature[,...]] [-q] " */
+/* //usage:    "[r fs-revision-level] [-E extended-options] [-v] [-F] " */
+//usage:       "[-L LABEL] "
+/* //usage:    "[-M last-mounted-directory] [-S] [-T filesystem-type] " */
+//usage:       "BLOCKDEV [KBYTES]"
+//usage:#define mkfs_ext2_full_usage "\n\n"
+//usage:       "	-b BLK_SIZE	Block size, bytes"
+/* //usage:  "\n	-c		Check device for bad blocks" */
+/* //usage:  "\n	-E opts		Set extended options" */
+/* //usage:  "\n	-f size		Fragment size in bytes" */
+//usage:     "\n	-F		Force"
+/* //usage:  "\n	-g N		Number of blocks in a block group" */
+//usage:     "\n	-i RATIO	Max number of files is filesystem_size / RATIO"
+//usage:     "\n	-I BYTES	Inode size (min 128)"
+/* //usage:  "\n	-j		Create a journal (ext3)" */
+/* //usage:  "\n	-J opts		Set journal options (size/device)" */
+/* //usage:  "\n	-l file		Read bad blocks list from file" */
+//usage:     "\n	-L LBL		Volume label"
+//usage:     "\n	-m PERCENT	Percent of blocks to reserve for admin"
+/* //usage:  "\n	-M dir		Set last mounted directory" */
+//usage:     "\n	-n		Dry run"
+/* //usage:  "\n	-N N		Number of inodes to create" */
+/* //usage:  "\n	-o os		Set the 'creator os' field" */
+/* //usage:  "\n	-O features	Dir_index/filetype/has_journal/journal_dev/sparse_super" */
+/* //usage:  "\n	-q		Quiet" */
+/* //usage:  "\n	-r rev		Set filesystem revision" */
+/* //usage:  "\n	-S		Write superblock and group descriptors only" */
+/* //usage:  "\n	-T fs-type	Set usage type (news/largefile/largefile4)" */
+/* //usage:  "\n	-v		Verbose" */
+
+#include "libbb.h"
+#include <linux/fs.h>
+#include "bb_e2fs_defs.h"
+
+#define ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT 0
+#define ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX    1
+
+#define EXT2_HASH_HALF_MD4       1
+#define EXT2_FLAGS_SIGNED_HASH   0x0001
+#define EXT2_FLAGS_UNSIGNED_HASH 0x0002
+
+// storage helpers
+char BUG_wrong_field_size(void);
+#define STORE_LE(field, value) \
+do { \
+	if (sizeof(field) == 4) \
+		field = SWAP_LE32(value); \
+	else if (sizeof(field) == 2) \
+		field = SWAP_LE16(value); \
+	else if (sizeof(field) == 1) \
+		field = (value); \
+	else \
+		BUG_wrong_field_size(); \
+} while (0)
+
+#define FETCH_LE32(field) \
+	(sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size())
+
+// All fields are little-endian
+struct ext2_dir {
+	uint32_t inode1;
+	uint16_t rec_len1;
+	uint8_t  name_len1;
+	uint8_t  file_type1;
+	char     name1[4];
+	uint32_t inode2;
+	uint16_t rec_len2;
+	uint8_t  name_len2;
+	uint8_t  file_type2;
+	char     name2[4];
+	uint32_t inode3;
+	uint16_t rec_len3;
+	uint8_t  name_len3;
+	uint8_t  file_type3;
+	char     name3[12];
+};
+
+static unsigned int_log2(unsigned arg)
+{
+	unsigned r = 0;
+	while ((arg >>= 1) != 0)
+		r++;
+	return r;
+}
+
+// taken from mkfs_minix.c. libbb candidate?
+// "uint32_t size", since we never use it for anything >32 bits
+static uint32_t div_roundup(uint32_t size, uint32_t n)
+{
+	// Overflow-resistant
+	uint32_t res = size / n;
+	if (res * n != size)
+		res++;
+	return res;
+}
+
+static void allocate(uint8_t *bitmap, uint32_t blocksize, uint32_t start, uint32_t end)
+{
+	uint32_t i;
+
+//bb_info_msg("ALLOC: [%u][%u][%u]: [%u-%u]:=[%x],[%x]", blocksize, start, end, start/8, blocksize - end/8 - 1, (1 << (start & 7)) - 1, (uint8_t)(0xFF00 >> (end & 7)));
+	memset(bitmap, 0, blocksize);
+	i = start / 8;
+	memset(bitmap, 0xFF, i);
+	bitmap[i] = (1 << (start & 7)) - 1; //0..7 => 00000000..01111111
+	i = end / 8;
+	bitmap[blocksize - i - 1] |= 0x7F00 >> (end & 7); //0..7 => 00000000..11111110
+	memset(bitmap + blocksize - i, 0xFF, i); // N.B. no overflow here!
+}
+
+static uint32_t has_super(uint32_t x)
+{
+	// 0, 1 and powers of 3, 5, 7 up to 2^32 limit
+	static const uint32_t supers[] = {
+		0, 1, 3, 5, 7, 9, 25, 27, 49, 81, 125, 243, 343, 625, 729,
+		2187, 2401, 3125, 6561, 15625, 16807, 19683, 59049, 78125,
+		117649, 177147, 390625, 531441, 823543, 1594323, 1953125,
+		4782969, 5764801, 9765625, 14348907, 40353607, 43046721,
+		48828125, 129140163, 244140625, 282475249, 387420489,
+		1162261467, 1220703125, 1977326743, 3486784401/* >2^31 */,
+	};
+	const uint32_t *sp = supers + ARRAY_SIZE(supers);
+	while (1) {
+		sp--;
+		if (x == *sp)
+			return 1;
+		if (x > *sp)
+			return 0;
+	}
+}
+
+#define fd 3	/* predefined output descriptor */
+
+static void PUT(uint64_t off, void *buf, uint32_t size)
+{
+//	bb_info_msg("PUT[%llu]:[%u]", off, size);
+	xlseek(fd, off, SEEK_SET);
+	xwrite(fd, buf, size);
+}
+
+// 128 and 256-byte inodes:
+// 128-byte inode is described by struct ext2_inode.
+// 256-byte one just has these fields appended:
+//      __u16   i_extra_isize;
+//      __u16   i_pad1;
+//      __u32   i_ctime_extra;  /* extra Change time (nsec << 2 | epoch) */
+//      __u32   i_mtime_extra;  /* extra Modification time (nsec << 2 | epoch) */
+//      __u32   i_atime_extra;  /* extra Access time (nsec << 2 | epoch) */
+//      __u32   i_crtime;       /* File creation time */
+//      __u32   i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/
+//      __u32   i_version_hi;   /* high 32 bits for 64-bit version */
+// the rest is padding.
+//
+// linux/ext2_fs.h has "#define i_size_high i_dir_acl" which suggests that even
+// 128-byte inode is capable of describing large files (i_dir_acl is meaningful
+// only for directories, which never need i_size_high).
+//
+// Standard mke2fs creates a filesystem with 256-byte inodes if it is
+// bigger than 0.5GB.
+
+// Standard mke2fs 1.41.9:
+// Usage: mke2fs [-c|-l filename] [-b block-size] [-f fragment-size]
+//	[-i bytes-per-inode] [-I inode-size] [-J journal-options]
+//	[-G meta group size] [-N number-of-inodes]
+//	[-m reserved-blocks-percentage] [-o creator-os]
+//	[-g blocks-per-group] [-L volume-label] [-M last-mounted-directory]
+//	[-O feature[,...]] [-r fs-revision] [-E extended-option[,...]]
+//	[-T fs-type] [-U UUID] [-jnqvFSV] device [blocks-count]
+//
+// Options not commented below are taken but silently ignored:
+enum {
+	OPT_c = 1 << 0,
+	OPT_l = 1 << 1,
+	OPT_b = 1 << 2,		// block size, in bytes
+	OPT_f = 1 << 3,
+	OPT_i = 1 << 4,		// bytes per inode
+	OPT_I = 1 << 5,		// custom inode size, in bytes
+	OPT_J = 1 << 6,
+	OPT_G = 1 << 7,
+	OPT_N = 1 << 8,
+	OPT_m = 1 << 9,		// percentage of blocks reserved for superuser
+	OPT_o = 1 << 10,
+	OPT_g = 1 << 11,
+	OPT_L = 1 << 12,	// label
+	OPT_M = 1 << 13,
+	OPT_O = 1 << 14,
+	OPT_r = 1 << 15,
+	OPT_E = 1 << 16,
+	OPT_T = 1 << 17,
+	OPT_U = 1 << 18,
+	OPT_j = 1 << 19,
+	OPT_n = 1 << 20,	// dry run: do not write anything
+	OPT_q = 1 << 21,
+	OPT_v = 1 << 22,
+	OPT_F = 1 << 23,
+	OPT_S = 1 << 24,
+	//OPT_V = 1 << 25,	// -V version. bbox applets don't support that
+};
+
+int mkfs_ext2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned i, pos, n;
+	unsigned bs, bpi;
+	unsigned blocksize, blocksize_log2;
+	unsigned inodesize, user_inodesize;
+	unsigned reserved_percent = 5;
+	unsigned long long kilobytes;
+	uint32_t nblocks, nblocks_full;
+	uint32_t nreserved;
+	uint32_t ngroups;
+	uint32_t bytes_per_inode;
+	uint32_t first_block;
+	uint32_t inodes_per_group;
+	uint32_t group_desc_blocks;
+	uint32_t inode_table_blocks;
+	uint32_t lost_and_found_blocks;
+	time_t timestamp;
+	const char *label = "";
+	struct stat st;
+	struct ext2_super_block *sb; // superblock
+	struct ext2_group_desc *gd; // group descriptors
+	struct ext2_inode *inode;
+	struct ext2_dir *dir;
+	uint8_t *buf;
+
+	// using global "option_mask32" instead of local "opts":
+	// we are register starved here
+	opt_complementary = "-1:b+:i+:I+:m+";
+	/*opts =*/ getopt32(argv, "cl:b:f:i:I:J:G:N:m:o:g:L:M:O:r:E:T:U:jnqvFS",
+		/*lbfi:*/ NULL, &bs, NULL, &bpi,
+		/*IJGN:*/ &user_inodesize, NULL, NULL, NULL,
+		/*mogL:*/ &reserved_percent, NULL, NULL, &label,
+		/*MOrE:*/ NULL, NULL, NULL, NULL,
+		/*TU:*/ NULL, NULL);
+	argv += optind; // argv[0] -- device
+
+	// open the device, check the device is a block device
+	xmove_fd(xopen(argv[0], O_WRONLY), fd);
+	xfstat(fd, &st, argv[0]);
+	if (!S_ISBLK(st.st_mode) && !(option_mask32 & OPT_F))
+		bb_error_msg_and_die("%s: not a block device", argv[0]);
+
+	// check if it is mounted
+	// N.B. what if we format a file? find_mount_point will return false negative since
+	// it is loop block device which is mounted!
+	if (find_mount_point(argv[0], 0))
+		bb_error_msg_and_die("can't format mounted filesystem");
+
+	// get size in kbytes
+	kilobytes = get_volume_size_in_bytes(fd, argv[1], 1024, /*extend:*/ !(option_mask32 & OPT_n)) / 1024;
+
+	bytes_per_inode = 16384;
+	if (kilobytes < 512*1024)
+		bytes_per_inode = 4096;
+	if (kilobytes < 3*1024)
+		bytes_per_inode = 8192;
+	if (option_mask32 & OPT_i)
+		bytes_per_inode = bpi;
+
+	// Determine block size and inode size
+	// block size is a multiple of 1024
+	// inode size is a multiple of 128
+	blocksize = 1024;
+	inodesize = sizeof(struct ext2_inode); // 128
+	if (kilobytes >= 512*1024) { // mke2fs 1.41.9 compat
+		blocksize = 4096;
+		inodesize = 256;
+	}
+	if (EXT2_MAX_BLOCK_SIZE > 4096) {
+		// kilobytes >> 22 == size in 4gigabyte chunks.
+		// if size >= 16k gigs, blocksize must be increased.
+		// Try "mke2fs -F image $((16 * 1024*1024*1024))"
+		while ((kilobytes >> 22) >= blocksize)
+			blocksize *= 2;
+	}
+	if (option_mask32 & OPT_b)
+		blocksize = bs;
+	if (blocksize < EXT2_MIN_BLOCK_SIZE
+	 || blocksize > EXT2_MAX_BLOCK_SIZE
+	 || (blocksize & (blocksize - 1)) // not power of 2
+	) {
+		bb_error_msg_and_die("blocksize %u is bad", blocksize);
+	}
+	// Do we have custom inode size?
+	if (option_mask32 & OPT_I) {
+		if (user_inodesize < sizeof(*inode)
+		 || user_inodesize > blocksize
+		 || (user_inodesize & (user_inodesize - 1)) // not power of 2
+		) {
+			bb_error_msg("-%c is bad", 'I');
+		} else {
+			inodesize = user_inodesize;
+		}
+	}
+
+	if ((int32_t)bytes_per_inode < blocksize)
+		bb_error_msg_and_die("-%c is bad", 'i');
+	// number of bits in one block, i.e. 8*blocksize
+#define blocks_per_group (8 * blocksize)
+	first_block = (EXT2_MIN_BLOCK_SIZE == blocksize);
+	blocksize_log2 = int_log2(blocksize);
+
+	// Determine number of blocks
+	kilobytes >>= (blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE);
+	nblocks = kilobytes;
+	if (nblocks != kilobytes)
+		bb_error_msg_and_die("block count doesn't fit in 32 bits");
+#define kilobytes kilobytes_unused_after_this
+	// Experimentally, standard mke2fs won't work on images smaller than 60k
+	if (nblocks < 60)
+		bb_error_msg_and_die("need >= 60 blocks");
+
+	// How many reserved blocks?
+	if (reserved_percent > 50)
+		bb_error_msg_and_die("-%c is bad", 'm');
+	nreserved = (uint64_t)nblocks * reserved_percent / 100;
+
+	// N.B. killing e2fsprogs feature! Unused blocks don't account in calculations
+	nblocks_full = nblocks;
+
+	// If last block group is too small, nblocks may be decreased in order
+	// to discard it, and control returns here to recalculate some
+	// parameters.
+	// Note: blocksize and bytes_per_inode are never recalculated.
+ retry:
+	// N.B. a block group can have no more than blocks_per_group blocks
+	ngroups = div_roundup(nblocks - first_block, blocks_per_group);
+
+	group_desc_blocks = div_roundup(ngroups, blocksize / sizeof(*gd));
+	// TODO: reserved blocks must be marked as such in the bitmaps,
+	// or resulting filesystem is corrupt
+	if (ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT) {
+		/*
+		 * From e2fsprogs: Calculate the number of GDT blocks to reserve for online
+		 * filesystem growth.
+		 * The absolute maximum number of GDT blocks we can reserve is determined by
+		 * the number of block pointers that can fit into a single block.
+		 * We set it at 1024x the current filesystem size, or
+		 * the upper block count limit (2^32), whichever is lower.
+		 */
+		uint32_t reserved_group_desc_blocks = 0xFFFFFFFF; // maximum block number
+		if (nblocks < reserved_group_desc_blocks / 1024)
+			reserved_group_desc_blocks = nblocks * 1024;
+		reserved_group_desc_blocks = div_roundup(reserved_group_desc_blocks - first_block, blocks_per_group);
+		reserved_group_desc_blocks = div_roundup(reserved_group_desc_blocks, blocksize / sizeof(*gd)) - group_desc_blocks;
+		if (reserved_group_desc_blocks > blocksize / sizeof(uint32_t))
+			reserved_group_desc_blocks = blocksize / sizeof(uint32_t);
+		//TODO: STORE_LE(sb->s_reserved_gdt_blocks, reserved_group_desc_blocks);
+		group_desc_blocks += reserved_group_desc_blocks;
+	}
+
+	{
+		// N.B. e2fsprogs does as follows!
+		uint32_t overhead, remainder;
+		// ninodes is the max number of inodes in this filesystem
+		uint32_t ninodes = ((uint64_t) nblocks_full * blocksize) / bytes_per_inode;
+		if (ninodes < EXT2_GOOD_OLD_FIRST_INO+1)
+			ninodes = EXT2_GOOD_OLD_FIRST_INO+1;
+		inodes_per_group = div_roundup(ninodes, ngroups);
+		// minimum number because the first EXT2_GOOD_OLD_FIRST_INO-1 are reserved
+		if (inodes_per_group < 16)
+			inodes_per_group = 16;
+		// a block group can't have more inodes than blocks
+		if (inodes_per_group > blocks_per_group)
+			inodes_per_group = blocks_per_group;
+		// adjust inodes per group so they completely fill the inode table blocks in the descriptor
+		inodes_per_group = (div_roundup(inodes_per_group * inodesize, blocksize) * blocksize) / inodesize;
+		// make sure the number of inodes per group is a multiple of 8
+		inodes_per_group &= ~7;
+		inode_table_blocks = div_roundup(inodes_per_group * inodesize, blocksize);
+
+		// to be useful, lost+found should occupy at least 2 blocks (but not exceeding 16*1024 bytes),
+		// and at most EXT2_NDIR_BLOCKS. So reserve these blocks right now
+		/* Or e2fsprogs comment verbatim (what does it mean?):
+		 * Ensure that lost+found is at least 2 blocks, so we always
+		 * test large empty blocks for big-block filesystems. */
+		lost_and_found_blocks = MIN(EXT2_NDIR_BLOCKS, 16 >> (blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE));
+
+		// the last group needs more attention: isn't it too small for possible overhead?
+		overhead = (has_super(ngroups - 1) ? (1/*sb*/ + group_desc_blocks) : 0) + 1/*bbmp*/ + 1/*ibmp*/ + inode_table_blocks;
+		remainder = (nblocks - first_block) % blocks_per_group;
+		////can't happen, nblocks >= 60 guarantees this
+		////if ((1 == ngroups)
+		//// && remainder
+		//// && (remainder < overhead + 1/* "/" */ + lost_and_found_blocks)
+		////) {
+		////	bb_error_msg_and_die("way small device");
+		////}
+
+		// Standard mke2fs uses 50. Looks like a bug in our calculation
+		// of "remainder" or "overhead" - we don't match standard mke2fs
+		// when we transition from one group to two groups
+		// (a bit after 8M image size), but it works for two->three groups
+		// transition (at 16M).
+		if (remainder && (remainder < overhead + 50)) {
+//bb_info_msg("CHOP[%u]", remainder);
+			nblocks -= remainder;
+			goto retry;
+		}
+	}
+
+	if (nblocks_full - nblocks)
+		printf("warning: %u blocks unused\n\n", nblocks_full - nblocks);
+	printf(
+		"Filesystem label=%s\n"
+		"OS type: Linux\n"
+		"Block size=%u (log=%u)\n"
+		"Fragment size=%u (log=%u)\n"
+		"%u inodes, %u blocks\n"
+		"%u blocks (%u%%) reserved for the super user\n"
+		"First data block=%u\n"
+		"Maximum filesystem blocks=%u\n"
+		"%u block groups\n"
+		"%u blocks per group, %u fragments per group\n"
+		"%u inodes per group"
+		, label
+		, blocksize, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE
+		, blocksize, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE
+		, inodes_per_group * ngroups, nblocks
+		, nreserved, reserved_percent
+		, first_block
+		, group_desc_blocks * (blocksize / (unsigned)sizeof(*gd)) * blocks_per_group
+		, ngroups
+		, blocks_per_group, blocks_per_group
+		, inodes_per_group
+	);
+	{
+		const char *fmt = "\nSuperblock backups stored on blocks:\n"
+			"\t%u";
+		pos = first_block;
+		for (i = 1; i < ngroups; i++) {
+			pos += blocks_per_group;
+			if (has_super(i)) {
+				printf(fmt, (unsigned)pos);
+				fmt = ", %u";
+			}
+		}
+	}
+	bb_putchar('\n');
+
+	if (option_mask32 & OPT_n) {
+		if (ENABLE_FEATURE_CLEAN_UP)
+			close(fd);
+		return EXIT_SUCCESS;
+	}
+
+	// TODO: 3/5 refuse if mounted
+	// TODO: 4/5 compat options
+	// TODO: 1/5 sanity checks
+	// TODO: 0/5 more verbose error messages
+	// TODO: 4/5 bigendianness: recheck, wait for ARM reporters
+	// TODO: 2/5 reserved GDT: how to mark but not allocate?
+	// TODO: 3/5 dir_index?
+
+	// fill the superblock
+	sb = xzalloc(1024);
+	STORE_LE(sb->s_rev_level, EXT2_DYNAMIC_REV); // revision 1 filesystem
+	STORE_LE(sb->s_magic, EXT2_SUPER_MAGIC);
+	STORE_LE(sb->s_inode_size, inodesize);
+	// set "Required extra isize" and "Desired extra isize" fields to 28
+	if (inodesize != sizeof(*inode)) {
+		STORE_LE(sb->s_min_extra_isize, 0x001c);
+		STORE_LE(sb->s_want_extra_isize, 0x001c);
+	}
+	STORE_LE(sb->s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
+	STORE_LE(sb->s_log_block_size, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE);
+	STORE_LE(sb->s_log_frag_size, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE);
+	// first 1024 bytes of the device are for boot record. If block size is 1024 bytes, then
+	// the first block is 1, otherwise 0
+	STORE_LE(sb->s_first_data_block, first_block);
+	// block and inode bitmaps occupy no more than one block, so maximum number of blocks is
+	STORE_LE(sb->s_blocks_per_group, blocks_per_group);
+	STORE_LE(sb->s_frags_per_group, blocks_per_group);
+	// blocks
+	STORE_LE(sb->s_blocks_count, nblocks);
+	// reserve blocks for superuser
+	STORE_LE(sb->s_r_blocks_count, nreserved);
+	// ninodes
+	STORE_LE(sb->s_inodes_per_group, inodes_per_group);
+	STORE_LE(sb->s_inodes_count, inodes_per_group * ngroups);
+	STORE_LE(sb->s_free_inodes_count, inodes_per_group * ngroups - EXT2_GOOD_OLD_FIRST_INO);
+	// timestamps
+	timestamp = time(NULL);
+	STORE_LE(sb->s_mkfs_time, timestamp);
+	STORE_LE(sb->s_wtime, timestamp);
+	STORE_LE(sb->s_lastcheck, timestamp);
+	// misc. Values are chosen to match mke2fs 1.41.9
+	STORE_LE(sb->s_state, 1); // TODO: what's 1?
+	STORE_LE(sb->s_creator_os, EXT2_OS_LINUX);
+	STORE_LE(sb->s_checkinterval, 24*60*60 * 180); // 180 days
+	STORE_LE(sb->s_errors, EXT2_ERRORS_DEFAULT);
+	// mke2fs 1.41.9 also sets EXT3_FEATURE_COMPAT_RESIZE_INODE
+	// and if >= 0.5GB, EXT3_FEATURE_RO_COMPAT_LARGE_FILE.
+	// we use values which match "mke2fs -O ^resize_inode":
+	// in this case 1.41.9 never sets EXT3_FEATURE_RO_COMPAT_LARGE_FILE.
+	STORE_LE(sb->s_feature_compat, EXT2_FEATURE_COMPAT_SUPP
+		| (EXT2_FEATURE_COMPAT_RESIZE_INO * ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT)
+		| (EXT2_FEATURE_COMPAT_DIR_INDEX * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX)
+	);
+	STORE_LE(sb->s_feature_incompat, EXT2_FEATURE_INCOMPAT_FILETYPE);
+	STORE_LE(sb->s_feature_ro_compat, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER);
+	STORE_LE(sb->s_flags, EXT2_FLAGS_UNSIGNED_HASH * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX);
+	generate_uuid(sb->s_uuid);
+	if (ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX) {
+		STORE_LE(sb->s_def_hash_version, EXT2_HASH_HALF_MD4);
+		generate_uuid((uint8_t *)sb->s_hash_seed);
+	}
+	/*
+	 * From e2fsprogs: add "jitter" to the superblock's check interval so that we
+	 * don't check all the filesystems at the same time.  We use a
+	 * kludgy hack of using the UUID to derive a random jitter value.
+	 */
+	STORE_LE(sb->s_max_mnt_count,
+		EXT2_DFL_MAX_MNT_COUNT
+		+ (sb->s_uuid[ARRAY_SIZE(sb->s_uuid)-1] % EXT2_DFL_MAX_MNT_COUNT));
+
+	// write the label
+	safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name));
+
+	// calculate filesystem skeleton structures
+	gd = xzalloc(group_desc_blocks * blocksize);
+	buf = xmalloc(blocksize);
+	sb->s_free_blocks_count = 0;
+	for (i = 0, pos = first_block, n = nblocks - first_block;
+		i < ngroups;
+		i++, pos += blocks_per_group, n -= blocks_per_group
+	) {
+		uint32_t overhead = pos + (has_super(i) ? (1/*sb*/ + group_desc_blocks) : 0);
+		uint32_t free_blocks;
+		// fill group descriptors
+		STORE_LE(gd[i].bg_block_bitmap, overhead + 0);
+		STORE_LE(gd[i].bg_inode_bitmap, overhead + 1);
+		STORE_LE(gd[i].bg_inode_table, overhead + 2);
+		overhead = overhead - pos + 1/*bbmp*/ + 1/*ibmp*/ + inode_table_blocks;
+		gd[i].bg_free_inodes_count = inodes_per_group;
+		//STORE_LE(gd[i].bg_used_dirs_count, 0);
+		// N.B. both "/" and "/lost+found" are within the first block group
+		// "/" occupies 1 block, "/lost+found" occupies lost_and_found_blocks...
+		if (0 == i) {
+			// ... thus increased overhead for the first block group ...
+			overhead += 1 + lost_and_found_blocks;
+			// ... and 2 used directories
+			STORE_LE(gd[i].bg_used_dirs_count, 2);
+			// well known reserved inodes belong to the first block too
+			gd[i].bg_free_inodes_count -= EXT2_GOOD_OLD_FIRST_INO;
+		}
+
+		// cache free block count of the group
+		free_blocks = (n < blocks_per_group ? n : blocks_per_group) - overhead;
+
+		// mark preallocated blocks as allocated
+//bb_info_msg("ALLOC: [%u][%u][%u]", blocksize, overhead, blocks_per_group - (free_blocks + overhead));
+		allocate(buf, blocksize,
+			// reserve "overhead" blocks
+			overhead,
+			// mark unused trailing blocks
+			blocks_per_group - (free_blocks + overhead)
+		);
+		// dump block bitmap
+		PUT((uint64_t)(FETCH_LE32(gd[i].bg_block_bitmap)) * blocksize, buf, blocksize);
+		STORE_LE(gd[i].bg_free_blocks_count, free_blocks);
+
+		// mark preallocated inodes as allocated
+		allocate(buf, blocksize,
+			// mark reserved inodes
+			inodes_per_group - gd[i].bg_free_inodes_count,
+			// mark unused trailing inodes
+			blocks_per_group - inodes_per_group
+		);
+		// dump inode bitmap
+		//PUT((uint64_t)(FETCH_LE32(gd[i].bg_block_bitmap)) * blocksize, buf, blocksize);
+		//but it's right after block bitmap, so we can just:
+		xwrite(fd, buf, blocksize);
+		STORE_LE(gd[i].bg_free_inodes_count, gd[i].bg_free_inodes_count);
+
+		// count overall free blocks
+		sb->s_free_blocks_count += free_blocks;
+	}
+	STORE_LE(sb->s_free_blocks_count, sb->s_free_blocks_count);
+
+	// dump filesystem skeleton structures
+//	printf("Writing superblocks and filesystem accounting information: ");
+	for (i = 0, pos = first_block; i < ngroups; i++, pos += blocks_per_group) {
+		// dump superblock and group descriptors and their backups
+		if (has_super(i)) {
+			// N.B. 1024 byte blocks are special
+			PUT(((uint64_t)pos * blocksize) + ((0 == i && 1024 != blocksize) ? 1024 : 0),
+					sb, 1024);
+			PUT(((uint64_t)pos * blocksize) + blocksize,
+					gd, group_desc_blocks * blocksize);
+		}
+	}
+
+	// zero boot sectors
+	memset(buf, 0, blocksize);
+	// Disabled: standard mke2fs doesn't do this, and
+	// on SPARC this destroys Sun disklabel.
+	// Users who need/want zeroing can easily do it with dd.
+	//PUT(0, buf, 1024); // N.B. 1024 <= blocksize, so buf[0..1023] contains zeros
+
+	// zero inode tables
+	for (i = 0; i < ngroups; ++i)
+		for (n = 0; n < inode_table_blocks; ++n)
+			PUT((uint64_t)(FETCH_LE32(gd[i].bg_inode_table) + n) * blocksize,
+				buf, blocksize);
+
+	// prepare directory inode
+	inode = (struct ext2_inode *)buf;
+	STORE_LE(inode->i_mode, S_IFDIR | S_IRWXU | S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH);
+	STORE_LE(inode->i_mtime, timestamp);
+	STORE_LE(inode->i_atime, timestamp);
+	STORE_LE(inode->i_ctime, timestamp);
+	STORE_LE(inode->i_size, blocksize);
+	// inode->i_blocks stores the number of 512 byte data blocks
+	// (512, because it goes directly to struct stat without scaling)
+	STORE_LE(inode->i_blocks, blocksize / 512);
+
+	// dump root dir inode
+	STORE_LE(inode->i_links_count, 3); // "/.", "/..", "/lost+found/.." point to this inode
+	STORE_LE(inode->i_block[0], FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks);
+	PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_ROOT_INO-1) * inodesize,
+				buf, inodesize);
+
+	// dump lost+found dir inode
+	STORE_LE(inode->i_links_count, 2); // both "/lost+found" and "/lost+found/." point to this inode
+	STORE_LE(inode->i_size, lost_and_found_blocks * blocksize);
+	STORE_LE(inode->i_blocks, (lost_and_found_blocks * blocksize) / 512);
+	n = FETCH_LE32(inode->i_block[0]) + 1;
+	for (i = 0; i < lost_and_found_blocks; ++i)
+		STORE_LE(inode->i_block[i], i + n); // use next block
+//bb_info_msg("LAST BLOCK USED[%u]", i + n);
+	PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_GOOD_OLD_FIRST_INO-1) * inodesize,
+				buf, inodesize);
+
+	// dump directories
+	memset(buf, 0, blocksize);
+	dir = (struct ext2_dir *)buf;
+
+	// dump 2nd+ blocks of "/lost+found"
+	STORE_LE(dir->rec_len1, blocksize); // e2fsck 1.41.4 compat (1.41.9 does not need this)
+	for (i = 1; i < lost_and_found_blocks; ++i)
+		PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks + 1+i) * blocksize,
+				buf, blocksize);
+
+	// dump 1st block of "/lost+found"
+	STORE_LE(dir->inode1, EXT2_GOOD_OLD_FIRST_INO);
+	STORE_LE(dir->rec_len1, 12);
+	STORE_LE(dir->name_len1, 1);
+	STORE_LE(dir->file_type1, EXT2_FT_DIR);
+	dir->name1[0] = '.';
+	STORE_LE(dir->inode2, EXT2_ROOT_INO);
+	STORE_LE(dir->rec_len2, blocksize - 12);
+	STORE_LE(dir->name_len2, 2);
+	STORE_LE(dir->file_type2, EXT2_FT_DIR);
+	dir->name2[0] = '.'; dir->name2[1] = '.';
+	PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks + 1) * blocksize, buf, blocksize);
+
+	// dump root dir block
+	STORE_LE(dir->inode1, EXT2_ROOT_INO);
+	STORE_LE(dir->rec_len2, 12);
+	STORE_LE(dir->inode3, EXT2_GOOD_OLD_FIRST_INO);
+	STORE_LE(dir->rec_len3, blocksize - 12 - 12);
+	STORE_LE(dir->name_len3, 10);
+	STORE_LE(dir->file_type3, EXT2_FT_DIR);
+	strcpy(dir->name3, "lost+found");
+	PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks + 0) * blocksize, buf, blocksize);
+
+	// cleanup
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(buf);
+		free(gd);
+		free(sb);
+	}
+
+	xclose(fd);
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/mkfs_ext2.txt b/ap/app/busybox/src/util-linux/mkfs_ext2.txt
new file mode 100644
index 0000000..273d5b6
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/mkfs_ext2.txt
@@ -0,0 +1,77 @@
+Difference between bbox mke2fs and standard one (dumpe2fs comparison):
+
+[upd: inode size has been fixed since then]
+
+Two significant differences:
+- standard mke2fs has resize_inode feature and thus has reserved GDT blocks,
+  which decreases free blocks;
+- inode size: 128/256 (since 679807k is >0.5G, standard mke2fs uses "big" inodes)
+  this affects inode table in block groups
+
+Filesystem volume name:   <none>                                        Filesystem volume name:   <none>
+Last mounted on:          <not available>                               Last mounted on:          <not available>
+Filesystem UUID:          f4839760-c89b-44b7-ac52-08b4e326c0b4          Filesystem UUID:          71179558-8c8e-4a56-ad54-018008f5c358
+Filesystem magic number:  0xEF53                                        Filesystem magic number:  0xEF53
+Filesystem revision #:    1 (dynamic)                                   Filesystem revision #:    1 (dynamic)
+Filesystem features:      ext_attr dir_index filetype sparse_super      Filesystem features:      ext_attr resize_inode dir_index filetype sparse_super large_file
+Filesystem flags:         signed_directory_hash                         Filesystem flags:         signed_directory_hash
+Default mount options:    (none)                                        Default mount options:    (none)
+Filesystem state:         clean                                         Filesystem state:         clean
+Errors behavior:          Continue                                      Errors behavior:          Continue
+Filesystem OS type:       Linux                                         Filesystem OS type:       Linux
+Inode count:              42624                                         Inode count:              42528
+Block count:              169951                                        Block count:              169951
+Reserved block count:     8497                                          Reserved block count:     8497
+Free blocks:              168594                                        Free blocks:              167103
+Free inodes:              42613                                         Free inodes:              42517
+First block:              0                                             First block:              0
+Block size:               4096                                          Block size:               4096
+Fragment size:            4096                                          Fragment size:            4096
+                                                                        Reserved GDT blocks:      41
+Blocks per group:         32768                                         Blocks per group:         32768
+Fragments per group:      32768                                         Fragments per group:      32768
+Inodes per group:         7104                                          Inodes per group:         7088
+Inode blocks per group:   222                                           Inode blocks per group:   443
+Filesystem created:       Wed Oct 21 13:40:55 2009                      Filesystem created:       Wed Oct 21 13:40:55 2009
+Last mount time:          n/a                                           Last mount time:          n/a
+Last write time:          Wed Oct 21 13:40:55 2009                      Last write time:          Wed Oct 21 13:40:55 2009
+Mount count:              0                                             Mount count:              0
+Maximum mount count:      20                                            Maximum mount count:      37
+Last checked:             Wed Oct 21 13:40:55 2009                      Last checked:             Wed Oct 21 13:40:55 2009
+Check interval:           15552000 (6 months)                           Check interval:           15552000 (6 months)
+Next check after:         Mon Apr 19 13:40:55 2010                      Next check after:         Mon Apr 19 13:40:55 2010
+Reserved blocks uid:      0 (user root)                                 Reserved blocks uid:      0 (user root)
+Reserved blocks gid:      0 (group root)                                Reserved blocks gid:      0 (group root)
+First inode:              11                                            First inode:              11
+Inode size:               128                                           Inode size:               256
+                                                                        Required extra isize:     28
+                                                                        Desired extra isize:      28
+Default directory hash:   half_md4                                      Default directory hash:   half_md4
+Directory Hash Seed:      ff94d047-c9ca-4fe5-b553-937a76101a89          Directory Hash Seed:      c270fd43-9868-4a92-ae99-050098e12835
+
+
+Group 0: (Blocks 0-32767)                                               Group 0: (Blocks 0-32767)
+  Primary superblock at 0, Group descriptors at 1-1                       Primary superblock at 0, Group descriptors at 1-1
+                                                                          Reserved GDT blocks at 2-42
+  Block bitmap at 2 (+2), Inode bitmap at 3 (+3)                          Block bitmap at 43 (+43), Inode bitmap at 44 (+44)
+  Inode table at 4-225 (+4)                                               Inode table at 45-487 (+45)
+  32537 free blocks, 7093 free inodes, 2 directories                      32274 free blocks, 7077 free inodes, 2 directories
+  Free blocks: 231-32767                                                  Free blocks: 494-32767
+  Free inodes: 12-7104                                                    Free inodes: 12-7088
+
+Group 1: (Blocks 32768-65535)                                           Group 1: (Blocks 32768-65535)
+  Backup superblock at 32768, Group descriptors at 32769-32769            Backup superblock at 32768, Group descriptors at 32769-32769
+                                                                          Reserved GDT blocks at 32770-32810
+  Block bitmap at 32770 (+2), Inode bitmap at 32771 (+3)                  Block bitmap at 32811 (+43), Inode bitmap at 32812 (+44)
+  Inode table at 32772-32993 (+4)                                         Inode table at 32813-33255 (+45)
+  32542 free blocks, 7104 free inodes, 0 directories                      32280 free blocks, 7088 free inodes, 0 directories
+  Free blocks: 32994-65535                                                Free blocks: 33256-65535
+  Free inodes: 7105-14208                                                 Free inodes: 7089-14176
+
+Group 2: (Blocks 65536-98303)                                           Group 2: (Blocks 65536-98303)
+  Block bitmap at 65536 (+0), Inode bitmap at 65537 (+1)                  Block bitmap at 65536 (+0), Inode bitmap at 65537 (+1)
+  Inode table at 65538-65759 (+2)                                         Inode table at 65538-65980 (+2)
+  32544 free blocks, 7104 free inodes, 0 directories                      32323 free blocks, 7088 free inodes, 0 directories
+  Free blocks: 65760-98303                                                Free blocks: 65981-98303
+  Free inodes: 14209-21312                                                Free inodes: 14177-21264
+...
diff --git a/ap/app/busybox/src/util-linux/mkfs_ext2_test.sh b/ap/app/busybox/src/util-linux/mkfs_ext2_test.sh
new file mode 100755
index 0000000..f5347cc
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/mkfs_ext2_test.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+# Disabling features we do not match exactly:
+system_mke2fs='/sbin/mke2fs -O ^resize_inode'
+bbox_mke2fs='./busybox mke2fs'
+
+gen_image() { # params: mke2fs_invocation image_name
+    >$2
+    dd seek=$((kilobytes-1)) bs=1K count=1 </dev/zero of=$2 >/dev/null 2>&1 || exit 1
+    $1 -F $2 $kilobytes >$2.raw_out 2>&1 || return 1
+    cat $2.raw_out \
+    | grep -v '^mke2fs [0-9]*\.[0-9]*\.[0-9]* ' \
+    | grep -v '^Maximum filesystem' \
+    | grep -v '^Writing inode tables' \
+    | grep -v '^Writing superblocks and filesystem accounting information' \
+    | grep -v '^This filesystem will be automatically checked every' \
+    | grep -v '^180 days, whichever comes first' \
+    | sed 's/blocks* unused./blocks unused/' \
+    | sed 's/block groups*/block groups/' \
+    | sed 's/ *$//' \
+    | sed 's/blocks (.*%) reserved/blocks reserved/' \
+    | grep -v '^$' \
+    >$2.out
+}
+
+test_mke2fs() {
+    echo Testing $kilobytes
+
+    gen_image "$system_mke2fs" image_std || return 1
+    gen_image "$bbox_mke2fs"   image_bb  || return 1
+
+    diff -ua image_bb.out image_std.out >image.out.diff || {
+	cat image.out.diff
+	return 1
+    }
+
+    e2fsck -f -n image_bb >image_bb_e2fsck.out 2>&1 || {
+	echo "e2fsck error on image_bb"
+	cat image_bb_e2fsck.out
+	exit 1
+    }
+}
+
+# -:bbox +:standard
+
+# kilobytes=60 is the minimal allowed size
+kilobytes=60
+while true; do
+    test_mke2fs || exit 1
+    : $((kilobytes++))
+    test $kilobytes = 200 && break
+done
+
+# Transition from one block group to two
+# fails in [8378..8410] range unless -O ^resize_inode
+kilobytes=$((1 * 8*1024 - 50))
+while true; do
+    test_mke2fs || exit 1
+    : $((kilobytes++))
+    test $kilobytes = $((1 * 8*1024 + 300)) && break
+done
+
+# Transition from 2 block groups to 3
+# works
+kilobytes=$((2 * 8*1024 - 50))
+while true; do
+    test_mke2fs || exit 1
+    : $((kilobytes++))
+    test $kilobytes = $((2 * 8*1024 + 400)) && break
+done
+
+# Transition from 3 block groups to 4
+# fails in [24825..24922] range unless -O ^resize_inode
+kilobytes=$((3 * 8*1024 - 50))
+while true; do
+    test_mke2fs || exit 1
+    : $((kilobytes++))
+    test $kilobytes = $((3 * 8*1024 + 500)) && break
+done
+
+# Transition from 4 block groups to 5
+# works
+kilobytes=$((4 * 8*1024 - 50))
+while true; do
+    test_mke2fs || exit 1
+    : $((kilobytes++))
+    test $kilobytes = $((4 * 8*1024 + 600)) && break
+done
+
+# Transition from 5 block groups to 6
+# fails in [41230..41391] range unless -O ^resize_inode
+kilobytes=$((5 * 8*1024 - 50))
+while true; do
+    test_mke2fs || exit 1
+    : $((kilobytes++))
+    test $kilobytes = $((5 * 8*1024 + 700)) && break
+done
+exit
+
+# Random sizes
+while true; do
+    kilobytes=$(( (RANDOM*RANDOM) % 5000000 + 60))
+    test_mke2fs || exit 1
+done
diff --git a/ap/app/busybox/src/util-linux/mkfs_minix.c b/ap/app/busybox/src/util-linux/mkfs_minix.c
new file mode 100644
index 0000000..59d7d23
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/mkfs_minix.c
@@ -0,0 +1,740 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfs.c - make a linux (minix) file-system.
+ *
+ * (C) 1991 Linus Torvalds.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/*
+ * DD.MM.YY
+ *
+ * 24.11.91  -	Time began. Used the fsck sources to get started.
+ *
+ * 25.11.91  -	Corrected some bugs. Added support for ".badblocks"
+ *		The algorithm for ".badblocks" is a bit weird, but
+ *		it should work. Oh, well.
+ *
+ * 25.01.92  -	Added the -l option for getting the list of bad blocks
+ *		out of a named file. (Dave Rivers, rivers@ponds.uucp)
+ *
+ * 28.02.92  -	Added %-information when using -c.
+ *
+ * 28.02.93  -	Added support for other namelengths than the original
+ *		14 characters so that I can test the new kernel routines..
+ *
+ * 09.10.93  -	Make exit status conform to that required by fsutil
+ *		(Rik Faith, faith@cs.unc.edu)
+ *
+ * 31.10.93  -	Added inode request feature, for backup floppies: use
+ *		32 inodes, for a news partition use more.
+ *		(Scott Heavner, sdh@po.cwru.edu)
+ *
+ * 03.01.94  -	Added support for file system valid flag.
+ *		(Dr. Wettstein, greg%wind.uucp@plains.nodak.edu)
+ *
+ * 30.10.94  -  added support for v2 filesystem
+ *	        (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
+ *
+ * 09.11.94  -	Added test to prevent overwrite of mounted fs adapted
+ *		from Theodore Ts'o's (tytso@athena.mit.edu) mke2fs
+ *		program.  (Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * 03.20.95  -	Clear first 512 bytes of filesystem to make certain that
+ *		the filesystem is not misidentified as a MS-DOS FAT filesystem.
+ *		(Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * 02.07.96  -  Added small patch from Russell King to make the program a
+ *		good deal more portable (janl@math.uio.no)
+ *
+ * Usage:  mkfs [-c | -l filename ] [-v] [-nXX] [-iXX] device [size-in-blocks]
+ *
+ *	-c for readability checking (SLOW!)
+ *      -l for getting a list of bad blocks from a file.
+ *	-n for namelength (currently the kernel only uses 14 or 30)
+ *	-i for number of inodes
+ *	-v for v2 filesystem
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ *
+ * Modified for BusyBox by Erik Andersen <andersen@debian.org> --
+ *	removed getopt based parser and added a hand rolled one.
+ */
+
+//usage:#define mkfs_minix_trivial_usage
+//usage:       "[-c | -l FILE] [-nXX] [-iXX] BLOCKDEV [KBYTES]"
+//usage:#define mkfs_minix_full_usage "\n\n"
+//usage:       "Make a MINIX filesystem\n"
+//usage:     "\n	-c		Check device for bad blocks"
+//usage:     "\n	-n [14|30]	Maximum length of filenames"
+//usage:     "\n	-i INODES	Number of inodes for the filesystem"
+//usage:     "\n	-l FILE		Read bad blocks list from FILE"
+//usage:     "\n	-v		Make version 2 filesystem"
+
+#include "libbb.h"
+#include <mntent.h>
+
+#include "minix.h"
+
+/* Store the very same times/uids/gids for image consistency */
+#if 1
+# define CUR_TIME 0
+# define GETUID 0
+# define GETGID 0
+#else
+/* Was using this. Is it useful? NB: this will break testsuite */
+# define CUR_TIME time(NULL)
+# define GETUID getuid()
+# define GETGID getgid()
+#endif
+
+enum {
+	MAX_GOOD_BLOCKS         = 512,
+	TEST_BUFFER_BLOCKS      = 16,
+};
+
+#if !ENABLE_FEATURE_MINIX2
+enum { version2 = 0 };
+#endif
+
+enum { dev_fd = 3 };
+
+struct globals {
+#if ENABLE_FEATURE_MINIX2
+	smallint version2;
+#define version2 G.version2
+#endif
+	char *device_name;
+	uint32_t total_blocks;
+	int badblocks;
+	int namelen;
+	int dirsize;
+	int magic;
+	char *inode_buffer;
+	char *inode_map;
+	char *zone_map;
+	int used_good_blocks;
+	unsigned long req_nr_inodes;
+	unsigned currently_testing;
+
+	char root_block[BLOCK_SIZE];
+	char superblock_buffer[BLOCK_SIZE];
+	char boot_block_buffer[512];
+	unsigned short good_blocks_table[MAX_GOOD_BLOCKS];
+	/* check_blocks(): buffer[] was the biggest static in entire bbox */
+	char check_blocks_buffer[BLOCK_SIZE * TEST_BUFFER_BLOCKS];
+
+	unsigned short ind_block1[BLOCK_SIZE >> 1];
+	unsigned short dind_block1[BLOCK_SIZE >> 1];
+	unsigned long ind_block2[BLOCK_SIZE >> 2];
+	unsigned long dind_block2[BLOCK_SIZE >> 2];
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+static ALWAYS_INLINE unsigned div_roundup(unsigned size, unsigned n)
+{
+	return (size + n-1) / n;
+}
+
+#define INODE_BUF1              (((struct minix1_inode*)G.inode_buffer) - 1)
+#define INODE_BUF2              (((struct minix2_inode*)G.inode_buffer) - 1)
+
+#define SB                      (*(struct minix_superblock*)G.superblock_buffer)
+
+#define SB_INODES               (SB.s_ninodes)
+#define SB_IMAPS                (SB.s_imap_blocks)
+#define SB_ZMAPS                (SB.s_zmap_blocks)
+#define SB_FIRSTZONE            (SB.s_firstdatazone)
+#define SB_ZONE_SIZE            (SB.s_log_zone_size)
+#define SB_MAXSIZE              (SB.s_max_size)
+#define SB_MAGIC                (SB.s_magic)
+
+#if !ENABLE_FEATURE_MINIX2
+# define SB_ZONES               (SB.s_nzones)
+# define INODE_BLOCKS           div_roundup(SB_INODES, MINIX1_INODES_PER_BLOCK)
+#else
+# define SB_ZONES               (version2 ? SB.s_zones : SB.s_nzones)
+# define INODE_BLOCKS           div_roundup(SB_INODES, \
+                                (version2 ? MINIX2_INODES_PER_BLOCK : MINIX1_INODES_PER_BLOCK))
+#endif
+
+#define INODE_BUFFER_SIZE       (INODE_BLOCKS * BLOCK_SIZE)
+#define NORM_FIRSTZONE          (2 + SB_IMAPS + SB_ZMAPS + INODE_BLOCKS)
+
+/* Before you ask "where they come from?": */
+/* setbit/clrbit are supplied by sys/param.h */
+
+static int minix_bit(const char* a, unsigned i)
+{
+	return a[i >> 3] & (1<<(i & 7));
+}
+
+static void minix_setbit(char *a, unsigned i)
+{
+	setbit(a, i);
+}
+static void minix_clrbit(char *a, unsigned i)
+{
+	clrbit(a, i);
+}
+
+/* Note: do not assume 0/1, it is 0/nonzero */
+#define zone_in_use(x)  minix_bit(G.zone_map,(x)-SB_FIRSTZONE+1)
+/*#define inode_in_use(x) minix_bit(G.inode_map,(x))*/
+
+#define mark_inode(x)   minix_setbit(G.inode_map,(x))
+#define unmark_inode(x) minix_clrbit(G.inode_map,(x))
+#define mark_zone(x)    minix_setbit(G.zone_map,(x)-SB_FIRSTZONE+1)
+#define unmark_zone(x)  minix_clrbit(G.zone_map,(x)-SB_FIRSTZONE+1)
+
+#ifndef BLKGETSIZE
+# define BLKGETSIZE     _IO(0x12,96)    /* return device size */
+#endif
+
+
+static long valid_offset(int fd, int offset)
+{
+	char ch;
+
+	if (lseek(fd, offset, SEEK_SET) < 0)
+		return 0;
+	if (read(fd, &ch, 1) < 1)
+		return 0;
+	return 1;
+}
+
+static int count_blocks(int fd)
+{
+	int high, low;
+
+	low = 0;
+	for (high = 1; valid_offset(fd, high); high *= 2)
+		low = high;
+
+	while (low < high - 1) {
+		const int mid = (low + high) / 2;
+
+		if (valid_offset(fd, mid))
+			low = mid;
+		else
+			high = mid;
+	}
+	valid_offset(fd, 0);
+	return (low + 1);
+}
+
+static int get_size(const char *file)
+{
+	int fd;
+	long size;
+
+	fd = xopen(file, O_RDWR);
+	if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+		close(fd);
+		return (size * 512);
+	}
+
+	size = count_blocks(fd);
+	close(fd);
+	return size;
+}
+
+static void write_tables(void)
+{
+	/* Mark the superblock valid. */
+	SB.s_state |= MINIX_VALID_FS;
+	SB.s_state &= ~MINIX_ERROR_FS;
+
+	msg_eol = "seek to 0 failed";
+	xlseek(dev_fd, 0, SEEK_SET);
+
+	msg_eol = "can't clear boot sector";
+	xwrite(dev_fd, G.boot_block_buffer, 512);
+
+	msg_eol = "seek to BLOCK_SIZE failed";
+	xlseek(dev_fd, BLOCK_SIZE, SEEK_SET);
+
+	msg_eol = "can't write superblock";
+	xwrite(dev_fd, G.superblock_buffer, BLOCK_SIZE);
+
+	msg_eol = "can't write inode map";
+	xwrite(dev_fd, G.inode_map, SB_IMAPS * BLOCK_SIZE);
+
+	msg_eol = "can't write zone map";
+	xwrite(dev_fd, G.zone_map, SB_ZMAPS * BLOCK_SIZE);
+
+	msg_eol = "can't write inodes";
+	xwrite(dev_fd, G.inode_buffer, INODE_BUFFER_SIZE);
+
+	msg_eol = "\n";
+}
+
+static void write_block(int blk, char *buffer)
+{
+	xlseek(dev_fd, blk * BLOCK_SIZE, SEEK_SET);
+	xwrite(dev_fd, buffer, BLOCK_SIZE);
+}
+
+static int get_free_block(void)
+{
+	int blk;
+
+	if (G.used_good_blocks + 1 >= MAX_GOOD_BLOCKS)
+		bb_error_msg_and_die("too many bad blocks");
+	if (G.used_good_blocks)
+		blk = G.good_blocks_table[G.used_good_blocks - 1] + 1;
+	else
+		blk = SB_FIRSTZONE;
+	while (blk < SB_ZONES && zone_in_use(blk))
+		blk++;
+	if (blk >= SB_ZONES)
+		bb_error_msg_and_die("not enough good blocks");
+	G.good_blocks_table[G.used_good_blocks] = blk;
+	G.used_good_blocks++;
+	return blk;
+}
+
+static void mark_good_blocks(void)
+{
+	int blk;
+
+	for (blk = 0; blk < G.used_good_blocks; blk++)
+		mark_zone(G.good_blocks_table[blk]);
+}
+
+static int next(int zone)
+{
+	if (!zone)
+		zone = SB_FIRSTZONE - 1;
+	while (++zone < SB_ZONES)
+		if (zone_in_use(zone))
+			return zone;
+	return 0;
+}
+
+static void make_bad_inode(void)
+{
+	struct minix1_inode *inode = &INODE_BUF1[MINIX_BAD_INO];
+	int i, j, zone;
+	int ind = 0, dind = 0;
+	/* moved to globals to reduce stack usage
+	unsigned short ind_block[BLOCK_SIZE >> 1];
+	unsigned short dind_block[BLOCK_SIZE >> 1];
+	*/
+#define ind_block (G.ind_block1)
+#define dind_block (G.dind_block1)
+
+#define NEXT_BAD (zone = next(zone))
+
+	if (!G.badblocks)
+		return;
+	mark_inode(MINIX_BAD_INO);
+	inode->i_nlinks = 1;
+	/* BTW, setting this makes all images different */
+	/* it's harder to check for bugs then - diff isn't helpful :(... */
+	inode->i_time = CUR_TIME;
+	inode->i_mode = S_IFREG + 0000;
+	inode->i_size = G.badblocks * BLOCK_SIZE;
+	zone = next(0);
+	for (i = 0; i < 7; i++) {
+		inode->i_zone[i] = zone;
+		if (!NEXT_BAD)
+			goto end_bad;
+	}
+	inode->i_zone[7] = ind = get_free_block();
+	memset(ind_block, 0, BLOCK_SIZE);
+	for (i = 0; i < 512; i++) {
+		ind_block[i] = zone;
+		if (!NEXT_BAD)
+			goto end_bad;
+	}
+	inode->i_zone[8] = dind = get_free_block();
+	memset(dind_block, 0, BLOCK_SIZE);
+	for (i = 0; i < 512; i++) {
+		write_block(ind, (char *) ind_block);
+		dind_block[i] = ind = get_free_block();
+		memset(ind_block, 0, BLOCK_SIZE);
+		for (j = 0; j < 512; j++) {
+			ind_block[j] = zone;
+			if (!NEXT_BAD)
+				goto end_bad;
+		}
+	}
+	bb_error_msg_and_die("too many bad blocks");
+ end_bad:
+	if (ind)
+		write_block(ind, (char *) ind_block);
+	if (dind)
+		write_block(dind, (char *) dind_block);
+#undef ind_block
+#undef dind_block
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void make_bad_inode2(void)
+{
+	struct minix2_inode *inode = &INODE_BUF2[MINIX_BAD_INO];
+	int i, j, zone;
+	int ind = 0, dind = 0;
+	/* moved to globals to reduce stack usage
+	unsigned long ind_block[BLOCK_SIZE >> 2];
+	unsigned long dind_block[BLOCK_SIZE >> 2];
+	*/
+#define ind_block (G.ind_block2)
+#define dind_block (G.dind_block2)
+
+	if (!G.badblocks)
+		return;
+	mark_inode(MINIX_BAD_INO);
+	inode->i_nlinks = 1;
+	inode->i_atime = inode->i_mtime = inode->i_ctime = CUR_TIME;
+	inode->i_mode = S_IFREG + 0000;
+	inode->i_size = G.badblocks * BLOCK_SIZE;
+	zone = next(0);
+	for (i = 0; i < 7; i++) {
+		inode->i_zone[i] = zone;
+		if (!NEXT_BAD)
+			goto end_bad;
+	}
+	inode->i_zone[7] = ind = get_free_block();
+	memset(ind_block, 0, BLOCK_SIZE);
+	for (i = 0; i < 256; i++) {
+		ind_block[i] = zone;
+		if (!NEXT_BAD)
+			goto end_bad;
+	}
+	inode->i_zone[8] = dind = get_free_block();
+	memset(dind_block, 0, BLOCK_SIZE);
+	for (i = 0; i < 256; i++) {
+		write_block(ind, (char *) ind_block);
+		dind_block[i] = ind = get_free_block();
+		memset(ind_block, 0, BLOCK_SIZE);
+		for (j = 0; j < 256; j++) {
+			ind_block[j] = zone;
+			if (!NEXT_BAD)
+				goto end_bad;
+		}
+	}
+	/* Could make triple indirect block here */
+	bb_error_msg_and_die("too many bad blocks");
+ end_bad:
+	if (ind)
+		write_block(ind, (char *) ind_block);
+	if (dind)
+		write_block(dind, (char *) dind_block);
+#undef ind_block
+#undef dind_block
+}
+#else
+void make_bad_inode2(void);
+#endif
+
+static void make_root_inode(void)
+{
+	struct minix1_inode *inode = &INODE_BUF1[MINIX_ROOT_INO];
+
+	mark_inode(MINIX_ROOT_INO);
+	inode->i_zone[0] = get_free_block();
+	inode->i_nlinks = 2;
+	inode->i_time = CUR_TIME;
+	if (G.badblocks)
+		inode->i_size = 3 * G.dirsize;
+	else {
+		G.root_block[2 * G.dirsize] = '\0';
+		G.root_block[2 * G.dirsize + 1] = '\0';
+		inode->i_size = 2 * G.dirsize;
+	}
+	inode->i_mode = S_IFDIR + 0755;
+	inode->i_uid = GETUID;
+	if (inode->i_uid)
+		inode->i_gid = GETGID;
+	write_block(inode->i_zone[0], G.root_block);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void make_root_inode2(void)
+{
+	struct minix2_inode *inode = &INODE_BUF2[MINIX_ROOT_INO];
+
+	mark_inode(MINIX_ROOT_INO);
+	inode->i_zone[0] = get_free_block();
+	inode->i_nlinks = 2;
+	inode->i_atime = inode->i_mtime = inode->i_ctime = CUR_TIME;
+	if (G.badblocks)
+		inode->i_size = 3 * G.dirsize;
+	else {
+		G.root_block[2 * G.dirsize] = '\0';
+		G.root_block[2 * G.dirsize + 1] = '\0';
+		inode->i_size = 2 * G.dirsize;
+	}
+	inode->i_mode = S_IFDIR + 0755;
+	inode->i_uid = GETUID;
+	if (inode->i_uid)
+		inode->i_gid = GETGID;
+	write_block(inode->i_zone[0], G.root_block);
+}
+#else
+void make_root_inode2(void);
+#endif
+
+/*
+ * Perform a test of a block; return the number of
+ * blocks readable.
+ */
+static size_t do_check(char *buffer, size_t try, unsigned current_block)
+{
+	ssize_t got;
+
+	/* Seek to the correct loc. */
+	msg_eol = "seek failed during testing of blocks";
+	xlseek(dev_fd, current_block * BLOCK_SIZE, SEEK_SET);
+	msg_eol = "\n";
+
+	/* Try the read */
+	got = read(dev_fd, buffer, try * BLOCK_SIZE);
+	if (got < 0)
+		got = 0;
+	try = ((size_t)got) / BLOCK_SIZE;
+
+	if (got & (BLOCK_SIZE - 1))
+		fprintf(stderr, "Short read at block %u\n", (unsigned)(current_block + try));
+	return try;
+}
+
+static void alarm_intr(int alnum UNUSED_PARAM)
+{
+	if (G.currently_testing >= SB_ZONES)
+		return;
+	signal(SIGALRM, alarm_intr);
+	alarm(5);
+	if (!G.currently_testing)
+		return;
+	printf("%d ...", G.currently_testing);
+	fflush_all();
+}
+
+static void check_blocks(void)
+{
+	size_t try, got;
+
+	G.currently_testing = 0;
+	signal(SIGALRM, alarm_intr);
+	alarm(5);
+	while (G.currently_testing < SB_ZONES) {
+		msg_eol = "seek failed in check_blocks";
+		xlseek(dev_fd, G.currently_testing * BLOCK_SIZE, SEEK_SET);
+		msg_eol = "\n";
+		try = TEST_BUFFER_BLOCKS;
+		if (G.currently_testing + try > SB_ZONES)
+			try = SB_ZONES - G.currently_testing;
+		got = do_check(G.check_blocks_buffer, try, G.currently_testing);
+		G.currently_testing += got;
+		if (got == try)
+			continue;
+		if (G.currently_testing < SB_FIRSTZONE)
+			bb_error_msg_and_die("bad blocks before data-area: cannot make fs");
+		mark_zone(G.currently_testing);
+		G.badblocks++;
+		G.currently_testing++;
+	}
+	alarm(0);
+	printf("%d bad block(s)\n", G.badblocks);
+}
+
+static void get_list_blocks(char *filename)
+{
+	FILE *listfile;
+	unsigned long blockno;
+
+	listfile = xfopen_for_read(filename);
+	while (!feof(listfile)) {
+		fscanf(listfile, "%ld\n", &blockno);
+		mark_zone(blockno);
+		G.badblocks++;
+	}
+	printf("%d bad block(s)\n", G.badblocks);
+}
+
+static void setup_tables(void)
+{
+	unsigned long inodes;
+	unsigned norm_firstzone;
+	unsigned sb_zmaps;
+	unsigned i;
+
+	/* memset(G.superblock_buffer, 0, BLOCK_SIZE); */
+	/* memset(G.boot_block_buffer, 0, 512); */
+	SB_MAGIC = G.magic;
+	SB_ZONE_SIZE = 0;
+	SB_MAXSIZE = version2 ? 0x7fffffff : (7 + 512 + 512 * 512) * 1024;
+	if (version2)
+		SB.s_zones = G.total_blocks;
+	else
+		SB.s_nzones = G.total_blocks;
+
+	/* some magic nrs: 1 inode / 3 blocks */
+	if (G.req_nr_inodes == 0)
+		inodes = G.total_blocks / 3;
+	else
+		inodes = G.req_nr_inodes;
+	/* Round up inode count to fill block size */
+	if (version2)
+		inodes = (inodes + MINIX2_INODES_PER_BLOCK - 1) &
+		                 ~(MINIX2_INODES_PER_BLOCK - 1);
+	else
+		inodes = (inodes + MINIX1_INODES_PER_BLOCK - 1) &
+		                 ~(MINIX1_INODES_PER_BLOCK - 1);
+	if (inodes > 65535)
+		inodes = 65535;
+	SB_INODES = inodes;
+	SB_IMAPS = div_roundup(SB_INODES + 1, BITS_PER_BLOCK);
+
+	/* Real bad hack but overwise mkfs.minix can be thrown
+	 * in infinite loop...
+	 * try:
+	 * dd if=/dev/zero of=test.fs count=10 bs=1024
+	 * mkfs.minix -i 200 test.fs
+	 */
+	/* This code is not insane: NORM_FIRSTZONE is not a constant,
+	 * it is calculated from SB_INODES, SB_IMAPS and SB_ZMAPS */
+	i = 999;
+	SB_ZMAPS = 0;
+	do {
+		norm_firstzone = NORM_FIRSTZONE;
+		sb_zmaps = div_roundup(G.total_blocks - norm_firstzone + 1, BITS_PER_BLOCK);
+		if (SB_ZMAPS == sb_zmaps) goto got_it;
+		SB_ZMAPS = sb_zmaps;
+		/* new SB_ZMAPS, need to recalc NORM_FIRSTZONE */
+	} while (--i);
+	bb_error_msg_and_die("incompatible size/inode count, try different -i N");
+ got_it:
+
+	SB_FIRSTZONE = norm_firstzone;
+	G.inode_map = xmalloc(SB_IMAPS * BLOCK_SIZE);
+	G.zone_map = xmalloc(SB_ZMAPS * BLOCK_SIZE);
+	memset(G.inode_map, 0xff, SB_IMAPS * BLOCK_SIZE);
+	memset(G.zone_map, 0xff, SB_ZMAPS * BLOCK_SIZE);
+	for (i = SB_FIRSTZONE; i < SB_ZONES; i++)
+		unmark_zone(i);
+	for (i = MINIX_ROOT_INO; i <= SB_INODES; i++)
+		unmark_inode(i);
+	G.inode_buffer = xzalloc(INODE_BUFFER_SIZE);
+	printf("%ld inodes\n", (long)SB_INODES);
+	printf("%ld blocks\n", (long)SB_ZONES);
+	printf("Firstdatazone=%ld (%ld)\n", (long)SB_FIRSTZONE, (long)norm_firstzone);
+	printf("Zonesize=%d\n", BLOCK_SIZE << SB_ZONE_SIZE);
+	printf("Maxsize=%ld\n", (long)SB_MAXSIZE);
+}
+
+int mkfs_minix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mkfs_minix_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opt;
+	char *tmp;
+	struct stat statbuf;
+	char *str_i;
+	char *listfile = NULL;
+
+	INIT_G();
+/* default (changed to 30, per Linus's suggestion, Sun Nov 21 08:05:07 1993) */
+	G.namelen = 30;
+	G.dirsize = 32;
+	G.magic = MINIX1_SUPER_MAGIC2;
+
+	if (INODE_SIZE1 * MINIX1_INODES_PER_BLOCK != BLOCK_SIZE)
+		bb_error_msg_and_die("bad inode size");
+#if ENABLE_FEATURE_MINIX2
+	if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE)
+		bb_error_msg_and_die("bad inode size");
+#endif
+
+	opt_complementary = "n+"; /* -n N */
+	opt = getopt32(argv, "ci:l:n:v", &str_i, &listfile, &G.namelen);
+	argv += optind;
+	//if (opt & 1) -c
+	if (opt & 2) G.req_nr_inodes = xatoul(str_i); // -i
+	//if (opt & 4) -l
+	if (opt & 8) { // -n
+		if (G.namelen == 14) G.magic = MINIX1_SUPER_MAGIC;
+		else if (G.namelen == 30) G.magic = MINIX1_SUPER_MAGIC2;
+		else bb_show_usage();
+		G.dirsize = G.namelen + 2;
+	}
+	if (opt & 0x10) { // -v
+#if ENABLE_FEATURE_MINIX2
+		version2 = 1;
+#else
+		bb_error_msg_and_die("not compiled with minix v2 support");
+#endif
+	}
+
+	G.device_name = *argv++;
+	if (!G.device_name)
+		bb_show_usage();
+	if (*argv)
+		G.total_blocks = xatou32(*argv);
+	else
+		G.total_blocks = get_size(G.device_name) / 1024;
+
+	if (G.total_blocks < 10)
+		bb_error_msg_and_die("must have at least 10 blocks");
+
+	if (version2) {
+		G.magic = MINIX2_SUPER_MAGIC2;
+		if (G.namelen == 14)
+			G.magic = MINIX2_SUPER_MAGIC;
+	} else if (G.total_blocks > 65535)
+		G.total_blocks = 65535;
+
+	/* Check if it is mounted */
+	if (find_mount_point(G.device_name, 0))
+		bb_error_msg_and_die("can't format mounted filesystem");
+
+	xmove_fd(xopen(G.device_name, O_RDWR), dev_fd);
+	xfstat(dev_fd, &statbuf, G.device_name);
+	if (!S_ISBLK(statbuf.st_mode))
+		opt &= ~1; // clear -c (check)
+
+/* I don't know why someone has special code to prevent mkfs.minix
+ * on IDE devices. Why IDE but not SCSI, etc?... */
+#if 0
+	else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
+		/* what is this? */
+		bb_error_msg_and_die("will not try "
+			"to make filesystem on '%s'", G.device_name);
+#endif
+
+	tmp = G.root_block;
+	*(short *) tmp = 1;
+	strcpy(tmp + 2, ".");
+	tmp += G.dirsize;
+	*(short *) tmp = 1;
+	strcpy(tmp + 2, "..");
+	tmp += G.dirsize;
+	*(short *) tmp = 2;
+	strcpy(tmp + 2, ".badblocks");
+
+	setup_tables();
+
+	if (opt & 1) // -c ?
+		check_blocks();
+	else if (listfile)
+		get_list_blocks(listfile);
+
+	if (version2) {
+		make_root_inode2();
+		make_bad_inode2();
+	} else {
+		make_root_inode();
+		make_bad_inode();
+	}
+
+	mark_good_blocks();
+	write_tables();
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/mkfs_reiser.c b/ap/app/busybox/src/util-linux/mkfs_reiser.c
new file mode 100644
index 0000000..b4efb9e
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/mkfs_reiser.c
@@ -0,0 +1,375 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfs_reiser: utility to create ReiserFS filesystem
+ *
+ * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define mkfs_reiser_trivial_usage
+//usage:       "[-f] [-l LABEL] BLOCKDEV [4K-BLOCKS]"
+//usage:#define mkfs_reiser_full_usage "\n\n"
+//usage:       "Make a ReiserFS V3 filesystem\n"
+//usage:     "\n	-f	Force"
+//usage:     "\n	-l LBL	Volume label"
+
+#include "libbb.h"
+#include <linux/fs.h>
+
+char BUG_wrong_field_size(void);
+#define STORE_LE(field, value) \
+do { \
+	if (sizeof(field) == 4) \
+		field = SWAP_LE32(value); \
+	else if (sizeof(field) == 2) \
+		field = SWAP_LE16(value); \
+	else if (sizeof(field) == 1) \
+		field = (value); \
+	else \
+		BUG_wrong_field_size(); \
+} while (0)
+
+#define FETCH_LE32(field) \
+	(sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size())
+
+struct journal_params {
+	uint32_t jp_journal_1st_block;      /* where does journal start from on its device */
+	uint32_t jp_journal_dev;            /* journal device st_rdev */
+	uint32_t jp_journal_size;           /* size of the journal on FS creation. used to make sure they don't overflow it */
+	uint32_t jp_journal_trans_max;      /* max number of blocks in a transaction.  */
+	uint32_t jp_journal_magic;          /* random value made on fs creation (this was sb_journal_block_count) */
+	uint32_t jp_journal_max_batch;      /* max number of blocks to batch into a trans */
+	uint32_t jp_journal_max_commit_age; /* in seconds, how old can an async commit be */
+	uint32_t jp_journal_max_trans_age;  /* in seconds, how old can a transaction be */
+};
+
+struct reiserfs_journal_header {
+	uint32_t jh_last_flush_trans_id;    /* id of last fully flushed transaction */
+	uint32_t jh_first_unflushed_offset; /* offset in the log of where to start replay after a crash */
+	uint32_t jh_mount_id;
+	struct journal_params jh_journal;
+	uint32_t jh_last_check_mount_id;    /* the mount id of the fs during the last reiserfsck --check. */
+};
+
+struct reiserfs_super_block {
+	uint32_t sb_block_count;            /* 0 number of block on data device */
+	uint32_t sb_free_blocks;            /* 4 free blocks count */
+	uint32_t sb_root_block;             /* 8 root of the tree */
+
+	struct journal_params sb_journal;   /* 12 */
+
+	uint16_t sb_blocksize;          /* 44 */
+	uint16_t sb_oid_maxsize;        /* 46 max size of object id array, see get_objectid() commentary */
+	uint16_t sb_oid_cursize;        /* 48 current size of object id array */
+	uint16_t sb_umount_state;       /* 50 this is set to 1 when filesystem was umounted, to 2 - when not */
+
+	char s_magic[10];               /* 52 "ReIsErFs" or "ReIsEr2Fs" or "ReIsEr3Fs" */
+	uint16_t sb_fs_state;           /* 62 it is set to used by fsck to mark which phase of rebuilding is done (used for fsck debugging) */
+	uint32_t sb_hash_function_code; /* 64 code of fuction which was/is/will be used to sort names in a directory. See codes in above */
+	uint16_t sb_tree_height;        /* 68 height of filesytem tree. Tree consisting of only one root block has 2 here */
+	uint16_t sb_bmap_nr;            /* 70 amount of bitmap blocks needed to address each block of file system */
+	uint16_t sb_version;            /* 72 this field is only reliable on filesystem with non-standard journal */
+	uint16_t sb_reserved_for_journal;  /* 74 size in blocks of journal area on main device, we need to keep after non-standard journal relocation */
+	uint32_t sb_inode_generation;   /* 76 */
+	uint32_t sb_flags;              /* 80 Right now used only by inode-attributes, if enabled */
+	unsigned char s_uuid[16];       /* 84 filesystem unique identifier */
+	unsigned char s_label[16];      /* 100 filesystem volume label */
+	uint16_t sb_mnt_count;          /* 116 */
+	uint16_t sb_max_mnt_count;      /* 118 */
+	uint32_t sb_lastcheck;          /* 120 */
+	uint32_t sb_check_interval;     /* 124 */
+/* zero filled by mkreiserfs and reiserfs_convert_objectid_map_v1() so any additions must be updated there as well. */
+	char s_unused[76];              /* 128 */
+	/* 204 */
+};
+
+/* Header of a disk block.  More precisely, header of a formatted leaf
+   or internal node, and not the header of an unformatted node. */
+struct block_head {
+	uint16_t blk2_level;        /* Level of a block in the tree. */
+	uint16_t blk2_nr_item;      /* Number of keys/items in a block. */
+	uint16_t blk2_free_space;   /* Block free space in bytes. */
+	uint16_t blk_reserved;
+	uint32_t reserved[4];
+};
+
+#define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024)
+
+#define REISERFS_3_6_SUPER_MAGIC_STRING "ReIsEr2Fs"
+#define REISERFS_FORMAT_3_6     2
+#define DEFAULT_MAX_MNT_COUNT   30                      /* 30 mounts */
+#define DEFAULT_CHECK_INTERVAL  (180 * 60 * 60 * 24)    /* 180 days */
+
+#define FS_CLEANLY_UMOUNTED     1 /* this was REISERFS_VALID_FS */
+
+#define JOURNAL_MIN_SIZE        512
+/* biggest possible single transaction, don't change for now (8/3/99) */
+#define JOURNAL_TRANS_MAX       1024
+#define JOURNAL_TRANS_MIN       256     /* need to check whether it works */
+#define JOURNAL_DEFAULT_RATIO   8       /* default journal size / max trans length */
+#define JOURNAL_MIN_RATIO       2
+/* max blocks to batch into one transaction, don't make this any bigger than 900 */
+#define JOURNAL_MAX_BATCH       900
+#define JOURNAL_MAX_COMMIT_AGE  30
+
+
+// Standard mkreiserfs 3.6.21:
+//   -b | --block-size N              size of file-system block, in bytes
+//   -j | --journal-device FILE       path to separate device to hold journal
+//   -s | --journal-size N            size of the journal in blocks
+//   -o | --journal-offset N          offset of the journal from the start of
+//                                    the separate device, in blocks
+//   -t | --transaction-max-size N    maximal size of transaction, in blocks
+//   -B | --badblocks file            store all bad blocks given in file on the fs
+//   -h | --hash rupasov|tea|r5       hash function to use by default
+//   -u | --uuid UUID                 store UUID in the superblock
+//   -l | --label LABEL               store LABEL in the superblock
+//   --format 3.5|3.6                 old 3.5 format or newer 3.6
+//   -f | --force                     specified once, make mkreiserfs the whole
+//                                    disk, not block device or mounted partition;
+//                                    specified twice, do not ask for confirmation
+//   -q | --quiet                     quiet work without messages, progress and
+//                                    questions. Useful if run in a script. For use
+//                                    by end users only.
+//   -d | --debug                     print debugging information during mkreiser
+//   -V                               print version and exit
+
+// Options not commented below are taken but silently ignored:
+enum {
+	OPT_b = 1 << 0,
+	OPT_j = 1 << 1,
+	OPT_s = 1 << 2,
+	OPT_o = 1 << 3,
+	OPT_t = 1 << 4,
+	OPT_B = 1 << 5,
+	OPT_h = 1 << 6,
+	OPT_u = 1 << 7,
+	OPT_l = 1 << 8,		// label
+	OPT_f = 1 << 9,		// ask no questions
+	OPT_q = 1 << 10,
+	OPT_d = 1 << 11,
+	//OPT_V = 1 << 12,	// -V version. bbox applets don't support that
+};
+
+int mkfs_reiser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mkfs_reiser_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned blocksize = 4096;
+	unsigned journal_blocks = 8192;
+	unsigned blocks, bitmap_blocks, i, block;
+	time_t timestamp;
+	const char *label = "";
+	struct stat st;
+	int fd;
+	uint8_t *buf;
+	struct reiserfs_super_block *sb;
+	struct journal_params *jp;
+	struct block_head *root;
+
+	// using global "option_mask32" instead of local "opts":
+	// we are register starved here
+	opt_complementary = "-1:b+";
+	/*opts =*/ getopt32(argv, "b:j:s:o:t:B:h:u:l:fqd",
+		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &label);
+	argv += optind; // argv[0] -- device
+
+	// check the device is a block device
+	fd = xopen(argv[0], O_WRONLY | O_EXCL);
+	xfstat(fd, &st, argv[0]);
+	if (!S_ISBLK(st.st_mode) && !(option_mask32 & OPT_f))
+		bb_error_msg_and_die("%s: not a block device", argv[0]);
+
+	// check if it is mounted
+	// N.B. what if we format a file? find_mount_point will return false negative since
+	// it is loop block device which is mounted!
+	if (find_mount_point(argv[0], 0))
+		bb_error_msg_and_die("can't format mounted filesystem");
+
+	// open the device, get size in blocks
+	blocks = get_volume_size_in_bytes(fd, argv[1], blocksize, /*extend:*/ 1) / blocksize;
+
+	// block number sanity check
+	// we have a limit: skipped area, super block, journal and root block
+	// all have to be addressed by one first bitmap
+	block = REISERFS_DISK_OFFSET_IN_BYTES / blocksize // boot area
+		+ 1		// sb
+		+ 1		// bitmap#0
+		+ journal_blocks+1	// journal
+	;
+
+	// count overhead
+	bitmap_blocks = (blocks - 1) / (blocksize * 8) + 1;
+	i = block + bitmap_blocks;
+
+	// check overhead
+	if (MIN(blocksize * 8, blocks) < i)
+		bb_error_msg_and_die("need >= %u blocks", i);
+
+	// ask confirmation?
+	// TODO: ???
+
+	// wipe out first REISERFS_DISK_OFFSET_IN_BYTES of device
+	// TODO: do we really need to wipe?!
+	xlseek(fd, REISERFS_DISK_OFFSET_IN_BYTES, SEEK_SET);
+
+	// fill superblock
+	sb = (struct reiserfs_super_block *)xzalloc(blocksize);
+	// block count
+	STORE_LE(sb->sb_block_count, blocks);
+	STORE_LE(sb->sb_free_blocks, blocks - i);
+	// TODO: decypher!
+	STORE_LE(sb->sb_root_block, block);
+	// fill journal related fields
+	jp = &sb->sb_journal;
+	STORE_LE(jp->jp_journal_1st_block, REISERFS_DISK_OFFSET_IN_BYTES / blocksize + 1/*sb*/ + 1/*bmp#0*/);
+	timestamp = time(NULL);
+	srandom(timestamp);
+	STORE_LE(jp->jp_journal_magic, random());
+	STORE_LE(jp->jp_journal_size, journal_blocks);
+	STORE_LE(jp->jp_journal_trans_max, JOURNAL_TRANS_MAX);
+	STORE_LE(jp->jp_journal_max_batch, JOURNAL_MAX_BATCH);
+	STORE_LE(jp->jp_journal_max_commit_age, JOURNAL_MAX_COMMIT_AGE);
+	// sizes
+	STORE_LE(sb->sb_blocksize, blocksize);
+	STORE_LE(sb->sb_oid_maxsize, (blocksize - sizeof(*sb)) / sizeof(uint32_t) / 2 * 2);
+	STORE_LE(sb->sb_oid_cursize, 2); // "." and ".."
+	strcpy(sb->s_magic, REISERFS_3_6_SUPER_MAGIC_STRING);
+	STORE_LE(sb->sb_bmap_nr, (bitmap_blocks > ((1LL << 16) - 1)) ? 0 : bitmap_blocks);
+	// misc
+	STORE_LE(sb->sb_version, REISERFS_FORMAT_3_6);
+	STORE_LE(sb->sb_lastcheck, timestamp);
+	STORE_LE(sb->sb_check_interval, DEFAULT_CHECK_INTERVAL);
+	STORE_LE(sb->sb_mnt_count, 1);
+	STORE_LE(sb->sb_max_mnt_count, DEFAULT_MAX_MNT_COUNT);
+	STORE_LE(sb->sb_umount_state, FS_CLEANLY_UMOUNTED);
+	STORE_LE(sb->sb_tree_height, 2);
+	STORE_LE(sb->sb_hash_function_code, 3); // R5_HASH
+	STORE_LE(sb->sb_flags, 1);
+	//STORE_LE(sb->sb_reserved_for_journal, 0);
+	// create UUID
+	generate_uuid(sb->s_uuid);
+	// write the label
+	safe_strncpy((char *)sb->s_label, label, sizeof(sb->s_label));
+
+	// TODO: EMPIRIC! ENDIANNESS!
+	// superblock has only 204 bytes. What are these?
+	buf = (uint8_t *)sb;
+	buf[205] = 1;
+	buf[209] = 3;
+
+	// put superblock
+	xwrite(fd, sb, blocksize);
+
+	// create bitmaps
+	buf = xzalloc(blocksize);
+
+	// bitmap #0 uses initial "block"+1 blocks
+	i = block + 1;
+	memset(buf, 0xFF, i / 8);
+	buf[i / 8] = (1 << (i & 7)) - 1; //0..7 => 00000000..01111111
+	// mark trailing absent blocks, if any
+	if (blocks < 8*blocksize) {
+		unsigned n = 8*blocksize - blocks;
+		i = n / 8;
+		buf[blocksize - i - 1] |= 0x7F00 >> (n & 7); //0..7 => 00000000..11111110
+		memset(buf + blocksize - i, 0xFF, i); // N.B. no overflow here!
+	}
+	// put bitmap #0
+	xwrite(fd, buf, blocksize);
+
+	// now go journal blocks
+	memset(buf, 0, blocksize);
+	for (i = 0; i < journal_blocks; i++)
+		xwrite(fd, buf, blocksize);
+	// dump journal control block
+	memcpy(&((struct reiserfs_journal_header *)buf)->jh_journal, &sb->sb_journal, sizeof(sb->sb_journal));
+	xwrite(fd, buf, blocksize);
+
+	// other bitmaps are in every (8*blocksize)-th block
+	// N.B. they use the only block -- namely bitmap itself!
+	buf[0] = 0x01;
+	// put bitmaps
+	for (i = 1; i < bitmap_blocks; i++) {
+		xlseek(fd, i*8*blocksize * blocksize, SEEK_SET);
+		// mark trailing absent blocks, if any
+		if (i == bitmap_blocks - 1 && (blocks % (8*blocksize))) {
+			unsigned n = 8*blocksize - blocks % (8*blocksize);
+			unsigned j = n / 8;
+			buf[blocksize - j - 1] |= 0x7F00 >> (n & 7); //0..7 => 00000000..11111110
+			memset(buf + blocksize - j, 0xFF, j); // N.B. no overflow here!
+		}
+		xwrite(fd, buf, blocksize);
+	}
+
+	// fill root block
+	// block head
+	memset(buf, 0, blocksize);
+	root = (struct block_head *)buf;
+	STORE_LE(root->blk2_level, 1); // leaf node
+	STORE_LE(root->blk2_nr_item, 2); // "." and ".."
+	STORE_LE(root->blk2_free_space, blocksize - sizeof(struct block_head));
+	// item head
+	// root directory
+	// TODO: EMPIRIC! ENDIANNESS!
+	// TODO: indented assignments seem to be timestamps
+buf[4] = 0134;
+buf[24] = 01;
+buf[28] = 02;
+buf[42] = 054;
+buf[44] = 0324;
+buf[45] = 017;
+buf[46] = 01;
+buf[48] = 01;
+buf[52] = 02;
+buf[56] = 01;
+buf[60] = 0364;
+buf[61] = 01;
+buf[64] = 02;
+buf[66] = 060;
+buf[68] = 0244;
+buf[69] = 017;
+buf[4004] = 01;
+buf[4008] = 01;
+buf[4012] = 02;
+buf[4016] = 050;
+buf[4018] = 04;
+buf[4020] = 02;
+buf[4028] = 01;
+buf[4032] = 040;
+buf[4034] = 04;
+
+buf[4036] = 056; buf[4037] = 056;	// ".."
+buf[4044] = 056;			// "."
+
+buf[4052] = 0355;
+buf[4053] = 0101;
+buf[4056] = 03;
+buf[4060] = 060;
+		buf[4076] = 0173;
+		buf[4077] = 0240;
+	buf[4078] = 0344;
+	buf[4079] = 0112;
+		buf[4080] = 0173;
+		buf[4081] = 0240;
+	buf[4082] = 0344;
+	buf[4083] = 0112;
+		buf[4084] = 0173;
+		buf[4085] = 0240;
+	buf[4086] = 0344;
+	buf[4087] = 0112;
+buf[4088] = 01;
+
+	// put root block
+	xlseek(fd, block * blocksize, SEEK_SET);
+	xwrite(fd, buf, blocksize);
+
+	// cleanup
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(buf);
+		free(sb);
+	}
+
+	xclose(fd);
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/mkfs_vfat.c b/ap/app/busybox/src/util-linux/mkfs_vfat.c
new file mode 100644
index 0000000..7d81ed0
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/mkfs_vfat.c
@@ -0,0 +1,630 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfs_vfat: utility to create FAT32 filesystem
+ * inspired by dosfstools
+ *
+ * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define mkfs_vfat_trivial_usage
+//usage:       "[-v] [-n LABEL] BLOCKDEV [KBYTES]"
+/* Accepted but ignored:
+       "[-c] [-C] [-I] [-l bad-block-file] [-b backup-boot-sector] "
+       "[-m boot-msg-file] [-i volume-id] "
+       "[-s sectors-per-cluster] [-S logical-sector-size] [-f number-of-FATs] "
+       "[-h hidden-sectors] [-F fat-size] [-r root-dir-entries] [-R reserved-sectors] "
+*/
+//usage:#define mkfs_vfat_full_usage "\n\n"
+//usage:       "Make a FAT32 filesystem\n"
+/* //usage:  "\n	-c	Check device for bad blocks" */
+//usage:     "\n	-v	Verbose"
+/* //usage:  "\n	-I	Allow to use entire disk device (e.g. /dev/hda)" */
+//usage:     "\n	-n LBL	Volume label"
+
+#include "libbb.h"
+
+#include <linux/hdreg.h> /* HDIO_GETGEO */
+#include <linux/fd.h>    /* FDGETPRM */
+#include <sys/mount.h>   /* BLKSSZGET */
+#if !defined(BLKSSZGET)
+# define BLKSSZGET _IO(0x12, 104)
+#endif
+//#include <linux/msdos_fs.h>
+
+#define SECTOR_SIZE             512
+
+#define SECTORS_PER_BLOCK	(BLOCK_SIZE / SECTOR_SIZE)
+
+// M$ says the high 4 bits of a FAT32 FAT entry are reserved
+#define EOF_FAT32       0x0FFFFFF8
+#define BAD_FAT32       0x0FFFFFF7
+#define MAX_CLUST_32    0x0FFFFFF0
+
+#define ATTR_VOLUME     8
+
+#define NUM_FATS        2
+
+/* FAT32 filesystem looks like this:
+ * sector -nn...-1: "hidden" sectors, all sectors before this partition
+ * (-h hidden-sectors sets it. Useful only for boot loaders,
+ *  they need to know _disk_ offset in order to be able to correctly
+ *  address sectors relative to start of disk)
+ * sector 0: boot sector
+ * sector 1: info sector
+ * sector 2: set aside for boot code which didn't fit into sector 0
+ * ...(zero-filled sectors)...
+ * sector B: backup copy of sector 0 [B set by -b backup-boot-sector]
+ * sector B+1: backup copy of sector 1
+ * sector B+2: backup copy of sector 2
+ * ...(zero-filled sectors)...
+ * sector R: FAT#1 [R set by -R reserved-sectors]
+ * ...(FAT#1)...
+ * sector R+fat_size: FAT#2
+ * ...(FAT#2)...
+ * sector R+fat_size*2: cluster #2
+ * ...(cluster #2)...
+ * sector R+fat_size*2+clust_size: cluster #3
+ * ...(the rest is filled by clusters till the end)...
+ */
+
+enum {
+// Perhaps this should remain constant
+	info_sector_number = 1,
+// TODO: make these cmdline options
+// dont forget sanity check: backup_boot_sector + 3 <= reserved_sect
+	backup_boot_sector = 3,
+	reserved_sect      = 6,
+};
+
+// how many blocks we try to read while testing
+#define TEST_BUFFER_BLOCKS      16
+
+struct msdos_dir_entry {
+	char     name[11];       /* 000 name and extension */
+	uint8_t  attr;           /* 00b attribute bits */
+	uint8_t  lcase;          /* 00c case for base and extension */
+	uint8_t  ctime_cs;       /* 00d creation time, centiseconds (0-199) */
+	uint16_t ctime;          /* 00e creation time */
+	uint16_t cdate;          /* 010 creation date */
+	uint16_t adate;          /* 012 last access date */
+	uint16_t starthi;        /* 014 high 16 bits of cluster in FAT32 */
+	uint16_t time;           /* 016 time */
+	uint16_t date;           /* 018 date */
+	uint16_t start;          /* 01a first cluster */
+	uint32_t size;           /* 01c file size in bytes */
+} PACKED;
+
+/* Example of boot sector's beginning:
+0000  eb 58 90 4d 53 57 49 4e  34 2e 31 00 02 08 26 00  |...MSWIN4.1...&.|
+0010  02 00 00 00 00 f8 00 00  3f 00 ff 00 3f 00 00 00  |........?...?...|
+0020  54 9b d0 00 0d 34 00 00  00 00 00 00 02 00 00 00  |T....4..........|
+0030  01 00 06 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+0040  80 00 29 71 df 51 e0 4e  4f 20 4e 41 4d 45 20 20  |..)q.Q.NO NAME  |
+0050  20 20 46 41 54 33 32 20  20 20 33 c9 8e d1 bc f4  |  FAT32   3.....|
+*/
+struct msdos_volume_info { /* (offsets are relative to start of boot sector) */
+	uint8_t  drive_number;    /* 040 BIOS drive number */
+	uint8_t  reserved;        /* 041 unused */
+	uint8_t  ext_boot_sign;	  /* 042 0x29 if fields below exist (DOS 3.3+) */
+	uint32_t volume_id32;     /* 043 volume ID number */
+	char     volume_label[11];/* 047 volume label */
+	char     fs_type[8];      /* 052 typically "FATnn" */
+} PACKED;                         /* 05a end. Total size 26 (0x1a) bytes */
+
+struct msdos_boot_sector {
+	/* We use strcpy to fill both, and gcc-4.4.x complains if they are separate */
+	char     boot_jump_and_sys_id[3+8]; /* 000 short or near jump instruction */
+	/*char   system_id[8];*/     /* 003 name - can be used to special case partition manager volumes */
+	uint16_t bytes_per_sect;     /* 00b bytes per logical sector */
+	uint8_t  sect_per_clust;     /* 00d sectors/cluster */
+	uint16_t reserved_sect;      /* 00e reserved sectors (sector offset of 1st FAT relative to volume start) */
+	uint8_t  fats;               /* 010 number of FATs */
+	uint16_t dir_entries;        /* 011 root directory entries */
+	uint16_t volume_size_sect;   /* 013 volume size in sectors */
+	uint8_t  media_byte;         /* 015 media code */
+	uint16_t sect_per_fat;       /* 016 sectors/FAT */
+	uint16_t sect_per_track;     /* 018 sectors per track */
+	uint16_t heads;              /* 01a number of heads */
+	uint32_t hidden;             /* 01c hidden sectors (sector offset of volume within physical disk) */
+	uint32_t fat32_volume_size_sect; /* 020 volume size in sectors (if volume_size_sect == 0) */
+	uint32_t fat32_sect_per_fat; /* 024 sectors/FAT */
+	uint16_t fat32_flags;        /* 028 bit 8: fat mirroring, low 4: active fat */
+	uint8_t  fat32_version[2];   /* 02a major, minor filesystem version (I see 0,0) */
+	uint32_t fat32_root_cluster; /* 02c first cluster in root directory */
+	uint16_t fat32_info_sector;  /* 030 filesystem info sector (usually 1) */
+	uint16_t fat32_backup_boot;  /* 032 backup boot sector (usually 6) */
+	uint32_t reserved2[3];       /* 034 unused */
+	struct msdos_volume_info vi; /* 040 */
+	char     boot_code[0x200 - 0x5a - 2]; /* 05a */
+#define BOOT_SIGN 0xAA55
+	uint16_t boot_sign;          /* 1fe */
+} PACKED;
+
+#define FAT_FSINFO_SIG1 0x41615252
+#define FAT_FSINFO_SIG2 0x61417272
+struct fat32_fsinfo {
+	uint32_t signature1;         /* 0x52,0x52,0x41,0x61, "RRaA" */
+	uint32_t reserved1[128 - 8];
+	uint32_t signature2;         /* 0x72,0x72,0x61,0x41, "rrAa" */
+	uint32_t free_clusters;      /* free cluster count.  -1 if unknown */
+	uint32_t next_cluster;       /* most recently allocated cluster */
+	uint32_t reserved2[3];
+	uint16_t reserved3;          /* 1fc */
+	uint16_t boot_sign;          /* 1fe */
+} PACKED;
+
+struct bug_check {
+	char BUG1[sizeof(struct msdos_dir_entry  ) == 0x20 ? 1 : -1];
+	char BUG2[sizeof(struct msdos_volume_info) == 0x1a ? 1 : -1];
+	char BUG3[sizeof(struct msdos_boot_sector) == 0x200 ? 1 : -1];
+	char BUG4[sizeof(struct fat32_fsinfo     ) == 0x200 ? 1 : -1];
+};
+
+static const char boot_code[] ALIGN1 =
+	"\x0e"          /* 05a:         push  cs */
+	"\x1f"          /* 05b:         pop   ds */
+	"\xbe\x77\x7c"  /*  write_msg:  mov   si, offset message_txt */
+	"\xac"          /* 05f:         lodsb */
+	"\x22\xc0"      /* 060:         and   al, al */
+	"\x74\x0b"      /* 062:         jz    key_press */
+	"\x56"          /* 064:         push  si */
+	"\xb4\x0e"      /* 065:         mov   ah, 0eh */
+	"\xbb\x07\x00"  /* 067:         mov   bx, 0007h */
+	"\xcd\x10"      /* 06a:         int   10h */
+	"\x5e"          /* 06c:         pop   si */
+	"\xeb\xf0"      /* 06d:         jmp   write_msg */
+	"\x32\xe4"      /*  key_press:  xor   ah, ah */
+	"\xcd\x16"      /* 071:         int   16h */
+	"\xcd\x19"      /* 073:         int   19h */
+	"\xeb\xfe"      /*  foo:        jmp   foo */
+	/* 077: message_txt: */
+	"This is not a bootable disk\r\n";
+
+
+#define MARK_CLUSTER(cluster, value) \
+	((uint32_t *)fat)[cluster] = SWAP_LE32(value)
+
+void BUG_unsupported_field_size(void);
+#define STORE_LE(field, value) \
+do { \
+	if (sizeof(field) == 4) \
+		field = SWAP_LE32(value); \
+	else if (sizeof(field) == 2) \
+		field = SWAP_LE16(value); \
+	else if (sizeof(field) == 1) \
+		field = (value); \
+	else \
+		BUG_unsupported_field_size(); \
+} while (0)
+
+/* compat:
+ * mkdosfs 2.11 (12 Mar 2005)
+ * Usage: mkdosfs [-A] [-c] [-C] [-v] [-I] [-l bad-block-file]
+ *        [-b backup-boot-sector]
+ *        [-m boot-msg-file] [-n volume-name] [-i volume-id]
+ *        [-s sectors-per-cluster] [-S logical-sector-size]
+ *        [-f number-of-FATs]
+ *        [-h hidden-sectors] [-F fat-size] [-r root-dir-entries]
+ *        [-R reserved-sectors]
+ *        /dev/name [blocks]
+ */
+int mkfs_vfat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct stat st;
+	const char *volume_label = "";
+	char *buf;
+	char *device_name;
+	uoff_t volume_size_bytes;
+	uoff_t volume_size_sect;
+	uint32_t total_clust;
+	uint32_t volume_id;
+	int dev;
+	unsigned bytes_per_sect;
+	unsigned sect_per_fat;
+	unsigned opts;
+	uint16_t sect_per_track;
+	uint8_t media_byte;
+	uint8_t sect_per_clust;
+	uint8_t heads;
+	enum {
+		OPT_A = 1 << 0,  // [IGNORED] atari format
+		OPT_b = 1 << 1,	 // [IGNORED] location of backup boot sector
+		OPT_c = 1 << 2,	 // [IGNORED] check filesystem
+		OPT_C = 1 << 3,	 // [IGNORED] create a new file
+		OPT_f = 1 << 4,	 // [IGNORED] number of FATs
+		OPT_F = 1 << 5,	 // [IGNORED, implied 32] choose FAT size
+		OPT_h = 1 << 6,	 // [IGNORED] number of hidden sectors
+		OPT_I = 1 << 7,	 // [IGNORED] don't bark at entire disk devices
+		OPT_i = 1 << 8,	 // [IGNORED] volume ID
+		OPT_l = 1 << 9,	 // [IGNORED] bad block filename
+		OPT_m = 1 << 10, // [IGNORED] message file
+		OPT_n = 1 << 11, // volume label
+		OPT_r = 1 << 12, // [IGNORED] root directory entries
+		OPT_R = 1 << 13, // [IGNORED] number of reserved sectors
+		OPT_s = 1 << 14, // [IGNORED] sectors per cluster
+		OPT_S = 1 << 15, // [IGNORED] sector size
+		OPT_v = 1 << 16, // verbose
+	};
+
+	opt_complementary = "-1";//:b+:f+:F+:h+:r+:R+:s+:S+:vv:c--l:l--c";
+	opts = getopt32(argv, "Ab:cCf:F:h:Ii:l:m:n:r:R:s:S:v",
+		NULL, NULL, NULL, NULL, NULL,
+		NULL, NULL, &volume_label, NULL, NULL, NULL, NULL);
+	argv += optind;
+
+	// cache device name
+	device_name = argv[0];
+	// default volume ID = creation time
+	volume_id = time(NULL);
+
+	dev = xopen(device_name, O_RDWR);
+	xfstat(dev, &st, device_name);
+
+	//
+	// Get image size and sector size
+	//
+	bytes_per_sect = SECTOR_SIZE;
+	if (!S_ISBLK(st.st_mode)) {
+		if (!S_ISREG(st.st_mode)) {
+			if (!argv[1])
+				bb_error_msg_and_die("image size must be specified");
+		}
+		// not a block device, skip bad sectors check
+		opts &= ~OPT_c;
+	} else {
+		int min_bytes_per_sect;
+#if 0
+		unsigned device_num;
+		// for true block devices we do check sanity
+		device_num = st.st_rdev & 0xff3f;
+		// do we allow to format the whole disk device?
+		if (!(opts & OPT_I) && (
+			device_num == 0x0300 || // hda, hdb
+			(device_num & 0xff0f) == 0x0800 || // sd
+			device_num == 0x0d00 || // xd
+			device_num == 0x1600 )  // hdc, hdd
+		)
+			bb_error_msg_and_die("will not try to make filesystem on full-disk device (use -I if wanted)");
+		// can't work on mounted filesystems
+		if (find_mount_point(device_name, 0))
+			bb_error_msg_and_die("can't format mounted filesystem");
+#endif
+		// get true sector size
+		// (parameter must be int*, not long* or size_t*)
+		xioctl(dev, BLKSSZGET, &min_bytes_per_sect);
+		if (min_bytes_per_sect > SECTOR_SIZE) {
+			bytes_per_sect = min_bytes_per_sect;
+			bb_error_msg("for this device sector size is %u", min_bytes_per_sect);
+		}
+	}
+	volume_size_bytes = get_volume_size_in_bytes(dev, argv[1], 1024, /*extend:*/ 1);
+	volume_size_sect = volume_size_bytes / bytes_per_sect;
+
+	//
+	// Find out or guess media parameters
+	//
+	media_byte = 0xf8;
+	heads = 255;
+	sect_per_track = 63;
+	sect_per_clust = 1;
+	{
+		struct hd_geometry geometry;
+		// size (in sectors), sect (per track), head
+		struct floppy_struct param;
+
+		// N.B. whether to use HDIO_GETGEO or HDIO_REQ?
+		if (ioctl(dev, HDIO_GETGEO, &geometry) == 0
+		 && geometry.sectors
+		 && geometry.heads
+		) {
+			// hard drive
+			sect_per_track = geometry.sectors;
+			heads = geometry.heads;
+
+ set_cluster_size:
+			/* 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
+			 */
+			sect_per_clust = 1;
+			if (volume_size_bytes >= 260*1024*1024) {
+				sect_per_clust = 8;
+				/* fight gcc: */
+				/* "error: integer overflow in expression" */
+				/* "error: right shift count >= width of type" */
+				if (sizeof(off_t) > 4) {
+					unsigned t = (volume_size_bytes >> 31 >> 1);
+					if (t >= 8/4)
+						sect_per_clust = 16;
+					if (t >= 16/4)
+						sect_per_clust = 32;
+				}
+			}
+		} else {
+			// floppy, loop, or regular file
+			int not_floppy = ioctl(dev, FDGETPRM, &param);
+			if (not_floppy == 0) {
+				// floppy disk
+				sect_per_track = param.sect;
+				heads = param.head;
+				volume_size_sect = param.size;
+				volume_size_bytes = param.size * SECTOR_SIZE;
+			}
+			// setup the media descriptor byte
+			switch (volume_size_sect) {
+			case 2*360:	// 5.25", 2, 9, 40 - 360K
+				media_byte = 0xfd;
+				break;
+			case 2*720:	// 3.5", 2, 9, 80 - 720K
+			case 2*1200:	// 5.25", 2, 15, 80 - 1200K
+				media_byte = 0xf9;
+				break;
+			default:	// anything else
+				if (not_floppy)
+					goto set_cluster_size;
+			case 2*1440:	// 3.5", 2, 18, 80 - 1440K
+			case 2*2880:	// 3.5", 2, 36, 80 - 2880K
+				media_byte = 0xf0;
+				break;
+			}
+			// not floppy, but size matches floppy exactly.
+			// perhaps it is a floppy image.
+			// we already set media_byte as if it is a floppy,
+			// now set sect_per_track and heads.
+			heads = 2;
+			sect_per_track = (unsigned)volume_size_sect / 160;
+			if (sect_per_track < 9)
+				sect_per_track = 9;
+		}
+	}
+
+	//
+	// Calculate number of clusters, sectors/cluster, sectors/FAT
+	// (an initial guess for sect_per_clust should already be set)
+	//
+	// "mkdosfs -v -F 32 image5k 5" is the minimum:
+	// 2 sectors for FATs and 2 data sectors
+	if ((off_t)(volume_size_sect - reserved_sect) < 4)
+		bb_error_msg_and_die("the image is too small for FAT32");
+	sect_per_fat = 1;
+	while (1) {
+		while (1) {
+			int spf_adj;
+			uoff_t tcl = (volume_size_sect - reserved_sect - NUM_FATS * sect_per_fat) / sect_per_clust;
+			// tcl may be > MAX_CLUST_32 here, but it may be
+			// because sect_per_fat is underestimated,
+			// and with increased sect_per_fat it still may become
+			// <= MAX_CLUST_32. Therefore, we do not check
+			// against MAX_CLUST_32, but against a bigger const:
+			if (tcl > 0x80ffffff)
+				goto next;
+			total_clust = tcl; // fits in uint32_t
+			// Every cluster needs 4 bytes in FAT. +2 entries since
+			// FAT has space for non-existent clusters 0 and 1.
+			// Let's see how many sectors that needs.
+			//May overflow at "*4":
+			//spf_adj = ((total_clust+2) * 4 + bytes_per_sect-1) / bytes_per_sect - sect_per_fat;
+			//Same in the more obscure, non-overflowing form:
+			spf_adj = ((total_clust+2) + (bytes_per_sect/4)-1) / (bytes_per_sect/4) - sect_per_fat;
+#if 0
+			bb_error_msg("sect_per_clust:%u sect_per_fat:%u total_clust:%u",
+					sect_per_clust, sect_per_fat, (int)tcl);
+			bb_error_msg("adjust to sect_per_fat:%d", spf_adj);
+#endif
+			if (spf_adj <= 0) {
+				// do not need to adjust sect_per_fat.
+				// so, was total_clust too big after all?
+				if (total_clust <= MAX_CLUST_32)
+					goto found_total_clust; // no
+				// yes, total_clust is _a bit_ too big
+				goto next;
+			}
+			// adjust sect_per_fat, go back and recalc total_clust
+			// (note: just "sect_per_fat += spf_adj" isn't ok)
+			sect_per_fat += ((unsigned)spf_adj / 2) | 1;
+		}
+ next:
+		if (sect_per_clust == 128)
+			bb_error_msg_and_die("can't make FAT32 with >128 sectors/cluster");
+		sect_per_clust *= 2;
+		sect_per_fat = (sect_per_fat / 2) | 1;
+	}
+ found_total_clust:
+
+	//
+	// Print info
+	//
+	if (opts & OPT_v) {
+		fprintf(stderr,
+			"Device '%s':\n"
+			"heads:%u, sectors/track:%u, bytes/sector:%u\n"
+			"media descriptor:%02x\n"
+			"total sectors:%"OFF_FMT"u, clusters:%u, sectors/cluster:%u\n"
+			"FATs:2, sectors/FAT:%u\n"
+			"volumeID:%08x, label:'%s'\n",
+			device_name,
+			heads, sect_per_track, bytes_per_sect,
+			(int)media_byte,
+			volume_size_sect, (int)total_clust, (int)sect_per_clust,
+			sect_per_fat,
+			(int)volume_id, volume_label
+		);
+	}
+
+	//
+	// Write filesystem image sequentially (no seeking)
+	//
+	{
+		// (a | b) is poor man's max(a, b)
+		unsigned bufsize = reserved_sect;
+		//bufsize |= sect_per_fat; // can be quite large
+		bufsize |= 2; // use this instead
+		bufsize |= sect_per_clust;
+		buf = xzalloc(bufsize * bytes_per_sect);
+	}
+
+	{ // boot and fsinfo sectors, and their copies
+		struct msdos_boot_sector *boot_blk = (void*)buf;
+		struct fat32_fsinfo *info = (void*)(buf + bytes_per_sect);
+
+		strcpy(boot_blk->boot_jump_and_sys_id, "\xeb\x58\x90" "mkdosfs");
+		STORE_LE(boot_blk->bytes_per_sect, bytes_per_sect);
+		STORE_LE(boot_blk->sect_per_clust, sect_per_clust);
+		// cast in needed on big endian to suppress a warning
+		STORE_LE(boot_blk->reserved_sect, (uint16_t)reserved_sect);
+		STORE_LE(boot_blk->fats, 2);
+		//STORE_LE(boot_blk->dir_entries, 0); // for FAT32, stays 0
+		if (volume_size_sect <= 0xffff)
+			STORE_LE(boot_blk->volume_size_sect, volume_size_sect);
+		STORE_LE(boot_blk->media_byte, media_byte);
+		// wrong: this would make Linux think that it's fat12/16:
+		//if (sect_per_fat <= 0xffff)
+		//	STORE_LE(boot_blk->sect_per_fat, sect_per_fat);
+		// works:
+		//STORE_LE(boot_blk->sect_per_fat, 0);
+		STORE_LE(boot_blk->sect_per_track, sect_per_track);
+		STORE_LE(boot_blk->heads, heads);
+		//STORE_LE(boot_blk->hidden, 0);
+		STORE_LE(boot_blk->fat32_volume_size_sect, volume_size_sect);
+		STORE_LE(boot_blk->fat32_sect_per_fat, sect_per_fat);
+		//STORE_LE(boot_blk->fat32_flags, 0);
+		//STORE_LE(boot_blk->fat32_version[2], 0,0);
+		STORE_LE(boot_blk->fat32_root_cluster, 2);
+		STORE_LE(boot_blk->fat32_info_sector, info_sector_number);
+		STORE_LE(boot_blk->fat32_backup_boot, backup_boot_sector);
+		//STORE_LE(boot_blk->reserved2[3], 0,0,0);
+		STORE_LE(boot_blk->vi.ext_boot_sign, 0x29);
+		STORE_LE(boot_blk->vi.volume_id32, volume_id);
+		strncpy(boot_blk->vi.fs_type, "FAT32   ", sizeof(boot_blk->vi.fs_type));
+		strncpy(boot_blk->vi.volume_label, volume_label, sizeof(boot_blk->vi.volume_label));
+		memcpy(boot_blk->boot_code, boot_code, sizeof(boot_code));
+		STORE_LE(boot_blk->boot_sign, BOOT_SIGN);
+
+		STORE_LE(info->signature1, FAT_FSINFO_SIG1);
+		STORE_LE(info->signature2, FAT_FSINFO_SIG2);
+		// we've allocated cluster 2 for the root dir
+		STORE_LE(info->free_clusters, (total_clust - 1));
+		STORE_LE(info->next_cluster, 2);
+		STORE_LE(info->boot_sign, BOOT_SIGN);
+
+		// 1st copy
+		xwrite(dev, buf, bytes_per_sect * backup_boot_sector);
+		// 2nd copy and possibly zero sectors
+		xwrite(dev, buf, bytes_per_sect * (reserved_sect - backup_boot_sector));
+	}
+
+	{ // file allocation tables
+		unsigned i,j;
+		unsigned char *fat = (void*)buf;
+
+		memset(buf, 0, bytes_per_sect * 2);
+		// initial FAT entries
+		MARK_CLUSTER(0, 0x0fffff00 | media_byte);
+		MARK_CLUSTER(1, 0xffffffff);
+		// mark cluster 2 as EOF (used for root dir)
+		MARK_CLUSTER(2, EOF_FAT32);
+		for (i = 0; i < NUM_FATS; i++) {
+			xwrite(dev, buf, bytes_per_sect);
+			for (j = 1; j < sect_per_fat; j++)
+				xwrite(dev, buf + bytes_per_sect, bytes_per_sect);
+		}
+	}
+
+	// root directory
+	// empty directory is just a set of zero bytes
+	memset(buf, 0, sect_per_clust * bytes_per_sect);
+	if (volume_label[0]) {
+		// create dir entry for volume_label
+		struct msdos_dir_entry *de;
+#if 0
+		struct tm tm_time;
+		uint16_t t, d;
+#endif
+		de = (void*)buf;
+		strncpy(de->name, volume_label, sizeof(de->name));
+		STORE_LE(de->attr, ATTR_VOLUME);
+#if 0
+		localtime_r(&create_time, &tm_time);
+		t = (tm_time.tm_sec >> 1) + (tm_time.tm_min << 5) + (tm_time.tm_hour << 11);
+		d = tm_time.tm_mday + ((tm_time.tm_mon+1) << 5) + ((tm_time.tm_year-80) << 9);
+		STORE_LE(de->time, t);
+		STORE_LE(de->date, d);
+		//STORE_LE(de->ctime_cs, 0);
+		de->ctime = de->time;
+		de->cdate = de->date;
+		de->adate = de->date;
+#endif
+	}
+	xwrite(dev, buf, sect_per_clust * bytes_per_sect);
+
+#if 0
+	if (opts & OPT_c) {
+		uoff_t volume_size_blocks;
+		unsigned start_data_sector;
+		unsigned start_data_block;
+		unsigned badblocks = 0;
+		int try, got;
+		off_t currently_testing;
+		char *blkbuf = xmalloc(BLOCK_SIZE * TEST_BUFFER_BLOCKS);
+
+		volume_size_blocks = (volume_size_bytes >> BLOCK_SIZE_BITS);
+		// N.B. the two following vars are in hard sectors, i.e. SECTOR_SIZE byte sectors!
+		start_data_sector = (reserved_sect + NUM_FATS * sect_per_fat) * (bytes_per_sect / SECTOR_SIZE);
+		start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / SECTORS_PER_BLOCK;
+
+		bb_info_msg("searching for bad blocks ");
+		currently_testing = 0;
+		try = TEST_BUFFER_BLOCKS;
+		while (currently_testing < volume_size_blocks) {
+			if (currently_testing + try > volume_size_blocks)
+				try = volume_size_blocks - currently_testing;
+			// perform a test on a block. return the number of blocks
+			// that could be read successfully.
+			// seek to the correct location
+			xlseek(dev, currently_testing * BLOCK_SIZE, SEEK_SET);
+			// try reading
+			got = read(dev, blkbuf, try * BLOCK_SIZE);
+			if (got < 0)
+				got = 0;
+			if (got & (BLOCK_SIZE - 1))
+				bb_error_msg("unexpected values in do_check: probably bugs");
+			got /= BLOCK_SIZE;
+			currently_testing += got;
+			if (got == try) {
+				try = TEST_BUFFER_BLOCKS;
+				continue;
+			}
+			try = 1;
+			if (currently_testing < start_data_block)
+				bb_error_msg_and_die("bad blocks before data-area: cannot make fs");
+
+			// mark all of the sectors in the block as bad
+			for (i = 0; i < SECTORS_PER_BLOCK; i++) {
+				int cluster = (currently_testing * SECTORS_PER_BLOCK + i - start_data_sector) / (int) (sect_per_clust) / (bytes_per_sect / SECTOR_SIZE);
+				if (cluster < 0)
+					bb_error_msg_and_die("invalid cluster number in mark_sector: probably bug!");
+				MARK_CLUSTER(cluster, BAD_FAT32);
+			}
+			badblocks++;
+			currently_testing++;
+		}
+		free(blkbuf);
+		if (badblocks)
+			bb_info_msg("%d bad block(s)", badblocks);
+	}
+#endif
+
+	// cleanup
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(buf);
+		close(dev);
+	}
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/mkswap.c b/ap/app/busybox/src/util-linux/mkswap.c
new file mode 100644
index 0000000..b5d2c49
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/mkswap.c
@@ -0,0 +1,149 @@
+/* vi: set sw=4 ts=4: */
+/* mkswap.c - format swap device (Linux v1 only)
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define mkswap_trivial_usage
+//usage:       "[-L LBL] BLOCKDEV [KBYTES]"
+//usage:#define mkswap_full_usage "\n\n"
+//usage:       "Prepare BLOCKDEV to be used as swap partition\n"
+//usage:     "\n	-L LBL	Label"
+
+#include "libbb.h"
+
+#if ENABLE_SELINUX
+static void mkswap_selinux_setcontext(int fd, const char *path)
+{
+	struct stat stbuf;
+
+	if (!is_selinux_enabled())
+		return;
+
+	xfstat(fd, &stbuf, path);
+	if (S_ISREG(stbuf.st_mode)) {
+		security_context_t newcon;
+		security_context_t oldcon = NULL;
+		context_t context;
+
+		if (fgetfilecon(fd, &oldcon) < 0) {
+			if (errno != ENODATA)
+				goto error;
+			if (matchpathcon(path, stbuf.st_mode, &oldcon) < 0)
+				goto error;
+		}
+		context = context_new(oldcon);
+		if (!context || context_type_set(context, "swapfile_t"))
+			goto error;
+		newcon = context_str(context);
+		if (!newcon)
+			goto error;
+		/* fsetfilecon_raw is hidden */
+		if (strcmp(oldcon, newcon) != 0 && fsetfilecon(fd, newcon) < 0)
+			goto error;
+		if (ENABLE_FEATURE_CLEAN_UP) {
+			context_free(context);
+			freecon(oldcon);
+		}
+	}
+	return;
+ error:
+	bb_perror_msg_and_die("SELinux relabeling failed");
+}
+#else
+# define mkswap_selinux_setcontext(fd, path) ((void)0)
+#endif
+
+/* from Linux 2.6.23 */
+/*
+ * Magic header for a swap area. ... Note that the first
+ * kilobyte is reserved for boot loader or disk label stuff.
+ */
+struct swap_header_v1 {
+/*	char     bootbits[1024];    Space for disklabel etc. */
+	uint32_t version;        /* second kbyte, word 0 */
+	uint32_t last_page;      /* 1 */
+	uint32_t nr_badpages;    /* 2 */
+	char     sws_uuid[16];   /* 3,4,5,6 */
+	char     sws_volume[16]; /* 7,8,9,10 */
+	uint32_t padding[117];   /* 11..127 */
+	uint32_t badpages[1];    /* 128 */
+	/* total 129 32-bit words in 2nd kilobyte */
+} FIX_ALIASING;
+
+#define NWORDS 129
+#define hdr ((struct swap_header_v1*)bb_common_bufsiz1)
+
+struct BUG_sizes {
+	char swap_header_v1_wrong[sizeof(*hdr)  != (NWORDS * 4) ? -1 : 1];
+	char bufsiz1_is_too_small[COMMON_BUFSIZE < (NWORDS * 4) ? -1 : 1];
+};
+
+/* Stored without terminating NUL */
+static const char SWAPSPACE2[sizeof("SWAPSPACE2")-1] ALIGN1 = "SWAPSPACE2";
+
+int mkswap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mkswap_main(int argc UNUSED_PARAM, char **argv)
+{
+	int fd;
+	unsigned pagesize;
+	off_t len;
+	const char *label = "";
+
+	opt_complementary = "-1"; /* at least one param */
+	/* TODO: -p PAGESZ, -U UUID */
+	getopt32(argv, "L:", &label);
+	argv += optind;
+
+	fd = xopen(argv[0], O_WRONLY);
+
+	/* Figure out how big the device is */
+	len = get_volume_size_in_bytes(fd, argv[1], 1024, /*extend:*/ 1);
+	pagesize = getpagesize();
+	len -= pagesize;
+
+	/* Announce our intentions */
+	printf("Setting up swapspace version 1, size = %"OFF_FMT"u bytes\n", len);
+	mkswap_selinux_setcontext(fd, argv[0]);
+
+	/* hdr is zero-filled so far. Clear the first kbyte, or else
+	 * mkswap-ing former FAT partition does NOT erase its signature.
+	 *
+	 * util-linux-ng 2.17.2 claims to erase it only if it does not see
+	 * a partition table and is not run on whole disk. -f forces it.
+	 */
+	xwrite(fd, hdr, 1024);
+
+	/* Fill the header. */
+	hdr->version = 1;
+	hdr->last_page = (uoff_t)len / pagesize;
+
+	if (ENABLE_FEATURE_MKSWAP_UUID) {
+		char uuid_string[32];
+		generate_uuid((void*)hdr->sws_uuid);
+		bin2hex(uuid_string, hdr->sws_uuid, 16);
+		/* f.e. UUID=dfd9c173-be52-4d27-99a5-c34c6c2ff55f */
+		printf("UUID=%.8s"  "-%.4s-%.4s-%.4s-%.12s\n",
+			uuid_string,
+			uuid_string+8,
+			uuid_string+8+4,
+			uuid_string+8+4+4,
+			uuid_string+8+4+4+4
+		);
+	}
+	safe_strncpy(hdr->sws_volume, label, 16);
+
+	/* Write the header.  Sync to disk because some kernel versions check
+	 * signature on disk (not in cache) during swapon. */
+	xwrite(fd, hdr, NWORDS * 4);
+	xlseek(fd, pagesize - 10, SEEK_SET);
+	xwrite(fd, SWAPSPACE2, 10);
+	fsync(fd);
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(fd);
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/more.c b/ap/app/busybox/src/util-linux/more.c
new file mode 100644
index 0000000..3595713
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/more.c
@@ -0,0 +1,212 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini more implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Latest version blended together by Erik Andersen <andersen@codepoet.org>,
+ * based on the original more implementation by Bruce, and code from the
+ * Debian boot-floppies team.
+ *
+ * Termios corrects by Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define more_trivial_usage
+//usage:       "[FILE]..."
+//usage:#define more_full_usage "\n\n"
+//usage:       "View FILE (or stdin) one screenful at a time"
+//usage:
+//usage:#define more_example_usage
+//usage:       "$ dmesg | more\n"
+
+#include "libbb.h"
+
+/* Support for FEATURE_USE_TERMIOS */
+
+struct globals {
+	int cin_fileno;
+	struct termios initial_settings;
+	struct termios new_settings;
+} FIX_ALIASING;
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() ((void)0)
+#define initial_settings (G.initial_settings)
+#define new_settings     (G.new_settings    )
+#define cin_fileno       (G.cin_fileno      )
+
+#define setTermSettings(fd, argp) \
+do { \
+	if (ENABLE_FEATURE_USE_TERMIOS) \
+		tcsetattr(fd, TCSANOW, argp); \
+} while (0)
+#define getTermSettings(fd, argp) tcgetattr(fd, argp)
+
+static void gotsig(int sig UNUSED_PARAM)
+{
+	/* bb_putchar_stderr doesn't use stdio buffering,
+	 * therefore it is safe in signal handler */
+	bb_putchar_stderr('\n');
+	setTermSettings(cin_fileno, &initial_settings);
+	_exit(EXIT_FAILURE);
+}
+
+#define CONVERTED_TAB_SIZE 8
+
+int more_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int more_main(int argc UNUSED_PARAM, char **argv)
+{
+	int c = c; /* for compiler */
+	int lines;
+	int input = 0;
+	int spaces = 0;
+	int please_display_more_prompt;
+	struct stat st;
+	FILE *file;
+	FILE *cin;
+	int len;
+	unsigned terminal_width;
+	unsigned terminal_height;
+
+	INIT_G();
+
+	argv++;
+	/* Another popular pager, most, detects when stdout
+	 * is not a tty and turns into cat. This makes sense. */
+	if (!isatty(STDOUT_FILENO))
+		return bb_cat(argv);
+	cin = fopen_for_read(CURRENT_TTY);
+	if (!cin)
+		return bb_cat(argv);
+
+	if (ENABLE_FEATURE_USE_TERMIOS) {
+		cin_fileno = fileno(cin);
+		getTermSettings(cin_fileno, &initial_settings);
+		new_settings = initial_settings;
+		new_settings.c_lflag &= ~(ICANON | ECHO);
+		new_settings.c_cc[VMIN] = 1;
+		new_settings.c_cc[VTIME] = 0;
+		setTermSettings(cin_fileno, &new_settings);
+		bb_signals(0
+			+ (1 << SIGINT)
+			+ (1 << SIGQUIT)
+			+ (1 << SIGTERM)
+			, gotsig);
+	}
+
+	do {
+		file = stdin;
+		if (*argv) {
+			file = fopen_or_warn(*argv, "r");
+			if (!file)
+				continue;
+		}
+		st.st_size = 0;
+		fstat(fileno(file), &st);
+
+		please_display_more_prompt = 0;
+		/* never returns w, h <= 1 */
+		get_terminal_width_height(fileno(cin), &terminal_width, &terminal_height);
+		terminal_height -= 1;
+
+		len = 0;
+		lines = 0;
+		while (spaces || (c = getc(file)) != EOF) {
+			int wrap;
+			if (spaces)
+				spaces--;
+ loop_top:
+			if (input != 'r' && please_display_more_prompt) {
+				len = printf("--More-- ");
+				if (st.st_size != 0) {
+					uoff_t d = (uoff_t)st.st_size / 100;
+					if (d == 0)
+						d = 1;
+					len += printf("(%u%% of %"OFF_FMT"u bytes)",
+						(int) ((uoff_t)ftello(file) / d),
+						st.st_size);
+				}
+				fflush_all();
+
+				/*
+				 * We've just displayed the "--More--" prompt, so now we need
+				 * to get input from the user.
+				 */
+				for (;;) {
+					input = getc(cin);
+					input = tolower(input);
+					if (!ENABLE_FEATURE_USE_TERMIOS)
+						printf("\033[A"); /* cursor up */
+					/* Erase the last message */
+					printf("\r%*s\r", len, "");
+
+					/* Due to various multibyte escape
+					 * sequences, it's not ok to accept
+					 * any input as a command to scroll
+					 * the screen. We only allow known
+					 * commands, else we show help msg. */
+					if (input == ' ' || input == '\n' || input == 'q' || input == 'r')
+						break;
+					len = printf("(Enter:next line Space:next page Q:quit R:show the rest)");
+				}
+				len = 0;
+				lines = 0;
+				please_display_more_prompt = 0;
+
+				if (input == 'q')
+					goto end;
+
+				/* The user may have resized the terminal.
+				 * Re-read the dimensions. */
+				if (ENABLE_FEATURE_USE_TERMIOS) {
+					get_terminal_width_height(cin_fileno, &terminal_width, &terminal_height);
+					terminal_height -= 1;
+				}
+			}
+
+			/* Crudely convert tabs into spaces, which are
+			 * a bajillion times easier to deal with. */
+			if (c == '\t') {
+				spaces = ((unsigned)~len) % CONVERTED_TAB_SIZE;
+				c = ' ';
+			}
+
+			/*
+			 * There are two input streams to worry about here:
+			 *
+			 * c    : the character we are reading from the file being "mored"
+			 * input: a character received from the keyboard
+			 *
+			 * If we hit a newline in the _file_ stream, we want to test and
+			 * see if any characters have been hit in the _input_ stream. This
+			 * allows the user to quit while in the middle of a file.
+			 */
+			wrap = (++len > terminal_width);
+			if (c == '\n' || wrap) {
+				/* Then outputting this character
+				 * will move us to a new line. */
+				if (++lines >= terminal_height || input == '\n')
+					please_display_more_prompt = 1;
+				len = 0;
+			}
+			if (c != '\n' && wrap) {
+				/* Then outputting this will also put a character on
+				 * the beginning of that new line. Thus we first want to
+				 * display the prompt (if any), so we skip the putchar()
+				 * and go back to the top of the loop, without reading
+				 * a new character. */
+				goto loop_top;
+			}
+			/* My small mind cannot fathom backspaces and UTF-8 */
+			putchar(c);
+			die_if_ferror_stdout(); /* if tty was destroyed (closed xterm, etc) */
+		}
+		fclose(file);
+		fflush_all();
+	} while (*argv && *++argv);
+ end:
+	setTermSettings(cin_fileno, &initial_settings);
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/mount.c b/ap/app/busybox/src/util-linux/mount.c
new file mode 100644
index 0000000..2f94b69
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/mount.c
@@ -0,0 +1,2305 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mount implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+// Design notes: There is no spec for mount.  Remind me to write one.
+//
+// mount_main() calls singlemount() which calls mount_it_now().
+//
+// mount_main() can loop through /etc/fstab for mount -a
+// singlemount() can loop through /etc/filesystems for fstype detection.
+// mount_it_now() does the actual mount.
+//
+
+//usage:#define mount_trivial_usage
+//usage:       "[OPTIONS] [-o OPTS] DEVICE NODE"
+//usage:#define mount_full_usage "\n\n"
+//usage:       "Mount a filesystem. Filesystem autodetection requires /proc.\n"
+//usage:     "\n	-a		Mount all filesystems in fstab"
+//usage:	IF_FEATURE_MOUNT_FAKE(
+//usage:	IF_FEATURE_MTAB_SUPPORT(
+//usage:     "\n	-f		Update /etc/mtab, but don't mount"
+//usage:	)
+//usage:	IF_NOT_FEATURE_MTAB_SUPPORT(
+//usage:     "\n	-f		Dry run"
+//usage:	)
+//usage:	)
+//usage:	IF_FEATURE_MOUNT_HELPERS(
+//usage:     "\n	-i		Don't run mount helper"
+//usage:	)
+//usage:	IF_FEATURE_MTAB_SUPPORT(
+//usage:     "\n	-n		Don't update /etc/mtab"
+//usage:	)
+//usage:	IF_FEATURE_MOUNT_VERBOSE(
+//usage:     "\n	-v		Verbose"
+//usage:	)
+////usage:   "\n	-s		Sloppy (ignored)"
+//usage:     "\n	-r		Read-only mount"
+//usage:     "\n	-w		Read-write mount (default)"
+//usage:     "\n	-t FSTYPE[,...]	Filesystem type(s)"
+//usage:     "\n	-O OPT		Mount only filesystems with option OPT (-a only)"
+//usage:     "\n-o OPT:"
+//usage:	IF_FEATURE_MOUNT_LOOP(
+//usage:     "\n	loop		Ignored (loop devices are autodetected)"
+//usage:	)
+//usage:	IF_FEATURE_MOUNT_FLAGS(
+//usage:     "\n	[a]sync		Writes are [a]synchronous"
+//usage:     "\n	[no]atime	Disable/enable updates to inode access times"
+//usage:     "\n	[no]diratime	Disable/enable atime updates to directories"
+//usage:     "\n	[no]relatime	Disable/enable atime updates relative to modification time"
+//usage:     "\n	[no]dev		(Dis)allow use of special device files"
+//usage:     "\n	[no]exec	(Dis)allow use of executable files"
+//usage:     "\n	[no]suid	(Dis)allow set-user-id-root programs"
+//usage:     "\n	[r]shared	Convert [recursively] to a shared subtree"
+//usage:     "\n	[r]slave	Convert [recursively] to a slave subtree"
+//usage:     "\n	[r]private	Convert [recursively] to a private subtree"
+//usage:     "\n	[un]bindable	Make mount point [un]able to be bind mounted"
+//usage:     "\n	[r]bind		Bind a file or directory [recursively] to another location"
+//usage:     "\n	move		Relocate an existing mount point"
+//usage:	)
+//usage:     "\n	remount		Remount a mounted filesystem, changing flags"
+//usage:     "\n	ro/rw		Same as -r/-w"
+//usage:     "\n"
+//usage:     "\nThere are filesystem-specific -o flags."
+//usage:
+//usage:#define mount_example_usage
+//usage:       "$ mount\n"
+//usage:       "/dev/hda3 on / type minix (rw)\n"
+//usage:       "proc on /proc type proc (rw)\n"
+//usage:       "devpts on /dev/pts type devpts (rw)\n"
+//usage:       "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
+//usage:       "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
+//usage:       "$ mount cd_image.iso mydir\n"
+//usage:#define mount_notes_usage
+//usage:       "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
+
+#include <mntent.h>
+#include <syslog.h>
+#include <sys/mount.h>
+// Grab more as needed from util-linux's mount/mount_constants.h
+#ifndef MS_DIRSYNC
+# define MS_DIRSYNC     (1 << 7) // Directory modifications are synchronous
+#endif
+#ifndef MS_UNION
+# define MS_UNION       (1 << 8)
+#endif
+#ifndef MS_BIND
+# define MS_BIND        (1 << 12)
+#endif
+#ifndef MS_MOVE
+# define MS_MOVE        (1 << 13)
+#endif
+#ifndef MS_RECURSIVE
+# define MS_RECURSIVE   (1 << 14)
+#endif
+#ifndef MS_SILENT
+# define MS_SILENT      (1 << 15)
+#endif
+// The shared subtree stuff, which went in around 2.6.15
+#ifndef MS_UNBINDABLE
+# define MS_UNBINDABLE  (1 << 17)
+#endif
+#ifndef MS_PRIVATE
+# define MS_PRIVATE     (1 << 18)
+#endif
+#ifndef MS_SLAVE
+# define MS_SLAVE       (1 << 19)
+#endif
+#ifndef MS_SHARED
+# define MS_SHARED      (1 << 20)
+#endif
+#ifndef MS_RELATIME
+# define MS_RELATIME    (1 << 21)
+#endif
+#ifndef MS_STRICTATIME
+# define MS_STRICTATIME (1 << 24)
+#endif
+
+/* Any ~MS_FOO value has this bit set: */
+#define BB_MS_INVERTED_VALUE (1u << 31)
+
+#include "libbb.h"
+#if ENABLE_FEATURE_MOUNT_LABEL
+# include "volume_id.h"
+#else
+# define resolve_mount_spec(fsname) ((void)0)
+#endif
+
+// Needed for nfs support only
+#include <sys/utsname.h>
+#undef TRUE
+#undef FALSE
+#if ENABLE_FEATURE_MOUNT_NFS
+/* This is just a warning of a common mistake.  Possibly this should be a
+ * uclibc faq entry rather than in busybox... */
+# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
+#  error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
+# endif
+# include <rpc/rpc.h>
+# include <rpc/pmap_prot.h>
+# include <rpc/pmap_clnt.h>
+#endif
+#ifndef MS_RELATIME
+#define	MS_RELATIME		(1 << 21)	/* Update atime relative to mtime/ctime */
+#endif
+
+
+#if defined(__dietlibc__)
+// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
+// dietlibc-0.30 does not have implementation of getmntent_r()
+static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
+		char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
+{
+	struct mntent* ment = getmntent(stream);
+	return memcpy(result, ment, sizeof(*ment));
+}
+#endif
+
+
+// Not real flags, but we want to be able to check for this.
+enum {
+	MOUNT_USERS  = (1 << 28) * ENABLE_DESKTOP,
+	MOUNT_NOAUTO = (1 << 29),
+	MOUNT_SWAP   = (1 << 30),
+};
+
+
+#define OPTION_STR "o:t:rwanfvsiO:"
+enum {
+	OPT_o = (1 << 0),
+	OPT_t = (1 << 1),
+	OPT_r = (1 << 2),
+	OPT_w = (1 << 3),
+	OPT_a = (1 << 4),
+	OPT_n = (1 << 5),
+	OPT_f = (1 << 6),
+	OPT_v = (1 << 7),
+	OPT_s = (1 << 8),
+	OPT_i = (1 << 9),
+	OPT_O = (1 << 10),
+};
+
+#if ENABLE_FEATURE_MTAB_SUPPORT
+#define USE_MTAB (!(option_mask32 & OPT_n))
+#else
+#define USE_MTAB 0
+#endif
+
+#if ENABLE_FEATURE_MOUNT_FAKE
+#define FAKE_IT (option_mask32 & OPT_f)
+#else
+#define FAKE_IT 0
+#endif
+
+#if ENABLE_FEATURE_MOUNT_HELPERS
+#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
+#else
+#define HELPERS_ALLOWED 0
+#endif
+
+
+// TODO: more "user" flag compatibility.
+// "user" option (from mount manpage):
+// Only the user that mounted a filesystem can unmount it again.
+// If any user should be able to unmount, then use users instead of user
+// in the fstab line.  The owner option is similar to the user option,
+// with the restriction that the user must be the owner of the special file.
+// This may be useful e.g. for /dev/fd if a login script makes
+// the console user owner of this device.
+
+// Standard mount options (from -o options or --options),
+// with corresponding flags
+static const int32_t mount_options[] = {
+	// MS_FLAGS set a bit.  ~MS_FLAGS disable that bit.  0 flags are NOPs.
+
+	IF_FEATURE_MOUNT_LOOP(
+		/* "loop" */ 0,
+	)
+
+	IF_FEATURE_MOUNT_FSTAB(
+		/* "defaults" */ 0,
+		/* "quiet" 0 - do not filter out, vfat wants to see it */
+		/* "noauto" */ MOUNT_NOAUTO,
+		/* "sw"     */ MOUNT_SWAP,
+		/* "swap"   */ MOUNT_SWAP,
+		IF_DESKTOP(/* "user"  */ MOUNT_USERS,)
+		IF_DESKTOP(/* "users" */ MOUNT_USERS,)
+		/* "_netdev" */ 0,
+		IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */
+	)
+
+	IF_FEATURE_MOUNT_FLAGS(
+		// vfs flags
+		/* "nosuid"      */ MS_NOSUID,
+		/* "suid"        */ ~MS_NOSUID,
+		/* "dev"         */ ~MS_NODEV,
+		/* "nodev"       */ MS_NODEV,
+		/* "exec"        */ ~MS_NOEXEC,
+		/* "noexec"      */ MS_NOEXEC,
+		/* "sync"        */ MS_SYNCHRONOUS,
+		/* "dirsync"     */ MS_DIRSYNC,
+		/* "async"       */ ~MS_SYNCHRONOUS,
+		/* "atime"       */ ~MS_NOATIME,
+		/* "noatime"     */ MS_NOATIME,
+		/* "diratime"    */ ~MS_NODIRATIME,
+		/* "nodiratime"  */ MS_NODIRATIME,
+		/* "mand"        */ MS_MANDLOCK,
+		/* "nomand"      */ ~MS_MANDLOCK,
+		/* "relatime"    */ MS_RELATIME,
+		/* "norelatime"  */ ~MS_RELATIME,
+		/* "strictatime" */ MS_STRICTATIME,
+		/* "loud"        */ ~MS_SILENT,
+		/* "rbind"       */ MS_BIND|MS_RECURSIVE,
+
+		// action flags
+		/* "union"       */ MS_UNION,
+		/* "bind"        */ MS_BIND,
+		/* "move"        */ MS_MOVE,
+		/* "shared"      */ MS_SHARED,
+		/* "slave"       */ MS_SLAVE,
+		/* "private"     */ MS_PRIVATE,
+		/* "unbindable"  */ MS_UNBINDABLE,
+		/* "rshared"     */ MS_SHARED|MS_RECURSIVE,
+		/* "rslave"      */ MS_SLAVE|MS_RECURSIVE,
+		/* "rprivate"    */ MS_PRIVATE|MS_RECURSIVE,
+		/* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
+	)
+
+	// Always understood.
+	/* "ro"      */ MS_RDONLY,  // vfs flag
+	/* "rw"      */ ~MS_RDONLY, // vfs flag
+	/* "remount" */ MS_REMOUNT  // action flag
+};
+
+static const char mount_option_str[] =
+	IF_FEATURE_MOUNT_LOOP(
+		"loop\0"
+	)
+	IF_FEATURE_MOUNT_FSTAB(
+		"defaults\0"
+		// "quiet\0" - do not filter out, vfat wants to see it
+		"noauto\0"
+		"sw\0"
+		"swap\0"
+		IF_DESKTOP("user\0")
+		IF_DESKTOP("users\0")
+		"_netdev\0"
+		IF_DESKTOP("comment=\0") /* systemd uses this in fstab */
+	)
+	IF_FEATURE_MOUNT_FLAGS(
+		// vfs flags
+		"nosuid\0"
+		"suid\0"
+		"dev\0"
+		"nodev\0"
+		"exec\0"
+		"noexec\0"
+		"sync\0"
+		"dirsync\0"
+		"async\0"
+		"atime\0"
+		"noatime\0"
+		"diratime\0"
+		"nodiratime\0"
+		"mand\0"
+		"nomand\0"
+		"relatime\0"
+		"norelatime\0"
+		"strictatime\0"
+		"loud\0"
+		"rbind\0"
+
+		// action flags
+		"union\0"
+		"bind\0"
+		"move\0"
+		"make-shared\0"
+		"make-slave\0"
+		"make-private\0"
+		"make-unbindable\0"
+		"make-rshared\0"
+		"make-rslave\0"
+		"make-rprivate\0"
+		"make-runbindable\0"
+	)
+
+	// Always understood.
+	"ro\0"        // vfs flag
+	"rw\0"        // vfs flag
+	"remount\0"   // action flag
+;
+
+
+struct globals {
+#if ENABLE_FEATURE_MOUNT_NFS
+	smalluint nfs_mount_version;
+#endif
+#if ENABLE_FEATURE_MOUNT_VERBOSE
+	unsigned verbose;
+#endif
+	llist_t *fslist;
+	char getmntent_buf[1];
+} FIX_ALIASING;
+enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define nfs_mount_version (G.nfs_mount_version)
+#if ENABLE_FEATURE_MOUNT_VERBOSE
+#define verbose           (G.verbose          )
+#else
+#define verbose           0
+#endif
+#define fslist            (G.fslist           )
+#define getmntent_buf     (G.getmntent_buf    )
+#define INIT_G() do { } while (0)
+
+#if ENABLE_FEATURE_MTAB_SUPPORT
+/*
+ * update_mtab_entry_on_move() is used to update entry in case of mount --move.
+ * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
+ * input mntent and replace it by new one.
+ */
+static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
+{
+	struct mntent *entries, *m;
+	int i, count;
+	FILE *mountTable;
+
+	mountTable = setmntent(bb_path_mtab_file, "r");
+	if (!mountTable) {
+		bb_perror_msg(bb_path_mtab_file);
+		return;
+	}
+
+	entries = NULL;
+	count = 0;
+	while ((m = getmntent(mountTable)) != NULL) {
+		entries = xrealloc_vector(entries, 3, count);
+		entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
+		entries[count].mnt_dir = xstrdup(m->mnt_dir);
+		entries[count].mnt_type = xstrdup(m->mnt_type);
+		entries[count].mnt_opts = xstrdup(m->mnt_opts);
+		entries[count].mnt_freq = m->mnt_freq;
+		entries[count].mnt_passno = m->mnt_passno;
+		count++;
+	}
+	endmntent(mountTable);
+
+	mountTable = setmntent(bb_path_mtab_file, "w");
+	if (mountTable) {
+		for (i = 0; i < count; i++) {
+			if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
+				addmntent(mountTable, &entries[i]);
+			else
+				addmntent(mountTable, mp);
+		}
+		endmntent(mountTable);
+	} else if (errno != EROFS)
+		bb_perror_msg(bb_path_mtab_file);
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		for (i = 0; i < count; i++) {
+			free(entries[i].mnt_fsname);
+			free(entries[i].mnt_dir);
+			free(entries[i].mnt_type);
+			free(entries[i].mnt_opts);
+		}
+		free(entries);
+	}
+}
+#endif
+
+#if ENABLE_FEATURE_MOUNT_VERBOSE
+static int verbose_mount(const char *source, const char *target,
+		const char *filesystemtype,
+		unsigned long mountflags, const void *data)
+{
+	int rc;
+
+	errno = 0;
+	rc = mount(source, target, filesystemtype, mountflags, data);
+	if (verbose >= 2)
+		bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
+			source, target, filesystemtype,
+			mountflags, (char*)data, rc);
+	return rc;
+}
+#else
+#define verbose_mount(...) mount(__VA_ARGS__)
+#endif
+
+// Append mount options to string
+static void append_mount_options(char **oldopts, const char *newopts)
+{
+	if (*oldopts && **oldopts) {
+		// Do not insert options which are already there
+		while (newopts[0]) {
+			char *p;
+			int len = strlen(newopts);
+			p = strchr(newopts, ',');
+			if (p) len = p - newopts;
+			p = *oldopts;
+			while (1) {
+				if (!strncmp(p, newopts, len)
+				 && (p[len] == ',' || p[len] == '\0'))
+					goto skip;
+				p = strchr(p,',');
+				if (!p) break;
+				p++;
+			}
+			p = xasprintf("%s,%.*s", *oldopts, len, newopts);
+			free(*oldopts);
+			*oldopts = p;
+ skip:
+			newopts += len;
+			while (newopts[0] == ',') newopts++;
+		}
+	} else {
+		if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
+		*oldopts = xstrdup(newopts);
+	}
+}
+
+// Use the mount_options list to parse options into flags.
+// Also update list of unrecognized options if unrecognized != NULL
+static unsigned long parse_mount_options(char *options, char **unrecognized)
+{
+	unsigned long flags = MS_SILENT;
+
+	// Loop through options
+	for (;;) {
+		unsigned i;
+		char *comma = strchr(options, ',');
+		const char *option_str = mount_option_str;
+
+		if (comma) *comma = '\0';
+
+// FIXME: use hasmntopt()
+		// Find this option in mount_options
+		for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
+			unsigned opt_len = strlen(option_str);
+
+			if (strncasecmp(option_str, options, opt_len) == 0
+			 && (options[opt_len] == '\0'
+			    /* or is it "comment=" thingy in fstab? */
+			    IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' ))
+			    )
+			) {
+				unsigned long fl = mount_options[i];
+				if (fl & BB_MS_INVERTED_VALUE)
+					flags &= fl;
+				else
+					flags |= fl;
+				goto found;
+			}
+			option_str += opt_len + 1;
+		}
+		// We did not recognize this option.
+		// If "unrecognized" is not NULL, append option there.
+		// Note that we should not append *empty* option -
+		// in this case we want to pass NULL, not "", to "data"
+		// parameter of mount(2) syscall.
+		// This is crucial for filesystems that don't accept
+		// any arbitrary mount options, like cgroup fs:
+		// "mount -t cgroup none /mnt"
+		if (options[0] && unrecognized) {
+			// Add it to strflags, to pass on to kernel
+			char *p = *unrecognized;
+			unsigned len = p ? strlen(p) : 0;
+			*unrecognized = p = xrealloc(p, len + strlen(options) + 2);
+
+			// Comma separated if it's not the first one
+			if (len) p[len++] = ',';
+			strcpy(p + len, options);
+		}
+ found:
+		if (!comma)
+			break;
+		// Advance to next option
+		*comma = ',';
+		options = ++comma;
+	}
+
+	return flags;
+}
+
+// Return a list of all block device backed filesystems
+static llist_t *get_block_backed_filesystems(void)
+{
+	static const char filesystems[2][sizeof("/proc/filesystems")] = {
+		"/etc/filesystems",
+		"/proc/filesystems",
+	};
+	char *fs, *buf;
+	llist_t *list = NULL;
+	int i;
+	FILE *f;
+
+	for (i = 0; i < 2; i++) {
+		f = fopen_for_read(filesystems[i]);
+		if (!f) continue;
+
+		while ((buf = xmalloc_fgetline(f)) != NULL) {
+			if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
+				goto next;
+			fs = skip_whitespace(buf);
+			if (*fs == '#' || *fs == '*' || !*fs)
+				goto next;
+
+			llist_add_to_end(&list, xstrdup(fs));
+ next:
+			free(buf);
+		}
+		if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
+	}
+
+	return list;
+}
+
+#if ENABLE_FEATURE_CLEAN_UP
+static void delete_block_backed_filesystems(void)
+{
+	llist_free(fslist, free);
+}
+#else
+void delete_block_backed_filesystems(void);
+#endif
+
+// Perform actual mount of specific filesystem at specific location.
+// NB: mp->xxx fields may be trashed on exit
+static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
+{
+	int rc = 0;
+
+	if (FAKE_IT) {
+		if (verbose >= 2)
+			bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
+				mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
+				vfsflags, filteropts);
+		goto mtab;
+	}
+
+	// Mount, with fallback to read-only if necessary.
+	for (;;) {
+		errno = 0;
+		rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
+				vfsflags, filteropts);
+
+		// If mount failed, try
+		// helper program mount.<mnt_type>
+		if (HELPERS_ALLOWED && rc && mp->mnt_type) {
+			char *args[8];
+			int errno_save = errno;
+			args[0] = xasprintf("mount.%s", mp->mnt_type);
+			rc = 1;
+			if (FAKE_IT)
+				args[rc++] = (char *)"-f";
+			if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
+				args[rc++] = (char *)"-n";
+			args[rc++] = mp->mnt_fsname;
+			args[rc++] = mp->mnt_dir;
+			if (filteropts) {
+				args[rc++] = (char *)"-o";
+				args[rc++] = filteropts;
+			}
+			args[rc] = NULL;
+			rc = spawn_and_wait(args);
+			free(args[0]);
+			if (!rc)
+				break;
+			errno = errno_save;
+		}
+
+		if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
+			break;
+		if (!(vfsflags & MS_SILENT))
+			bb_error_msg("%s is write-protected, mounting read-only",
+						mp->mnt_fsname);
+		vfsflags |= MS_RDONLY;
+	}
+
+	// Abort entirely if permission denied.
+
+	if (rc && errno == EPERM)
+		bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+
+	// If the mount was successful, and we're maintaining an old-style
+	// mtab file by hand, add the new entry to it now.
+ mtab:
+	if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
+		char *fsname;
+		FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
+		const char *option_str = mount_option_str;
+		int i;
+
+		if (!mountTable) {
+			bb_perror_msg(bb_path_mtab_file);
+			goto ret;
+		}
+
+		// Add vfs string flags
+		for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
+			if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
+				append_mount_options(&(mp->mnt_opts), option_str);
+			option_str += strlen(option_str) + 1;
+		}
+
+		// Remove trailing / (if any) from directory we mounted on
+		i = strlen(mp->mnt_dir) - 1;
+		while (i > 0 && mp->mnt_dir[i] == '/')
+			mp->mnt_dir[i--] = '\0';
+
+		// Convert to canonical pathnames as needed
+		mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
+		fsname = NULL;
+		if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
+			mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
+			mp->mnt_type = (char*)"bind";
+		}
+		mp->mnt_freq = mp->mnt_passno = 0;
+
+		// Write and close
+#if ENABLE_FEATURE_MTAB_SUPPORT
+		if (vfsflags & MS_MOVE)
+			update_mtab_entry_on_move(mp);
+		else
+#endif
+			addmntent(mountTable, mp);
+		endmntent(mountTable);
+
+		if (ENABLE_FEATURE_CLEAN_UP) {
+			free(mp->mnt_dir);
+			free(fsname);
+		}
+	}
+ ret:
+	return rc;
+}
+
+#if ENABLE_FEATURE_MOUNT_NFS
+
+/*
+ * Linux NFS mount
+ * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ *
+ * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
+ * numbers to be specified on the command line.
+ *
+ * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
+ * Omit the call to connect() for Linux version 1.3.11 or later.
+ *
+ * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
+ * Implemented the "bg", "fg" and "retry" mount options for NFS.
+ *
+ * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+ *
+ * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
+ * plus NFSv3 stuff.
+ */
+
+#define MOUNTPORT 635
+#define MNTPATHLEN 1024
+#define MNTNAMLEN 255
+#define FHSIZE 32
+#define FHSIZE3 64
+
+typedef char fhandle[FHSIZE];
+
+typedef struct {
+	unsigned int fhandle3_len;
+	char *fhandle3_val;
+} fhandle3;
+
+enum mountstat3 {
+	MNT_OK = 0,
+	MNT3ERR_PERM = 1,
+	MNT3ERR_NOENT = 2,
+	MNT3ERR_IO = 5,
+	MNT3ERR_ACCES = 13,
+	MNT3ERR_NOTDIR = 20,
+	MNT3ERR_INVAL = 22,
+	MNT3ERR_NAMETOOLONG = 63,
+	MNT3ERR_NOTSUPP = 10004,
+	MNT3ERR_SERVERFAULT = 10006,
+};
+typedef enum mountstat3 mountstat3;
+
+struct fhstatus {
+	unsigned int fhs_status;
+	union {
+		fhandle fhs_fhandle;
+	} fhstatus_u;
+};
+typedef struct fhstatus fhstatus;
+
+struct mountres3_ok {
+	fhandle3 fhandle;
+	struct {
+		unsigned int auth_flavours_len;
+		char *auth_flavours_val;
+	} auth_flavours;
+};
+typedef struct mountres3_ok mountres3_ok;
+
+struct mountres3 {
+	mountstat3 fhs_status;
+	union {
+		mountres3_ok mountinfo;
+	} mountres3_u;
+};
+typedef struct mountres3 mountres3;
+
+typedef char *dirpath;
+
+typedef char *name;
+
+typedef struct mountbody *mountlist;
+
+struct mountbody {
+	name ml_hostname;
+	dirpath ml_directory;
+	mountlist ml_next;
+};
+typedef struct mountbody mountbody;
+
+typedef struct groupnode *groups;
+
+struct groupnode {
+	name gr_name;
+	groups gr_next;
+};
+typedef struct groupnode groupnode;
+
+typedef struct exportnode *exports;
+
+struct exportnode {
+	dirpath ex_dir;
+	groups ex_groups;
+	exports ex_next;
+};
+typedef struct exportnode exportnode;
+
+struct ppathcnf {
+	int pc_link_max;
+	short pc_max_canon;
+	short pc_max_input;
+	short pc_name_max;
+	short pc_path_max;
+	short pc_pipe_buf;
+	uint8_t pc_vdisable;
+	char pc_xxx;
+	short pc_mask[2];
+};
+typedef struct ppathcnf ppathcnf;
+
+#define MOUNTPROG 100005
+#define MOUNTVERS 1
+
+#define MOUNTPROC_NULL 0
+#define MOUNTPROC_MNT 1
+#define MOUNTPROC_DUMP 2
+#define MOUNTPROC_UMNT 3
+#define MOUNTPROC_UMNTALL 4
+#define MOUNTPROC_EXPORT 5
+#define MOUNTPROC_EXPORTALL 6
+
+#define MOUNTVERS_POSIX 2
+
+#define MOUNTPROC_PATHCONF 7
+
+#define MOUNT_V3 3
+
+#define MOUNTPROC3_NULL 0
+#define MOUNTPROC3_MNT 1
+#define MOUNTPROC3_DUMP 2
+#define MOUNTPROC3_UMNT 3
+#define MOUNTPROC3_UMNTALL 4
+#define MOUNTPROC3_EXPORT 5
+
+enum {
+#ifndef NFS_FHSIZE
+	NFS_FHSIZE = 32,
+#endif
+#ifndef NFS_PORT
+	NFS_PORT = 2049
+#endif
+};
+
+/*
+ * We want to be able to compile mount on old kernels in such a way
+ * that the binary will work well on more recent kernels.
+ * Thus, if necessary we teach nfsmount.c the structure of new fields
+ * that will come later.
+ *
+ * Moreover, the new kernel includes conflict with glibc includes
+ * so it is easiest to ignore the kernel altogether (at compile time).
+ */
+
+struct nfs2_fh {
+	char            data[32];
+};
+struct nfs3_fh {
+	unsigned short  size;
+	unsigned char   data[64];
+};
+
+struct nfs_mount_data {
+	int		version;	/* 1 */
+	int		fd;		/* 1 */
+	struct nfs2_fh	old_root;	/* 1 */
+	int		flags;		/* 1 */
+	int		rsize;		/* 1 */
+	int		wsize;		/* 1 */
+	int		timeo;		/* 1 */
+	int		retrans;	/* 1 */
+	int		acregmin;	/* 1 */
+	int		acregmax;	/* 1 */
+	int		acdirmin;	/* 1 */
+	int		acdirmax;	/* 1 */
+	struct sockaddr_in addr;	/* 1 */
+	char		hostname[256];	/* 1 */
+	int		namlen;		/* 2 */
+	unsigned int	bsize;		/* 3 */
+	struct nfs3_fh	root;		/* 4 */
+};
+
+/* bits in the flags field */
+enum {
+	NFS_MOUNT_SOFT = 0x0001,	/* 1 */
+	NFS_MOUNT_INTR = 0x0002,	/* 1 */
+	NFS_MOUNT_SECURE = 0x0004,	/* 1 */
+	NFS_MOUNT_POSIX = 0x0008,	/* 1 */
+	NFS_MOUNT_NOCTO = 0x0010,	/* 1 */
+	NFS_MOUNT_NOAC = 0x0020,	/* 1 */
+	NFS_MOUNT_TCP = 0x0040,		/* 2 */
+	NFS_MOUNT_VER3 = 0x0080,	/* 3 */
+	NFS_MOUNT_KERBEROS = 0x0100,	/* 3 */
+	NFS_MOUNT_NONLM = 0x0200,	/* 3 */
+	NFS_MOUNT_NOACL = 0x0800,	/* 4 */
+	NFS_MOUNT_NORDIRPLUS = 0x4000
+};
+
+
+/*
+ * We need to translate between nfs status return values and
+ * the local errno values which may not be the same.
+ *
+ * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
+ * "after #include <errno.h> the symbol errno is reserved for any use,
+ *  it cannot even be used as a struct tag or field name".
+ */
+#ifndef EDQUOT
+# define EDQUOT ENOSPC
+#endif
+/* Convert each NFSERR_BLAH into EBLAH */
+static const uint8_t nfs_err_stat[] = {
+	 1,  2,  5,  6, 13, 17,
+	19, 20, 21, 22, 27, 28,
+	30, 63, 66, 69, 70, 71
+};
+#if ( \
+	EPERM | ENOENT      | EIO      | ENXIO | EACCES| EEXIST | \
+	ENODEV| ENOTDIR     | EISDIR   | EINVAL| EFBIG | ENOSPC | \
+	EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
+typedef uint8_t nfs_err_type;
+#else
+typedef uint16_t nfs_err_type;
+#endif
+static const nfs_err_type nfs_err_errnum[] = {
+	EPERM , ENOENT      , EIO      , ENXIO , EACCES, EEXIST,
+	ENODEV, ENOTDIR     , EISDIR   , EINVAL, EFBIG , ENOSPC,
+	EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
+};
+static char *nfs_strerror(int status)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
+		if (nfs_err_stat[i] == status)
+			return strerror(nfs_err_errnum[i]);
+	}
+	return xasprintf("unknown nfs status return value: %d", status);
+}
+
+static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
+{
+	return xdr_opaque(xdrs, objp, FHSIZE);
+}
+
+static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
+{
+	if (!xdr_u_int(xdrs, &objp->fhs_status))
+		return FALSE;
+	if (objp->fhs_status == 0)
+		return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
+	return TRUE;
+}
+
+static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
+{
+	return xdr_string(xdrs, objp, MNTPATHLEN);
+}
+
+static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
+{
+	return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
+			(unsigned int *) &objp->fhandle3_len,
+			FHSIZE3);
+}
+
+static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
+{
+	if (!xdr_fhandle3(xdrs, &objp->fhandle))
+		return FALSE;
+	return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
+			&(objp->auth_flavours.auth_flavours_len),
+			~0,
+			sizeof(int),
+			(xdrproc_t) xdr_int);
+}
+
+static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
+{
+	return xdr_enum(xdrs, (enum_t *) objp);
+}
+
+static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
+{
+	if (!xdr_mountstat3(xdrs, &objp->fhs_status))
+		return FALSE;
+	if (objp->fhs_status == MNT_OK)
+		return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
+	return TRUE;
+}
+
+#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
+
+/*
+ * Unfortunately, the kernel prints annoying console messages
+ * in case of an unexpected nfs mount version (instead of
+ * just returning some error).  Therefore we'll have to try
+ * and figure out what version the kernel expects.
+ *
+ * Variables:
+ *	KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
+ *	NFS_MOUNT_VERSION: these nfsmount sources at compile time
+ *	nfs_mount_version: version this source and running kernel can handle
+ */
+static void
+find_kernel_nfs_mount_version(void)
+{
+	int kernel_version;
+
+	if (nfs_mount_version)
+		return;
+
+	nfs_mount_version = 4; /* default */
+
+	kernel_version = get_linux_version_code();
+	if (kernel_version) {
+		if (kernel_version < KERNEL_VERSION(2,2,18))
+			nfs_mount_version = 3;
+		/* else v4 since 2.3.99pre4 */
+	}
+}
+
+static void
+get_mountport(struct pmap *pm_mnt,
+	struct sockaddr_in *server_addr,
+	long unsigned prog,
+	long unsigned version,
+	long unsigned proto,
+	long unsigned port)
+{
+	struct pmaplist *pmap;
+
+	server_addr->sin_port = PMAPPORT;
+/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
+ * I understand it like "IPv6 for this is not 100% ready" */
+	pmap = pmap_getmaps(server_addr);
+
+	if (version > MAX_NFSPROT)
+		version = MAX_NFSPROT;
+	if (!prog)
+		prog = MOUNTPROG;
+	pm_mnt->pm_prog = prog;
+	pm_mnt->pm_vers = version;
+	pm_mnt->pm_prot = proto;
+	pm_mnt->pm_port = port;
+
+	while (pmap) {
+		if (pmap->pml_map.pm_prog != prog)
+			goto next;
+		if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
+			goto next;
+		if (version > 2 && pmap->pml_map.pm_vers != version)
+			goto next;
+		if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
+			goto next;
+		if (pmap->pml_map.pm_vers > MAX_NFSPROT
+		 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
+		 || (port && pmap->pml_map.pm_port != port)
+		) {
+			goto next;
+		}
+		memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
+ next:
+		pmap = pmap->pml_next;
+	}
+	if (!pm_mnt->pm_vers)
+		pm_mnt->pm_vers = MOUNTVERS;
+	if (!pm_mnt->pm_port)
+		pm_mnt->pm_port = MOUNTPORT;
+	if (!pm_mnt->pm_prot)
+		pm_mnt->pm_prot = IPPROTO_TCP;
+}
+
+#if BB_MMU
+static int daemonize(void)
+{
+	int pid = fork();
+	if (pid < 0) /* error */
+		return -errno;
+	if (pid > 0) /* parent */
+		return 0;
+	/* child */
+	close(0);
+	xopen(bb_dev_null, O_RDWR);
+	xdup2(0, 1);
+	xdup2(0, 2);
+	setsid();
+	openlog(applet_name, LOG_PID, LOG_DAEMON);
+	logmode = LOGMODE_SYSLOG;
+	return 1;
+}
+#else
+static inline int daemonize(void) { return -ENOSYS; }
+#endif
+
+/* TODO */
+static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
+{
+	return 0;
+}
+
+/* RPC strerror analogs are terminally idiotic:
+ * *mandatory* prefix and \n at end.
+ * This hopefully helps. Usage:
+ * error_msg_rpc(clnt_*error*(" ")) */
+static void error_msg_rpc(const char *msg)
+{
+	int len;
+	while (msg[0] == ' ' || msg[0] == ':') msg++;
+	len = strlen(msg);
+	while (len && msg[len-1] == '\n') len--;
+	bb_error_msg("%.*s", len, msg);
+}
+
+/* NB: mp->xxx fields may be trashed on exit */
+static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
+{
+	CLIENT *mclient;
+	char *hostname;
+	char *pathname;
+	char *mounthost;
+	/* prior to 2.6.23, kernel took NFS options in a form of this struct
+	 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
+	 * then data pointer is interpreted as a string. */
+	struct nfs_mount_data data;
+	char *opt;
+	struct hostent *hp;
+	struct sockaddr_in server_addr;
+	struct sockaddr_in mount_server_addr;
+	int msock, fsock;
+	union {
+		struct fhstatus nfsv2;
+		struct mountres3 nfsv3;
+	} status;
+	int daemonized;
+	char *s;
+	int port;
+	int mountport;
+	int proto;
+#if BB_MMU
+	smallint bg = 0;
+#else
+	enum { bg = 0 };
+#endif
+	int retry;
+	int mountprog;
+	int mountvers;
+	int nfsprog;
+	int nfsvers;
+	int retval;
+	/* these all are one-bit really. gcc 4.3.1 likes this combination: */
+	smallint tcp;
+	smallint soft;
+	int intr;
+	int posix;
+	int nocto;
+	int noac;
+	int nordirplus;
+	int nolock;
+	int noacl;
+
+	find_kernel_nfs_mount_version();
+
+	daemonized = 0;
+	mounthost = NULL;
+	retval = ETIMEDOUT;
+	msock = fsock = -1;
+	mclient = NULL;
+
+	/* NB: hostname, mounthost, filteropts must be free()d prior to return */
+
+	filteropts = xstrdup(filteropts); /* going to trash it later... */
+
+	hostname = xstrdup(mp->mnt_fsname);
+	/* mount_main() guarantees that ':' is there */
+	s = strchr(hostname, ':');
+	pathname = s + 1;
+	*s = '\0';
+	/* Ignore all but first hostname in replicated mounts
+	 * until they can be fully supported. (mack@sgi.com) */
+	s = strchr(hostname, ',');
+	if (s) {
+		*s = '\0';
+		bb_error_msg("warning: multiple hostnames not supported");
+	}
+
+	server_addr.sin_family = AF_INET;
+	if (!inet_aton(hostname, &server_addr.sin_addr)) {
+		hp = gethostbyname(hostname);
+		if (hp == NULL) {
+			bb_herror_msg("%s", hostname);
+			goto fail;
+		}
+		if (hp->h_length != (int)sizeof(struct in_addr)) {
+			bb_error_msg_and_die("only IPv4 is supported");
+		}
+		memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
+	}
+
+	memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
+
+	/* add IP address to mtab options for use when unmounting */
+
+	if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
+		mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
+	} else {
+		char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
+					mp->mnt_opts[0] ? "," : "",
+					inet_ntoa(server_addr.sin_addr));
+		free(mp->mnt_opts);
+		mp->mnt_opts = tmp;
+	}
+
+	/* Set default options.
+	 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
+	 * let the kernel decide.
+	 * timeo is filled in after we know whether it'll be TCP or UDP. */
+	memset(&data, 0, sizeof(data));
+	data.retrans  = 3;
+	data.acregmin = 3;
+	data.acregmax = 60;
+	data.acdirmin = 30;
+	data.acdirmax = 60;
+	data.namlen   = NAME_MAX;
+
+	soft = 0;
+	intr = 0;
+	posix = 0;
+	nocto = 0;
+	nolock = 0;
+	noac = 0;
+	nordirplus = 0;
+	noacl = 0;
+	retry = 10000;		/* 10000 minutes ~ 1 week */
+	tcp = 1;			/* nfs-utils uses tcp per default */
+
+	mountprog = MOUNTPROG;
+	mountvers = 0;
+	port = 0;
+	mountport = 0;
+	nfsprog = 100003;
+	nfsvers = 0;
+
+	/* parse options */
+	if (filteropts)	for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
+		char *opteq = strchr(opt, '=');
+		if (opteq) {
+			int val, idx;
+			static const char options[] ALIGN1 =
+				/* 0 */ "rsize\0"
+				/* 1 */ "wsize\0"
+				/* 2 */ "timeo\0"
+				/* 3 */ "retrans\0"
+				/* 4 */ "acregmin\0"
+				/* 5 */ "acregmax\0"
+				/* 6 */ "acdirmin\0"
+				/* 7 */ "acdirmax\0"
+				/* 8 */ "actimeo\0"
+				/* 9 */ "retry\0"
+				/* 10 */ "port\0"
+				/* 11 */ "mountport\0"
+				/* 12 */ "mounthost\0"
+				/* 13 */ "mountprog\0"
+				/* 14 */ "mountvers\0"
+				/* 15 */ "nfsprog\0"
+				/* 16 */ "nfsvers\0"
+				/* 17 */ "vers\0"
+				/* 18 */ "proto\0"
+				/* 19 */ "namlen\0"
+				/* 20 */ "addr\0";
+
+			*opteq++ = '\0';
+			idx = index_in_strings(options, opt);
+			switch (idx) {
+			case 12: // "mounthost"
+				mounthost = xstrndup(opteq,
+						strcspn(opteq, " \t\n\r,"));
+				continue;
+			case 18: // "proto"
+				if (!strncmp(opteq, "tcp", 3))
+					tcp = 1;
+				else if (!strncmp(opteq, "udp", 3))
+					tcp = 0;
+				else
+					bb_error_msg("warning: unrecognized proto= option");
+				continue;
+			case 20: // "addr" - ignore
+				continue;
+			case -1: // unknown
+				if (vfsflags & MS_REMOUNT)
+					continue;
+			}
+
+			val = xatoi_positive(opteq);
+			switch (idx) {
+			case 0: // "rsize"
+				data.rsize = val;
+				continue;
+			case 1: // "wsize"
+				data.wsize = val;
+				continue;
+			case 2: // "timeo"
+				data.timeo = val;
+				continue;
+			case 3: // "retrans"
+				data.retrans = val;
+				continue;
+			case 4: // "acregmin"
+				data.acregmin = val;
+				continue;
+			case 5: // "acregmax"
+				data.acregmax = val;
+				continue;
+			case 6: // "acdirmin"
+				data.acdirmin = val;
+				continue;
+			case 7: // "acdirmax"
+				data.acdirmax = val;
+				continue;
+			case 8: // "actimeo"
+				data.acregmin = val;
+				data.acregmax = val;
+				data.acdirmin = val;
+				data.acdirmax = val;
+				continue;
+			case 9: // "retry"
+				retry = val;
+				continue;
+			case 10: // "port"
+				port = val;
+				continue;
+			case 11: // "mountport"
+				mountport = val;
+				continue;
+			case 13: // "mountprog"
+				mountprog = val;
+				continue;
+			case 14: // "mountvers"
+				mountvers = val;
+				continue;
+			case 15: // "nfsprog"
+				nfsprog = val;
+				continue;
+			case 16: // "nfsvers"
+			case 17: // "vers"
+				nfsvers = val;
+				continue;
+			case 19: // "namlen"
+				//if (nfs_mount_version >= 2)
+					data.namlen = val;
+				//else
+				//	bb_error_msg("warning: option namlen is not supported\n");
+				continue;
+			default:
+				bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
+				goto fail;
+			}
+		}
+		else { /* not of the form opt=val */
+			static const char options[] ALIGN1 =
+				"bg\0"
+				"fg\0"
+				"soft\0"
+				"hard\0"
+				"intr\0"
+				"posix\0"
+				"cto\0"
+				"ac\0"
+				"tcp\0"
+				"udp\0"
+				"lock\0"
+				"rdirplus\0"
+				"acl\0";
+			int val = 1;
+			if (!strncmp(opt, "no", 2)) {
+				val = 0;
+				opt += 2;
+			}
+			switch (index_in_strings(options, opt)) {
+			case 0: // "bg"
+#if BB_MMU
+				bg = val;
+#endif
+				break;
+			case 1: // "fg"
+#if BB_MMU
+				bg = !val;
+#endif
+				break;
+			case 2: // "soft"
+				soft = val;
+				break;
+			case 3: // "hard"
+				soft = !val;
+				break;
+			case 4: // "intr"
+				intr = val;
+				break;
+			case 5: // "posix"
+				posix = val;
+				break;
+			case 6: // "cto"
+				nocto = !val;
+				break;
+			case 7: // "ac"
+				noac = !val;
+				break;
+			case 8: // "tcp"
+				tcp = val;
+				break;
+			case 9: // "udp"
+				tcp = !val;
+				break;
+			case 10: // "lock"
+				if (nfs_mount_version >= 3)
+					nolock = !val;
+				else
+					bb_error_msg("warning: option nolock is not supported");
+				break;
+			case 11: //rdirplus
+				nordirplus = !val;
+				break;
+			case 12: // acl
+				noacl = !val;
+				break;
+			default:
+				bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
+				goto fail;
+			}
+		}
+	}
+	proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
+
+	data.flags = (soft ? NFS_MOUNT_SOFT : 0)
+		| (intr ? NFS_MOUNT_INTR : 0)
+		| (posix ? NFS_MOUNT_POSIX : 0)
+		| (nocto ? NFS_MOUNT_NOCTO : 0)
+		| (noac ? NFS_MOUNT_NOAC : 0)
+		| (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
+		| (noacl ? NFS_MOUNT_NOACL : 0);
+	if (nfs_mount_version >= 2)
+		data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
+	if (nfs_mount_version >= 3)
+		data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
+	if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
+		bb_error_msg("NFSv%d not supported", nfsvers);
+		goto fail;
+	}
+	if (nfsvers && !mountvers)
+		mountvers = (nfsvers < 3) ? 1 : nfsvers;
+	if (nfsvers && nfsvers < mountvers) {
+		mountvers = nfsvers;
+	}
+
+	/* Adjust options if none specified */
+	if (!data.timeo)
+		data.timeo = tcp ? 70 : 7;
+
+	data.version = nfs_mount_version;
+
+	if (vfsflags & MS_REMOUNT)
+		goto do_mount;
+
+	/*
+	 * If the previous mount operation on the same host was
+	 * backgrounded, and the "bg" for this mount is also set,
+	 * give up immediately, to avoid the initial timeout.
+	 */
+	if (bg && we_saw_this_host_before(hostname)) {
+		daemonized = daemonize();
+		if (daemonized <= 0) { /* parent or error */
+			retval = -daemonized;
+			goto ret;
+		}
+	}
+
+	/* Create mount daemon client */
+	/* See if the nfs host = mount host. */
+	if (mounthost) {
+		if (mounthost[0] >= '0' && mounthost[0] <= '9') {
+			mount_server_addr.sin_family = AF_INET;
+			mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
+		} else {
+			hp = gethostbyname(mounthost);
+			if (hp == NULL) {
+				bb_herror_msg("%s", mounthost);
+				goto fail;
+			}
+			if (hp->h_length != (int)sizeof(struct in_addr)) {
+				bb_error_msg_and_die("only IPv4 is supported");
+			}
+			mount_server_addr.sin_family = AF_INET;
+			memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
+		}
+	}
+
+	/*
+	 * The following loop implements the mount retries. When the mount
+	 * times out, and the "bg" option is set, we background ourself
+	 * and continue trying.
+	 *
+	 * The case where the mount point is not present and the "bg"
+	 * option is set, is treated as a timeout. This is done to
+	 * support nested mounts.
+	 *
+	 * The "retry" count specified by the user is the number of
+	 * minutes to retry before giving up.
+	 */
+	{
+		struct timeval total_timeout;
+		struct timeval retry_timeout;
+		struct pmap pm_mnt;
+		time_t t;
+		time_t prevt;
+		time_t timeout;
+
+		retry_timeout.tv_sec = 3;
+		retry_timeout.tv_usec = 0;
+		total_timeout.tv_sec = 20;
+		total_timeout.tv_usec = 0;
+/* FIXME: use monotonic()? */
+		timeout = time(NULL) + 60 * retry;
+		prevt = 0;
+		t = 30;
+ retry:
+		/* Be careful not to use too many CPU cycles */
+		if (t - prevt < 30)
+			sleep(30);
+
+		get_mountport(&pm_mnt, &mount_server_addr,
+				mountprog,
+				mountvers,
+				proto,
+				mountport);
+		nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
+
+		/* contact the mount daemon via TCP */
+		mount_server_addr.sin_port = htons(pm_mnt.pm_port);
+		msock = RPC_ANYSOCK;
+
+		switch (pm_mnt.pm_prot) {
+		case IPPROTO_UDP:
+			mclient = clntudp_create(&mount_server_addr,
+						pm_mnt.pm_prog,
+						pm_mnt.pm_vers,
+						retry_timeout,
+						&msock);
+			if (mclient)
+				break;
+			mount_server_addr.sin_port = htons(pm_mnt.pm_port);
+			msock = RPC_ANYSOCK;
+		case IPPROTO_TCP:
+			mclient = clnttcp_create(&mount_server_addr,
+						pm_mnt.pm_prog,
+						pm_mnt.pm_vers,
+						&msock, 0, 0);
+			break;
+		default:
+			mclient = NULL;
+		}
+		if (!mclient) {
+			if (!daemonized && prevt == 0)
+				error_msg_rpc(clnt_spcreateerror(" "));
+		} else {
+			enum clnt_stat clnt_stat;
+
+			/* Try to mount hostname:pathname */
+			mclient->cl_auth = authunix_create_default();
+
+			/* Make pointers in xdr_mountres3 NULL so
+			 * that xdr_array allocates memory for us
+			 */
+			memset(&status, 0, sizeof(status));
+
+			if (pm_mnt.pm_vers == 3)
+				clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
+						(xdrproc_t) xdr_dirpath,
+						(caddr_t) &pathname,
+						(xdrproc_t) xdr_mountres3,
+						(caddr_t) &status,
+						total_timeout);
+			else
+				clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
+						(xdrproc_t) xdr_dirpath,
+						(caddr_t) &pathname,
+						(xdrproc_t) xdr_fhstatus,
+						(caddr_t) &status,
+						total_timeout);
+
+			if (clnt_stat == RPC_SUCCESS)
+				goto prepare_kernel_data; /* we're done */
+			if (errno != ECONNREFUSED) {
+				error_msg_rpc(clnt_sperror(mclient, " "));
+				goto fail;	/* don't retry */
+			}
+			/* Connection refused */
+			if (!daemonized && prevt == 0) /* print just once */
+				error_msg_rpc(clnt_sperror(mclient, " "));
+			auth_destroy(mclient->cl_auth);
+			clnt_destroy(mclient);
+			mclient = NULL;
+			close(msock);
+			msock = -1;
+		}
+
+		/* Timeout. We are going to retry... maybe */
+		if (!bg)
+			goto fail;
+		if (!daemonized) {
+			daemonized = daemonize();
+			if (daemonized <= 0) { /* parent or error */
+				retval = -daemonized;
+				goto ret;
+			}
+		}
+		prevt = t;
+		t = time(NULL);
+		if (t >= timeout)
+			/* TODO error message */
+			goto fail;
+
+		goto retry;
+	}
+
+ prepare_kernel_data:
+
+	if (nfsvers == 2) {
+		if (status.nfsv2.fhs_status != 0) {
+			bb_error_msg("%s:%s failed, reason given by server: %s",
+				hostname, pathname,
+				nfs_strerror(status.nfsv2.fhs_status));
+			goto fail;
+		}
+		memcpy(data.root.data,
+				(char *) status.nfsv2.fhstatus_u.fhs_fhandle,
+				NFS_FHSIZE);
+		data.root.size = NFS_FHSIZE;
+		memcpy(data.old_root.data,
+				(char *) status.nfsv2.fhstatus_u.fhs_fhandle,
+				NFS_FHSIZE);
+	} else {
+		fhandle3 *my_fhandle;
+		if (status.nfsv3.fhs_status != 0) {
+			bb_error_msg("%s:%s failed, reason given by server: %s",
+				hostname, pathname,
+				nfs_strerror(status.nfsv3.fhs_status));
+			goto fail;
+		}
+		my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
+		memset(data.old_root.data, 0, NFS_FHSIZE);
+		memset(&data.root, 0, sizeof(data.root));
+		data.root.size = my_fhandle->fhandle3_len;
+		memcpy(data.root.data,
+				(char *) my_fhandle->fhandle3_val,
+				my_fhandle->fhandle3_len);
+
+		data.flags |= NFS_MOUNT_VER3;
+	}
+
+	/* Create nfs socket for kernel */
+	if (tcp) {
+		if (nfs_mount_version < 3) {
+			bb_error_msg("NFS over TCP is not supported");
+			goto fail;
+		}
+		fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	} else
+		fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (fsock < 0) {
+		bb_perror_msg("nfs socket");
+		goto fail;
+	}
+	if (bindresvport(fsock, 0) < 0) {
+		bb_perror_msg("nfs bindresvport");
+		goto fail;
+	}
+	if (port == 0) {
+		server_addr.sin_port = PMAPPORT;
+		port = pmap_getport(&server_addr, nfsprog, nfsvers,
+					tcp ? IPPROTO_TCP : IPPROTO_UDP);
+		if (port == 0)
+			port = NFS_PORT;
+	}
+	server_addr.sin_port = htons(port);
+
+	/* Prepare data structure for kernel */
+	data.fd = fsock;
+	memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
+	strncpy(data.hostname, hostname, sizeof(data.hostname));
+
+	/* Clean up */
+	auth_destroy(mclient->cl_auth);
+	clnt_destroy(mclient);
+	close(msock);
+	msock = -1;
+
+	if (bg) {
+		/* We must wait until mount directory is available */
+		struct stat statbuf;
+		int delay = 1;
+		while (stat(mp->mnt_dir, &statbuf) == -1) {
+			if (!daemonized) {
+				daemonized = daemonize();
+				if (daemonized <= 0) { /* parent or error */
+/* FIXME: parent doesn't close fsock - ??! */
+					retval = -daemonized;
+					goto ret;
+				}
+			}
+			sleep(delay);	/* 1, 2, 4, 8, 16, 30, ... */
+			delay *= 2;
+			if (delay > 30)
+				delay = 30;
+		}
+	}
+
+	/* Perform actual mount */
+ do_mount:
+	retval = mount_it_now(mp, vfsflags, (char*)&data);
+	goto ret;
+
+	/* Abort */
+ fail:
+	if (msock >= 0) {
+		if (mclient) {
+			auth_destroy(mclient->cl_auth);
+			clnt_destroy(mclient);
+		}
+		close(msock);
+	}
+	if (fsock >= 0)
+		close(fsock);
+
+ ret:
+	free(hostname);
+	free(mounthost);
+	free(filteropts);
+	return retval;
+}
+
+#else // !ENABLE_FEATURE_MOUNT_NFS
+
+/* Linux 2.6.23+ supports nfs mounts with options passed as a string.
+ * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
+ * (However, note that then you lose any chances that NFS over IPv6 would work).
+ */
+static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
+{
+	len_and_sockaddr *lsa;
+	char *opts;
+	char *end;
+	char *dotted;
+	int ret;
+
+# if ENABLE_FEATURE_IPV6
+	end = strchr(mp->mnt_fsname, ']');
+	if (end && end[1] == ':')
+		end++;
+	else
+# endif
+		/* mount_main() guarantees that ':' is there */
+		end = strchr(mp->mnt_fsname, ':');
+
+	*end = '\0';
+	lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
+	*end = ':';
+	dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+	if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
+	opts = xasprintf("%s%saddr=%s",
+		filteropts ? filteropts : "",
+		filteropts ? "," : "",
+		dotted
+	);
+	if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
+	ret = mount_it_now(mp, vfsflags, opts);
+	if (ENABLE_FEATURE_CLEAN_UP) free(opts);
+
+	return ret;
+}
+
+#endif // !ENABLE_FEATURE_MOUNT_NFS
+
+// Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
+// type detection.  Returns 0 for success, nonzero for failure.
+// NB: mp->xxx fields may be trashed on exit
+static int singlemount(struct mntent *mp, int ignore_busy)
+{
+	int rc = -1;
+	unsigned long vfsflags;
+	char *loopFile = NULL, *filteropts = NULL;
+	llist_t *fl = NULL;
+	struct stat st;
+
+	errno = 0;
+
+	vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
+
+	// Treat fstype "auto" as unspecified
+	if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
+		mp->mnt_type = NULL;
+
+	// Might this be a virtual filesystem?
+	if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
+		char *args[35];
+		char *s;
+		int n;
+		// fsname: "cmd#arg1#arg2..."
+		// WARNING: allows execution of arbitrary commands!
+		// Try "mount 'sh#-c#sh' bogus_dir".
+		// It is safe ONLY because non-root
+		// cannot use two-argument mount command
+		// and using one-argument "mount 'sh#-c#sh'" doesn't work:
+		// "mount: can't find sh#-c#sh in /etc/fstab"
+		// (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
+
+		s = mp->mnt_fsname;
+		n = 0;
+		args[n++] = s;
+		while (*s && n < 35 - 2) {
+			if (*s++ == '#' && *s != '#') {
+				s[-1] = '\0';
+				args[n++] = s;
+			}
+		}
+		args[n++] = mp->mnt_dir;
+		args[n] = NULL;
+		rc = spawn_and_wait(args);
+		goto report_error;
+	}
+
+	// Might this be an CIFS filesystem?
+	if (ENABLE_FEATURE_MOUNT_CIFS
+	 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
+	 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
+	 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
+	) {
+		int len;
+		char c;
+		char *hostname, *share;
+		char *dotted, *ip;
+		len_and_sockaddr *lsa;
+
+		// Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
+
+		hostname = mp->mnt_fsname + 2;
+		len = strcspn(hostname, "/\\");
+		share = hostname + len + 1;
+		if (len == 0          // 3rd char is a [back]slash (IOW: empty hostname)
+		 || share[-1] == '\0' // no [back]slash after hostname
+		 || share[0] == '\0'  // empty share name
+		) {
+			goto report_error;
+		}
+		c = share[-1];
+		share[-1] = '\0';
+		len = strcspn(share, "/\\");
+
+		// "unc=\\hostname\share" option is mandatory
+		// after CIFS option parsing was rewritten in Linux 3.4.
+		// Must use backslashes.
+		// If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
+		{
+			char *unc = xasprintf(
+				share[len] != '\0'  /* "/dir1/dir2" exists? */
+					? "unc=\\\\%s\\%.*s,prefixpath=%s"
+					: "unc=\\\\%s\\%.*s",
+				hostname,
+				len, share,
+				share + len + 1  /* "dir1/dir2" */
+			);
+ 			parse_mount_options(unc, &filteropts);
+ 			if (ENABLE_FEATURE_CLEAN_UP) free(unc);
+		}
+
+		lsa = host2sockaddr(hostname, 0);
+		share[-1] = c;
+		if (!lsa)
+			goto report_error;
+
+		// Insert "ip=..." option into options
+		dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+		if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
+		ip = xasprintf("ip=%s", dotted);
+		if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
+		parse_mount_options(ip, &filteropts);
+		if (ENABLE_FEATURE_CLEAN_UP) free(ip);
+
+		mp->mnt_type = (char*)"cifs";
+		rc = mount_it_now(mp, vfsflags, filteropts);
+
+		goto report_error;
+	}
+
+	// Might this be an NFS filesystem?
+	if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0)
+	 && strchr(mp->mnt_fsname, ':') != NULL
+	) {
+		if (!mp->mnt_type)
+			mp->mnt_type = (char*)"nfs";
+		rc = nfsmount(mp, vfsflags, filteropts);
+		goto report_error;
+	}
+
+	// Look at the file.  (Not found isn't a failure for remount, or for
+	// a synthetic filesystem like proc or sysfs.)
+	// (We use stat, not lstat, in order to allow
+	// mount symlink_to_file_or_blkdev dir)
+	if (!stat(mp->mnt_fsname, &st)
+	 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
+	) {
+		// Do we need to allocate a loopback device for it?
+		if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
+			loopFile = bb_simplify_path(mp->mnt_fsname);
+			mp->mnt_fsname = NULL; // will receive malloced loop dev name
+			if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ (vfsflags & MS_RDONLY)) < 0) {
+				if (errno == EPERM || errno == EACCES)
+					bb_error_msg(bb_msg_perm_denied_are_you_root);
+				else
+					bb_perror_msg("can't setup loop device");
+				return errno;
+			}
+
+		// Autodetect bind mounts
+		} else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
+			vfsflags |= MS_BIND;
+	}
+
+	// If we know the fstype (or don't need to), jump straight
+	// to the actual mount.
+	if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
+		char *next;
+		for (;;) {
+			next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
+			if (next)
+				*next = '\0';
+			rc = mount_it_now(mp, vfsflags, filteropts);
+			if (rc == 0 || !next)
+				break;
+			mp->mnt_type = next + 1;
+		}
+	} else {
+		// Loop through filesystem types until mount succeeds
+		// or we run out
+
+		// Initialize list of block backed filesystems.
+		// This has to be done here so that during "mount -a",
+		// mounts after /proc shows up can autodetect.
+		if (!fslist) {
+			fslist = get_block_backed_filesystems();
+			if (ENABLE_FEATURE_CLEAN_UP && fslist)
+				atexit(delete_block_backed_filesystems);
+		}
+
+		for (fl = fslist; fl; fl = fl->link) {
+			mp->mnt_type = fl->data;
+			rc = mount_it_now(mp, vfsflags, filteropts);
+			if (rc == 0)
+				break;
+		}
+	}
+
+	// If mount failed, clean up loop file (if any).
+	if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
+		del_loop(mp->mnt_fsname);
+		if (ENABLE_FEATURE_CLEAN_UP) {
+			free(loopFile);
+			free(mp->mnt_fsname);
+		}
+	}
+
+ report_error:
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(filteropts);
+
+	if (errno == EBUSY && ignore_busy)
+		return 0;
+	if (rc != 0)
+		bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
+	return rc;
+}
+
+// -O support
+//    -O interprets a list of filter options which select whether a mount
+// point will be mounted: only mounts with options matching *all* filtering
+// options will be selected.
+//    By default each -O filter option must be present in the list of mount
+// options, but if it is prefixed by "no" then it must be absent.
+// For example,
+//  -O a,nob,c  matches  -o a,c  but fails to match  -o a,b,c
+//              (and also fails to match  -o a  because  -o c  is absent).
+//
+// It is different from -t in that each option is matched exactly; a leading
+// "no" at the beginning of one option does not negate the rest.
+static int match_opt(const char *fs_opt_in, const char *O_opt)
+{
+	if (!O_opt)
+		return 1;
+
+	while (*O_opt) {
+		const char *fs_opt = fs_opt_in;
+		int O_len;
+		int match;
+
+		// If option begins with "no" then treat as an inverted match:
+		// matching is a failure
+		match = 0;
+		if (O_opt[0] == 'n' && O_opt[1] == 'o') {
+			match = 1;
+			O_opt += 2;
+		}
+		// Isolate the current O option
+		O_len = strchrnul(O_opt, ',') - O_opt;
+		// Check for a match against existing options
+		while (1) {
+			if (strncmp(fs_opt, O_opt, O_len) == 0
+			 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
+			) {
+				if (match)
+					return 0;  // "no" prefix, but option found
+				match = 1;  // current O option found, go check next one
+				break;
+			}
+			fs_opt = strchr(fs_opt, ',');
+			if (!fs_opt)
+				break;
+			fs_opt++;
+		}
+		if (match == 0)
+			return 0;     // match wanted but not found
+		if (O_opt[O_len] == '\0') // end?
+			break;
+		// Step to the next O option
+		O_opt += O_len + 1;
+	}
+	// If we get here then everything matched
+	return 1;
+}
+
+// Parse options, if necessary parse fstab/mtab, and call singlemount for
+// each directory to be mounted.
+int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mount_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *cmdopts = xzalloc(1);
+	char *fstype = NULL;
+	char *O_optmatch = NULL;
+	char *storage_path;
+	llist_t *lst_o = NULL;
+	const char *fstabname;
+	FILE *fstab;
+	int i, j;
+	int rc = EXIT_SUCCESS;
+	unsigned long cmdopt_flags;
+	unsigned opt;
+	struct mntent mtpair[2], *mtcur = mtpair;
+	IF_NOT_DESKTOP(const int nonroot = 0;)
+
+	IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
+
+	INIT_G();
+
+	// Parse long options, like --bind and --move.  Note that -o option
+	// and --option are synonymous.  Yes, this means --remount,rw works.
+	for (i = j = 1; argv[i]; i++) {
+		if (argv[i][0] == '-' && argv[i][1] == '-')
+			append_mount_options(&cmdopts, argv[i] + 2);
+		else
+			argv[j++] = argv[i];
+	}
+	argv[j] = NULL;
+
+	// Parse remaining options
+	// Max 2 params; -o is a list, -v is a counter
+	opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
+	opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
+			IF_FEATURE_MOUNT_VERBOSE(, &verbose));
+	while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
+	if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
+	if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
+	argv += optind;
+
+	// If we have no arguments, show currently mounted filesystems
+	if (!argv[0]) {
+		if (!(opt & OPT_a)) {
+			FILE *mountTable = setmntent(bb_path_mtab_file, "r");
+
+			if (!mountTable)
+				bb_error_msg_and_die("no %s", bb_path_mtab_file);
+
+			while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
+								GETMNTENT_BUFSIZE))
+			{
+				// Don't show rootfs. FIXME: why??
+				// util-linux 2.12a happily shows rootfs...
+				//if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
+
+				if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
+					printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
+							mtpair->mnt_dir, mtpair->mnt_type,
+							mtpair->mnt_opts);
+			}
+			if (ENABLE_FEATURE_CLEAN_UP)
+				endmntent(mountTable);
+			return EXIT_SUCCESS;
+		}
+		storage_path = NULL;
+	} else {
+		// When we have two arguments, the second is the directory and we can
+		// skip looking at fstab entirely.  We can always abspath() the directory
+		// argument when we get it.
+		if (argv[1]) {
+			if (nonroot)
+				bb_error_msg_and_die(bb_msg_you_must_be_root);
+			mtpair->mnt_fsname = argv[0];
+			mtpair->mnt_dir = argv[1];
+			mtpair->mnt_type = fstype;
+			mtpair->mnt_opts = cmdopts;
+			resolve_mount_spec(&mtpair->mnt_fsname);
+			rc = singlemount(mtpair, /*ignore_busy:*/ 0);
+			return rc;
+		}
+		storage_path = bb_simplify_path(argv[0]); // malloced
+	}
+
+	// Past this point, we are handling either "mount -a [opts]"
+	// or "mount [opts] single_param"
+
+	cmdopt_flags = parse_mount_options(cmdopts, NULL);
+	if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
+		bb_error_msg_and_die(bb_msg_you_must_be_root);
+
+	// If we have a shared subtree flag, don't worry about fstab or mtab.
+	if (ENABLE_FEATURE_MOUNT_FLAGS
+	 && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
+	) {
+		// verbose_mount(source, target, type, flags, data)
+		rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
+		if (rc)
+			bb_simple_perror_msg_and_die(argv[0]);
+		return rc;
+	}
+
+	// Open either fstab or mtab
+	fstabname = "/etc/fstab";
+	if (cmdopt_flags & MS_REMOUNT) {
+		// WARNING. I am not sure this matches util-linux's
+		// behavior. It's possible util-linux does not
+		// take -o opts from mtab (takes only mount source).
+		fstabname = bb_path_mtab_file;
+	}
+	fstab = setmntent(fstabname, "r");
+	if (!fstab)
+		bb_perror_msg_and_die("can't read '%s'", fstabname);
+
+	// Loop through entries until we find what we're looking for
+	memset(mtpair, 0, sizeof(mtpair));
+	for (;;) {
+		struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
+
+		// Get next fstab entry
+		if (!getmntent_r(fstab, mtcur, getmntent_buf
+					+ (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
+				GETMNTENT_BUFSIZE/2)
+		) { // End of fstab/mtab is reached
+			mtcur = mtother; // the thing we found last time
+			break;
+		}
+
+		// If we're trying to mount something specific and this isn't it,
+		// skip it.  Note we must match the exact text in fstab (ala
+		// "proc") or a full path from root
+		if (argv[0]) {
+
+			// Is this what we're looking for?
+			if (strcmp(argv[0], mtcur->mnt_fsname) != 0
+			 && strcmp(storage_path, mtcur->mnt_fsname) != 0
+			 && strcmp(argv[0], mtcur->mnt_dir) != 0
+			 && strcmp(storage_path, mtcur->mnt_dir) != 0
+			) {
+				continue; // no
+			}
+
+			// Remember this entry.  Something later may have
+			// overmounted it, and we want the _last_ match.
+			mtcur = mtother;
+
+		// If we're mounting all
+		} else {
+			struct mntent *mp;
+			// No, mount -a won't mount anything,
+			// even user mounts, for mere humans
+			if (nonroot)
+				bb_error_msg_and_die(bb_msg_you_must_be_root);
+
+			// Does type match? (NULL matches always)
+			if (!match_fstype(mtcur, fstype))
+				continue;
+
+			// Skip noauto and swap anyway
+			if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
+			// swap is bogus "fstype", parse_mount_options can't check fstypes
+			 || strcasecmp(mtcur->mnt_type, "swap") == 0
+			) {
+				continue;
+			}
+
+			// Does (at least one) option match?
+			// (NULL matches always)
+			if (!match_opt(mtcur->mnt_opts, O_optmatch))
+				continue;
+
+			resolve_mount_spec(&mtcur->mnt_fsname);
+
+			// NFS mounts want this to be xrealloc-able
+			mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
+
+			// If nothing is mounted on this directory...
+			// (otherwise repeated "mount -a" mounts everything again)
+			mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
+			// We do not check fsname match of found mount point -
+			// "/" may have fsname of "/dev/root" while fstab
+			// says "/dev/something_else".
+			if (mp) {
+				if (verbose) {
+					bb_error_msg("according to %s, "
+						"%s is already mounted on %s",
+						bb_path_mtab_file,
+						mp->mnt_fsname, mp->mnt_dir);
+				}
+			} else {
+				// ...mount this thing
+				if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
+					// Count number of failed mounts
+					rc++;
+				}
+			}
+			free(mtcur->mnt_opts);
+		}
+	}
+
+	// End of fstab/mtab is reached.
+	// Were we looking for something specific?
+	if (argv[0]) { // yes
+		unsigned long l;
+
+		// If we didn't find anything, complain
+		if (!mtcur->mnt_fsname)
+			bb_error_msg_and_die("can't find %s in %s",
+				argv[0], fstabname);
+
+		// What happens when we try to "mount swap_partition"?
+		// (fstab containts "swap_partition swap swap defaults 0 0")
+		// util-linux-ng 2.13.1 does this:
+		// stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
+		// mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
+		// lstat("swap", 0x7fff62a3a640)           = -1 ENOENT (No such file or directory)
+		// write(2, "mount: mount point swap does not exist\n", 39) = 39
+		// exit_group(32)                          = ?
+#if 0
+		// In case we want to simply skip swap partitions:
+		l = parse_mount_options(mtcur->mnt_opts, NULL);
+		if ((l & MOUNT_SWAP)
+		// swap is bogus "fstype", parse_mount_options can't check fstypes
+		 || strcasecmp(mtcur->mnt_type, "swap") == 0
+		) {
+			goto ret;
+		}
+#endif
+		if (nonroot) {
+			// fstab must have "users" or "user"
+			l = parse_mount_options(mtcur->mnt_opts, NULL);
+			if (!(l & MOUNT_USERS))
+				bb_error_msg_and_die(bb_msg_you_must_be_root);
+		}
+
+		//util-linux-2.12 does not do this check.
+		//// If nothing is mounted on this directory...
+		//// (otherwise repeated "mount FOO" mounts FOO again)
+		//mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
+		//if (mp) {
+		//	bb_error_msg("according to %s, "
+		//		"%s is already mounted on %s",
+		//		bb_path_mtab_file,
+		//		mp->mnt_fsname, mp->mnt_dir);
+		//} else {
+			// ...mount the last thing we found
+			mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
+			append_mount_options(&(mtcur->mnt_opts), cmdopts);
+			resolve_mount_spec(&mtpair->mnt_fsname);
+			rc = singlemount(mtcur, /*ignore_busy:*/ 0);
+			if (ENABLE_FEATURE_CLEAN_UP)
+				free(mtcur->mnt_opts);
+		//}
+	}
+
+ //ret:
+	if (ENABLE_FEATURE_CLEAN_UP)
+		endmntent(fstab);
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(storage_path);
+		free(cmdopts);
+	}
+
+//TODO: exitcode should be ORed mask of (from "man mount"):
+// 0 success
+// 1 incorrect invocation or permissions
+// 2 system error (out of memory, cannot fork, no more loop devices)
+// 4 internal mount bug or missing nfs support in mount
+// 8 user interrupt
+//16 problems writing or locking /etc/mtab
+//32 mount failure
+//64 some mount succeeded
+	return rc;
+}
diff --git a/ap/app/busybox/src/util-linux/pivot_root.c b/ap/app/busybox/src/util-linux/pivot_root.c
new file mode 100644
index 0000000..83f01fa
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/pivot_root.c
@@ -0,0 +1,34 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pivot_root.c - Change root file system.  Based on util-linux 2.10s
+ *
+ * busyboxed by Evin Robertson
+ * pivot_root syscall stubbed by Erik Andersen, so it will compile
+ *     regardless of the kernel being used.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define pivot_root_trivial_usage
+//usage:       "NEW_ROOT PUT_OLD"
+//usage:#define pivot_root_full_usage "\n\n"
+//usage:       "Move the current root file system to PUT_OLD and make NEW_ROOT\n"
+//usage:       "the new root file system"
+
+#include "libbb.h"
+
+extern int pivot_root(const char * new_root,const char * put_old);
+
+int pivot_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pivot_root_main(int argc, char **argv)
+{
+	if (argc != 3)
+		bb_show_usage();
+
+	if (pivot_root(argv[1], argv[2]) < 0) {
+		/* prints "pivot_root: <strerror text>" */
+		bb_perror_nomsg_and_die();
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/rdate.c b/ap/app/busybox/src/util-linux/rdate.c
new file mode 100644
index 0000000..6e35cd5
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/rdate.c
@@ -0,0 +1,79 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * The Rdate command will ask a time server for the RFC 868 time
+ * and optionally set the system time.
+ *
+ * by Sterling Huxley <sterling@europa.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+*/
+
+//usage:#define rdate_trivial_usage
+//usage:       "[-sp] HOST"
+//usage:#define rdate_full_usage "\n\n"
+//usage:       "Get and possibly set the system date/time from a remote HOST\n"
+//usage:     "\n	-s	Set the system date/time (default)"
+//usage:     "\n	-p	Print the date/time"
+
+#include "libbb.h"
+
+enum { RFC_868_BIAS = 2208988800UL };
+
+static void socket_timeout(int sig UNUSED_PARAM)
+{
+	bb_error_msg_and_die("timeout connecting to time server");
+}
+
+static time_t askremotedate(const char *host)
+{
+	uint32_t nett;
+	int fd;
+
+	/* Add a timeout for dead or inaccessible servers */
+	alarm(10);
+	signal(SIGALRM, socket_timeout);
+
+	fd = create_and_connect_stream_or_die(host, bb_lookup_port("time", "tcp", 37));
+
+	if (safe_read(fd, &nett, 4) != 4)    /* read time from server */
+		bb_error_msg_and_die("%s did not send the complete time", host);
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(fd);
+
+	/* Convert from network byte order to local byte order.
+	 * RFC 868 time is the number of seconds
+	 * since 00:00 (midnight) 1 January 1900 GMT
+	 * the RFC 868 time 2,208,988,800 corresponds to 00:00  1 Jan 1970 GMT
+	 * Subtract the RFC 868 time to get Linux epoch.
+	 */
+
+	return ntohl(nett) - RFC_868_BIAS;
+}
+
+int rdate_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rdate_main(int argc UNUSED_PARAM, char **argv)
+{
+	time_t remote_time;
+	unsigned flags;
+
+	opt_complementary = "-1";
+	flags = getopt32(argv, "sp");
+
+	remote_time = askremotedate(argv[optind]);
+
+	if (!(flags & 2)) { /* no -p (-s may be present) */
+		time_t current_time;
+
+		time(&current_time);
+		if (current_time == remote_time)
+			bb_error_msg("current time matches remote time");
+		else
+			if (stime(&remote_time) < 0)
+				bb_perror_msg_and_die("can't set time of day");
+	}
+
+	if (flags != 1) /* not lone -s */
+		printf("%s", ctime(&remote_time));
+
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/rdev.c b/ap/app/busybox/src/util-linux/rdev.c
new file mode 100644
index 0000000..4652817
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/rdev.c
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * rdev - print device node associated with a filesystem
+ *
+ * Copyright (c) 2008 Nuovation System Designs, LLC
+ *   Grant Erickson <gerickson@nuovations.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ *
+ */
+
+//usage:#define rdev_trivial_usage
+//usage:       ""
+//usage:#define rdev_full_usage "\n\n"
+//usage:       "Print the device node associated with the filesystem mounted at '/'"
+//usage:
+//usage:#define rdev_example_usage
+//usage:       "$ rdev\n"
+//usage:       "/dev/mtdblock9 /\n"
+
+#include "libbb.h"
+
+int rdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rdev_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	const char *root_device = find_block_device("/");
+
+	if (root_device) {
+		printf("%s /\n", root_device);
+		return EXIT_SUCCESS;
+	}
+	return EXIT_FAILURE;
+}
diff --git a/ap/app/busybox/src/util-linux/readprofile.c b/ap/app/busybox/src/util-linux/readprofile.c
new file mode 100644
index 0000000..974fe89
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/readprofile.c
@@ -0,0 +1,263 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  readprofile.c - used to read /proc/profile
+ *
+ *  Copyright (C) 1994,1996 Alessandro Rubini (rubini@ipvvis.unipv.it)
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/*
+ * 1999-02-22 Arkadiusz Mickiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ * 1999-09-01 Stephane Eranian <eranian@cello.hpl.hp.com>
+ * - 64bit clean patch
+ * 3Feb2001 Andrew Morton <andrewm@uow.edu.au>
+ * - -M option to write profile multiplier.
+ * 2001-11-07 Werner Almesberger <wa@almesberger.net>
+ * - byte order auto-detection and -n option
+ * 2001-11-09 Werner Almesberger <wa@almesberger.net>
+ * - skip step size (index 0)
+ * 2002-03-09 John Levon <moz@compsoc.man.ac.uk>
+ * - make maplineno do something
+ * 2002-11-28 Mads Martin Joergensen +
+ * - also try /boot/System.map-`uname -r`
+ * 2003-04-09 Werner Almesberger <wa@almesberger.net>
+ * - fixed off-by eight error and improved heuristics in byte order detection
+ * 2003-08-12 Nikita Danilov <Nikita@Namesys.COM>
+ * - added -s option; example of use:
+ * "readprofile -s -m /boot/System.map-test | grep __d_lookup | sort -n -k3"
+ *
+ * Taken from util-linux and adapted for busybox by
+ * Paul Mundt <lethal@linux-sh.org>.
+ */
+
+//usage:#define readprofile_trivial_usage
+//usage:       "[OPTIONS]"
+//usage:#define readprofile_full_usage "\n\n"
+//usage:       "	-m mapfile	(Default: /boot/System.map)"
+//usage:     "\n	-p profile	(Default: /proc/profile)"
+//usage:     "\n	-M NUM		Set the profiling multiplier to NUM"
+//usage:     "\n	-i		Print only info about the sampling step"
+//usage:     "\n	-v		Verbose"
+//usage:     "\n	-a		Print all symbols, even if count is 0"
+//usage:     "\n	-b		Print individual histogram-bin counts"
+//usage:     "\n	-s		Print individual counters within functions"
+//usage:     "\n	-r		Reset all the counters (root only)"
+//usage:     "\n	-n		Disable byte order auto-detection"
+
+#include "libbb.h"
+#include <sys/utsname.h>
+
+#define S_LEN 128
+
+/* These are the defaults */
+static const char defaultmap[] ALIGN1 = "/boot/System.map";
+static const char defaultpro[] ALIGN1 = "/proc/profile";
+
+int readprofile_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int readprofile_main(int argc UNUSED_PARAM, char **argv)
+{
+	FILE *map;
+	const char *mapFile, *proFile;
+	unsigned long indx = 1;
+	size_t len;
+	uint64_t add0 = 0;
+	unsigned int step;
+	unsigned int *buf, total, fn_len;
+	unsigned long long fn_add, next_add;     /* current and next address */
+	char fn_name[S_LEN], next_name[S_LEN];   /* current and next name */
+	char mapline[S_LEN];
+	char mode[8];
+	int maplineno = 1;
+	int header_printed;
+	int multiplier = 0;
+	unsigned opt;
+	enum {
+		OPT_M = (1 << 0),
+		OPT_m = (1 << 1),
+		OPT_p = (1 << 2),
+		OPT_n = (1 << 3),
+		OPT_a = (1 << 4),
+		OPT_b = (1 << 5),
+		OPT_s = (1 << 6),
+		OPT_i = (1 << 7),
+		OPT_r = (1 << 8),
+		OPT_v = (1 << 9),
+	};
+#define optMult    (opt & OPT_M)
+#define optNative  (opt & OPT_n)
+#define optAll     (opt & OPT_a)
+#define optBins    (opt & OPT_b)
+#define optSub     (opt & OPT_s)
+#define optInfo    (opt & OPT_i)
+#define optReset   (opt & OPT_r)
+#define optVerbose (opt & OPT_v)
+
+#define next (current^1)
+
+	proFile = defaultpro;
+	mapFile = defaultmap;
+
+	opt_complementary = "M+"; /* -M N */
+	opt = getopt32(argv, "M:m:p:nabsirv", &multiplier, &mapFile, &proFile);
+
+	if (opt & (OPT_M|OPT_r)) { /* mult or reset, or both */
+		int fd, to_write;
+
+		/*
+		 * When writing the multiplier, if the length of the write is
+		 * not sizeof(int), the multiplier is not changed
+		 */
+		to_write = sizeof(int);
+		if (!optMult)
+			to_write = 1;  /* sth different from sizeof(int) */
+
+		fd = xopen(defaultpro, O_WRONLY);
+		xwrite(fd, &multiplier, to_write);
+		close(fd);
+		return EXIT_SUCCESS;
+	}
+
+	/*
+	 * Use an fd for the profiling buffer, to skip stdio overhead
+	 */
+	len = MAXINT(ssize_t);
+	buf = xmalloc_xopen_read_close(proFile, &len);
+	if (!optNative) {
+		int entries = len / sizeof(*buf);
+		int big = 0, small = 0, i;
+		unsigned *p;
+
+		for (p = buf+1; p < buf+entries; p++) {
+			if (*p & ~0U << (sizeof(*buf)*4))
+				big++;
+			if (*p & ((1 << (sizeof(*buf)*4))-1))
+				small++;
+		}
+		if (big > small) {
+			bb_error_msg("assuming reversed byte order, "
+				"use -n to force native byte order");
+			for (p = buf; p < buf+entries; p++)
+				for (i = 0; i < sizeof(*buf)/2; i++) {
+					unsigned char *b = (unsigned char *) p;
+					unsigned char tmp;
+
+					tmp = b[i];
+					b[i] = b[sizeof(*buf)-i-1];
+					b[sizeof(*buf)-i-1] = tmp;
+				}
+		}
+	}
+
+	step = buf[0];
+	if (optInfo) {
+		printf("Sampling_step: %i\n", step);
+		return EXIT_SUCCESS;
+	}
+
+	total = 0;
+
+	map = xfopen_for_read(mapFile);
+
+	while (fgets(mapline, S_LEN, map)) {
+		if (sscanf(mapline, "%llx %s %s", &fn_add, mode, fn_name) != 3)
+			bb_error_msg_and_die("%s(%i): wrong map line",
+					mapFile, maplineno);
+
+		if (!strcmp(fn_name, "_stext")) /* only elf works like this */ {
+			add0 = fn_add;
+			break;
+		}
+		maplineno++;
+	}
+
+	if (!add0)
+		bb_error_msg_and_die("can't find \"_stext\" in %s", mapFile);
+
+	/*
+	 * Main loop.
+	 */
+	while (fgets(mapline, S_LEN, map)) {
+		unsigned int this = 0;
+
+		if (sscanf(mapline, "%llx %s %s", &next_add, mode, next_name) != 3)
+			bb_error_msg_and_die("%s(%i): wrong map line",
+					mapFile, maplineno);
+
+		header_printed = 0;
+
+		/* ignore any LEADING (before a '[tT]' symbol is found)
+		   Absolute symbols */
+		if ((*mode == 'A' || *mode == '?') && total == 0) continue;
+		if (*mode != 'T' && *mode != 't'
+		 && *mode != 'W' && *mode != 'w'
+		) {
+			break;  /* only text is profiled */
+		}
+
+		if (indx >= len / sizeof(*buf))
+			bb_error_msg_and_die("profile address out of range. "
+					"Wrong map file?");
+
+		while (indx < (next_add-add0)/step) {
+			if (optBins && (buf[indx] || optAll)) {
+				if (!header_printed) {
+					printf("%s:\n", fn_name);
+					header_printed = 1;
+				}
+				printf("\t%"PRIx64"\t%u\n", (indx - 1)*step + add0, buf[indx]);
+			}
+			this += buf[indx++];
+		}
+		total += this;
+
+		if (optBins) {
+			if (optVerbose || this > 0)
+				printf("  total\t\t\t\t%u\n", this);
+		} else if ((this || optAll)
+		        && (fn_len = next_add-fn_add) != 0
+		) {
+			if (optVerbose)
+				printf("%016llx %-40s %6i %8.4f\n", fn_add,
+					fn_name, this, this/(double)fn_len);
+			else
+				printf("%6i %-40s %8.4f\n",
+					this, fn_name, this/(double)fn_len);
+			if (optSub) {
+				unsigned long long scan;
+
+				for (scan = (fn_add-add0)/step + 1;
+				     scan < (next_add-add0)/step; scan++) {
+					unsigned long long addr;
+
+					addr = (scan - 1)*step + add0;
+					printf("\t%#llx\t%s+%#llx\t%u\n",
+						addr, fn_name, addr - fn_add,
+						buf[scan]);
+				}
+			}
+		}
+
+		fn_add = next_add;
+		strcpy(fn_name, next_name);
+
+		maplineno++;
+	}
+
+	/* clock ticks, out of kernel text - probably modules */
+	printf("%6i %s\n", buf[len/sizeof(*buf)-1], "*unknown*");
+
+	/* trailer */
+	if (optVerbose)
+		printf("%016x %-40s %6i %8.4f\n",
+			0, "total", total, total/(double)(fn_add-add0));
+	else
+		printf("%6i %-40s %8.4f\n",
+			total, "total", total/(double)(fn_add-add0));
+
+	fclose(map);
+	free(buf);
+
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/rev.c b/ap/app/busybox/src/util-linux/rev.c
new file mode 100644
index 0000000..3c1b22f
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/rev.c
@@ -0,0 +1,121 @@
+/*
+ * rev implementation for busybox
+ *
+ * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//applet:IF_REV(APPLET(rev, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_REV) += rev.o
+
+//config:config REV
+//config:	bool "rev"
+//config:	default y
+//config:	help
+//config:	  Reverse lines of a file or files.
+
+//usage:#define rev_trivial_usage
+//usage:	"[FILE]..."
+//usage:#define rev_full_usage "\n\n"
+//usage:	"Reverse lines of FILE"
+
+#include "libbb.h"
+#include "unicode.h"
+
+#undef CHAR_T
+#if ENABLE_UNICODE_SUPPORT
+# define CHAR_T wchar_t
+#else
+# define CHAR_T char
+#endif
+
+/* In-place invert */
+static void strrev(CHAR_T *s, int len)
+{
+	int i;
+
+	if (len != 0) {
+		len--;
+		if (len != 0 && s[len] == '\n')
+			len--;
+	}
+
+	for (i = 0; i < len; i++, len--) {
+		CHAR_T c = s[i];
+		s[i] = s[len];
+		s[len] = c;
+	}
+}
+
+int rev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rev_main(int argc UNUSED_PARAM, char **argv)
+{
+	int retval;
+	size_t bufsize;
+	char *buf;
+
+	init_unicode();
+
+	getopt32(argv, "");
+	argv += optind;
+	if (!argv[0])
+		argv = (char **)&bb_argv_dash;
+
+	retval = EXIT_SUCCESS;
+	bufsize = 256;
+	buf = xmalloc(bufsize);
+	do {
+		size_t pos;
+		FILE *fp;
+
+		fp = fopen_or_warn_stdin(*argv++);
+		if (!fp) {
+			retval = EXIT_FAILURE;
+			continue;
+		}
+
+		pos = 0;
+		while (1) {
+			/* Read one line */
+			buf[bufsize - 1] = 1; /* not 0 */
+			if (!fgets(buf + pos, bufsize - pos, fp))
+				break; /* EOF/error */
+			if (buf[bufsize - 1] == '\0' /* fgets filled entire buffer */
+			 && buf[bufsize - 2] != '\n' /* and did not read '\n' */
+			 && !feof(fp)
+			) {
+				/* Line is too long, extend buffer */
+				pos = bufsize - 1;
+				bufsize += 64 + bufsize / 8;
+				buf = xrealloc(buf, bufsize);
+				continue;
+			}
+
+			/* Process and print it */
+#if ENABLE_UNICODE_SUPPORT
+			{
+				wchar_t *tmp = xmalloc(bufsize * sizeof(wchar_t));
+				/* Convert to wchar_t (might error out!) */
+				int len  = mbstowcs(tmp, buf, bufsize);
+				if (len >= 0) {
+					strrev(tmp, len);
+					/* Convert back to char */
+					wcstombs(buf, tmp, bufsize);
+				}
+				free(tmp);
+			}
+#else
+			strrev(buf, strlen(buf));
+#endif
+			fputs(buf, stdout);
+		}
+		fclose(fp);
+	} while (*argv);
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(buf);
+
+	fflush_stdout_and_exit(retval);
+}
diff --git a/ap/app/busybox/src/util-linux/rtcwake.c b/ap/app/busybox/src/util-linux/rtcwake.c
new file mode 100644
index 0000000..735a298
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/rtcwake.c
@@ -0,0 +1,226 @@
+/*
+ * rtcwake -- enter a system sleep state until specified wakeup time.
+ *
+ * This version was taken from util-linux and scrubbed down for busybox.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ *
+ * This uses cross-platform Linux interfaces to enter a system sleep state,
+ * and leave it no later than a specified time.  It uses any RTC framework
+ * driver that supports standard driver model wakeup flags.
+ *
+ * This is normally used like the old "apmsleep" utility, to wake from a
+ * suspend state like ACPI S1 (standby) or S3 (suspend-to-RAM).  Most
+ * platforms can implement those without analogues of BIOS, APM, or ACPI.
+ *
+ * On some systems, this can also be used like "nvram-wakeup", waking
+ * from states like ACPI S4 (suspend to disk).  Not all systems have
+ * persistent media that are appropriate for such suspend modes.
+ *
+ * The best way to set the system's RTC is so that it holds the current
+ * time in UTC.  Use the "-l" flag to tell this program that the system
+ * RTC uses a local timezone instead (maybe you dual-boot MS-Windows).
+ * That flag should not be needed on systems with adjtime support.
+ */
+
+//usage:#define rtcwake_trivial_usage
+//usage:       "[-a | -l | -u] [-d DEV] [-m MODE] [-s SEC | -t TIME]"
+//usage:#define rtcwake_full_usage "\n\n"
+//usage:       "Enter a system sleep state until specified wakeup time\n"
+//usage:	IF_LONG_OPTS(
+//usage:     "\n	-a,--auto	Read clock mode from adjtime"
+//usage:     "\n	-l,--local	Clock is set to local time"
+//usage:     "\n	-u,--utc	Clock is set to UTC time"
+//usage:     "\n	-d,--device=DEV	Specify the RTC device"
+//usage:     "\n	-m,--mode=MODE	Set the sleep state (default: standby)"
+//usage:     "\n	-s,--seconds=SEC Set the timeout in SEC seconds from now"
+//usage:     "\n	-t,--time=TIME	Set the timeout to TIME seconds from epoch"
+//usage:	)
+//usage:	IF_NOT_LONG_OPTS(
+//usage:     "\n	-a	Read clock mode from adjtime"
+//usage:     "\n	-l	Clock is set to local time"
+//usage:     "\n	-u	Clock is set to UTC time"
+//usage:     "\n	-d DEV	Specify the RTC device"
+//usage:     "\n	-m MODE	Set the sleep state (default: standby)"
+//usage:     "\n	-s SEC	Set the timeout in SEC seconds from now"
+//usage:     "\n	-t TIME	Set the timeout to TIME seconds from epoch"
+//usage:	)
+
+#include "libbb.h"
+#include "rtc_.h"
+
+#define SYS_RTC_PATH   "/sys/class/rtc/%s/device/power/wakeup"
+#define SYS_POWER_PATH "/sys/power/state"
+#define DEFAULT_MODE   "standby"
+
+static NOINLINE bool may_wakeup(const char *rtcname)
+{
+	ssize_t ret;
+	char buf[128];
+
+	/* strip "/dev/" from the rtcname here */
+	rtcname = skip_dev_pfx(rtcname);
+
+	snprintf(buf, sizeof(buf), SYS_RTC_PATH, rtcname);
+	ret = open_read_close(buf, buf, sizeof(buf));
+	if (ret < 0)
+		return false;
+
+	/* wakeup events could be disabled or not supported */
+	return strncmp(buf, "enabled\n", 8) == 0;
+}
+
+static NOINLINE void setup_alarm(int fd, time_t *wakeup, time_t rtc_time)
+{
+	struct tm *ptm;
+	struct linux_rtc_wkalrm wake;
+
+	/* The wakeup time is in POSIX time (more or less UTC).
+	 * Ideally RTCs use that same time; but PCs can't do that
+	 * if they need to boot MS-Windows.  Messy...
+	 *
+	 * When running in utc mode this process's timezone is UTC,
+	 * so we'll pass a UTC date to the RTC.
+	 *
+	 * Else mode is local so the time given to the RTC
+	 * will instead use the local time zone.
+	 */
+	ptm = localtime(wakeup);
+
+	wake.time.tm_sec = ptm->tm_sec;
+	wake.time.tm_min = ptm->tm_min;
+	wake.time.tm_hour = ptm->tm_hour;
+	wake.time.tm_mday = ptm->tm_mday;
+	wake.time.tm_mon = ptm->tm_mon;
+	wake.time.tm_year = ptm->tm_year;
+	/* wday, yday, and isdst fields are unused by Linux */
+	wake.time.tm_wday = -1;
+	wake.time.tm_yday = -1;
+	wake.time.tm_isdst = -1;
+
+	/* many rtc alarms only support up to 24 hours from 'now',
+	 * so use the "more than 24 hours" request only if we must
+	 */
+	if ((rtc_time + (24 * 60 * 60)) > *wakeup) {
+		xioctl(fd, RTC_ALM_SET, &wake.time);
+		xioctl(fd, RTC_AIE_ON, 0);
+	} else {
+		/* avoid an extra AIE_ON call */
+		wake.enabled = 1;
+		xioctl(fd, RTC_WKALM_SET, &wake);
+	}
+}
+
+#define RTCWAKE_OPT_AUTO         0x01
+#define RTCWAKE_OPT_LOCAL        0x02
+#define RTCWAKE_OPT_UTC          0x04
+#define RTCWAKE_OPT_DEVICE       0x08
+#define RTCWAKE_OPT_SUSPEND_MODE 0x10
+#define RTCWAKE_OPT_SECONDS      0x20
+#define RTCWAKE_OPT_TIME         0x40
+
+int rtcwake_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rtcwake_main(int argc UNUSED_PARAM, char **argv)
+{
+	time_t rtc_time;
+
+	unsigned opt;
+	const char *rtcname = NULL;
+	const char *suspend;
+	const char *opt_seconds;
+	const char *opt_time;
+
+	time_t sys_time;
+	time_t alarm_time = 0;
+	unsigned seconds = 0;
+	int utc = -1;
+	int fd;
+
+#if ENABLE_LONG_OPTS
+	static const char rtcwake_longopts[] ALIGN1 =
+		"auto\0"    No_argument "a"
+		"local\0"   No_argument "l"
+		"utc\0"     No_argument "u"
+		"device\0"  Required_argument "d"
+		"mode\0"    Required_argument "m"
+		"seconds\0" Required_argument "s"
+		"time\0"    Required_argument "t"
+		;
+	applet_long_options = rtcwake_longopts;
+#endif
+	opt = getopt32(argv, "alud:m:s:t:", &rtcname, &suspend, &opt_seconds, &opt_time);
+
+	/* this is the default
+	if (opt & RTCWAKE_OPT_AUTO)
+		utc = -1;
+	*/
+	if (opt & (RTCWAKE_OPT_UTC | RTCWAKE_OPT_LOCAL))
+		utc = opt & RTCWAKE_OPT_UTC;
+	if (!(opt & RTCWAKE_OPT_SUSPEND_MODE))
+		suspend = DEFAULT_MODE;
+	if (opt & RTCWAKE_OPT_SECONDS)
+		/* alarm time, seconds-to-sleep (relative) */
+		seconds = xatoi(opt_seconds);
+	if (opt & RTCWAKE_OPT_TIME)
+		/* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */
+		alarm_time = xatol(opt_time);
+
+	if (!alarm_time && !seconds)
+		bb_error_msg_and_die("must provide wake time");
+
+	if (utc == -1)
+		utc = rtc_adjtime_is_utc();
+
+	/* the rtcname is relative to /dev */
+	xchdir("/dev");
+
+	/* this RTC must exist and (if we'll sleep) be wakeup-enabled */
+	fd = rtc_xopen(&rtcname, O_RDONLY);
+
+	if (strcmp(suspend, "on") && !may_wakeup(rtcname))
+		bb_error_msg_and_die("%s not enabled for wakeup events", rtcname);
+
+	/* relative or absolute alarm time, normalized to time_t */
+	sys_time = time(NULL);
+	{
+		struct tm tm_time;
+		rtc_read_tm(&tm_time, fd);
+		rtc_time = rtc_tm2time(&tm_time, utc);
+	}
+
+
+	if (alarm_time) {
+		if (alarm_time < sys_time)
+			bb_error_msg_and_die("time doesn't go backward to %s", ctime(&alarm_time));
+		alarm_time += sys_time - rtc_time;
+	} else
+		alarm_time = rtc_time + seconds + 1;
+	setup_alarm(fd, &alarm_time, rtc_time);
+
+	sync();
+	printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time));
+	fflush_all();
+	usleep(10 * 1000);
+
+	if (strcmp(suspend, "on"))
+		xopen_xwrite_close(SYS_POWER_PATH, suspend);
+	else {
+		/* "fake" suspend ... we'll do the delay ourselves */
+		unsigned long data;
+
+		do {
+			ssize_t ret = safe_read(fd, &data, sizeof(data));
+			if (ret < 0) {
+				bb_perror_msg("rtc read");
+				break;
+			}
+		} while (!(data & RTC_AF));
+	}
+
+	xioctl(fd, RTC_AIE_OFF, 0);
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(fd);
+
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/script.c b/ap/app/busybox/src/util-linux/script.c
new file mode 100644
index 0000000..8fb991d
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/script.c
@@ -0,0 +1,204 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * script implementation for busybox
+ *
+ * pascal.bellard@ads-lu.com
+ *
+ * Based on code from util-linux v 2.12r
+ * Copyright (c) 1980
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define script_trivial_usage
+//usage:       "[-afq" IF_SCRIPTREPLAY("t") "] [-c PROG] [OUTFILE]"
+//usage:#define script_full_usage "\n\n"
+//usage:       "	-a	Append output"
+//usage:     "\n	-c PROG	Run PROG, not shell"
+//usage:     "\n	-f	Flush output after each write"
+//usage:     "\n	-q	Quiet"
+//usage:	IF_SCRIPTREPLAY(
+//usage:     "\n	-t	Send timing to stderr"
+//usage:	)
+
+#include "libbb.h"
+
+int script_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int script_main(int argc UNUSED_PARAM, char **argv)
+{
+	int opt;
+	int mode;
+	int child_pid;
+	int attr_ok; /* NB: 0: ok */
+	int winsz_ok;
+	int pty;
+	char pty_line[GETPTY_BUFSIZE];
+	struct termios tt, rtt;
+	struct winsize win;
+	const char *fname = "typescript";
+	const char *shell;
+	char shell_opt[] = "-i";
+	char *shell_arg = NULL;
+	enum {
+		OPT_a = (1 << 0),
+		OPT_c = (1 << 1),
+		OPT_f = (1 << 2),
+		OPT_q = (1 << 3),
+		OPT_t = (1 << 4),
+	};
+
+#if ENABLE_LONG_OPTS
+	static const char getopt_longopts[] ALIGN1 =
+		"append\0"  No_argument       "a"
+		"command\0" Required_argument "c"
+		"flush\0"   No_argument       "f"
+		"quiet\0"   No_argument       "q"
+		IF_SCRIPTREPLAY("timing\0" No_argument "t")
+		;
+
+	applet_long_options = getopt_longopts;
+#endif
+
+	opt_complementary = "?1"; /* max one arg */
+	opt = getopt32(argv, "ac:fq" IF_SCRIPTREPLAY("t") , &shell_arg);
+	//argc -= optind;
+	argv += optind;
+	if (argv[0]) {
+		fname = argv[0];
+	}
+	mode = O_CREAT|O_TRUNC|O_WRONLY;
+	if (opt & OPT_a) {
+		mode = O_CREAT|O_APPEND|O_WRONLY;
+	}
+	if (opt & OPT_c) {
+		shell_opt[1] = 'c';
+	}
+	if (!(opt & OPT_q)) {
+		printf("Script started, file is %s\n", fname);
+	}
+	shell = get_shell_name();
+
+	pty = xgetpty(pty_line);
+
+	/* get current stdin's tty params */
+	attr_ok = tcgetattr(0, &tt);
+	winsz_ok = ioctl(0, TIOCGWINSZ, (char *)&win);
+
+	rtt = tt;
+	cfmakeraw(&rtt);
+	rtt.c_lflag &= ~ECHO;
+	tcsetattr(0, TCSAFLUSH, &rtt);
+
+	/* "script" from util-linux exits when child exits,
+	 * we wouldn't wait for EOF from slave pty
+	 * (output may be produced by grandchildren of child) */
+	signal(SIGCHLD, record_signo);
+
+	/* TODO: SIGWINCH? pass window size changes down to slave? */
+
+	child_pid = xvfork();
+
+	if (child_pid) {
+		/* parent */
+#define buf bb_common_bufsiz1
+		struct pollfd pfd[2];
+		int outfd, count, loop;
+		double oldtime = ENABLE_SCRIPTREPLAY ? time(NULL) : 0;
+		smallint fd_count = 2;
+
+		outfd = xopen(fname, mode);
+		pfd[0].fd = pty;
+		pfd[0].events = POLLIN;
+		pfd[1].fd = STDIN_FILENO;
+		pfd[1].events = POLLIN;
+		ndelay_on(pty); /* this descriptor is not shared, can do this */
+		/* ndelay_on(STDIN_FILENO); - NO, stdin can be shared! Pity :( */
+
+		/* copy stdin to pty master input,
+		 * copy pty master output to stdout and file */
+		/* TODO: don't use full_write's, use proper write buffering */
+		while (fd_count && !bb_got_signal) {
+			/* not safe_poll! we want SIGCHLD to EINTR poll */
+			if (poll(pfd, fd_count, -1) < 0 && errno != EINTR) {
+				/* If child exits too quickly, we may get EIO:
+				 * for example, try "script -c true" */
+				break;
+			}
+			if (pfd[0].revents) {
+				errno = 0;
+				count = safe_read(pty, buf, sizeof(buf));
+				if (count <= 0 && errno != EAGAIN) {
+					/* err/eof from pty: exit */
+					goto restore;
+				}
+				if (count > 0) {
+					if (ENABLE_SCRIPTREPLAY && (opt & OPT_t)) {
+						struct timeval tv;
+						double newtime;
+
+						gettimeofday(&tv, NULL);
+						newtime = tv.tv_sec + (double) tv.tv_usec / 1000000;
+						fprintf(stderr, "%f %u\n", newtime - oldtime, count);
+						oldtime = newtime;
+					}
+					full_write(STDOUT_FILENO, buf, count);
+					full_write(outfd, buf, count);
+					if (opt & OPT_f) {
+						fsync(outfd);
+					}
+				}
+			}
+			if (pfd[1].revents) {
+				count = safe_read(STDIN_FILENO, buf, sizeof(buf));
+				if (count <= 0) {
+					/* err/eof from stdin: don't read stdin anymore */
+					pfd[1].revents = 0;
+					fd_count--;
+				} else {
+					full_write(pty, buf, count);
+				}
+			}
+		}
+		/* If loop was exited because SIGCHLD handler set bb_got_signal,
+		 * there still can be some buffered output. But dont loop forever:
+		 * we won't pump orphaned grandchildren's output indefinitely.
+		 * Testcase: running this in script:
+		 *      exec dd if=/dev/zero bs=1M count=1
+		 * must have "1+0 records in, 1+0 records out" captured too.
+		 * (util-linux's script doesn't do this. buggy :) */
+		loop = 999;
+		/* pty is in O_NONBLOCK mode, we exit as soon as buffer is empty */
+		while (--loop && (count = safe_read(pty, buf, sizeof(buf))) > 0) {
+			full_write(STDOUT_FILENO, buf, count);
+			full_write(outfd, buf, count);
+		}
+ restore:
+		if (attr_ok == 0)
+			tcsetattr(0, TCSAFLUSH, &tt);
+		if (!(opt & OPT_q))
+			printf("Script done, file is %s\n", fname);
+		return EXIT_SUCCESS;
+	}
+
+	/* child: make pty slave to be input, output, error; run shell */
+	close(pty); /* close pty master */
+	/* open pty slave to fd 0,1,2 */
+	close(0);
+	xopen(pty_line, O_RDWR); /* uses fd 0 */
+	xdup2(0, 1);
+	xdup2(0, 2);
+	/* copy our original stdin tty's parameters to pty */
+	if (attr_ok == 0)
+		tcsetattr(0, TCSAFLUSH, &tt);
+	if (winsz_ok == 0)
+		ioctl(0, TIOCSWINSZ, (char *)&win);
+	/* set pty as a controlling tty */
+	setsid();
+	ioctl(0, TIOCSCTTY, 0 /* 0: don't forcibly steal */);
+
+	/* Non-ignored signals revert to SIG_DFL on exec anyway */
+	/*signal(SIGCHLD, SIG_DFL);*/
+	execl(shell, shell, shell_opt, shell_arg, (char *) NULL);
+	bb_simple_perror_msg_and_die(shell);
+}
diff --git a/ap/app/busybox/src/util-linux/scriptreplay.c b/ap/app/busybox/src/util-linux/scriptreplay.c
new file mode 100644
index 0000000..382f56d
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/scriptreplay.c
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * scriptreplay - play back typescripts, using timing information
+ *
+ * pascal.bellard@ads-lu.com
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ */
+
+//usage:#define scriptreplay_trivial_usage
+//usage:       "timingfile [typescript [divisor]]"
+//usage:#define scriptreplay_full_usage "\n\n"
+//usage:       "Play back typescripts, using timing information"
+
+#include "libbb.h"
+
+int scriptreplay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int scriptreplay_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *script = "typescript";
+	double delay, factor = 1000000.0;
+	int fd;
+	unsigned long count;
+	FILE *tfp;
+
+	if (!argv[1])
+		bb_show_usage();
+
+	if (argv[2]) {
+		script = argv[2];
+		if (argv[3])
+			factor /= atof(argv[3]);
+	}
+
+	tfp = xfopen_for_read(argv[1]);
+	fd = xopen(script, O_RDONLY);
+	while (fscanf(tfp, "%lf %lu\n", &delay, &count) == 2) {
+		usleep(delay * factor);
+		bb_copyfd_exact_size(fd, STDOUT_FILENO, count);
+	}
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		close(fd);
+		fclose(tfp);
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/util-linux/setarch.c b/ap/app/busybox/src/util-linux/setarch.c
new file mode 100644
index 0000000..7b9421a
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/setarch.c
@@ -0,0 +1,61 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * linux32/linux64 allows for changing uname emulation.
+ *
+ * Copyright 2002 Andi Kleen, SuSE Labs.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define setarch_trivial_usage
+//usage:       "personality PROG ARGS"
+//usage:#define setarch_full_usage "\n\n"
+//usage:       "Personality may be:\n"
+//usage:       "	linux32		Set 32bit uname emulation\n"
+//usage:       "	linux64		Set 64bit uname emulation"
+//usage:
+//usage:#define linux32_trivial_usage NOUSAGE_STR
+//usage:#define linux32_full_usage ""
+//usage:
+//usage:#define linux64_trivial_usage NOUSAGE_STR
+//usage:#define linux64_full_usage ""
+
+#include <sys/personality.h>
+
+#include "libbb.h"
+
+int setarch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int setarch_main(int argc UNUSED_PARAM, char **argv)
+{
+	int pers;
+
+	/* Figure out what personality we are supposed to switch to ...
+	 * we can be invoked as either:
+	 * argv[0],argv[1] == "setarch","personality"
+	 * argv[0]         == "personality"
+	 */
+	if (ENABLE_SETARCH && applet_name[0] == 's'
+	 && argv[1] && strncpy(argv[1], "linux", 5)
+	) {
+		applet_name = argv[1];
+		argv++;
+	}
+	if (applet_name[5] == '6') /* linux64 */
+		pers = PER_LINUX;
+	else if (applet_name[5] == '3') /* linux32 */
+		pers = PER_LINUX32;
+	else
+		bb_show_usage();
+
+	argv++;
+	if (argv[0] == NULL)
+		bb_show_usage();
+
+	/* Try to set personality */
+	if (personality(pers) >= 0) {
+		/* Try to execute the program */
+		BB_EXECVP(argv[0], argv);
+	}
+
+	bb_simple_perror_msg_and_die(argv[0]);
+}
diff --git a/ap/app/busybox/src/util-linux/swaponoff.c b/ap/app/busybox/src/util-linux/swaponoff.c
new file mode 100644
index 0000000..54867ec
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/swaponoff.c
@@ -0,0 +1,143 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini swapon/swapoff implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define swapon_trivial_usage
+//usage:       "[-a]" IF_FEATURE_SWAPON_PRI(" [-p PRI]") " [DEVICE]"
+//usage:#define swapon_full_usage "\n\n"
+//usage:       "Start swapping on DEVICE\n"
+//usage:     "\n	-a	Start swapping on all swap devices"
+//usage:	IF_FEATURE_SWAPON_PRI(
+//usage:     "\n	-p PRI	Set swap device priority"
+//usage:	)
+//usage:
+//usage:#define swapoff_trivial_usage
+//usage:       "[-a] [DEVICE]"
+//usage:#define swapoff_full_usage "\n\n"
+//usage:       "Stop swapping on DEVICE\n"
+//usage:     "\n	-a	Stop swapping on all swap devices"
+
+#include "libbb.h"
+#include <mntent.h>
+#ifndef __BIONIC__
+# include <sys/swap.h>
+#endif
+
+#if ENABLE_FEATURE_MOUNT_LABEL
+# include "volume_id.h"
+#else
+# define resolve_mount_spec(fsname) ((void)0)
+#endif
+
+#ifndef MNTTYPE_SWAP
+# define MNTTYPE_SWAP "swap"
+#endif
+
+#if ENABLE_FEATURE_SWAPON_PRI
+struct globals {
+	int flags;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define g_flags (G.flags)
+#else
+#define g_flags 0
+#endif
+#define INIT_G() do { } while (0)
+
+static int swap_enable_disable(char *device)
+{
+	int status;
+	struct stat st;
+
+	resolve_mount_spec(&device);
+	xstat(device, &st);
+
+#if ENABLE_DESKTOP
+	/* test for holes */
+	if (S_ISREG(st.st_mode))
+		if (st.st_blocks * (off_t)512 < st.st_size)
+			bb_error_msg("warning: swap file has holes");
+#endif
+
+	if (applet_name[5] == 'n')
+		status = swapon(device, g_flags);
+	else
+		status = swapoff(device);
+
+	if (status != 0) {
+		bb_simple_perror_msg(device);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int do_em_all(void)
+{
+	struct mntent *m;
+	FILE *f;
+	int err;
+
+	f = setmntent("/etc/fstab", "r");
+	if (f == NULL)
+		bb_perror_msg_and_die("/etc/fstab");
+
+	err = 0;
+	while ((m = getmntent(f)) != NULL) {
+		if (strcmp(m->mnt_type, MNTTYPE_SWAP) == 0) {
+			/* swapon -a should ignore entries with noauto,
+			 * but swapoff -a should process them */
+			if (applet_name[5] != 'n'
+			 || hasmntopt(m, MNTOPT_NOAUTO) == NULL
+			) {
+				err += swap_enable_disable(m->mnt_fsname);
+			}
+		}
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		endmntent(f);
+
+	return err;
+}
+
+int swap_on_off_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int swap_on_off_main(int argc UNUSED_PARAM, char **argv)
+{
+	int ret;
+
+	INIT_G();
+
+#if !ENABLE_FEATURE_SWAPON_PRI
+	ret = getopt32(argv, "a");
+#else
+	if (applet_name[5] == 'n')
+		opt_complementary = "p+";
+	ret = getopt32(argv, (applet_name[5] == 'n') ? "ap:" : "a", &g_flags);
+
+	if (ret & 2) { // -p
+		g_flags = SWAP_FLAG_PREFER |
+			((g_flags & SWAP_FLAG_PRIO_MASK) << SWAP_FLAG_PRIO_SHIFT);
+		ret &= 1;
+	}
+#endif
+
+	if (ret /* & 1: not needed */) // -a
+		return do_em_all();
+
+	argv += optind;
+	if (!*argv)
+		bb_show_usage();
+
+	/* ret = 0; redundant */
+	do {
+		ret += swap_enable_disable(*argv);
+	} while (*++argv);
+
+	return ret;
+}
diff --git a/ap/app/busybox/src/util-linux/switch_root.c b/ap/app/busybox/src/util-linux/switch_root.c
new file mode 100644
index 0000000..a301b36
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/switch_root.c
@@ -0,0 +1,219 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright 2005 Rob Landley <rob@landley.net>
+ *
+ * Switch from rootfs to another filesystem as the root of the mount tree.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define switch_root_trivial_usage
+//usage:       "[-c /dev/console] NEW_ROOT NEW_INIT [ARGS]"
+//usage:#define switch_root_full_usage "\n\n"
+//usage:       "Free initramfs and switch to another root fs:\n"
+//usage:       "chroot to NEW_ROOT, delete all in /, move NEW_ROOT to /,\n"
+//usage:       "execute NEW_INIT. PID must be 1. NEW_ROOT must be a mountpoint.\n"
+//usage:     "\n	-c DEV	Reopen stdio to DEV after switch"
+
+#include <sys/vfs.h>
+#include <sys/mount.h>
+#include "libbb.h"
+// Make up for header deficiencies
+#ifndef RAMFS_MAGIC
+# define RAMFS_MAGIC ((unsigned)0x858458f6)
+#endif
+#ifndef TMPFS_MAGIC
+# define TMPFS_MAGIC ((unsigned)0x01021994)
+#endif
+#ifndef MS_MOVE
+# define MS_MOVE     8192
+#endif
+
+// Recursively delete contents of rootfs
+static void delete_contents(const char *directory, dev_t rootdev)
+{
+	DIR *dir;
+	struct dirent *d;
+	struct stat st;
+
+	// Don't descend into other filesystems
+	if (lstat(directory, &st) || st.st_dev != rootdev)
+		return;
+
+	// Recursively delete the contents of directories
+	if (S_ISDIR(st.st_mode)) {
+		dir = opendir(directory);
+		if (dir) {
+			while ((d = readdir(dir))) {
+				char *newdir = d->d_name;
+
+				// Skip . and ..
+				if (DOT_OR_DOTDOT(newdir))
+					continue;
+
+				// Recurse to delete contents
+				newdir = concat_path_file(directory, newdir);
+				delete_contents(newdir, rootdev);
+				free(newdir);
+			}
+			closedir(dir);
+
+			// Directory should now be empty, zap it
+			rmdir(directory);
+		}
+	} else {
+		// It wasn't a directory, zap it
+		unlink(directory);
+	}
+}
+
+int switch_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int switch_root_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *newroot, *console = NULL;
+	struct stat st;
+	struct statfs stfs;
+	dev_t rootdev;
+
+	// Parse args (-c console)
+	opt_complementary = "-2"; // minimum 2 params
+	getopt32(argv, "+c:", &console); // '+': stop at first non-option
+	argv += optind;
+	newroot = *argv++;
+
+	// Change to new root directory and verify it's a different fs
+	xchdir(newroot);
+	xstat("/", &st);
+	rootdev = st.st_dev;
+	xstat(".", &st);
+	if (st.st_dev == rootdev || getpid() != 1) {
+		// Show usage, it says new root must be a mountpoint
+		// and we must be PID 1
+		bb_show_usage();
+	}
+
+	// Additional sanity checks: we're about to rm -rf /, so be REALLY SURE
+	// we mean it. I could make this a CONFIG option, but I would get email
+	// from all the people who WILL destroy their filesystems.
+	if (stat("/init", &st) != 0 || !S_ISREG(st.st_mode)) {
+		bb_error_msg_and_die("/init is not a regular file");
+	}
+	statfs("/", &stfs); // this never fails
+	if ((unsigned)stfs.f_type != RAMFS_MAGIC
+	 && (unsigned)stfs.f_type != TMPFS_MAGIC
+	) {
+		bb_error_msg_and_die("root filesystem is not ramfs/tmpfs");
+	}
+
+	// Zap everything out of rootdev
+	delete_contents("/", rootdev);
+
+	// Overmount / with newdir and chroot into it
+	if (mount(".", "/", NULL, MS_MOVE, NULL)) {
+		// For example, fails when newroot is not a mountpoint
+		bb_perror_msg_and_die("error moving root");
+	}
+	xchroot(".");
+	// The chdir is needed to recalculate "." and ".." links
+	/*xchdir("/"); - done in xchroot */
+
+	// If a new console specified, redirect stdin/stdout/stderr to it
+	if (console) {
+		close(0);
+		xopen(console, O_RDWR);
+		xdup2(0, 1);
+		xdup2(0, 2);
+	}
+
+	// Exec real init
+	execv(argv[0], argv);
+	bb_perror_msg_and_die("can't execute '%s'", argv[0]);
+}
+
+/*
+From: Rob Landley <rob@landley.net>
+Date: Tue, Jun 16, 2009 at 7:47 PM
+Subject: Re: switch_root...
+
+...
+...
+...
+
+If you're _not_ running out of init_ramfs (if for example you're using initrd
+instead), you probably shouldn't use switch_root because it's the wrong tool.
+
+Basically what the sucker does is something like the following shell script:
+
+ find / -xdev | xargs rm -rf
+ cd "$1"
+ shift
+ mount --move . /
+ exec chroot . "$@"
+
+There are a couple reasons that won't work as a shell script:
+
+1) If you delete the commands out of your $PATH, your shell scripts can't run
+more commands, but you can't start using dynamically linked _new_ commands
+until after you do the chroot because the path to the dynamic linker is wrong.
+So there's a step that needs to be sort of atomic but can't be as a shell
+script.  (You can work around this with static linking or very carefully laid
+out paths and sequencing, but it's brittle, ugly, and non-obvious.)
+
+2) The "find | rm" bit will acually delete everything because the mount points
+still show up (even if their contents don't), and rm -rf will then happily zap
+that.  So the first line is an oversimplification of what you need to do _not_
+to descend into other filesystems and delete their contents.
+
+The reason we do this is to free up memory, by the way.  Since initramfs is a
+ramfs, deleting its contents frees up the memory it uses.  (We leave it with
+one remaining dentry for the new mount point, but that's ok.)
+
+Note that you cannot ever umount rootfs, for approximately the same reason you
+can't kill PID 1.  The kernel tracks mount points as a doubly linked list, and
+the pointer to the start/end of that list always points to an entry that's
+known to be there (rootfs), so it never has to worry about moving that pointer
+and it never has to worry about the list being empty.  (Back around 2.6.13
+there _was_ a bug that let you umount rootfs, and the system locked hard the
+instant you did so endlessly looping to find the end of the mount list and
+never stopping.  They fixed it.)
+
+Oh, and the reason we mount --move _and_ do the chroot is due to the way "/"
+works.  Each process has two special symlinks, ".", and "/".  Each of them
+points to the dentry of a directory, and give you a location paths can start
+from.  (Historically ".." was also special, because you could enter a
+directory via a symlink so backing out to the directory you came from doesn't
+necessarily mean the one physically above where "." points to.  These days I
+think it's just handed off to the filesystem.)
+
+Anyway, path resolution starts with "." or "/" (although the "./" at the start
+of the path may be implicit), meaning it's relative to one of those two
+directories.  Your current directory, and your current root directory.  The
+chdir() syscall changes where "." points to, and the chroot() syscall changes
+where "/" points to.  (Again, both are per-process which is why chroot only
+affects your current process and its child processes.)
+
+Note that chroot() does _not_ change where "." points to, and back before they
+put crazy security checks into the kernel your current directory could be
+somewhere you could no longer access after the chroot.  (The command line
+chroot does a cd as well, the chroot _syscall_ is what I'm talking about.)
+
+The reason mounting something new over / has no obvious effect is the same
+reason mounting something over your current directory has no obvious effect:
+the . and / links aren't recalculated after a mount, so they still point to
+the same dentry they did before, even if that dentry is no longer accessible
+by other means.  Note that "cd ." is a NOP, and "chroot /" is a nop; both look
+up the cached dentry and set it right back.  They don't re-parse any paths,
+because they're what all paths your process uses would be relative to.
+
+That's why the careful sequencing above: we cd into the new mount point before
+we do the mount --move.  Moving the mount point would otherwise make it
+totally inaccessible to is because cd-ing to the old path wouldn't give it to
+us anymore, and cd "/" just gives us the cached dentry from when the process
+was created (in this case the old initramfs one).  But the "." symlink gives
+us the dentry of the filesystem we just moved, so we can then "chroot ." to
+copy that dentry to "/" and get the new filesystem.  If we _didn't_ save that
+dentry in "." we couldn't get it back after the mount --move.
+
+(Yes, this is all screwy and I had to email questions to Linus Torvalds to get
+it straight myself.  I keep meaning to write up a "how mount actually works"
+document someday...)
+*/
diff --git a/ap/app/busybox/src/util-linux/umount.c b/ap/app/busybox/src/util-linux/umount.c
new file mode 100644
index 0000000..4c2e882
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/umount.c
@@ -0,0 +1,206 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini umount implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005 by Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define umount_trivial_usage
+//usage:       "[OPTIONS] FILESYSTEM|DIRECTORY"
+//usage:#define umount_full_usage "\n\n"
+//usage:       "Unmount file systems\n"
+//usage:	IF_FEATURE_UMOUNT_ALL(
+//usage:     "\n	-a	Unmount all file systems" IF_FEATURE_MTAB_SUPPORT(" in /etc/mtab")
+//usage:	)
+//usage:	IF_FEATURE_MTAB_SUPPORT(
+//usage:     "\n	-n	Don't erase /etc/mtab entries"
+//usage:	)
+//usage:     "\n	-r	Try to remount devices as read-only if mount is busy"
+//usage:     "\n	-l	Lazy umount (detach filesystem)"
+//usage:     "\n	-f	Force umount (i.e., unreachable NFS server)"
+//usage:	IF_FEATURE_MOUNT_LOOP(
+//usage:     "\n	-D	Don't free loop device even if it has been used"
+//usage:	)
+//usage:
+//usage:#define umount_example_usage
+//usage:       "$ umount /dev/hdc1\n"
+
+#include <mntent.h>
+#include <sys/mount.h>
+#include "libbb.h"
+
+#if defined(__dietlibc__)
+// TODO: This does not belong here.
+/* 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
+ * dietlibc-0.30 does not have implementation of getmntent_r() */
+static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
+		char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
+{
+	struct mntent* ment = getmntent(stream);
+	return memcpy(result, ment, sizeof(*ment));
+}
+#endif
+
+/* Ignored: -v -t -i
+ * bbox always acts as if -d is present.
+ * -D can be used to suppress it (bbox extension).
+ * Rationale:
+ * (1) util-linux's umount does it if "loop=..." is seen in /etc/mtab:
+ * thus, on many systems, bare umount _does_ drop loop devices.
+ * (2) many users request this feature.
+ */
+#define OPTION_STRING           "fldDnra" "vt:i"
+#define OPT_FORCE               (1 << 0) // Same as MNT_FORCE
+#define OPT_LAZY                (1 << 1) // Same as MNT_DETACH
+//#define OPT_FREE_LOOP           (1 << 2) // -d is assumed always present
+#define OPT_DONT_FREE_LOOP      (1 << 3)
+#define OPT_NO_MTAB             (1 << 4)
+#define OPT_REMOUNT             (1 << 5)
+#define OPT_ALL                 (ENABLE_FEATURE_UMOUNT_ALL ? (1 << 6) : 0)
+
+int umount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int umount_main(int argc UNUSED_PARAM, char **argv)
+{
+	int doForce;
+	struct mntent me;
+	FILE *fp;
+	char *fstype = NULL;
+	int status = EXIT_SUCCESS;
+	unsigned opt;
+	struct mtab_list {
+		char *dir;
+		char *device;
+		struct mtab_list *next;
+	} *mtl, *m;
+
+	opt = getopt32(argv, OPTION_STRING, &fstype);
+	//argc -= optind;
+	argv += optind;
+
+	// MNT_FORCE and MNT_DETACH (from linux/fs.h) must match
+	// OPT_FORCE and OPT_LAZY, otherwise this trick won't work:
+	doForce = MAX((opt & OPT_FORCE), (opt & OPT_LAZY));
+
+	/* Get a list of mount points from mtab.  We read them all in now mostly
+	 * for umount -a (so we don't have to worry about the list changing while
+	 * we iterate over it, or about getting stuck in a loop on the same failing
+	 * entry.  Notice that this also naturally reverses the list so that -a
+	 * umounts the most recent entries first. */
+	m = mtl = NULL;
+
+	// If we're umounting all, then m points to the start of the list and
+	// the argument list should be empty (which will match all).
+	fp = setmntent(bb_path_mtab_file, "r");
+	if (!fp) {
+		if (opt & OPT_ALL)
+			bb_error_msg_and_die("can't open '%s'", bb_path_mtab_file);
+	} else {
+		while (getmntent_r(fp, &me, bb_common_bufsiz1, sizeof(bb_common_bufsiz1))) {
+			/* Match fstype if passed */
+			if (!match_fstype(&me, fstype))
+				continue;
+			m = xzalloc(sizeof(*m));
+			m->next = mtl;
+			m->device = xstrdup(me.mnt_fsname);
+			m->dir = xstrdup(me.mnt_dir);
+			mtl = m;
+		}
+		endmntent(fp);
+	}
+
+	// If we're not umounting all, we need at least one argument.
+	if (!(opt & OPT_ALL) && !fstype) {
+		if (!argv[0])
+			bb_show_usage();
+		m = NULL;
+	}
+
+	// Loop through everything we're supposed to umount, and do so.
+	for (;;) {
+		int curstat;
+		char *zapit = *argv;
+		char *path;
+
+		// Do we already know what to umount this time through the loop?
+		if (m)
+			path = xstrdup(m->dir);
+		// For umount -a, end of mtab means time to exit.
+		else if (opt & OPT_ALL)
+			break;
+		// Use command line argument (and look it up in mtab list)
+		else {
+			if (!zapit)
+				break;
+			argv++;
+			path = xmalloc_realpath(zapit);
+			if (path) {
+				for (m = mtl; m; m = m->next)
+					if (strcmp(path, m->dir) == 0 || strcmp(path, m->device) == 0)
+						break;
+			}
+		}
+		// If we couldn't find this sucker in /etc/mtab, punt by passing our
+		// command line argument straight to the umount syscall.  Otherwise,
+		// umount the directory even if we were given the block device.
+		if (m) zapit = m->dir;
+
+		// Let's ask the thing nicely to unmount.
+		curstat = umount(zapit);
+
+		// Force the unmount, if necessary.
+		if (curstat && doForce)
+			curstat = umount2(zapit, doForce);
+
+		// If still can't umount, maybe remount read-only?
+		if (curstat) {
+			if ((opt & OPT_REMOUNT) && errno == EBUSY && m) {
+				// Note! Even if we succeed here, later we should not
+				// free loop device or erase mtab entry!
+				const char *msg = "%s busy - remounted read-only";
+				curstat = mount(m->device, zapit, NULL, MS_REMOUNT|MS_RDONLY, NULL);
+				if (curstat) {
+					msg = "can't remount %s read-only";
+					status = EXIT_FAILURE;
+				}
+				bb_error_msg(msg, m->device);
+			} else {
+				status = EXIT_FAILURE;
+				bb_perror_msg("can't %sumount %s", (doForce ? "forcibly " : ""), zapit);
+			}
+		} else {
+			// De-allocate the loop device.  This ioctl should be ignored on
+			// any non-loop block devices.
+			if (ENABLE_FEATURE_MOUNT_LOOP && !(opt & OPT_DONT_FREE_LOOP) && m)
+				del_loop(m->device);
+			if (ENABLE_FEATURE_MTAB_SUPPORT && !(opt & OPT_NO_MTAB) && m)
+				erase_mtab(m->dir);
+		}
+
+		// Find next matching mtab entry for -a or umount /dev
+		// Note this means that "umount /dev/blah" will unmount all instances
+		// of /dev/blah, not just the most recent.
+		if (m) {
+			while ((m = m->next) != NULL)
+				// NB: if m is non-NULL, path is non-NULL as well
+				if ((opt & OPT_ALL) || strcmp(path, m->device) == 0)
+					break;
+		}
+		free(path);
+	}
+
+	// Free mtab list if necessary
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		while (mtl) {
+			m = mtl->next;
+			free(mtl->device);
+			free(mtl->dir);
+			free(mtl);
+			mtl = m;
+		}
+	}
+
+	return status;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/Kbuild.src b/ap/app/busybox/src/util-linux/volume_id/Kbuild.src
new file mode 100644
index 0000000..759fdaa
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/Kbuild.src
@@ -0,0 +1,47 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+
+lib-$(CONFIG_BLKID)                             += get_devname.o
+lib-$(CONFIG_FINDFS)                            += get_devname.o
+lib-$(CONFIG_FEATURE_MOUNT_LABEL)               += get_devname.o
+
+lib-$(CONFIG_VOLUMEID)                          += volume_id.o util.o
+lib-$(CONFIG_FEATURE_VOLUMEID_BTRFS)            += btrfs.o
+lib-$(CONFIG_FEATURE_VOLUMEID_EXT)              += ext.o
+lib-$(CONFIG_FEATURE_VOLUMEID_FAT)              += fat.o
+lib-$(CONFIG_FEATURE_VOLUMEID_HFS)              += hfs.o
+### lib-$(CONFIG_FEATURE_VOLUMEID_HIGHPOINTRAID)    += highpoint.o
+### lib-$(CONFIG_FEATURE_VOLUMEID_ISWRAID)          += isw_raid.o
+### lib-$(CONFIG_FEATURE_VOLUMEID_LSIRAID)          += lsi_raid.o
+### lib-$(CONFIG_FEATURE_VOLUMEID_VIARAID)          += via_raid.o
+### lib-$(CONFIG_FEATURE_VOLUMEID_SILICONRAID)      += silicon_raid.o
+### lib-$(CONFIG_FEATURE_VOLUMEID_NVIDIARAID)       += nvidia_raid.o
+### lib-$(CONFIG_FEATURE_VOLUMEID_PROMISERAID)      += promise_raid.o
+lib-$(CONFIG_FEATURE_VOLUMEID_ISO9660)          += iso9660.o
+lib-$(CONFIG_FEATURE_VOLUMEID_JFS)              += jfs.o
+lib-$(CONFIG_FEATURE_VOLUMEID_LINUXRAID)        += linux_raid.o
+lib-$(CONFIG_FEATURE_VOLUMEID_LINUXSWAP)        += linux_swap.o
+### lib-$(CONFIG_FEATURE_VOLUMEID_LVM)              += lvm.o
+### lib-$(CONFIG_FEATURE_VOLUMEID_MAC)              += mac.o
+### lib-$(CONFIG_FEATURE_VOLUMEID_MSDOS)            += msdos.o
+lib-$(CONFIG_FEATURE_VOLUMEID_NILFS)            += nilfs.o
+lib-$(CONFIG_FEATURE_VOLUMEID_NTFS)             += ntfs.o
+lib-$(CONFIG_FEATURE_VOLUMEID_EXFAT)            += exfat.o
+lib-$(CONFIG_FEATURE_VOLUMEID_REISERFS)         += reiserfs.o
+lib-$(CONFIG_FEATURE_VOLUMEID_UDF)              += udf.o
+### lib-$(CONFIG_FEATURE_VOLUMEID_UFS)              += ufs.o
+lib-$(CONFIG_FEATURE_VOLUMEID_XFS)              += xfs.o
+lib-$(CONFIG_FEATURE_VOLUMEID_CRAMFS)           += cramfs.o
+### lib-$(CONFIG_FEATURE_VOLUMEID_HPFS)             += hpfs.o
+lib-$(CONFIG_FEATURE_VOLUMEID_ROMFS)            += romfs.o
+lib-$(CONFIG_FEATURE_VOLUMEID_SYSV)             += sysv.o
+### lib-$(CONFIG_FEATURE_VOLUMEID_MINIX)            += minix.o
+lib-$(CONFIG_FEATURE_VOLUMEID_LUKS)             += luks.o
+lib-$(CONFIG_FEATURE_VOLUMEID_OCFS2)            += ocfs2.o
diff --git a/ap/app/busybox/src/util-linux/volume_id/btrfs.c b/ap/app/busybox/src/util-linux/volume_id/btrfs.c
new file mode 100644
index 0000000..ee71d2e
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/btrfs.c
@@ -0,0 +1,108 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2009 Vladimir Dronnikov <dronnikov@gmail.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+#define BTRFS_UUID_SIZE 16
+#define BTRFS_LABEL_SIZE 256
+#define BTRFS_CSUM_SIZE 32
+#define BTRFS_FSID_SIZE 16
+
+#define BTRFS_MAGIC "_BHRfS_M"
+
+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[BTRFS_UUID_SIZE];
+	uint8_t fsid[BTRFS_UUID_SIZE];
+} PACKED;
+
+struct btrfs_super_block {
+	uint8_t csum[BTRFS_CSUM_SIZE];
+	uint8_t fsid[BTRFS_FSID_SIZE];	// UUID
+	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 dev_item;
+	uint8_t label[BTRFS_LABEL_SIZE];	// LABEL
+	// ...
+} PACKED;
+
+int FAST_FUNC volume_id_probe_btrfs(struct volume_id *id /*,uint64_t off*/)
+{
+	// btrfs has superblocks at 64K, 64M and 256G
+	// minimum btrfs size is 256M
+	// so we never step out the device if we analyze
+	// the first and the second superblocks
+	struct btrfs_super_block *sb;
+	unsigned off = 64;
+
+	while (off < 64*1024*1024) {
+		off *= 1024;
+		dbg("btrfs: probing at offset 0x%x", off);
+
+		sb = volume_id_get_buffer(id, off, sizeof(*sb));
+		if (sb == NULL)
+			return -1;
+
+		if (memcmp(sb->magic, BTRFS_MAGIC, 8) != 0)
+			return -1;
+	}
+
+	// N.B.: btrfs natively supports 256 (>VOLUME_ID_LABEL_SIZE) size labels
+	volume_id_set_label_string(id, sb->label, VOLUME_ID_LABEL_SIZE);
+	volume_id_set_uuid(id, sb->fsid, UUID_DCE);
+	IF_FEATURE_BLKID_TYPE(id->type = "btrfs";)
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/cramfs.c b/ap/app/busybox/src/util-linux/volume_id/cramfs.c
new file mode 100644
index 0000000..28e9970
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/cramfs.c
@@ -0,0 +1,59 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct cramfs_super {
+	uint32_t	magic;
+	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;
+	} PACKED info;
+	uint8_t		name[16];
+} PACKED;
+
+int FAST_FUNC volume_id_probe_cramfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct cramfs_super *cs;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	cs = volume_id_get_buffer(id, off, 0x200);
+	if (cs == NULL)
+		return -1;
+
+	if (cs->magic == cpu_to_be32(0x453dcd28)) {
+//		volume_id_set_label_raw(id, cs->name, 16);
+		volume_id_set_label_string(id, cs->name, 16);
+
+//		volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+		IF_FEATURE_BLKID_TYPE(id->type = "cramfs";)
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/exfat.c b/ap/app/busybox/src/util-linux/volume_id/exfat.c
new file mode 100644
index 0000000..a38a891
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/exfat.c
@@ -0,0 +1,130 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+#define EXFAT_SB_OFFSET		0
+#define EXFAT_DIR_ENTRY_SZ	32
+#define EXFAT_MAX_DIR_ENTRIES	100
+
+struct exfat_super_block {
+/* 0x00 */	uint8_t		boot_jump[3];
+/* 0x03 */	uint8_t		fs_name[8];
+/* 0x0B */	uint8_t		must_be_zero[53];
+/* 0x40 */	uint64_t	partition_offset;
+/* 0x48 */	uint64_t	volume_length;
+/* 0x50 */	uint32_t	fat_offset;		// Sector address of 1st FAT
+/* 0x54 */	uint32_t	fat_size;		// In sectors
+/* 0x58 */	uint32_t	cluster_heap_offset;	// Sector address of Data Region
+/* 0x5C */	uint32_t	cluster_count;
+/* 0x60 */	uint32_t	root_dir;		// Cluster address of Root Directory
+/* 0x64 */	uint8_t		vol_serial_nr[4];	// Volume ID
+/* 0x68 */	uint16_t	fs_revision;		// VV.MM
+/* 0x6A */	uint16_t	vol_flags;
+/* 0x6C */	uint8_t		bytes_per_sector;	// Power of 2: 9 => 512, 12 => 4096
+/* 0x6D */	uint8_t		sectors_per_cluster;	// Power of 2
+/* 0x6E */	uint8_t		nr_of_fats;		// 2 for TexFAT
+/* 0x6F */	// ...
+} PACKED;
+
+struct exfat_dir_entry {
+/* 0x00 */	uint8_t		entry_type;
+		union {
+			struct volume_label {
+/* 0x01 */			uint8_t		char_count;		// Length of label
+/* 0x02 */			uint16_t	vol_label[11];		// UTF16 string without null termination
+/* 0x18 */			uint8_t		reserved[8];
+/* 0x20 */		} PACKED label;
+			struct volume_guid {
+/* 0x01 */			uint8_t		sec_count;
+/* 0x02 */			uint16_t	set_checksum;
+/* 0x04 */			uint16_t	flags;
+/* 0x06 */			uint8_t		vol_guid[16];
+/* 0x16 */			uint8_t		reserved[10];
+/* 0x20 */		} PACKED guid;
+		} PACKED type;
+} PACKED;
+
+int FAST_FUNC volume_id_probe_exfat(struct volume_id *id /*,uint64_t off*/)
+{
+	struct exfat_super_block *sb;
+	struct exfat_dir_entry *de;
+	unsigned	sector_sz;
+	unsigned	cluster_sz;
+	uint64_t	root_dir_off;
+	unsigned	count;
+	unsigned	need_lbl_guid;
+
+	// Primary super block
+	dbg("exFAT: probing at offset 0x%x", EXFAT_SB_OFFSET);
+	sb = volume_id_get_buffer(id, EXFAT_SB_OFFSET, sizeof(*sb));
+
+	if (!sb)
+		return -1;
+
+	if (memcmp(sb->fs_name, "EXFAT   ", 8) != 0)
+		return -1;
+
+	sector_sz = 1 << sb->bytes_per_sector;
+	cluster_sz = sector_sz << sb->sectors_per_cluster;
+	// There are no clusters 0 and 1, so the first cluster is 2.
+	root_dir_off = (uint64_t)EXFAT_SB_OFFSET +
+		// Hmm... should we cast sector_sz/cluster_sz to uint64_t?
+		(le32_to_cpu(sb->cluster_heap_offset)) * sector_sz +
+		(le32_to_cpu(sb->root_dir) - 2) * cluster_sz;
+	dbg("exFAT: sector size 0x%x bytes", sector_sz);
+	dbg("exFAT: cluster size 0x%x bytes", cluster_sz);
+	dbg("exFAT: root dir is at 0x%llx", (long long)root_dir_off);
+
+	// Use DOS uuid as fallback, if no GUID set
+	volume_id_set_uuid(id, sb->vol_serial_nr, UUID_DOS);
+
+	// EXFAT_MAX_DIR_ENTRIES is used as a safety belt.
+	// The Root Directory may hold an unlimited number of entries,
+	// so we do not want to check all. Usually label and GUID
+	// are in the beginning, but there are no guarantees.
+	need_lbl_guid = (1 << 0) | (1 << 1);
+	for (count = 0; count < EXFAT_MAX_DIR_ENTRIES; count++) {
+		de = volume_id_get_buffer(id, root_dir_off + (count * EXFAT_DIR_ENTRY_SZ), EXFAT_DIR_ENTRY_SZ);
+		if (de == NULL)
+			break;
+		if (de->entry_type == 0x00) {
+			// End of Directory Marker
+			dbg("exFAT: End of root directory reached after %u entries", count);
+			break;
+		}
+		if (de->entry_type == 0x83) {
+			// Volume Label Directory Entry
+			volume_id_set_label_unicode16(id, (uint8_t *)de->type.label.vol_label,
+						LE, 2 * de->type.label.char_count);
+			need_lbl_guid &= ~(1 << 0);
+		}
+		if (de->entry_type == 0xA0) {
+			// Volume GUID Directory Entry
+			volume_id_set_uuid(id, de->type.guid.vol_guid, UUID_DCE);
+			need_lbl_guid &= ~(1 << 1);
+		}
+		if (!need_lbl_guid)
+			break;
+	}
+
+	IF_FEATURE_BLKID_TYPE(id->type = "exfat";)
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/ext.c b/ap/app/busybox/src/util-linux/volume_id/ext.c
new file mode 100644
index 0000000..9745106
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/ext.c
@@ -0,0 +1,60 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+#include "bb_e2fs_defs.h"
+
+#define EXT_SUPERBLOCK_OFFSET			0x400
+
+int FAST_FUNC volume_id_probe_ext(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct ext2_super_block *es;
+
+	dbg("ext: probing at offset 0x%llx", (unsigned long long) off);
+
+	es = volume_id_get_buffer(id, off + EXT_SUPERBLOCK_OFFSET, 0x200);
+	if (es == NULL)
+		return -1;
+
+	if (es->s_magic != cpu_to_le16(EXT2_SUPER_MAGIC)) {
+		dbg("ext: no magic found");
+		return -1;
+	}
+
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+//	volume_id_set_label_raw(id, es->volume_name, 16);
+	volume_id_set_label_string(id, (void*)es->s_volume_name, 16);
+	volume_id_set_uuid(id, es->s_uuid, UUID_DCE);
+	dbg("ext: label '%s' uuid '%s'", id->label, id->uuid);
+
+#if ENABLE_FEATURE_BLKID_TYPE
+	if ((es->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_HUGE_FILE | EXT4_FEATURE_RO_COMPAT_DIR_NLINK))
+	 || (es->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_64BIT))
+	) {
+		id->type = "ext4";
+	}
+	else if (es->s_feature_compat & cpu_to_le32(EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+		id->type = "ext3";
+	else
+		id->type = "ext2";
+#endif
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/fat.c b/ap/app/busybox/src/util-linux/volume_id/fat.c
new file mode 100644
index 0000000..904fbb2
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/fat.c
@@ -0,0 +1,338 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+/* linux/msdos_fs.h says: */
+#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
+
+struct vfat_super_block {
+	uint8_t		boot_jump[3];
+	uint8_t		sysid[8];
+	uint16_t	sector_size_bytes;
+	uint8_t		sectors_per_cluster;
+	uint16_t	reserved_sct;
+	uint8_t		fats;
+	uint16_t	dir_entries;
+	uint16_t	sectors;
+	uint8_t		media;
+	uint16_t	fat_length;
+	uint16_t	secs_track;
+	uint16_t	heads;
+	uint32_t	hidden;
+	uint32_t	total_sect;
+	union {
+		struct fat_super_block {
+			uint8_t		unknown[3];
+			uint8_t		serno[4];
+			uint8_t		label[11];
+			uint8_t		magic[8];
+			uint8_t		dummy2[192];
+			uint8_t		pmagic[2];
+		} PACKED fat;
+		struct fat32_super_block {
+			uint32_t	fat32_length;
+			uint16_t	flags;
+			uint8_t		version[2];
+			uint32_t	root_cluster;
+			uint16_t	insfo_sector;
+			uint16_t	backup_boot;
+			uint16_t	reserved2[6];
+			uint8_t		unknown[3];
+			uint8_t		serno[4];
+			uint8_t		label[11];
+			uint8_t		magic[8];
+			uint8_t		dummy2[164];
+			uint8_t		pmagic[2];
+		} PACKED fat32;
+	} PACKED type;
+} 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;
+} PACKED;
+
+static uint8_t *get_attr_volume_id(struct vfat_dir_entry *dir, int count)
+{
+	for (;--count >= 0; dir++) {
+		/* end marker */
+		if (dir->name[0] == 0x00) {
+			dbg("end of dir");
+			break;
+		}
+
+		/* empty entry */
+		if (dir->name[0] == FAT_ENTRY_FREE)
+			continue;
+
+		/* long name */
+		if ((dir->attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME)
+			continue;
+
+		if ((dir->attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) == FAT_ATTR_VOLUME_ID) {
+			/* labels do not have file data */
+			if (dir->cluster_high != 0 || dir->cluster_low != 0)
+				continue;
+
+			dbg("found ATTR_VOLUME_ID id in root dir");
+			return dir->name;
+		}
+
+		dbg("skip dir entry");
+	}
+
+	return NULL;
+}
+
+int FAST_FUNC volume_id_probe_vfat(struct volume_id *id /*,uint64_t fat_partition_off*/)
+{
+#define fat_partition_off ((uint64_t)0)
+	struct vfat_super_block *vs;
+	struct vfat_dir_entry *dir;
+	uint16_t sector_size_bytes;
+	uint16_t dir_entries;
+	uint32_t sect_count;
+	uint16_t reserved_sct;
+	uint32_t fat_size_sct;
+	uint32_t root_cluster;
+	uint32_t dir_size_sct;
+	uint32_t cluster_count;
+	uint64_t root_start_off;
+	uint32_t start_data_sct;
+	uint8_t *buf;
+	uint32_t buf_size;
+	uint8_t *label = NULL;
+	uint32_t next_cluster;
+	int maxloop;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) fat_partition_off);
+
+	vs = volume_id_get_buffer(id, fat_partition_off, 0x200);
+	if (vs == NULL)
+		return -1;
+
+	/* believe only that's fat, don't trust the version
+	 * the cluster_count will tell us
+	 */
+	if (memcmp(vs->sysid, "NTFS", 4) == 0)
+		return -1;
+
+	if (memcmp(vs->type.fat32.magic, "MSWIN", 5) == 0)
+		goto valid;
+
+	if (memcmp(vs->type.fat32.magic, "FAT32   ", 8) == 0)
+		goto valid;
+
+	if (memcmp(vs->type.fat.magic, "FAT16   ", 8) == 0)
+		goto valid;
+
+	if (memcmp(vs->type.fat.magic, "MSDOS", 5) == 0)
+		goto valid;
+
+	if (memcmp(vs->type.fat.magic, "FAT12   ", 8) == 0)
+		goto valid;
+
+	/*
+	 * There are old floppies out there without a magic, so we check
+	 * for well known values and guess if it's a fat volume
+	 */
+
+	/* boot jump address check */
+	if ((vs->boot_jump[0] != 0xeb || vs->boot_jump[2] != 0x90)
+	 && vs->boot_jump[0] != 0xe9
+	) {
+		return -1;
+	}
+
+	/* heads check */
+	if (vs->heads == 0)
+		return -1;
+
+	/* cluster size check */
+	if (vs->sectors_per_cluster == 0
+	 || (vs->sectors_per_cluster & (vs->sectors_per_cluster-1))
+	) {
+		return -1;
+	}
+
+	/* media check */
+	if (vs->media < 0xf8 && vs->media != 0xf0)
+		return -1;
+
+	/* fat count*/
+	if (vs->fats != 2)
+		return -1;
+
+ valid:
+	/* sector size check */
+	sector_size_bytes = le16_to_cpu(vs->sector_size_bytes);
+	if (sector_size_bytes != 0x200 && sector_size_bytes != 0x400
+	 && sector_size_bytes != 0x800 && sector_size_bytes != 0x1000
+	) {
+		return -1;
+	}
+
+	dbg("sector_size_bytes 0x%x", sector_size_bytes);
+	dbg("sectors_per_cluster 0x%x", vs->sectors_per_cluster);
+
+	reserved_sct = le16_to_cpu(vs->reserved_sct);
+	dbg("reserved_sct 0x%x", reserved_sct);
+
+	sect_count = le16_to_cpu(vs->sectors);
+	if (sect_count == 0)
+		sect_count = le32_to_cpu(vs->total_sect);
+	dbg("sect_count 0x%x", sect_count);
+
+	fat_size_sct = le16_to_cpu(vs->fat_length);
+	if (fat_size_sct == 0)
+		fat_size_sct = le32_to_cpu(vs->type.fat32.fat32_length);
+	fat_size_sct *= vs->fats;
+	dbg("fat_size_sct 0x%x", fat_size_sct);
+
+	dir_entries = le16_to_cpu(vs->dir_entries);
+	dir_size_sct = ((dir_entries * sizeof(struct vfat_dir_entry)) +
+			(sector_size_bytes-1)) / sector_size_bytes;
+	dbg("dir_size_sct 0x%x", dir_size_sct);
+
+	cluster_count = sect_count - (reserved_sct + fat_size_sct + dir_size_sct);
+	cluster_count /= vs->sectors_per_cluster;
+	dbg("cluster_count 0x%x", cluster_count);
+
+//	if (cluster_count < FAT12_MAX) {
+//		strcpy(id->type_version, "FAT12");
+//	} else if (cluster_count < FAT16_MAX) {
+//		strcpy(id->type_version, "FAT16");
+//	} else {
+//		strcpy(id->type_version, "FAT32");
+//		goto fat32;
+//	}
+	if (cluster_count >= FAT16_MAX)
+		goto fat32;
+
+	/* the label may be an attribute in the root directory */
+	root_start_off = (reserved_sct + fat_size_sct) * sector_size_bytes;
+	dbg("root dir start 0x%llx", (unsigned long long) root_start_off);
+	dbg("expected entries 0x%x", dir_entries);
+
+	buf_size = dir_entries * sizeof(struct vfat_dir_entry);
+	buf = volume_id_get_buffer(id, fat_partition_off + root_start_off, buf_size);
+	if (buf == NULL)
+		goto ret;
+
+	label = get_attr_volume_id((struct vfat_dir_entry*) buf, dir_entries);
+
+	vs = volume_id_get_buffer(id, fat_partition_off, 0x200);
+	if (vs == NULL)
+		return -1;
+
+	if (label != NULL && memcmp(label, "NO NAME    ", 11) != 0) {
+//		volume_id_set_label_raw(id, label, 11);
+		volume_id_set_label_string(id, label, 11);
+	} else if (memcmp(vs->type.fat.label, "NO NAME    ", 11) != 0) {
+//		volume_id_set_label_raw(id, vs->type.fat.label, 11);
+		volume_id_set_label_string(id, vs->type.fat.label, 11);
+	}
+	volume_id_set_uuid(id, vs->type.fat.serno, UUID_DOS);
+	goto ret;
+
+ fat32:
+	/* FAT32 root dir is a cluster chain like any other directory */
+	buf_size = vs->sectors_per_cluster * sector_size_bytes;
+	root_cluster = le32_to_cpu(vs->type.fat32.root_cluster);
+	start_data_sct = reserved_sct + fat_size_sct;
+
+	next_cluster = root_cluster;
+	maxloop = 100;
+	while (--maxloop) {
+		uint64_t next_off_sct;
+		uint64_t next_off;
+		uint64_t fat_entry_off;
+		int count;
+
+		dbg("next_cluster 0x%x", (unsigned)next_cluster);
+		next_off_sct = (uint64_t)(next_cluster - 2) * vs->sectors_per_cluster;
+		next_off = (start_data_sct + next_off_sct) * sector_size_bytes;
+		dbg("cluster offset 0x%llx", (unsigned long long) next_off);
+
+		/* get cluster */
+		buf = volume_id_get_buffer(id, fat_partition_off + next_off, buf_size);
+		if (buf == NULL)
+			goto ret;
+
+		dir = (struct vfat_dir_entry*) buf;
+		count = buf_size / sizeof(struct vfat_dir_entry);
+		dbg("expected entries 0x%x", count);
+
+		label = get_attr_volume_id(dir, count);
+		if (label)
+			break;
+
+		/* get FAT entry */
+		fat_entry_off = (reserved_sct * sector_size_bytes) + (next_cluster * sizeof(uint32_t));
+		dbg("fat_entry_off 0x%llx", (unsigned long long)fat_entry_off);
+		buf = volume_id_get_buffer(id, fat_partition_off + fat_entry_off, buf_size);
+		if (buf == NULL)
+			goto ret;
+
+		/* set next cluster */
+		next_cluster = le32_to_cpu(*(uint32_t*)buf) & 0x0fffffff;
+		if (next_cluster < 2 || next_cluster > FAT32_MAX)
+			break;
+	}
+	if (maxloop == 0)
+		dbg("reached maximum follow count of root cluster chain, give up");
+
+	vs = volume_id_get_buffer(id, fat_partition_off, 0x200);
+	if (vs == NULL)
+		return -1;
+
+	if (label != NULL && memcmp(label, "NO NAME    ", 11) != 0) {
+//		volume_id_set_label_raw(id, label, 11);
+		volume_id_set_label_string(id, label, 11);
+	} else if (memcmp(vs->type.fat32.label, "NO NAME    ", 11) != 0) {
+//		volume_id_set_label_raw(id, vs->type.fat32.label, 11);
+		volume_id_set_label_string(id, vs->type.fat32.label, 11);
+	}
+	volume_id_set_uuid(id, vs->type.fat32.serno, UUID_DOS);
+
+ ret:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "vfat";)
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/get_devname.c b/ap/app/busybox/src/util-linux/volume_id/get_devname.c
new file mode 100644
index 0000000..665cb9b
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/get_devname.c
@@ -0,0 +1,311 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Support functions for mounting devices by label/uuid
+ *
+ * Copyright (C) 2006 by Jason Schoon <floydpink@gmail.com>
+ * Some portions cribbed from e2fsprogs, util-linux, dosfstools
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include <sys/mount.h> /* BLKGETSIZE64 */
+#if !defined(BLKGETSIZE64)
+# define BLKGETSIZE64 _IOR(0x12,114,size_t)
+#endif
+#include "volume_id_internal.h"
+
+static struct uuidCache_s {
+	struct uuidCache_s *next;
+//	int major, minor;
+	char *device;
+	char *label;
+	char *uc_uuid; /* prefix makes it easier to grep for */
+	IF_FEATURE_BLKID_TYPE(const char *type;)
+} *uuidCache;
+
+#if !ENABLE_FEATURE_BLKID_TYPE
+#define get_label_uuid(fd, label, uuid, type) \
+	get_label_uuid(fd, label, uuid)
+#define uuidcache_addentry(device, label, uuid, type) \
+	uuidcache_addentry(device, label, uuid)
+#endif
+
+/* Returns !0 on error.
+ * Otherwise, returns malloc'ed strings for label and uuid
+ * (and they can't be NULL, although they can be "").
+ * NB: closes fd. */
+static int
+get_label_uuid(int fd, char **label, char **uuid, const char **type)
+{
+	int rv = 1;
+	uint64_t size;
+	struct volume_id *vid;
+
+	/* fd is owned by vid now */
+	vid = volume_id_open_node(fd);
+
+	if (ioctl(/*vid->*/fd, BLKGETSIZE64, &size) != 0)
+		size = 0;
+
+	if (volume_id_probe_all(vid, /*0,*/ size) != 0)
+		goto ret;
+
+	if (vid->label[0] != '\0' || vid->uuid[0] != '\0'
+#if ENABLE_FEATURE_BLKID_TYPE
+	 || vid->type != NULL
+#endif
+	) {
+		*label = xstrndup(vid->label, sizeof(vid->label));
+		*uuid  = xstrndup(vid->uuid, sizeof(vid->uuid));
+#if ENABLE_FEATURE_BLKID_TYPE
+		*type = vid->type;
+		dbg("found label '%s', uuid '%s', type '%s'", *label, *uuid, *type);
+#else
+		dbg("found label '%s', uuid '%s'", *label, *uuid);
+#endif
+		rv = 0;
+	}
+ ret:
+	free_volume_id(vid); /* also closes fd */
+	return rv;
+}
+
+/* NB: we take ownership of (malloc'ed) label and uuid */
+static void
+uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uuid, const char *type)
+{
+	struct uuidCache_s *last;
+
+	if (!uuidCache) {
+		last = uuidCache = xzalloc(sizeof(*uuidCache));
+	} else {
+		for (last = uuidCache; last->next; last = last->next)
+			continue;
+		last->next = xzalloc(sizeof(*uuidCache));
+		last = last->next;
+	}
+	/*last->next = NULL; - xzalloc did it*/
+//	last->major = major;
+//	last->minor = minor;
+	last->device = device;
+	last->label = label;
+	last->uc_uuid = uuid;
+	IF_FEATURE_BLKID_TYPE(last->type = type;)
+}
+
+/* If get_label_uuid() on device_name returns success,
+ * add a cache entry for this device.
+ * If device node does not exist, it will be temporarily created. */
+static int FAST_FUNC
+uuidcache_check_device(const char *device,
+		struct stat *statbuf,
+		void *userData UNUSED_PARAM,
+		int depth UNUSED_PARAM)
+{
+	/* note: this check rejects links to devices, among other nodes */
+	if (!S_ISBLK(statbuf->st_mode))
+		return TRUE;
+
+	/* Users report that mucking with floppies (especially non-present
+	 * ones) is significant PITA. This is a horribly dirty hack,
+	 * but it is very useful in real world.
+	 * If this will ever need to be enabled, consider using O_NONBLOCK.
+	 */
+	if (major(statbuf->st_rdev) == 2)
+		return TRUE;
+
+	add_to_uuid_cache(device);
+
+	return TRUE;
+}
+
+static struct uuidCache_s*
+uuidcache_init(int scan_devices)
+{
+	dbg("DBG: uuidCache=%x, uuidCache");
+	if (uuidCache)
+		return uuidCache;
+
+	/* We were scanning /proc/partitions
+	 * and /proc/sys/dev/cdrom/info here.
+	 * Missed volume managers. I see that "standard" blkid uses these:
+	 * /dev/mapper/control
+	 * /proc/devices
+	 * /proc/evms/volumes
+	 * /proc/lvm/VGs
+	 * This is unacceptably complex. Let's just scan /dev.
+	 * (Maybe add scanning of /sys/block/XXX/dev for devices
+	 * somehow not having their /dev/XXX entries created?) */
+	if (scan_devices)
+		recursive_action("/dev", ACTION_RECURSE,
+			uuidcache_check_device, /* file_action */
+			NULL, /* dir_action */
+			NULL, /* userData */
+			0 /* depth */);
+
+	return uuidCache;
+}
+
+#define UUID   1
+#define VOL    2
+
+#ifdef UNUSED
+static char *
+get_spec_by_x(int n, const char *t, int *majorPtr, int *minorPtr)
+{
+	struct uuidCache_s *uc;
+
+	uc = uuidcache_init(/*scan_devices:*/ 1);
+	while (uc) {
+		switch (n) {
+		case UUID:
+			if (strcmp(t, uc->uc_uuid) == 0) {
+				*majorPtr = uc->major;
+				*minorPtr = uc->minor;
+				return uc->device;
+			}
+			break;
+		case VOL:
+			if (strcmp(t, uc->label) == 0) {
+				*majorPtr = uc->major;
+				*minorPtr = uc->minor;
+				return uc->device;
+			}
+			break;
+		}
+		uc = uc->next;
+	}
+	return NULL;
+}
+
+static unsigned char
+fromhex(char c)
+{
+	if (isdigit(c))
+		return (c - '0');
+	return ((c|0x20) - 'a' + 10);
+}
+
+static char *
+get_spec_by_uuid(const char *s, int *major, int *minor)
+{
+	unsigned char uuid[16];
+	int i;
+
+	if (strlen(s) != 36 || s[8] != '-' || s[13] != '-'
+	 || s[18] != '-' || s[23] != '-'
+	) {
+		goto bad_uuid;
+	}
+	for (i = 0; i < 16; i++) {
+		if (*s == '-')
+			s++;
+		if (!isxdigit(s[0]) || !isxdigit(s[1]))
+			goto bad_uuid;
+		uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1]));
+		s += 2;
+	}
+	return get_spec_by_x(UUID, (char *)uuid, major, minor);
+
+ bad_uuid:
+	fprintf(stderr, _("mount: bad UUID"));
+	return 0;
+}
+
+static char *
+get_spec_by_volume_label(const char *s, int *major, int *minor)
+{
+	return get_spec_by_x(VOL, s, major, minor);
+}
+#endif // UNUSED
+
+/* Used by blkid */
+void display_uuid_cache(int scan_devices)
+{
+	struct uuidCache_s *uc;
+
+	uc = uuidcache_init(scan_devices);
+	while (uc) {
+		printf("%s:", uc->device);
+		if (uc->label[0])
+			printf(" LABEL=\"%s\"", uc->label);
+		if (uc->uc_uuid[0])
+			printf(" UUID=\"%s\"", uc->uc_uuid);
+#if ENABLE_FEATURE_BLKID_TYPE
+	if (uc->type)
+		printf(" TYPE=\"%s\"", uc->type);
+#endif
+		bb_putchar('\n');
+		uc = uc->next;
+	}
+}
+
+int add_to_uuid_cache(const char *device)
+{
+	char *uuid = uuid; /* for compiler */
+	char *label = label;
+#if ENABLE_FEATURE_BLKID_TYPE
+	const char *type = type;
+#endif
+	int fd;
+
+	fd = open(device, O_RDONLY);
+	if (fd < 0)
+		return 0;
+
+	/* get_label_uuid() closes fd in all cases (success & failure) */
+	if (get_label_uuid(fd, &label, &uuid, &type) == 0) {
+		/* uuidcache_addentry() takes ownership of all four params */
+		uuidcache_addentry(xstrdup(device), /*ma, mi,*/ label, uuid, type);
+		return 1;
+	}
+	return 0;
+}
+
+
+/* Used by mount and findfs */
+
+char *get_devname_from_label(const char *spec)
+{
+	struct uuidCache_s *uc;
+
+	uc = uuidcache_init(/*scan_devices:*/ 1);
+	while (uc) {
+		if (uc->label[0] && strcmp(spec, uc->label) == 0) {
+			return xstrdup(uc->device);
+		}
+		uc = uc->next;
+	}
+	return NULL;
+}
+
+char *get_devname_from_uuid(const char *spec)
+{
+	struct uuidCache_s *uc;
+
+	uc = uuidcache_init(/*scan_devices:*/ 1);
+	while (uc) {
+		/* case of hex numbers doesn't matter */
+		if (strcasecmp(spec, uc->uc_uuid) == 0) {
+			return xstrdup(uc->device);
+		}
+		uc = uc->next;
+	}
+	return NULL;
+}
+
+int resolve_mount_spec(char **fsname)
+{
+	char *tmp = *fsname;
+
+	if (strncmp(*fsname, "UUID=", 5) == 0)
+		tmp = get_devname_from_uuid(*fsname + 5);
+	else if (strncmp(*fsname, "LABEL=", 6) == 0)
+		tmp = get_devname_from_label(*fsname + 6);
+
+	if (tmp == *fsname)
+		return 0; /* no UUID= or LABEL= prefix found */
+
+	if (tmp)
+		*fsname = tmp;
+	return 1;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/hfs.c b/ap/app/busybox/src/util-linux/volume_id/hfs.c
new file mode 100644
index 0000000..3d9704d
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/hfs.c
@@ -0,0 +1,313 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+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];
+} 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;
+} PACKED;
+
+struct hfsplus_bnode_descriptor {
+	uint32_t	next;
+	uint32_t	prev;
+	uint8_t		type;
+	uint8_t		height;
+	uint16_t	num_recs;
+	uint16_t	reserved;
+} 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;
+} PACKED;
+
+struct hfsplus_catalog_key {
+	uint16_t	key_len;
+	uint32_t	parent_id;
+	uint16_t	unicode_len;
+	uint8_t		unicode[255 * 2];
+} PACKED;
+
+struct hfsplus_extent {
+	uint32_t	start_block;
+	uint32_t	block_count;
+} 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];
+} 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;
+} PACKED;
+
+#define HFS_SUPERBLOCK_OFFSET		0x400
+#define HFS_NODE_LEAF			0xff
+#define HFSPLUS_POR_CNID		1
+
+static void FAST_FUNC hfs_set_uuid(struct volume_id *id, const uint8_t *hfs_id)
+{
+#define hfs_id_len 8
+	md5_ctx_t md5c;
+	uint8_t uuid[16];
+	unsigned i;
+
+	for (i = 0; i < hfs_id_len; i++)
+		if (hfs_id[i] != 0)
+			goto do_md5;
+	return;
+ do_md5:
+	md5_begin(&md5c);
+	md5_hash(&md5c, "\263\342\17\71\362\222\21\326\227\244\0\60\145\103\354\254", 16);
+	md5_hash(&md5c, hfs_id, hfs_id_len);
+	md5_end(&md5c, uuid);
+	uuid[6] = 0x30 | (uuid[6] & 0x0f);
+	uuid[8] = 0x80 | (uuid[8] & 0x3f);
+	volume_id_set_uuid(id, uuid, UUID_DCE);
+}
+
+int FAST_FUNC volume_id_probe_hfs_hfsplus(struct volume_id *id /*,uint64_t off*/)
+{
+	uint64_t off = 0;
+	unsigned blocksize;
+	unsigned cat_block;
+	unsigned ext_block_start;
+	unsigned ext_block_count;
+	int ext;
+	unsigned leaf_node_head;
+	unsigned leaf_node_count;
+	unsigned leaf_node_size;
+	unsigned leaf_block;
+	uint64_t leaf_off;
+	unsigned alloc_block_size;
+	unsigned alloc_first_block;
+	unsigned embed_first_block;
+	unsigned record_count;
+	struct hfsplus_vol_header *hfsplus;
+	struct hfsplus_bnode_descriptor *descr;
+	struct hfsplus_bheader_record *bnode;
+	struct hfsplus_catalog_key *key;
+	unsigned label_len;
+	struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
+	struct hfs_mdb *hfs;
+	const uint8_t *buf;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
+	if (buf == NULL)
+		return -1;
+
+	hfs = (struct hfs_mdb *) buf;
+	if (hfs->signature[0] != 'B' || hfs->signature[1] != 'D')
+		goto checkplus;
+
+	/* it may be just a hfs wrapper for hfs+ */
+	if (hfs->embed_sig[0] == 'H' && hfs->embed_sig[1] == '+') {
+		alloc_block_size = be32_to_cpu(hfs->al_blk_size);
+		dbg("alloc_block_size 0x%x", alloc_block_size);
+
+		alloc_first_block = be16_to_cpu(hfs->al_bl_st);
+		dbg("alloc_first_block 0x%x", alloc_first_block);
+
+		embed_first_block = be16_to_cpu(hfs->embed_startblock);
+		dbg("embed_first_block 0x%x", embed_first_block);
+
+		off += (alloc_first_block * 512) +
+		       (embed_first_block * alloc_block_size);
+		dbg("hfs wrapped hfs+ found at offset 0x%llx", (unsigned long long) off);
+
+		buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
+		if (buf == NULL)
+			return -1;
+		goto checkplus;
+	}
+
+	if (hfs->label_len > 0 && hfs->label_len < 28) {
+//		volume_id_set_label_raw(id, hfs->label, hfs->label_len);
+		volume_id_set_label_string(id, hfs->label, hfs->label_len) ;
+	}
+
+	hfs_set_uuid(id, hfs->finder_info.id);
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "hfs";)
+
+	return 0;
+
+ checkplus:
+	hfsplus = (struct hfsplus_vol_header *) buf;
+	if (hfs->signature[0] == 'H')
+		if (hfs->signature[1] == '+' || hfs->signature[1] == 'X')
+			goto hfsplus;
+	return -1;
+
+ hfsplus:
+	hfs_set_uuid(id, hfsplus->finder_info.id);
+
+	blocksize = be32_to_cpu(hfsplus->blocksize);
+	dbg("blocksize %u", blocksize);
+
+	memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
+	cat_block = be32_to_cpu(extents[0].start_block);
+	dbg("catalog start block 0x%x", cat_block);
+
+	buf = volume_id_get_buffer(id, off + (cat_block * blocksize), 0x2000);
+	if (buf == NULL)
+		goto found;
+
+	bnode = (struct hfsplus_bheader_record *)
+		&buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+	leaf_node_head = be32_to_cpu(bnode->leaf_head);
+	dbg("catalog leaf node 0x%x", leaf_node_head);
+
+	leaf_node_size = be16_to_cpu(bnode->node_size);
+	dbg("leaf node size 0x%x", leaf_node_size);
+
+	leaf_node_count = be32_to_cpu(bnode->leaf_count);
+	dbg("leaf node count 0x%x", leaf_node_count);
+	if (leaf_node_count == 0)
+		goto found;
+
+	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);
+		dbg("extent start block 0x%x, count 0x%x", ext_block_start, ext_block_count);
+
+		if (ext_block_count == 0)
+			goto found;
+
+		/* this is our extent */
+		if (leaf_block < ext_block_count)
+			break;
+
+		leaf_block -= ext_block_count;
+	}
+	if (ext == HFSPLUS_EXTENT_COUNT)
+		goto found;
+	dbg("found block in extent %i", ext);
+
+	leaf_off = (ext_block_start + leaf_block) * blocksize;
+
+	buf = volume_id_get_buffer(id, off + leaf_off, leaf_node_size);
+	if (buf == NULL)
+		goto found;
+
+	descr = (struct hfsplus_bnode_descriptor *) buf;
+	dbg("descriptor type 0x%x", descr->type);
+
+	record_count = be16_to_cpu(descr->num_recs);
+	dbg("number of records %u", record_count);
+	if (record_count == 0)
+		goto found;
+
+	if (descr->type != HFS_NODE_LEAF)
+		goto found;
+
+	key = (struct hfsplus_catalog_key *)
+		&buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+	dbg("parent id 0x%x", be32_to_cpu(key->parent_id));
+	if (key->parent_id != cpu_to_be32(HFSPLUS_POR_CNID))
+		goto found;
+
+	label_len = be16_to_cpu(key->unicode_len) * 2;
+	dbg("label unicode16 len %i", label_len);
+//	volume_id_set_label_raw(id, key->unicode, label_len);
+	volume_id_set_label_unicode16(id, key->unicode, BE, label_len);
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "hfsplus";)
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/iso9660.c b/ap/app/busybox/src/util-linux/volume_id/iso9660.c
new file mode 100644
index 0000000..1d7693a
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/iso9660.c
@@ -0,0 +1,120 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+#define ISO_SUPERBLOCK_OFFSET		0x8000
+#define ISO_SECTOR_SIZE			0x800
+#define ISO_VD_OFFSET			(ISO_SUPERBLOCK_OFFSET + ISO_SECTOR_SIZE)
+#define ISO_VD_PRIMARY			0x1
+#define ISO_VD_SUPPLEMENTARY		0x2
+#define ISO_VD_END			0xff
+#define ISO_VD_MAX			16
+
+struct iso_volume_descriptor {
+	uint8_t		vd_type;
+	uint8_t		vd_id[5];
+	uint8_t		vd_version;
+	uint8_t		flags;
+	uint8_t		system_id[32];
+	uint8_t		volume_id[32];
+	uint8_t		unused[8];
+	uint8_t		space_size[8];
+	uint8_t		escape_sequences[8];
+} PACKED;
+
+struct high_sierra_volume_descriptor {
+	uint8_t		foo[8];
+	uint8_t		type;
+	uint8_t		id[4];
+	uint8_t		version;
+} PACKED;
+
+int FAST_FUNC volume_id_probe_iso9660(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	uint8_t *buf;
+	struct iso_volume_descriptor *is;
+	struct high_sierra_volume_descriptor *hs;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	buf = volume_id_get_buffer(id, off + ISO_SUPERBLOCK_OFFSET, 0x200);
+	if (buf == NULL)
+		return -1;
+
+	is = (struct iso_volume_descriptor *) buf;
+
+	if (memcmp(is->vd_id, "CD001", 5) == 0) {
+		int vd_offset;
+		int i;
+
+		dbg("read label from PVD");
+//		volume_id_set_label_raw(id, is->volume_id, 32);
+		volume_id_set_label_string(id, is->volume_id, 32);
+
+		dbg("looking for SVDs");
+		vd_offset = ISO_VD_OFFSET;
+		for (i = 0; i < ISO_VD_MAX; i++) {
+			uint8_t svd_label[64];
+
+			is = volume_id_get_buffer(id, off + vd_offset, 0x200);
+			if (is == NULL || is->vd_type == ISO_VD_END)
+				break;
+			if (is->vd_type != ISO_VD_SUPPLEMENTARY)
+				continue;
+
+			dbg("found SVD at offset 0x%llx", (unsigned long long) (off + vd_offset));
+			if (memcmp(is->escape_sequences, "%/@", 3) == 0
+			 || memcmp(is->escape_sequences, "%/C", 3) == 0
+			 || memcmp(is->escape_sequences, "%/E", 3) == 0
+			) {
+				dbg("Joliet extension found");
+				volume_id_set_unicode16((char *)svd_label, sizeof(svd_label), is->volume_id, BE, 32);
+				if (memcmp(id->label, svd_label, 16) == 0) {
+					dbg("SVD label is identical, use the possibly longer PVD one");
+					break;
+				}
+
+//				volume_id_set_label_raw(id, is->volume_id, 32);
+				volume_id_set_label_string(id, svd_label, 32);
+//				strcpy(id->type_version, "Joliet Extension");
+				goto found;
+			}
+			vd_offset += ISO_SECTOR_SIZE;
+		}
+		goto found;
+	}
+
+	hs = (struct high_sierra_volume_descriptor *) buf;
+
+	if (memcmp(hs->id, "CDROM", 5) == 0) {
+//		strcpy(id->type_version, "High Sierra");
+		goto found;
+	}
+
+	return -1;
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "iso9660";)
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/jfs.c b/ap/app/busybox/src/util-linux/volume_id/jfs.c
new file mode 100644
index 0000000..5333af2
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/jfs.c
@@ -0,0 +1,60 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct jfs_super_block {
+	uint8_t		magic[4];
+	uint32_t	version;
+	uint64_t	size;
+	uint32_t	bsize;
+	uint32_t	dummy1;
+	uint32_t	pbsize;
+	uint32_t	dummy2[27];
+	uint8_t		uuid[16];
+	uint8_t		label[16];
+	uint8_t		loguuid[16];
+} PACKED;
+
+#define JFS_SUPERBLOCK_OFFSET			0x8000
+
+int FAST_FUNC volume_id_probe_jfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct jfs_super_block *js;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	js = volume_id_get_buffer(id, off + JFS_SUPERBLOCK_OFFSET, 0x200);
+	if (js == NULL)
+		return -1;
+
+	if (memcmp(js->magic, "JFS1", 4) != 0)
+		return -1;
+
+//	volume_id_set_label_raw(id, js->label, 16);
+	volume_id_set_label_string(id, js->label, 16);
+	volume_id_set_uuid(id, js->uuid, UUID_DCE);
+
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "jfs";)
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/linux_raid.c b/ap/app/busybox/src/util-linux/volume_id/linux_raid.c
new file mode 100644
index 0000000..209eaab
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/linux_raid.c
@@ -0,0 +1,81 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct mdp_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;
+} PACKED;
+
+#define MD_RESERVED_BYTES		0x10000
+#define MD_MAGIC			0xa92b4efc
+
+int FAST_FUNC volume_id_probe_linux_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size)
+{
+	typedef uint32_t aliased_uint32_t FIX_ALIASING;
+#define off ((uint64_t)0)
+	uint64_t sboff;
+	uint8_t uuid[16];
+	struct mdp_super_block *mdp;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	sboff = (size & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES;
+	mdp = volume_id_get_buffer(id, off + sboff, 0x800);
+	if (mdp == NULL)
+		return -1;
+
+	if (mdp->md_magic != cpu_to_le32(MD_MAGIC))
+		return -1;
+
+	*(aliased_uint32_t*)uuid = mdp->set_uuid0;
+	memcpy(&uuid[4], &mdp->set_uuid1, 12);
+	volume_id_set_uuid(id, uuid, UUID_DCE);
+
+//	snprintf(id->type_version, sizeof(id->type_version)-1, "%u.%u.%u",
+//		le32_to_cpu(mdp->major_version),
+//		le32_to_cpu(mdp->minor_version),
+//		le32_to_cpu(mdp->patch_version));
+
+	dbg("found raid signature");
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+	IF_FEATURE_BLKID_TYPE(id->type = "linux_raid_member";)
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/linux_swap.c b/ap/app/busybox/src/util-linux/volume_id/linux_swap.c
new file mode 100644
index 0000000..1ee534a
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/linux_swap.c
@@ -0,0 +1,78 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct swap_header_v1_2 {
+	uint8_t		bootbits[1024];
+	uint32_t	version;
+	uint32_t	last_page;
+	uint32_t	nr_badpages;
+	uint8_t		uuid[16];
+	uint8_t		volume_name[16];
+} PACKED;
+
+#define LARGEST_PAGESIZE			0x4000
+
+int FAST_FUNC volume_id_probe_linux_swap(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct swap_header_v1_2 *sw;
+	const uint8_t *buf;
+	unsigned page;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	/* the swap signature is at the end of the PAGE_SIZE */
+	for (page = 0x1000; page <= LARGEST_PAGESIZE; page <<= 1) {
+			buf = volume_id_get_buffer(id, off + page-10, 10);
+			if (buf == NULL)
+				return -1;
+
+			if (memcmp(buf, "SWAP-SPACE", 10) == 0) {
+//				id->type_version[0] = '1';
+//				id->type_version[1] = '\0';
+				goto found;
+			}
+
+			if (memcmp(buf, "SWAPSPACE2", 10) == 0
+			 || memcmp(buf, "S1SUSPEND", 9) == 0
+			 || memcmp(buf, "S2SUSPEND", 9) == 0
+			 || memcmp(buf, "ULSUSPEND", 9) == 0
+			) {
+				sw = volume_id_get_buffer(id, off, sizeof(struct swap_header_v1_2));
+				if (sw == NULL)
+					return -1;
+//				id->type_version[0] = '2';
+//				id->type_version[1] = '\0';
+//				volume_id_set_label_raw(id, sw->volume_name, 16);
+				volume_id_set_label_string(id, sw->volume_name, 16);
+				volume_id_set_uuid(id, sw->uuid, UUID_DCE);
+				goto found;
+			}
+	}
+	return -1;
+
+found:
+//	volume_id_set_usage(id, VOLUME_ID_OTHER);
+	IF_FEATURE_BLKID_TYPE(id->type = "swap";)
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/luks.c b/ap/app/busybox/src/util-linux/volume_id/luks.c
new file mode 100644
index 0000000..f9b3766
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/luks.c
@@ -0,0 +1,100 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 W. Michael Petullo <mike@flyn.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+#define LUKS_MAGIC_L             6
+#define UUID_STRING_L           40
+#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_NUMKEYS             8
+
+static const uint8_t LUKS_MAGIC[] = { 'L','U','K','S', 0xba, 0xbe };
+
+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];
+	struct {
+		uint32_t	active;
+		uint32_t	passwordIterations;
+		uint8_t		passwordSalt[LUKS_SALTSIZE];
+		uint32_t	keyMaterialOffset;
+		uint32_t	stripes;
+	} keyblock[LUKS_NUMKEYS];
+};
+
+enum {
+	EXPECTED_SIZE_luks_phdr = 0
+		+ 1 * LUKS_MAGIC_L
+		+ 2
+		+ 1 * LUKS_CIPHERNAME_L
+		+ 1 * LUKS_CIPHERMODE_L
+		+ 1 * LUKS_HASHSPEC_L
+		+ 4
+		+ 4
+		+ 1 * LUKS_DIGESTSIZE
+		+ 1 * LUKS_SALTSIZE
+		+ 4
+		+ 1 * UUID_STRING_L
+		+ LUKS_NUMKEYS * (0
+		  + 4
+		  + 4
+		  + 1 * LUKS_SALTSIZE
+		  + 4
+		  + 4
+		  )
+};
+
+struct BUG_bad_size_luks_phdr {
+	char BUG_bad_size_luks_phdr[
+		sizeof(struct luks_phdr) == EXPECTED_SIZE_luks_phdr ?
+		1 : -1];
+};
+
+int FAST_FUNC volume_id_probe_luks(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct luks_phdr *header;
+
+	header = volume_id_get_buffer(id, off, sizeof(*header));
+	if (header == NULL)
+		return -1;
+
+	if (memcmp(header->magic, LUKS_MAGIC, LUKS_MAGIC_L))
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_CRYPTO);
+	volume_id_set_uuid(id, header->uuid, UUID_DCE_STRING);
+	IF_FEATURE_BLKID_TYPE(id->type = "crypto_LUKS";)
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/nilfs.c b/ap/app/busybox/src/util-linux/volume_id/nilfs.c
new file mode 100644
index 0000000..b88a9e4
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/nilfs.c
@@ -0,0 +1,96 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+#define NILFS_UUID_SIZE 16
+#define NILFS_LABEL_SIZE 80
+#define NILFS_SB1_OFFSET 0x400
+#define NILFS_SB2_OFFSET 0x1000
+#define NILFS_MAGIC 0x3434
+
+struct nilfs2_super_block {
+/* 0x00 */	uint32_t s_rev_level;				// Major revision level.
+/* 0x04 */	uint16_t s_minor_rev_level;			// Minor revision level.
+/* 0x06 */	uint16_t s_magic;				// Magic signature.
+/* 0x08 */	uint16_t s_bytes;
+/* 0x0A */	uint16_t s_flags;
+/* 0x0C */	uint32_t s_crc_seed;
+/* 0x10 */	uint32_t s_sum;
+/* 0x14 */	uint32_t s_log_block_size;
+/* 0x18 */	uint64_t s_nsegments;
+/* 0x20 */	uint64_t s_dev_size;				// Block device size in bytes.
+/* 0x28 */	uint64_t s_first_data_block;
+/* 0x30 */	uint32_t s_blocks_per_segment;
+/* 0x34 */	uint32_t s_r_segments_percentage;
+/* 0x38 */	uint64_t s_last_cno;
+/* 0x40 */	uint64_t s_last_pseg;
+/* 0x48 */	uint64_t s_last_seq;
+/* 0x50 */	uint64_t s_free_blocks_count;
+/* 0x58 */	uint64_t s_ctime;
+/* 0x60 */	uint64_t s_mtime;
+/* 0x68 */	uint64_t s_wtime;
+/* 0x70 */	uint16_t s_mnt_count;
+/* 0x72 */	uint16_t s_max_mnt_count;
+/* 0x74 */	uint16_t s_state;
+/* 0x76 */	uint16_t s_errors;
+/* 0x78 */	uint64_t s_lastcheck;
+/* 0x80 */	uint32_t s_checkinterval;
+/* 0x84 */	uint32_t s_creator_os;
+/* 0x88 */	uint16_t s_def_resuid;
+/* 0x8A */	uint16_t s_def_resgid;
+/* 0x8C */	uint32_t s_first_ino;
+/* 0x90 */	uint16_t s_inode_size;
+/* 0x92 */	uint16_t s_dat_entry_size;
+/* 0x94 */	uint16_t s_checkpoint_size;
+/* 0x96 */	uint16_t s_segment_usage_size;
+/* 0x98 */	uint8_t s_uuid[NILFS_UUID_SIZE];		// 128-bit UUID for volume.
+/* 0xA8 */	uint8_t s_volume_name[NILFS_LABEL_SIZE];	// Volume label.
+/* 0xF8 */	// ...
+} PACKED;
+
+int FAST_FUNC volume_id_probe_nilfs(struct volume_id *id /*,uint64_t off*/)
+{
+	struct nilfs2_super_block *sb;
+
+	// Primary super block
+	dbg("nilfs: probing at offset 0x%x", NILFS_SB1_OFFSET);
+
+	sb = volume_id_get_buffer(id, NILFS_SB1_OFFSET, sizeof(*sb));
+
+	if (sb == NULL)
+		return -1;
+
+	if (sb->s_magic != NILFS_MAGIC)
+		return -1;
+
+	// The secondary superblock is not always used, so ignore it for now.
+	// When used it is at 4K from the end of the partition (sb->s_dev_size - NILFS_SB2_OFFSET).
+
+	volume_id_set_label_string(id, sb->s_volume_name, NILFS_LABEL_SIZE < VOLUME_ID_LABEL_SIZE ?
+				NILFS_LABEL_SIZE : VOLUME_ID_LABEL_SIZE);
+	volume_id_set_uuid(id, sb->s_uuid, UUID_DCE);
+
+	if (sb->s_rev_level == 2)
+		IF_FEATURE_BLKID_TYPE(id->type = "nilfs2");
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/ntfs.c b/ap/app/busybox/src/util-linux/volume_id/ntfs.c
new file mode 100644
index 0000000..7b2612f
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/ntfs.c
@@ -0,0 +1,194 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct ntfs_super_block {
+	uint8_t		jump[3];
+	uint8_t		oem_id[8];
+	uint16_t	bytes_per_sector;
+	uint8_t		sectors_per_cluster;
+	uint16_t	reserved_sectors;
+	uint8_t		fats;
+	uint16_t	root_entries;
+	uint16_t	sectors;
+	uint8_t		media_type;
+	uint16_t	sectors_per_fat;
+	uint16_t	sectors_per_track;
+	uint16_t	heads;
+	uint32_t	hidden_sectors;
+	uint32_t	large_sectors;
+	uint16_t	unused[2];
+	uint64_t	number_of_sectors;
+	uint64_t	mft_cluster_location;
+	uint64_t	mft_mirror_cluster_location;
+	int8_t		cluster_per_mft_record;
+	uint8_t		reserved1[3];
+	int8_t		cluster_per_index_record;
+	uint8_t		reserved2[3];
+	uint8_t		volume_serial[8];
+	uint16_t	checksum;
+} PACKED;
+
+struct master_file_table_record {
+	uint8_t		magic[4];
+	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;
+} 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;
+} PACKED;
+
+struct volume_info {
+	uint64_t	reserved;
+	uint8_t		major_ver;
+	uint8_t		minor_ver;
+} PACKED;
+
+#define MFT_RECORD_VOLUME			3
+#define MFT_RECORD_ATTR_VOLUME_NAME		0x60
+#define MFT_RECORD_ATTR_VOLUME_INFO		0x70
+#define MFT_RECORD_ATTR_OBJECT_ID		0x40
+#define MFT_RECORD_ATTR_END			0xffffffffu
+
+int FAST_FUNC volume_id_probe_ntfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	unsigned sector_size;
+	unsigned cluster_size;
+	uint64_t mft_cluster;
+	uint64_t mft_off;
+	unsigned mft_record_size;
+	unsigned attr_type;
+	unsigned attr_off;
+	unsigned attr_len;
+	unsigned val_off;
+	unsigned val_len;
+	struct master_file_table_record *mftr;
+	struct ntfs_super_block *ns;
+	const uint8_t *buf;
+	const uint8_t *val;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	ns = volume_id_get_buffer(id, off, 0x200);
+	if (ns == NULL)
+		return -1;
+
+	if (memcmp(ns->oem_id, "NTFS", 4) != 0)
+		return -1;
+
+	volume_id_set_uuid(id, ns->volume_serial, UUID_NTFS);
+
+	sector_size = le16_to_cpu(ns->bytes_per_sector);
+	cluster_size = ns->sectors_per_cluster * sector_size;
+	mft_cluster = le64_to_cpu(ns->mft_cluster_location);
+	mft_off = mft_cluster * cluster_size;
+
+	if (ns->cluster_per_mft_record < 0)
+		/* size = -log2(mft_record_size); normally 1024 Bytes */
+		mft_record_size = 1 << -ns->cluster_per_mft_record;
+	else
+		mft_record_size = ns->cluster_per_mft_record * cluster_size;
+
+	dbg("sectorsize  0x%x", sector_size);
+	dbg("clustersize 0x%x", cluster_size);
+	dbg("mftcluster  %llu", (unsigned long long) mft_cluster);
+	dbg("mftoffset  0x%llx", (unsigned long long) mft_off);
+	dbg("cluster per mft_record  %i", ns->cluster_per_mft_record);
+	dbg("mft record size  %i", mft_record_size);
+
+	buf = volume_id_get_buffer(id, off + mft_off + (MFT_RECORD_VOLUME * mft_record_size),
+			mft_record_size);
+	if (buf == NULL)
+		goto found;
+
+	mftr = (struct master_file_table_record*) buf;
+
+	dbg("mftr->magic '%c%c%c%c'", mftr->magic[0], mftr->magic[1], mftr->magic[2], mftr->magic[3]);
+	if (memcmp(mftr->magic, "FILE", 4) != 0)
+		goto found;
+
+	attr_off = le16_to_cpu(mftr->attrs_offset);
+	dbg("file $Volume's attributes are at offset %i", attr_off);
+
+	while (1) {
+		struct file_attribute *attr;
+
+		attr = (struct file_attribute*) &buf[attr_off];
+		attr_type = le32_to_cpu(attr->type);
+		attr_len = le16_to_cpu(attr->len);
+		val_off = le16_to_cpu(attr->value_offset);
+		val_len = le32_to_cpu(attr->value_len);
+		attr_off += attr_len;
+
+		if (attr_len == 0)
+			break;
+
+		if (attr_off >= mft_record_size)
+			break;
+
+		if (attr_type == MFT_RECORD_ATTR_END)
+			break;
+
+		dbg("found attribute type 0x%x, len %i, at offset %i",
+			attr_type, attr_len, attr_off);
+
+//		if (attr_type == MFT_RECORD_ATTR_VOLUME_INFO) {
+//			struct volume_info *info;
+//			dbg("found info, len %i", val_len);
+//			info = (struct volume_info*) (((uint8_t *) attr) + val_off);
+//			snprintf(id->type_version, sizeof(id->type_version)-1,
+//				 "%u.%u", info->major_ver, info->minor_ver);
+//		}
+
+		if (attr_type == MFT_RECORD_ATTR_VOLUME_NAME) {
+			dbg("found label, len %i", val_len);
+			if (val_len > VOLUME_ID_LABEL_SIZE)
+				val_len = VOLUME_ID_LABEL_SIZE;
+
+			val = ((uint8_t *) attr) + val_off;
+//			volume_id_set_label_raw(id, val, val_len);
+			volume_id_set_label_unicode16(id, val, LE, val_len);
+		}
+	}
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "ntfs";)
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/ocfs2.c b/ap/app/busybox/src/util-linux/volume_id/ocfs2.c
new file mode 100644
index 0000000..fcdb151
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/ocfs2.c
@@ -0,0 +1,106 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) Andre Masella <andre@masella.no-ip.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+/* All these values are taken from ocfs2-tools's ocfs2_fs.h */
+#define OCFS2_VOL_UUID_LEN			16
+#define OCFS2_MAX_VOL_LABEL_LEN			64
+#define OCFS2_SUPERBLOCK_OFFSET			0x2000
+
+
+/* This is the superblock. The OCFS2 header files have structs in structs.
+This is one has been simplified since we only care about the superblock.
+*/
+
+struct ocfs2_super_block {
+	uint8_t		i_signature[8];			/* Signature for validation */
+	uint32_t	i_generation;			/* Generation number */
+	int16_t		i_suballoc_slot;			/* Slot suballocator this inode belongs to */
+	uint16_t	i_suballoc_bit;			/* Bit offset in suballocator block group */
+	uint32_t	i_reserved0;
+	uint32_t	i_clusters;			/* Cluster count */
+	uint32_t	i_uid;				/* Owner UID */
+	uint32_t	i_gid;				/* Owning GID */
+	uint64_t	i_size;				/* Size in bytes */
+	uint16_t	i_mode;				/* File mode */
+	uint16_t	i_links_count;			/* Links count */
+	uint32_t	i_flags;				/* File flags */
+	uint64_t	i_atime;				/* Access time */
+	uint64_t	i_ctime;				/* Creation time */
+	uint64_t	i_mtime;				/* Modification time */
+	uint64_t	i_dtime;				/* Deletion time */
+	uint64_t	i_blkno;				/* Offset on disk, in blocks */
+	uint64_t	i_last_eb_blk;			/* Pointer to last extent block */
+	uint32_t	i_fs_generation;			/* Generation per fs-instance */
+	uint32_t	i_atime_nsec;
+	uint32_t	i_ctime_nsec;
+	uint32_t	i_mtime_nsec;
+	uint64_t	i_reserved1[9];
+	uint64_t	i_pad1;				/* Generic way to refer to this 64bit union */
+	/* Normally there is a union of the different block types, but we only care about the superblock. */
+	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;				/* File system state */
+	uint16_t	s_errors;				/* Behaviour when detecting errors */
+	uint32_t	s_checkinterval;			/* Max time between checks */
+	uint64_t	s_lastcheck;			/* Time of last check */
+	uint32_t	s_creator_os;			/* OS */
+	uint32_t	s_feature_compat;			/* Compatible feature set */
+	uint32_t	s_feature_incompat;		/* Incompatible feature set */
+	uint32_t	s_feature_ro_compat;		/* Readonly-compatible feature set */
+	uint64_t	s_root_blkno;			/* Offset, in blocks, of root directory dinode */
+	uint64_t	s_system_dir_blkno;		/* Offset, in blocks, of system directory dinode */
+	uint32_t	s_blocksize_bits;			/* Blocksize for this fs */
+	uint32_t	s_clustersize_bits;		/* Clustersize for this fs */
+	uint16_t	s_max_slots;			/* Max number of simultaneous mounts before tunefs required */
+	uint16_t	s_reserved1;
+	uint32_t	s_reserved2;
+	uint64_t	s_first_cluster_group;		/* Block offset of 1st cluster group header */
+	uint8_t		s_label[OCFS2_MAX_VOL_LABEL_LEN];	/* Label for mounting, etc. */
+	uint8_t		s_uuid[OCFS2_VOL_UUID_LEN];	/* 128-bit uuid */
+} PACKED;
+
+int FAST_FUNC volume_id_probe_ocfs2(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct ocfs2_super_block *os;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	os = volume_id_get_buffer(id, off + OCFS2_SUPERBLOCK_OFFSET, 0x200);
+	if (os == NULL)
+		return -1;
+
+	if (memcmp(os->i_signature, "OCFSV2", 6) != 0) {
+		return -1;
+	}
+
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+//	volume_id_set_label_raw(id, os->s_label, OCFS2_MAX_VOL_LABEL_LEN < VOLUME_ID_LABEL_SIZE ?
+//					OCFS2_MAX_VOL_LABEL_LEN : VOLUME_ID_LABEL_SIZE);
+	volume_id_set_label_string(id, os->s_label, OCFS2_MAX_VOL_LABEL_LEN < VOLUME_ID_LABEL_SIZE ?
+					OCFS2_MAX_VOL_LABEL_LEN : VOLUME_ID_LABEL_SIZE);
+	volume_id_set_uuid(id, os->s_uuid, UUID_DCE);
+	IF_FEATURE_BLKID_TYPE(id->type = "ocfs2";)
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/reiserfs.c b/ap/app/busybox/src/util-linux/volume_id/reiserfs.c
new file mode 100644
index 0000000..67b4a18
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/reiserfs.c
@@ -0,0 +1,113 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2005 Tobias Klauser <tklauser@access.unizh.ch>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct reiserfs_super_block {
+	uint32_t	blocks_count;
+	uint32_t	free_blocks;
+	uint32_t	root_block;
+	uint32_t	journal_block;
+	uint32_t	journal_dev;
+	uint32_t	orig_journal_size;
+	uint32_t	dummy2[5];
+	uint16_t	blocksize;
+	uint16_t	dummy3[3];
+	uint8_t		magic[12];
+	uint32_t	dummy4[5];
+	uint8_t		uuid[16];
+	uint8_t		label[16];
+} PACKED;
+
+struct reiser4_super_block {
+	uint8_t		magic[16];
+	uint16_t	dummy[2];
+	uint8_t		uuid[16];
+	uint8_t		label[16];
+	uint64_t	dummy2;
+} PACKED;
+
+#define REISERFS1_SUPERBLOCK_OFFSET		0x2000
+#define REISERFS_SUPERBLOCK_OFFSET		0x10000
+
+int FAST_FUNC volume_id_probe_reiserfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct reiserfs_super_block *rs;
+	struct reiser4_super_block *rs4;
+
+	dbg("reiserfs: probing at offset 0x%llx", (unsigned long long) off);
+
+	rs = volume_id_get_buffer(id, off + REISERFS_SUPERBLOCK_OFFSET, 0x200);
+	if (rs == NULL)
+		return -1;
+
+	if (memcmp(rs->magic, "ReIsErFs", 8) == 0) {
+		dbg("reiserfs: ReIsErFs, no label");
+//		strcpy(id->type_version, "3.5");
+		goto found;
+	}
+	if (memcmp(rs->magic, "ReIsEr2Fs", 9) == 0) {
+		dbg("reiserfs: ReIsEr2Fs");
+//		strcpy(id->type_version, "3.6");
+		goto found_label;
+	}
+	if (memcmp(rs->magic, "ReIsEr3Fs", 9) == 0) {
+		dbg("reiserfs: ReIsEr3Fs");
+//		strcpy(id->type_version, "JR");
+		goto found_label;
+	}
+
+	rs4 = (struct reiser4_super_block *) rs;
+	if (memcmp(rs4->magic, "ReIsEr4", 7) == 0) {
+//		strcpy(id->type_version, "4");
+//		volume_id_set_label_raw(id, rs4->label, 16);
+		volume_id_set_label_string(id, rs4->label, 16);
+		volume_id_set_uuid(id, rs4->uuid, UUID_DCE);
+		dbg("reiserfs: ReIsEr4, label '%s' uuid '%s'", id->label, id->uuid);
+		goto found;
+	}
+
+	rs = volume_id_get_buffer(id, off + REISERFS1_SUPERBLOCK_OFFSET, 0x200);
+	if (rs == NULL)
+		return -1;
+
+	if (memcmp(rs->magic, "ReIsErFs", 8) == 0) {
+		dbg("reiserfs: ReIsErFs, no label");
+//		strcpy(id->type_version, "3.5");
+		goto found;
+	}
+
+	dbg("reiserfs: no signature found");
+	return -1;
+
+ found_label:
+//	volume_id_set_label_raw(id, rs->label, 16);
+	volume_id_set_label_string(id, rs->label, 16);
+	volume_id_set_uuid(id, rs->uuid, UUID_DCE);
+	dbg("reiserfs: label '%s' uuid '%s'", id->label, id->uuid);
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "reiserfs";)
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/romfs.c b/ap/app/busybox/src/util-linux/volume_id/romfs.c
new file mode 100644
index 0000000..15653be
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/romfs.c
@@ -0,0 +1,55 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct romfs_super {
+	uint8_t magic[8];
+	uint32_t size;
+	uint32_t checksum;
+	uint8_t name[];
+} PACKED;
+
+int FAST_FUNC volume_id_probe_romfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct romfs_super *rfs;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	rfs = volume_id_get_buffer(id, off, 0x200);
+	if (rfs == NULL)
+		return -1;
+
+	if (memcmp(rfs->magic, "-rom1fs-", 4) == 0) {
+		size_t len = strlen((char *)rfs->name);
+
+		if (len) {
+//			volume_id_set_label_raw(id, rfs->name, len);
+			volume_id_set_label_string(id, rfs->name, len);
+		}
+
+//		volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+		IF_FEATURE_BLKID_TYPE(id->type = "romfs";)
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/squashfs.c b/ap/app/busybox/src/util-linux/volume_id/squashfs.c
new file mode 100644
index 0000000..c5b4f9c
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/squashfs.c
@@ -0,0 +1,49 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_SQUASHFS) += squashfs.o
+
+#include "volume_id_internal.h"
+
+struct squashfs_superblock {
+	uint32_t	magic;
+/*
+	uint32_t	dummy[6];
+	uint16_t        major;
+	uint16_t        minor;
+*/
+} PACKED;
+
+int FAST_FUNC volume_id_probe_squashfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct squashfs_superblock *sb;
+
+	dbg("SquashFS: probing at offset 0x%llx", (unsigned long long) off);
+	sb = volume_id_get_buffer(id, off, 0x200);
+	if (!sb)
+		return -1;
+
+	// Old SquashFS (pre 4.0) can be both big and little endian, so test for both.
+	// Likewise, it is commonly used in firwmare with some non-standard signatures.
+#define pack(a,b,c,d) ( (uint32_t)((a * 256 + b) * 256 + c) * 256 + d )
+#define SIG1 pack('s','q','s','h')
+#define SIG2 pack('h','s','q','s')
+#define SIG3 pack('s','h','s','q')
+#define SIG4 pack('q','s','h','s')
+	if (sb->magic == SIG1
+	 || sb->magic == SIG2
+	 || sb->magic == SIG3
+	 || sb->magic == SIG4
+	) {
+		IF_FEATURE_BLKID_TYPE(id->type = "squashfs";)
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/sysv.c b/ap/app/busybox/src/util-linux/volume_id/sysv.c
new file mode 100644
index 0000000..6eb9646
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/sysv.c
@@ -0,0 +1,125 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+#define SYSV_NICINOD			100
+#define SYSV_NICFREE			50
+
+struct sysv_super {
+	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;
+} PACKED;
+
+#define XENIX_NICINOD				100
+#define XENIX_NICFREE				100
+
+struct xenix_super {
+	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;
+} PACKED;
+
+#define SYSV_SUPERBLOCK_BLOCK			0x01
+#define SYSV_MAGIC				0xfd187e20
+#define XENIX_SUPERBLOCK_BLOCK			0x18
+#define XENIX_MAGIC				0x2b5544
+#define SYSV_MAX_BLOCKSIZE			0x800
+
+int FAST_FUNC volume_id_probe_sysv(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct sysv_super *vs;
+	struct xenix_super *xs;
+	unsigned boff;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	for (boff = 0x200; boff <= SYSV_MAX_BLOCKSIZE; boff <<= 1) {
+		vs = volume_id_get_buffer(id, off + (boff * SYSV_SUPERBLOCK_BLOCK), 0x200);
+		if (vs == NULL)
+			return -1;
+
+		if (vs->s_magic == cpu_to_le32(SYSV_MAGIC) || vs->s_magic == cpu_to_be32(SYSV_MAGIC)) {
+//			volume_id_set_label_raw(id, vs->s_fname, 6);
+			volume_id_set_label_string(id, vs->s_fname, 6);
+			IF_FEATURE_BLKID_TYPE(id->type = "sysv");
+			goto found;
+		}
+	}
+
+	for (boff = 0x200; boff <= SYSV_MAX_BLOCKSIZE; boff <<= 1) {
+		xs = volume_id_get_buffer(id, off + (boff + XENIX_SUPERBLOCK_BLOCK), 0x200);
+		if (xs == NULL)
+			return -1;
+
+		if (xs->s_magic == cpu_to_le32(XENIX_MAGIC) || xs->s_magic == cpu_to_be32(XENIX_MAGIC)) {
+//			volume_id_set_label_raw(id, xs->s_fname, 6);
+			volume_id_set_label_string(id, xs->s_fname, 6);
+			IF_FEATURE_BLKID_TYPE(id->type = "xenix";)
+			goto found;
+		}
+	}
+
+	return -1;
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/udf.c b/ap/app/busybox/src/util-linux/volume_id/udf.c
new file mode 100644
index 0000000..d3747fb
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/udf.c
@@ -0,0 +1,172 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.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;
+	} PACKED tag;
+	union {
+		struct anchor_descriptor {
+			uint32_t	length;
+			uint32_t	location;
+		} PACKED anchor;
+		struct primary_descriptor {
+			uint32_t	seq_num;
+			uint32_t	desc_num;
+			struct dstring {
+				uint8_t	clen;
+				uint8_t	c[31];
+			} PACKED ident;
+		} PACKED primary;
+	} PACKED type;
+} PACKED;
+
+struct volume_structure_descriptor {
+	uint8_t		type;
+	uint8_t		id[5];
+	uint8_t		version;
+} PACKED;
+
+#define UDF_VSD_OFFSET			0x8000
+
+int FAST_FUNC volume_id_probe_udf(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct volume_descriptor *vd;
+	struct volume_structure_descriptor *vsd;
+	unsigned bs;
+	unsigned b;
+	unsigned type;
+	unsigned count;
+	unsigned loc;
+	unsigned clen;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	vsd = volume_id_get_buffer(id, off + UDF_VSD_OFFSET, 0x200);
+	if (vsd == NULL)
+		return -1;
+
+	if (memcmp(vsd->id, "NSR02", 5) == 0)
+		goto blocksize;
+	if (memcmp(vsd->id, "NSR03", 5) == 0)
+		goto blocksize;
+	if (memcmp(vsd->id, "BEA01", 5) == 0)
+		goto blocksize;
+	if (memcmp(vsd->id, "BOOT2", 5) == 0)
+		goto blocksize;
+	if (memcmp(vsd->id, "CD001", 5) == 0)
+		goto blocksize;
+	if (memcmp(vsd->id, "CDW02", 5) == 0)
+		goto blocksize;
+	if (memcmp(vsd->id, "TEA03", 5) == 0)
+		goto blocksize;
+	return -1;
+
+blocksize:
+	/* search the next VSD to get the logical block size of the volume */
+	for (bs = 0x800; bs < 0x8000; bs += 0x800) {
+		vsd = volume_id_get_buffer(id, off + UDF_VSD_OFFSET + bs, 0x800);
+		if (vsd == NULL)
+			return -1;
+		dbg("test for blocksize: 0x%x", bs);
+		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 = volume_id_get_buffer(id, off + UDF_VSD_OFFSET + (b * bs), 0x800);
+		if (vsd == NULL)
+			return -1;
+
+		dbg("vsd: %c%c%c%c%c",
+			vsd->id[0], vsd->id[1], vsd->id[2], vsd->id[3], vsd->id[4]);
+
+		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 */
+	vd = volume_id_get_buffer(id, off + (256 * bs), 0x200);
+	if (vd == NULL)
+		return -1;
+
+	type = le16_to_cpu(vd->tag.id);
+	if (type != 2) /* TAG_ID_AVDP */
+		goto found;
+
+	/* get desriptor list address and block count */
+	count = le32_to_cpu(vd->type.anchor.length) / bs;
+	loc = le32_to_cpu(vd->type.anchor.location);
+	dbg("0x%x descriptors starting at logical secor 0x%x", count, loc);
+
+	/* pick the primary descriptor from the list */
+	for (b = 0; b < count; b++) {
+		vd = volume_id_get_buffer(id, off + ((loc + b) * bs), 0x200);
+		if (vd == NULL)
+			return -1;
+
+		type = le16_to_cpu(vd->tag.id);
+		dbg("descriptor type %i", type);
+
+		/* check validity */
+		if (type == 0)
+			goto found;
+		if (le32_to_cpu(vd->tag.location) != loc + b)
+			goto found;
+
+		if (type == 1) /* TAG_ID_PVD */
+			goto pvd;
+	}
+	goto found;
+
+ pvd:
+//	volume_id_set_label_raw(id, &(vd->type.primary.ident.clen), 32);
+
+	clen = vd->type.primary.ident.clen;
+	dbg("label string charsize=%i bit", clen);
+	if (clen == 8)
+		volume_id_set_label_string(id, vd->type.primary.ident.c, 31);
+	else if (clen == 16)
+		volume_id_set_label_unicode16(id, vd->type.primary.ident.c, BE, 31);
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "udf";)
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/unused_highpoint.c b/ap/app/busybox/src/util-linux/volume_id/unused_highpoint.c
new file mode 100644
index 0000000..17b7b32
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/unused_highpoint.c
@@ -0,0 +1,86 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct hpt37x_meta {
+	uint8_t		filler1[32];
+	uint32_t	magic;
+} PACKED;
+
+struct hpt45x_meta {
+	uint32_t	magic;
+} PACKED;
+
+#define HPT37X_CONFIG_OFF		0x1200
+#define HPT37X_MAGIC_OK			0x5a7816f0
+#define HPT37X_MAGIC_BAD		0x5a7816fd
+
+#define HPT45X_MAGIC_OK			0x5a7816f3
+#define HPT45X_MAGIC_BAD		0x5a7816fd
+
+
+int FAST_FUNC volume_id_probe_highpoint_37x_raid(struct volume_id *id, uint64_t off)
+{
+	struct hpt37x_meta *hpt;
+	uint32_t magic;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	hpt = volume_id_get_buffer(id, off + HPT37X_CONFIG_OFF, 0x200);
+	if (hpt == NULL)
+		return -1;
+
+	magic = hpt->magic;
+	if (magic != cpu_to_le32(HPT37X_MAGIC_OK) && magic != cpu_to_le32(HPT37X_MAGIC_BAD))
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type = "highpoint_raid_member";
+
+	return 0;
+}
+
+int FAST_FUNC volume_id_probe_highpoint_45x_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	struct hpt45x_meta *hpt;
+	uint64_t meta_off;
+	uint32_t magic;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	meta_off = ((size / 0x200)-11) * 0x200;
+	hpt = volume_id_get_buffer(id, off + meta_off, 0x200);
+	if (hpt == NULL)
+		return -1;
+
+	magic = hpt->magic;
+	if (magic != cpu_to_le32(HPT45X_MAGIC_OK) && magic != cpu_to_le32(HPT45X_MAGIC_BAD))
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type = "highpoint_raid_member";
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/unused_hpfs.c b/ap/app/busybox/src/util-linux/volume_id/unused_hpfs.c
new file mode 100644
index 0000000..4429524
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/unused_hpfs.c
@@ -0,0 +1,48 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct hpfs_super {
+	uint8_t		magic[4];
+	uint8_t		version;
+} PACKED;
+
+#define HPFS_SUPERBLOCK_OFFSET			0x2000
+
+int FAST_FUNC volume_id_probe_hpfs(struct volume_id *id, uint64_t off)
+{
+	struct hpfs_super *hs;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	hs = volume_id_get_buffer(id, off + HPFS_SUPERBLOCK_OFFSET, 0x200);
+	if (hs == NULL)
+		return -1;
+
+	if (memcmp(hs->magic, "\x49\xe8\x95\xf9", 4) == 0) {
+//		sprintf(id->type_version, "%u", hs->version);
+//		volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+//		id->type = "hpfs";
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/unused_isw_raid.c b/ap/app/busybox/src/util-linux/volume_id/unused_isw_raid.c
new file mode 100644
index 0000000..7ab47b3
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/unused_isw_raid.c
@@ -0,0 +1,58 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct isw_meta {
+	uint8_t		sig[32];
+	uint32_t	check_sum;
+	uint32_t	mpb_size;
+	uint32_t	family_num;
+	uint32_t	generation_num;
+} PACKED;
+
+#define ISW_SIGNATURE		"Intel Raid ISM Cfg Sig. "
+
+
+int FAST_FUNC volume_id_probe_intel_software_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	uint64_t meta_off;
+	struct isw_meta *isw;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	meta_off = ((size / 0x200)-2) * 0x200;
+	isw = volume_id_get_buffer(id, off + meta_off, 0x200);
+	if (isw == NULL)
+		return -1;
+
+	if (memcmp(isw->sig, ISW_SIGNATURE, sizeof(ISW_SIGNATURE)-1) != 0)
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	memcpy(id->type_version, &isw->sig[sizeof(ISW_SIGNATURE)-1], 6);
+//	id->type = "isw_raid_member";
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/unused_lsi_raid.c b/ap/app/busybox/src/util-linux/volume_id/unused_lsi_raid.c
new file mode 100644
index 0000000..e6cc8ed
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/unused_lsi_raid.c
@@ -0,0 +1,52 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct lsi_meta {
+	uint8_t		sig[6];
+} PACKED;
+
+#define LSI_SIGNATURE		"$XIDE$"
+
+int FAST_FUNC volume_id_probe_lsi_mega_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	uint64_t meta_off;
+	struct lsi_meta *lsi;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	meta_off = ((size / 0x200)-1) * 0x200;
+	lsi = volume_id_get_buffer(id, off + meta_off, 0x200);
+	if (lsi == NULL)
+		return -1;
+
+	if (memcmp(lsi->sig, LSI_SIGNATURE, sizeof(LSI_SIGNATURE)-1) != 0)
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type = "lsi_mega_raid_member";
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/unused_lvm.c b/ap/app/busybox/src/util-linux/volume_id/unused_lvm.c
new file mode 100644
index 0000000..2206498
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/unused_lvm.c
@@ -0,0 +1,87 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct lvm1_super_block {
+	uint8_t	id[2];
+} PACKED;
+
+struct lvm2_super_block {
+	uint8_t		id[8];
+	uint64_t	sector_xl;
+	uint32_t	crc_xl;
+	uint32_t	offset_xl;
+	uint8_t		type[8];
+} PACKED;
+
+#define LVM1_SB_OFF			0x400
+
+int FAST_FUNC volume_id_probe_lvm1(struct volume_id *id, uint64_t off)
+{
+	struct lvm1_super_block *lvm;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	lvm = volume_id_get_buffer(id, off + LVM1_SB_OFF, 0x800);
+	if (lvm == NULL)
+		return -1;
+
+	if (lvm->id[0] != 'H' || lvm->id[1] != 'M')
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type = "LVM1_member";
+
+	return 0;
+}
+
+#define LVM2_LABEL_ID			"LABELONE"
+#define LVM2LABEL_SCAN_SECTORS		4
+
+int FAST_FUNC volume_id_probe_lvm2(struct volume_id *id, uint64_t off)
+{
+	const uint8_t *buf;
+	unsigned soff;
+	struct lvm2_super_block *lvm;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	buf = volume_id_get_buffer(id, off, LVM2LABEL_SCAN_SECTORS * 0x200);
+	if (buf == NULL)
+		return -1;
+
+
+	for (soff = 0; soff < LVM2LABEL_SCAN_SECTORS * 0x200; soff += 0x200) {
+		lvm = (struct lvm2_super_block *) &buf[soff];
+
+		if (memcmp(lvm->id, LVM2_LABEL_ID, 8) == 0)
+			goto found;
+	}
+
+	return -1;
+
+ found:
+//	memcpy(id->type_version, lvm->type, 8);
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type = "LVM2_member";
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/unused_mac.c b/ap/app/busybox/src/util-linux/volume_id/unused_mac.c
new file mode 100644
index 0000000..e8deb97
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/unused_mac.c
@@ -0,0 +1,123 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct mac_driver_desc {
+	uint8_t		signature[2];
+	uint16_t	block_size;
+	uint32_t	block_count;
+} PACKED;
+
+struct mac_partition {
+	uint8_t		signature[2];
+	uint16_t	res1;
+	uint32_t	map_count;
+	uint32_t	start_block;
+	uint32_t	block_count;
+	uint8_t		name[32];
+	uint8_t		type[32];
+} PACKED;
+
+int FAST_FUNC volume_id_probe_mac_partition_map(struct volume_id *id, uint64_t off)
+{
+	const uint8_t *buf;
+	struct mac_driver_desc *driver;
+	struct mac_partition *part;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	buf = volume_id_get_buffer(id, off, 0x200);
+	if (buf == NULL)
+		return -1;
+
+	part = (struct mac_partition *) buf;
+	if (part->signature[0] == 'P' && part->signature[1] == 'M' /* "PM" */
+	 && (memcmp(part->type, "Apple_partition_map", 19) == 0)
+	) {
+		/* linux creates an own subdevice for the map
+		 * just return the type if the drive header is missing */
+//		volume_id_set_usage(id, VOLUME_ID_PARTITIONTABLE);
+//		id->type = "mac_partition_map";
+		return 0;
+	}
+
+	driver = (struct mac_driver_desc *) buf;
+	if (driver->signature[0] == 'E' && driver->signature[1] == 'R') { /* "ER" */
+		/* we are on a main device, like a CD
+		 * just try to probe the first partition from the map */
+		unsigned bsize = be16_to_cpu(driver->block_size);
+		int part_count;
+		int i;
+
+		/* get first entry of partition table */
+		buf = volume_id_get_buffer(id, off +  bsize, 0x200);
+		if (buf == NULL)
+			return -1;
+
+		part = (struct mac_partition *) buf;
+		if (part->signature[0] != 'P' || part->signature[1] != 'M') /* not "PM" */
+			return -1;
+
+		part_count = be32_to_cpu(part->map_count);
+		dbg("expecting %d partition entries", part_count);
+
+		if (id->partitions != NULL)
+			free(id->partitions);
+		id->partitions = xzalloc(part_count * sizeof(struct volume_id_partition));
+
+		id->partition_count = part_count;
+
+		for (i = 0; i < part_count; i++) {
+			uint64_t poff;
+			uint64_t plen;
+
+			buf = volume_id_get_buffer(id, off + ((i+1) * bsize), 0x200);
+			if (buf == NULL)
+				return -1;
+
+			part = (struct mac_partition *) buf;
+			if (part->signature[0] != 'P' || part->signature[1] != 'M') /* not "PM" */
+				return -1;
+
+			poff = be32_to_cpu(part->start_block) * bsize;
+			plen = be32_to_cpu(part->block_count) * bsize;
+			dbg("found '%s' partition entry at 0x%llx, len 0x%llx",
+					part->type, (unsigned long long) poff,
+					(unsigned long long) plen);
+
+//			id->partitions[i].pt_off = poff;
+//			id->partitions[i].pt_len = plen;
+
+//			if (memcmp(part->type, "Apple_Free", 10) == 0) {
+//				volume_id_set_usage_part(&id->partitions[i], VOLUME_ID_UNUSED);
+//			} else if (memcmp(part->type, "Apple_partition_map", 19) == 0) {
+//				volume_id_set_usage_part(&id->partitions[i], VOLUME_ID_PARTITIONTABLE);
+//			} else {
+//				volume_id_set_usage_part(&id->partitions[i], VOLUME_ID_UNPROBED);
+//			}
+		}
+//		volume_id_set_usage(id, VOLUME_ID_PARTITIONTABLE);
+//		id->type = "mac_partition_map";
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/unused_minix.c b/ap/app/busybox/src/util-linux/volume_id/unused_minix.c
new file mode 100644
index 0000000..a3e1077
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/unused_minix.c
@@ -0,0 +1,75 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+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;
+} PACKED;
+
+#define MINIX_SUPERBLOCK_OFFSET			0x400
+
+int FAST_FUNC volume_id_probe_minix(struct volume_id *id, uint64_t off)
+{
+	struct minix_super_block *ms;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	ms = volume_id_get_buffer(id, off + MINIX_SUPERBLOCK_OFFSET, 0x200);
+	if (ms == NULL)
+		return -1;
+
+	if (ms->s_magic == cpu_to_le16(0x137f)) {
+//		id->type_version[0] = '1';
+		goto found;
+	}
+
+	if (ms->s_magic == cpu_to_le16(0x1387)) {
+//		id->type_version[0] = '1';
+		goto found;
+	}
+
+	if (ms->s_magic == cpu_to_le16(0x2468)) {
+//		id->type_version[0] = '2';
+		goto found;
+	}
+
+	if (ms->s_magic == cpu_to_le16(0x2478)) {
+//		id->type_version[0] = '2';
+		goto found;
+	}
+
+	return -1;
+
+ found:
+//	id->type_version[1] = '\0';
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+//	id->type = "minix";
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/unused_msdos.c b/ap/app/busybox/src/util-linux/volume_id/unused_msdos.c
new file mode 100644
index 0000000..2e8cb19
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/unused_msdos.c
@@ -0,0 +1,195 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct msdos_partition_entry {
+	uint8_t		boot_ind;
+	uint8_t		head;
+	uint8_t		sector;
+	uint8_t		cyl;
+	uint8_t		sys_ind;
+	uint8_t		end_head;
+	uint8_t		end_sector;
+	uint8_t		end_cyl;
+	uint32_t	start_sect;
+	uint32_t	nr_sects;
+} PACKED;
+
+#define MSDOS_PARTTABLE_OFFSET		0x1be
+#define MSDOS_SIG_OFF			0x1fe
+#define BSIZE				0x200
+#define DOS_EXTENDED_PARTITION		0x05
+#define LINUX_EXTENDED_PARTITION	0x85
+#define WIN98_EXTENDED_PARTITION	0x0f
+#define LINUX_RAID_PARTITION		0xfd
+#define is_extended(type) \
+	(type == DOS_EXTENDED_PARTITION ||	\
+	 type == WIN98_EXTENDED_PARTITION ||	\
+	 type == LINUX_EXTENDED_PARTITION)
+#define is_raid(type) \
+	(type == LINUX_RAID_PARTITION)
+
+int FAST_FUNC volume_id_probe_msdos_part_table(struct volume_id *id, uint64_t off)
+{
+	const uint8_t *buf;
+	int i;
+	uint64_t poff;
+	uint64_t plen;
+	uint64_t extended = 0;
+	uint64_t current;
+	uint64_t next;
+	int limit;
+	int empty = 1;
+	struct msdos_partition_entry *part;
+	struct volume_id_partition *p;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	buf = volume_id_get_buffer(id, off, 0x200);
+	if (buf == NULL)
+		return -1;
+
+	if (buf[MSDOS_SIG_OFF] != 0x55 || buf[MSDOS_SIG_OFF + 1] != 0xaa)
+		return -1;
+
+	/* check flags on all entries for a valid partition table */
+	part = (struct msdos_partition_entry*) &buf[MSDOS_PARTTABLE_OFFSET];
+	for (i = 0; i < 4; i++) {
+		if (part[i].boot_ind != 0
+		 && part[i].boot_ind != 0x80
+		) {
+			return -1;
+		}
+
+		if (part[i].nr_sects != 0)
+			empty = 0;
+	}
+	if (empty == 1)
+		return -1;
+
+	if (id->partitions != NULL)
+		free(id->partitions);
+	id->partitions = xzalloc(VOLUME_ID_PARTITIONS_MAX *
+				sizeof(struct volume_id_partition));
+
+	for (i = 0; i < 4; i++) {
+		poff = (uint64_t) le32_to_cpu(part[i].start_sect) * BSIZE;
+		plen = (uint64_t) le32_to_cpu(part[i].nr_sects) * BSIZE;
+
+		if (plen == 0)
+			continue;
+
+		p = &id->partitions[i];
+
+//		p->pt_type_raw = part[i].sys_ind;
+
+		if (is_extended(part[i].sys_ind)) {
+			dbg("found extended partition at 0x%llx", (unsigned long long) poff);
+//			volume_id_set_usage_part(p, VOLUME_ID_PARTITIONTABLE);
+//			p->type = "msdos_extended_partition";
+			if (extended == 0)
+				extended = off + poff;
+		} else {
+			dbg("found 0x%x data partition at 0x%llx, len 0x%llx",
+				part[i].sys_ind, (unsigned long long) poff, (unsigned long long) plen);
+
+//			if (is_raid(part[i].sys_ind))
+//				volume_id_set_usage_part(p, VOLUME_ID_RAID);
+//			else
+//				volume_id_set_usage_part(p, VOLUME_ID_UNPROBED);
+		}
+
+//		p->pt_off = off + poff;
+//		p->pt_len = plen;
+		id->partition_count = i+1;
+	}
+
+	next = extended;
+	current = extended;
+	limit = 50;
+
+	/* follow extended partition chain and add data partitions */
+	while (next != 0) {
+		if (limit-- == 0) {
+			dbg("extended chain limit reached");
+			break;
+		}
+
+		buf = volume_id_get_buffer(id, current, 0x200);
+		if (buf == NULL)
+			break;
+
+		part = (struct msdos_partition_entry*) &buf[MSDOS_PARTTABLE_OFFSET];
+
+		if (buf[MSDOS_SIG_OFF] != 0x55 || buf[MSDOS_SIG_OFF + 1] != 0xaa)
+			break;
+
+		next = 0;
+
+		for (i = 0; i < 4; i++) {
+			poff = (uint64_t) le32_to_cpu(part[i].start_sect) * BSIZE;
+			plen = (uint64_t) le32_to_cpu(part[i].nr_sects) * BSIZE;
+
+			if (plen == 0)
+				continue;
+
+			if (is_extended(part[i].sys_ind)) {
+				dbg("found extended partition at 0x%llx", (unsigned long long) poff);
+				if (next == 0)
+					next = extended + poff;
+			} else {
+				dbg("found 0x%x data partition at 0x%llx, len 0x%llx",
+					part[i].sys_ind, (unsigned long long) poff, (unsigned long long) plen);
+
+				/* we always start at the 5th entry */
+//				while (id->partition_count < 4)
+//					volume_id_set_usage_part(&id->partitions[id->partition_count++], VOLUME_ID_UNUSED);
+				if (id->partition_count < 4)
+					id->partition_count = 4;
+
+				p = &id->partitions[id->partition_count];
+
+//				if (is_raid(part[i].sys_ind))
+//					volume_id_set_usage_part(p, VOLUME_ID_RAID);
+//				else
+//					volume_id_set_usage_part(p, VOLUME_ID_UNPROBED);
+
+//				p->pt_off = current + poff;
+//				p->pt_len = plen;
+				id->partition_count++;
+
+//				p->pt_type_raw = part[i].sys_ind;
+
+				if (id->partition_count >= VOLUME_ID_PARTITIONS_MAX) {
+					dbg("too many partitions");
+					next = 0;
+				}
+			}
+		}
+
+		current = next;
+	}
+
+//	volume_id_set_usage(id, VOLUME_ID_PARTITIONTABLE);
+//	id->type = "msdos_partition_table";
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/unused_nvidia_raid.c b/ap/app/busybox/src/util-linux/volume_id/unused_nvidia_raid.c
new file mode 100644
index 0000000..9e84729
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/unused_nvidia_raid.c
@@ -0,0 +1,56 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct nvidia_meta {
+	uint8_t		vendor[8];
+	uint32_t	size;
+	uint32_t	chksum;
+	uint16_t	version;
+} PACKED;
+
+#define NVIDIA_SIGNATURE		"NVIDIA"
+
+int FAST_FUNC volume_id_probe_nvidia_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	uint64_t meta_off;
+	struct nvidia_meta *nv;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	meta_off = ((size / 0x200)-2) * 0x200;
+	nv = volume_id_get_buffer(id, off + meta_off, 0x200);
+	if (nv == NULL)
+		return -1;
+
+	if (memcmp(nv->vendor, NVIDIA_SIGNATURE, sizeof(NVIDIA_SIGNATURE)-1) != 0)
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	snprintf(id->type_version, sizeof(id->type_version)-1, "%u", le16_to_cpu(nv->version));
+//	id->type = "nvidia_raid_member";
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/unused_promise_raid.c b/ap/app/busybox/src/util-linux/volume_id/unused_promise_raid.c
new file mode 100644
index 0000000..0b0d006
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/unused_promise_raid.c
@@ -0,0 +1,63 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct promise_meta {
+	uint8_t	sig[24];
+} PACKED;
+
+#define PDC_CONFIG_OFF		0x1200
+#define PDC_SIGNATURE		"Promise Technology, Inc."
+
+int FAST_FUNC volume_id_probe_promise_fasttrack_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	static const unsigned short sectors[] = {
+		63, 255, 256, 16, 399
+	};
+
+	struct promise_meta *pdc;
+	unsigned i;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x40000)
+		return -1;
+
+	for (i = 0; i < ARRAY_SIZE(sectors); i++) {
+		uint64_t meta_off;
+
+		meta_off = ((size / 0x200) - sectors[i]) * 0x200;
+		pdc = volume_id_get_buffer(id, off + meta_off, 0x200);
+		if (pdc == NULL)
+			return -1;
+
+		if (memcmp(pdc->sig, PDC_SIGNATURE, sizeof(PDC_SIGNATURE)-1) == 0)
+			goto found;
+	}
+	return -1;
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type = "promise_fasttrack_raid_member";
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/unused_silicon_raid.c b/ap/app/busybox/src/util-linux/volume_id/unused_silicon_raid.c
new file mode 100644
index 0000000..878b881
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/unused_silicon_raid.c
@@ -0,0 +1,69 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct silicon_meta {
+	uint8_t		unknown0[0x2E];
+	uint8_t		ascii_version[0x36 - 0x2E];
+	uint8_t		diskname[0x56 - 0x36];
+	uint8_t		unknown1[0x60 - 0x56];
+	uint32_t	magic;
+	uint32_t	unknown1a[0x6C - 0x64];
+	uint32_t	array_sectors_low;
+	uint32_t	array_sectors_high;
+	uint8_t		unknown2[0x78 - 0x74];
+	uint32_t	thisdisk_sectors;
+	uint8_t		unknown3[0x100 - 0x7C];
+	uint8_t		unknown4[0x104 - 0x100];
+	uint16_t	product_id;
+	uint16_t	vendor_id;
+	uint16_t	minor_ver;
+	uint16_t	major_ver;
+} PACKED;
+
+#define SILICON_MAGIC		0x2F000000
+
+int FAST_FUNC volume_id_probe_silicon_medley_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	uint64_t meta_off;
+	struct silicon_meta *sil;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	meta_off = ((size / 0x200)-1) * 0x200;
+	sil = volume_id_get_buffer(id, off + meta_off, 0x200);
+	if (sil == NULL)
+		return -1;
+
+	if (sil->magic != cpu_to_le32(SILICON_MAGIC))
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	snprintf(id->type_version, sizeof(id->type_version)-1, "%u.%u",
+//		le16_to_cpu(sil->major_ver), le16_to_cpu(sil->minor_ver));
+//	id->type = "silicon_medley_raid_member";
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/unused_ufs.c b/ap/app/busybox/src/util-linux/volume_id/unused_ufs.c
new file mode 100644
index 0000000..9f925d9
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/unused_ufs.c
@@ -0,0 +1,206 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.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;
+	} PACKED 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];
+		} PACKED 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];
+			} PACKED fs_cstotal;
+			struct ufs_timeval {
+				int32_t		tv_sec;
+				int32_t		tv_usec;
+			} PACKED fs_time;
+			int64_t		fs_size;
+			int64_t		fs_dsize;
+			uint64_t	fs_csaddr;
+			int64_t		fs_pendingblocks;
+			int32_t		fs_pendinginodes;
+		} 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];
+		} PACKED 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];
+		} PACKED 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;
+		} PACKED 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];
+} PACKED;
+
+#define UFS_MAGIC			0x00011954
+#define UFS2_MAGIC			0x19540119
+#define UFS_MAGIC_FEA			0x00195612
+#define UFS_MAGIC_LFN			0x00095014
+
+int FAST_FUNC volume_id_probe_ufs(struct volume_id *id, uint64_t off)
+{
+	static const short offsets[] = { 0, 8, 64, 256 };
+
+	uint32_t magic;
+	unsigned i;
+	struct ufs_super_block *ufs;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	for (i = 0; i < ARRAY_SIZE(offsets); i++) {
+		ufs = volume_id_get_buffer(id, off + (offsets[i] * 0x400), 0x800);
+		if (ufs == NULL)
+			return -1;
+
+		dbg("offset 0x%x", offsets[i] * 0x400);
+		magic = ufs->fs_magic;
+		if ((magic == cpu_to_be32(UFS_MAGIC))
+		 || (magic == cpu_to_be32(UFS2_MAGIC))
+		 || (magic == cpu_to_be32(UFS_MAGIC_FEA))
+		 || (magic == cpu_to_be32(UFS_MAGIC_LFN))
+		) {
+			dbg("magic 0x%08x(be)", magic);
+			goto found;
+		}
+		if ((magic == cpu_to_le32(UFS_MAGIC))
+		 || (magic == cpu_to_le32(UFS2_MAGIC))
+		 || (magic == cpu_to_le32(UFS_MAGIC_FEA))
+		 || (magic == cpu_to_le32(UFS_MAGIC_LFN))
+		) {
+			dbg("magic 0x%08x(le)", magic);
+			goto found;
+		}
+	}
+	return -1;
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+//	id->type = "ufs";
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/unused_via_raid.c b/ap/app/busybox/src/util-linux/volume_id/unused_via_raid.c
new file mode 100644
index 0000000..a11eec1
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/unused_via_raid.c
@@ -0,0 +1,68 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct via_meta {
+	uint16_t	signature;
+	uint8_t		version_number;
+	struct via_array {
+		uint16_t	disk_bits;
+		uint8_t		disk_array_ex;
+		uint32_t	capacity_low;
+		uint32_t	capacity_high;
+		uint32_t	serial_checksum;
+	} PACKED array;
+	uint32_t	serial_checksum[8];
+	uint8_t		checksum;
+} PACKED;
+
+#define VIA_SIGNATURE		0xAA55
+
+int FAST_FUNC volume_id_probe_via_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	uint64_t meta_off;
+	struct via_meta *via;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	meta_off = ((size / 0x200)-1) * 0x200;
+
+	via = volume_id_get_buffer(id, off + meta_off, 0x200);
+	if (via == NULL)
+		return -1;
+
+	if (via->signature != cpu_to_le16(VIA_SIGNATURE))
+		return -1;
+
+	if (via->version_number > 1)
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type_version[0] = '0' + via->version_number;
+//	id->type_version[1] = '\0';
+//	id->type = "via_raid_member";
+
+	return 0;
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/util.c b/ap/app/busybox/src/util-linux/volume_id/util.c
new file mode 100644
index 0000000..061545f
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/util.c
@@ -0,0 +1,265 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+void volume_id_set_unicode16(char *str, size_t len, const uint8_t *buf, enum endian endianess, size_t count)
+{
+	unsigned i, j;
+	unsigned c;
+
+	j = 0;
+	for (i = 0; i + 2 <= count; i += 2) {
+		if (endianess == LE)
+			c = (buf[i+1] << 8) | buf[i];
+		else
+			c = (buf[i] << 8) | buf[i+1];
+		if (c == 0)
+			break;
+		if (j+1 >= len)
+			break;
+		if (c < 0x80) {
+			/* 0xxxxxxx */
+		} else {
+			uint8_t topbits = 0xc0;
+			if (j+2 >= len)
+				break;
+			if (c < 0x800) {
+				/* 110yyyxx 10xxxxxx */
+			} else {
+				if (j+3 >= len)
+					break;
+				/* 1110yyyy 10yyyyxx 10xxxxxx */
+				str[j++] = (uint8_t) (0xe0 | (c >> 12));
+				topbits = 0x80;
+			}
+			str[j++] = (uint8_t) (topbits | ((c >> 6) & 0x3f));
+			c = 0x80 | (c & 0x3f);
+		}
+		str[j++] = (uint8_t) c;
+	}
+	str[j] = '\0';
+}
+
+#ifdef UNUSED
+static const char *usage_to_string(enum volume_id_usage usage_id)
+{
+	switch (usage_id) {
+	case VOLUME_ID_FILESYSTEM:
+		return "filesystem";
+	case VOLUME_ID_PARTITIONTABLE:
+		return "partitiontable";
+	case VOLUME_ID_OTHER:
+		return "other";
+	case VOLUME_ID_RAID:
+		return "raid";
+	case VOLUME_ID_DISKLABEL:
+		return "disklabel";
+	case VOLUME_ID_CRYPTO:
+		return "crypto";
+	case VOLUME_ID_UNPROBED:
+		return "unprobed";
+	case VOLUME_ID_UNUSED:
+		return "unused";
+	}
+	return NULL;
+}
+
+void volume_id_set_usage_part(struct volume_id_partition *part, enum volume_id_usage usage_id)
+{
+	part->usage_id = usage_id;
+	part->usage = usage_to_string(usage_id);
+}
+
+void volume_id_set_usage(struct volume_id *id, enum volume_id_usage usage_id)
+{
+	id->usage_id = usage_id;
+	id->usage = usage_to_string(usage_id);
+}
+
+void volume_id_set_label_raw(struct volume_id *id, const uint8_t *buf, size_t count)
+{
+	memcpy(id->label_raw, buf, count);
+	id->label_raw_len = count;
+}
+#endif
+
+#ifdef NOT_NEEDED
+static size_t strnlen(const char *s, size_t maxlen)
+{
+	size_t i;
+	if (!maxlen) return 0;
+	if (!s) return 0;
+	for (i = 0; *s && i < maxlen; ++s) ++i;
+	return i;
+}
+#endif
+
+void volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t count)
+{
+	unsigned i;
+
+	memcpy(id->label, buf, count);
+
+	/* remove trailing whitespace */
+	i = strnlen(id->label, count);
+	while (i--) {
+		if (!isspace(id->label[i]))
+			break;
+	}
+	id->label[i+1] = '\0';
+}
+
+void volume_id_set_label_unicode16(struct volume_id *id, const uint8_t *buf, enum endian endianess, size_t count)
+{
+	volume_id_set_unicode16(id->label, sizeof(id->label), buf, endianess, count);
+}
+
+void volume_id_set_uuid(struct volume_id *id, const uint8_t *buf, enum uuid_format format)
+{
+	unsigned i;
+	unsigned count = (format == UUID_DCE_STRING ? VOLUME_ID_UUID_SIZE : 4 << format);
+
+//	memcpy(id->uuid_raw, buf, count);
+//	id->uuid_raw_len = count;
+
+	/* if set, create string in the same format, the native platform uses */
+	for (i = 0; i < count; i++)
+		if (buf[i] != 0)
+			goto set;
+	return; /* all bytes are zero, leave it empty ("") */
+
+set:
+	switch (format) {
+	case UUID_DOS:
+		sprintf(id->uuid, "%02X%02X-%02X%02X",
+			buf[3], buf[2], buf[1], buf[0]);
+		break;
+	case UUID_NTFS:
+		sprintf(id->uuid, "%02X%02X%02X%02X%02X%02X%02X%02X",
+			buf[7], buf[6], buf[5], buf[4],
+			buf[3], buf[2], buf[1], buf[0]);
+		break;
+	case UUID_DCE:
+		sprintf(id->uuid,
+			"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+			buf[0], buf[1], buf[2], buf[3],
+			buf[4], buf[5],
+			buf[6], buf[7],
+			buf[8], buf[9],
+			buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
+		break;
+	case UUID_DCE_STRING:
+		memcpy(id->uuid, buf, count);
+		id->uuid[count] = '\0';
+		break;
+	}
+}
+
+/* Do not use xlseek here. With it, single corrupted filesystem
+ * may result in attempt to seek past device -> exit.
+ * It's better to ignore such fs and continue.  */
+void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len)
+{
+	uint8_t *dst;
+	unsigned small_off;
+	ssize_t read_len;
+
+	dbg("get buffer off 0x%llx(%llu), len 0x%zx",
+		(unsigned long long) off, (unsigned long long) off, len);
+
+	/* check if requested area fits in superblock buffer */
+	if (off + len <= SB_BUFFER_SIZE
+	 /* && off <= SB_BUFFER_SIZE - want this paranoid overflow check? */
+	) {
+		if (id->sbbuf == NULL) {
+			id->sbbuf = xmalloc(SB_BUFFER_SIZE);
+		}
+		small_off = off;
+		dst = id->sbbuf;
+
+		/* check if we need to read */
+		len += off;
+		if (len <= id->sbbuf_len)
+			goto ret; /* we already have it */
+
+		dbg("read sbbuf len:0x%x", (unsigned) len);
+		id->sbbuf_len = len;
+		off = 0;
+		goto do_read;
+	}
+
+	if (len > SEEK_BUFFER_SIZE) {
+		dbg("seek buffer too small %d", SEEK_BUFFER_SIZE);
+		return NULL;
+	}
+	dst = id->seekbuf;
+
+	/* check if we need to read */
+	if ((off >= id->seekbuf_off)
+	 && ((off + len) <= (id->seekbuf_off + id->seekbuf_len))
+	) {
+		small_off = off - id->seekbuf_off; /* can't overflow */
+		goto ret; /* we already have it */
+	}
+
+	id->seekbuf_off = off;
+	id->seekbuf_len = len;
+	id->seekbuf = xrealloc(id->seekbuf, len);
+	small_off = 0;
+	dst = id->seekbuf;
+	dbg("read seekbuf off:0x%llx len:0x%zx",
+				(unsigned long long) off, len);
+ do_read:
+	if (lseek(id->fd, off, SEEK_SET) != off) {
+		dbg("seek(0x%llx) failed", (unsigned long long) off);
+		goto err;
+	}
+	read_len = full_read(id->fd, dst, len);
+	if (read_len != len) {
+		dbg("requested 0x%x bytes, got 0x%x bytes",
+				(unsigned) len, (unsigned) read_len);
+ err:
+		/* No filesystem can be this tiny. It's most likely
+		 * non-associated loop device, empty drive and so on.
+		 * Flag it, making it possible to short circuit future
+		 * accesses. Rationale:
+		 * users complained of slow blkid due to empty floppy drives.
+		 */
+		if (off < 64*1024)
+			id->error = 1;
+		/* id->seekbuf_len or id->sbbuf_len is wrong now! Fixing. */
+		volume_id_free_buffer(id);
+		return NULL;
+	}
+ ret:
+	return dst + small_off;
+}
+
+void volume_id_free_buffer(struct volume_id *id)
+{
+	free(id->sbbuf);
+	id->sbbuf = NULL;
+	id->sbbuf_len = 0;
+	free(id->seekbuf);
+	id->seekbuf = NULL;
+	id->seekbuf_len = 0;
+	id->seekbuf_off = 0; /* paranoia */
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/volume_id.c b/ap/app/busybox/src/util-linux/volume_id/volume_id.c
new file mode 100644
index 0000000..f0fc84c
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/volume_id.c
@@ -0,0 +1,255 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+
+/* Some detection routines do not set label or uuid anyway,
+ * so they are disabled. */
+
+/* Looks for partitions, we don't use it: */
+#define ENABLE_FEATURE_VOLUMEID_MAC           0
+/* #define ENABLE_FEATURE_VOLUMEID_MSDOS      0 - NB: this one
+ * was not properly added to probe table anyway - ??! */
+
+/* None of RAIDs have label or uuid, except LinuxRAID: */
+#define ENABLE_FEATURE_VOLUMEID_HIGHPOINTRAID 0
+#define ENABLE_FEATURE_VOLUMEID_ISWRAID       0
+#define ENABLE_FEATURE_VOLUMEID_LSIRAID       0
+#define ENABLE_FEATURE_VOLUMEID_LVM           0
+#define ENABLE_FEATURE_VOLUMEID_NVIDIARAID    0
+#define ENABLE_FEATURE_VOLUMEID_PROMISERAID   0
+#define ENABLE_FEATURE_VOLUMEID_SILICONRAID   0
+#define ENABLE_FEATURE_VOLUMEID_VIARAID       0
+
+/* These filesystems also have no label or uuid: */
+#define ENABLE_FEATURE_VOLUMEID_MINIX         0
+#define ENABLE_FEATURE_VOLUMEID_HPFS          0
+#define ENABLE_FEATURE_VOLUMEID_UFS           0
+
+
+typedef int FAST_FUNC (*raid_probe_fptr)(struct volume_id *id, /*uint64_t off,*/ uint64_t size);
+typedef int FAST_FUNC (*probe_fptr)(struct volume_id *id /*, uint64_t off*/);
+
+static const raid_probe_fptr raid1[] = {
+#if ENABLE_FEATURE_VOLUMEID_LINUXRAID
+	volume_id_probe_linux_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_ISWRAID
+	volume_id_probe_intel_software_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_LSIRAID
+	volume_id_probe_lsi_mega_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_VIARAID
+	volume_id_probe_via_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_SILICONRAID
+	volume_id_probe_silicon_medley_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_NVIDIARAID
+	volume_id_probe_nvidia_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_PROMISERAID
+	volume_id_probe_promise_fasttrack_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_HIGHPOINTRAID
+	volume_id_probe_highpoint_45x_raid,
+#endif
+};
+
+static const probe_fptr raid2[] = {
+#if ENABLE_FEATURE_VOLUMEID_LVM
+	volume_id_probe_lvm1,
+	volume_id_probe_lvm2,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_HIGHPOINTRAID
+	volume_id_probe_highpoint_37x_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_LUKS
+	volume_id_probe_luks,
+#endif
+};
+
+/* signature in the first block, only small buffer needed */
+static const probe_fptr fs1[] = {
+#if ENABLE_FEATURE_VOLUMEID_FAT
+	volume_id_probe_vfat,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_EXFAT
+	volume_id_probe_exfat,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_MAC
+	volume_id_probe_mac_partition_map,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_SQUASHFS
+	volume_id_probe_squashfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_XFS
+	volume_id_probe_xfs,
+#endif
+};
+
+/* fill buffer with maximum */
+static const probe_fptr fs2[] = {
+#if ENABLE_FEATURE_VOLUMEID_LINUXSWAP
+	volume_id_probe_linux_swap,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_EXT
+	volume_id_probe_ext,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_BTRFS
+	volume_id_probe_btrfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_REISERFS
+	volume_id_probe_reiserfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_JFS
+	volume_id_probe_jfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_UDF
+	volume_id_probe_udf,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_ISO9660
+	volume_id_probe_iso9660,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_HFS
+	volume_id_probe_hfs_hfsplus,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_UFS
+	volume_id_probe_ufs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_NILFS
+	volume_id_probe_nilfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_NTFS
+	volume_id_probe_ntfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_CRAMFS
+	volume_id_probe_cramfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_ROMFS
+	volume_id_probe_romfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_HPFS
+	volume_id_probe_hpfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_SYSV
+	volume_id_probe_sysv,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_MINIX
+	volume_id_probe_minix,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_OCFS2
+	volume_id_probe_ocfs2,
+#endif
+};
+
+int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size)
+{
+	unsigned i;
+
+	/* probe for raid first, cause fs probes may be successful on raid members */
+	if (size) {
+		for (i = 0; i < ARRAY_SIZE(raid1); i++) {
+			if (raid1[i](id, /*off,*/ size) == 0)
+				goto ret;
+			if (id->error)
+				goto ret;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(raid2); i++) {
+		if (raid2[i](id /*,off*/) == 0)
+			goto ret;
+		if (id->error)
+			goto ret;
+	}
+
+	/* signature in the first block, only small buffer needed */
+	for (i = 0; i < ARRAY_SIZE(fs1); i++) {
+		if (fs1[i](id /*,off*/) == 0)
+			goto ret;
+		if (id->error)
+			goto ret;
+	}
+
+	/* fill buffer with maximum */
+	volume_id_get_buffer(id, 0, SB_BUFFER_SIZE);
+
+	for (i = 0; i < ARRAY_SIZE(fs2); i++) {
+		if (fs2[i](id /*,off*/) == 0)
+			goto ret;
+		if (id->error)
+			goto ret;
+	}
+
+ ret:
+	volume_id_free_buffer(id);
+	return (- id->error); /* 0 or -1 */
+}
+
+/* open volume by device node */
+struct volume_id* FAST_FUNC volume_id_open_node(int fd)
+{
+	struct volume_id *id;
+
+	id = xzalloc(sizeof(struct volume_id));
+	id->fd = fd;
+	///* close fd on device close */
+	//id->fd_close = 1;
+	return id;
+}
+
+#ifdef UNUSED
+/* open volume by major/minor */
+struct volume_id* FAST_FUNC volume_id_open_dev_t(dev_t devt)
+{
+	struct volume_id *id;
+	char *tmp_node[VOLUME_ID_PATH_MAX];
+
+	tmp_node = xasprintf("/dev/.volume_id-%u-%u-%u",
+		(unsigned)getpid(), (unsigned)major(devt), (unsigned)minor(devt));
+
+	/* create temporary node to open block device */
+	unlink(tmp_node);
+	if (mknod(tmp_node, (S_IFBLK | 0600), devt) != 0)
+		bb_perror_msg_and_die("can't mknod(%s)", tmp_node);
+
+	id = volume_id_open_node(tmp_node);
+	unlink(tmp_node);
+	free(tmp_node);
+	return id;
+}
+#endif
+
+void FAST_FUNC free_volume_id(struct volume_id *id)
+{
+	if (id == NULL)
+		return;
+
+	//if (id->fd_close != 0) - always true
+		close(id->fd);
+	volume_id_free_buffer(id);
+#ifdef UNUSED_PARTITION_CODE
+	free(id->partitions);
+#endif
+	free(id);
+}
diff --git a/ap/app/busybox/src/util-linux/volume_id/volume_id_internal.h b/ap/app/busybox/src/util-linux/volume_id/volume_id_internal.h
new file mode 100644
index 0000000..3f02bd5
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/volume_id_internal.h
@@ -0,0 +1,240 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "libbb.h"
+#include "volume_id.h"
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+#define dbg(...) ((void)0)
+/* #define dbg(...) bb_error_msg(__VA_ARGS__) */
+
+/* volume_id.h */
+
+#define VOLUME_ID_VERSION		48
+
+#define VOLUME_ID_LABEL_SIZE		64
+#define VOLUME_ID_UUID_SIZE		36
+#define VOLUME_ID_FORMAT_SIZE		32
+#define VOLUME_ID_PARTITIONS_MAX	256
+
+enum volume_id_usage {
+	VOLUME_ID_UNUSED,
+	VOLUME_ID_UNPROBED,
+	VOLUME_ID_OTHER,
+	VOLUME_ID_FILESYSTEM,
+	VOLUME_ID_PARTITIONTABLE,
+	VOLUME_ID_RAID,
+	VOLUME_ID_DISKLABEL,
+	VOLUME_ID_CRYPTO,
+};
+
+#ifdef UNUSED_PARTITION_CODE
+struct volume_id_partition {
+//	const char	*type;
+//	const char	*usage;
+//	smallint	usage_id;
+//	uint8_t		pt_type_raw;
+//	uint64_t	pt_off;
+//	uint64_t	pt_len;
+};
+#endif
+
+struct volume_id {
+	int		fd;
+//	int		fd_close:1;
+	int		error;
+	size_t		sbbuf_len;
+	size_t		seekbuf_len;
+	uint8_t		*sbbuf;
+	uint8_t		*seekbuf;
+	uint64_t	seekbuf_off;
+#ifdef UNUSED_PARTITION_CODE
+	struct volume_id_partition *partitions;
+	size_t		partition_count;
+#endif
+//	uint8_t		label_raw[VOLUME_ID_LABEL_SIZE];
+//	size_t		label_raw_len;
+	char		label[VOLUME_ID_LABEL_SIZE+1];
+//	uint8_t		uuid_raw[VOLUME_ID_UUID_SIZE];
+//	size_t		uuid_raw_len;
+	/* uuid is stored in ASCII (not binary) form here: */
+	char		uuid[VOLUME_ID_UUID_SIZE+1];
+//	char		type_version[VOLUME_ID_FORMAT_SIZE];
+//	smallint	usage_id;
+//	const char	*usage;
+#if ENABLE_FEATURE_BLKID_TYPE
+	const char	*type;
+#endif
+};
+
+struct volume_id* FAST_FUNC volume_id_open_node(int fd);
+int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size);
+void FAST_FUNC free_volume_id(struct volume_id *id);
+
+/* util.h */
+
+/* size of superblock buffer, reiserfs block is at 64k */
+#define SB_BUFFER_SIZE				0x11000
+/* size of seek buffer, FAT cluster is 32k max */
+#define SEEK_BUFFER_SIZE			0x10000
+
+#define bswap16(x) (uint16_t)	( \
+				(((uint16_t)(x) & 0x00ffu) << 8) | \
+				(((uint16_t)(x) & 0xff00u) >> 8))
+
+#define bswap32(x) (uint32_t)	( \
+				(((uint32_t)(x) & 0xff000000u) >> 24) | \
+				(((uint32_t)(x) & 0x00ff0000u) >>  8) | \
+				(((uint32_t)(x) & 0x0000ff00u) <<  8) | \
+				(((uint32_t)(x) & 0x000000ffu) << 24))
+
+#define bswap64(x) (uint64_t)	( \
+				(((uint64_t)(x) & 0xff00000000000000ull) >> 56) | \
+				(((uint64_t)(x) & 0x00ff000000000000ull) >> 40) | \
+				(((uint64_t)(x) & 0x0000ff0000000000ull) >> 24) | \
+				(((uint64_t)(x) & 0x000000ff00000000ull) >>  8) | \
+				(((uint64_t)(x) & 0x00000000ff000000ull) <<  8) | \
+				(((uint64_t)(x) & 0x0000000000ff0000ull) << 24) | \
+				(((uint64_t)(x) & 0x000000000000ff00ull) << 40) | \
+				(((uint64_t)(x) & 0x00000000000000ffull) << 56))
+
+#if BB_LITTLE_ENDIAN
+#define le16_to_cpu(x) (x)
+#define le32_to_cpu(x) (x)
+#define le64_to_cpu(x) (x)
+#define be16_to_cpu(x) bswap16(x)
+#define be32_to_cpu(x) bswap32(x)
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#define cpu_to_be32(x) bswap32(x)
+#else
+#define le16_to_cpu(x) bswap16(x)
+#define le32_to_cpu(x) bswap32(x)
+#define le64_to_cpu(x) bswap64(x)
+#define be16_to_cpu(x) (x)
+#define be32_to_cpu(x) (x)
+#define cpu_to_le16(x) bswap16(x)
+#define cpu_to_le32(x) bswap32(x)
+#define cpu_to_be32(x) (x)
+#endif
+
+/* volume_id_set_uuid(id,buf,fmt) assumes size of uuid buf
+ * by shifting: 4 << fmt, except for fmt == UUID_DCE_STRING.
+ * The constants below should match sizes.
+ */
+enum uuid_format {
+	UUID_DOS = 0,		/* 4 bytes */
+	UUID_NTFS = 1,		/* 8 bytes */
+	UUID_DCE = 2,		/* 16 bytes */
+	UUID_DCE_STRING = 3,	/* 36 bytes (VOLUME_ID_UUID_SIZE) */
+};
+
+enum endian {
+	LE = 0,
+	BE = 1
+};
+
+void volume_id_set_unicode16(char *str, size_t len, const uint8_t *buf, enum endian endianess, size_t count);
+//void volume_id_set_usage(struct volume_id *id, enum volume_id_usage usage_id);
+//void volume_id_set_usage_part(struct volume_id_partition *part, enum volume_id_usage usage_id);
+//void volume_id_set_label_raw(struct volume_id *id, const uint8_t *buf, size_t count);
+void volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t count);
+void volume_id_set_label_unicode16(struct volume_id *id, const uint8_t *buf, enum endian endianess, size_t count);
+void volume_id_set_uuid(struct volume_id *id, const uint8_t *buf, enum uuid_format format);
+void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len);
+void volume_id_free_buffer(struct volume_id *id);
+
+
+/* Probe routines */
+
+/* RAID */
+
+//int FAST_FUNC volume_id_probe_highpoint_37x_raid(struct volume_id *id /*,uint64_t off*/);
+//int FAST_FUNC volume_id_probe_highpoint_45x_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_intel_software_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+int FAST_FUNC volume_id_probe_linux_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_lsi_mega_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_nvidia_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_promise_fasttrack_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_silicon_medley_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_via_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_lvm1(struct volume_id *id /*,uint64_t off*/);
+//int FAST_FUNC volume_id_probe_lvm2(struct volume_id *id /*,uint64_t off*/);
+
+/* FS */
+
+int FAST_FUNC volume_id_probe_btrfs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_cramfs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_ext(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_vfat(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_hfs_hfsplus(struct volume_id *id /*,uint64_t off*/);
+
+//int FAST_FUNC volume_id_probe_hpfs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_iso9660(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_jfs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_linux_swap(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_luks(struct volume_id *id /*,uint64_t off*/);
+
+//int FAST_FUNC volume_id_probe_mac_partition_map(struct volume_id *id /*,uint64_t off*/);
+
+//int FAST_FUNC volume_id_probe_minix(struct volume_id *id /*,uint64_t off*/);
+
+//int FAST_FUNC volume_id_probe_msdos_part_table(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_nilfs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_ntfs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_exfat(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_ocfs2(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_reiserfs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_romfs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_squashfs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_sysv(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_udf(struct volume_id *id /*,uint64_t off*/);
+
+//int FAST_FUNC volume_id_probe_ufs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_xfs(struct volume_id *id /*,uint64_t off*/);
+
+POP_SAVED_FUNCTION_VISIBILITY
diff --git a/ap/app/busybox/src/util-linux/volume_id/xfs.c b/ap/app/busybox/src/util-linux/volume_id/xfs.c
new file mode 100644
index 0000000..8474602
--- /dev/null
+++ b/ap/app/busybox/src/util-linux/volume_id/xfs.c
@@ -0,0 +1,60 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *	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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "volume_id_internal.h"
+
+struct xfs_super_block {
+	uint8_t	magic[4];
+	uint32_t	blocksize;
+	uint64_t	dblocks;
+	uint64_t	rblocks;
+	uint32_t	dummy1[2];
+	uint8_t	uuid[16];
+	uint32_t	dummy2[15];
+	uint8_t	fname[12];
+	uint32_t	dummy3[2];
+	uint64_t	icount;
+	uint64_t	ifree;
+	uint64_t	fdblocks;
+} PACKED;
+
+int FAST_FUNC volume_id_probe_xfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct xfs_super_block *xs;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	xs = volume_id_get_buffer(id, off, 0x200);
+	if (xs == NULL)
+		return -1;
+
+	if (memcmp(xs->magic, "XFSB", 4) != 0)
+		return -1;
+
+//	volume_id_set_label_raw(id, xs->fname, 12);
+	volume_id_set_label_string(id, xs->fname, 12);
+	volume_id_set_uuid(id, xs->uuid, UUID_DCE);
+
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "xfs";)
+
+	return 0;
+}