aboutsummaryrefslogtreecommitdiff
path: root/util-linux
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2015-05-15 10:20:47 +0200
committerBjørn Mork <bjorn@mork.no>2015-05-15 10:20:47 +0200
commit73b16af8feec390afbabd9356d6e5e83c0390838 (patch)
tree3730020ba2f9caeb9d7815a975af51830b51ce11 /util-linux
busybox: imported from http://www.busybox.net/downloads/busybox-1.13.3.tar.bz2busybox-1.13.3
Signed-off-by: Bjørn Mork <bjorn@mork.no>
Diffstat (limited to 'util-linux')
-rw-r--r--util-linux/Config.in845
-rw-r--r--util-linux/Kbuild37
-rw-r--r--util-linux/blkid.c18
-rw-r--r--util-linux/dmesg.c67
-rw-r--r--util-linux/fbset.c404
-rw-r--r--util-linux/fdformat.c130
-rw-r--r--util-linux/fdisk.c2997
-rw-r--r--util-linux/fdisk_aix.c73
-rw-r--r--util-linux/fdisk_osf.c1052
-rw-r--r--util-linux/fdisk_sgi.c886
-rw-r--r--util-linux/fdisk_sun.c727
-rw-r--r--util-linux/findfs.c38
-rw-r--r--util-linux/freeramdisk.c33
-rw-r--r--util-linux/fsck_minix.c1309
-rw-r--r--util-linux/getopt.c354
-rw-r--r--util-linux/hexdump.c151
-rw-r--r--util-linux/hwclock.c127
-rw-r--r--util-linux/ipcrm.c220
-rw-r--r--util-linux/ipcs.c621
-rw-r--r--util-linux/losetup.c79
-rw-r--r--util-linux/mdev.c487
-rw-r--r--util-linux/minix.h98
-rw-r--r--util-linux/mkfs_minix.c734
-rw-r--r--util-linux/mkswap.c129
-rw-r--r--util-linux/more.c205
-rw-r--r--util-linux/mount.c1951
-rw-r--r--util-linux/pivot_root.c27
-rw-r--r--util-linux/rdate.c71
-rw-r--r--util-linux/rdev.c24
-rw-r--r--util-linux/readprofile.c247
-rw-r--r--util-linux/rtcwake.c199
-rw-r--r--util-linux/script.c186
-rw-r--r--util-linux/setarch.c48
-rw-r--r--util-linux/swaponoff.c102
-rw-r--r--util-linux/switch_root.c115
-rw-r--r--util-linux/umount.c173
-rw-r--r--util-linux/volume_id/Kbuild42
-rw-r--r--util-linux/volume_id/cramfs.c58
-rw-r--r--util-linux/volume_id/ext.c73
-rw-r--r--util-linux/volume_id/fat.c331
-rw-r--r--util-linux/volume_id/get_devname.c254
-rw-r--r--util-linux/volume_id/hfs.c291
-rw-r--r--util-linux/volume_id/iso9660.c119
-rw-r--r--util-linux/volume_id/jfs.c59
-rw-r--r--util-linux/volume_id/linux_raid.c79
-rw-r--r--util-linux/volume_id/linux_swap.c73
-rw-r--r--util-linux/volume_id/luks.c99
-rw-r--r--util-linux/volume_id/ntfs.c193
-rw-r--r--util-linux/volume_id/ocfs2.c105
-rw-r--r--util-linux/volume_id/reiserfs.c112
-rw-r--r--util-linux/volume_id/romfs.c54
-rw-r--r--util-linux/volume_id/sysv.c125
-rw-r--r--util-linux/volume_id/udf.c172
-rw-r--r--util-linux/volume_id/unused_highpoint.c86
-rw-r--r--util-linux/volume_id/unused_hpfs.c49
-rw-r--r--util-linux/volume_id/unused_isw_raid.c58
-rw-r--r--util-linux/volume_id/unused_lsi_raid.c52
-rw-r--r--util-linux/volume_id/unused_lvm.c87
-rw-r--r--util-linux/volume_id/unused_mac.c123
-rw-r--r--util-linux/volume_id/unused_minix.c75
-rw-r--r--util-linux/volume_id/unused_msdos.c193
-rw-r--r--util-linux/volume_id/unused_nvidia_raid.c56
-rw-r--r--util-linux/volume_id/unused_promise_raid.c63
-rw-r--r--util-linux/volume_id/unused_silicon_raid.c69
-rw-r--r--util-linux/volume_id/unused_ufs.c206
-rw-r--r--util-linux/volume_id/unused_via_raid.c68
-rw-r--r--util-linux/volume_id/util.c272
-rw-r--r--util-linux/volume_id/volume_id.c238
-rw-r--r--util-linux/volume_id/volume_id_internal.h233
-rw-r--r--util-linux/volume_id/xfs.c59
70 files changed, 19190 insertions, 0 deletions
diff --git a/util-linux/Config.in b/util-linux/Config.in
new file mode 100644
index 0000000..976507b
--- /dev/null
+++ b/util-linux/Config.in
@@ -0,0 +1,845 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Linux System Utilities"
+
+config BLKID
+ bool "blkid"
+ default n
+ select VOLUMEID
+ help
+ Lists labels and UUIDs of all filesystems.
+ WARNING:
+ With all submodules selected, it will add ~8k to busybox.
+
+config DMESG
+ bool "dmesg"
+ default n
+ 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 n
+ 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 n
+ 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 n
+ 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 n
+ 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 n
+ help
+ fdformat is used to low-level format a floppy disk.
+
+config FDISK
+ bool "fdisk"
+ default n
+ 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
+ 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_FDISK_ADVANCED
+ bool "Support expert mode"
+ default n
+ 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 n
+ 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 FREERAMDISK
+ bool "freeramdisk"
+ default n
+ 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 n
+ 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_MINIX
+ bool "mkfs_minix"
+ default n
+ 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.
+
+comment "Minix filesystem support"
+ depends on FSCK_MINIX || MKFS_MINIX
+
+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 GETOPT
+ bool "getopt"
+ default n
+ 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 HEXDUMP
+ bool "hexdump"
+ default n
+ 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 n
+ 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 n
+ select HEXDUMP
+ help
+ hd is an alias to hexdump -C.
+
+config HWCLOCK
+ bool "hwclock"
+ default n
+ 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 n
+ depends on HWCLOCK && GETOPT_LONG
+ 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 y
+ 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 n
+ select FEATURE_SUID
+ 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 n
+ select FEATURE_SUID
+ 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 n
+ 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 MDEV
+ bool "mdev"
+ default n
+ help
+ mdev is a mini-udev implementation for dynamically creating device
+ nodes in the /dev directory.
+
+ For more information, please see docs/mdev.txt
+
+config FEATURE_MDEV_CONF
+ bool "Support /etc/mdev.conf"
+ default n
+ depends on MDEV
+ help
+ Add support for the mdev config file to control ownership and
+ permissions of the device nodes.
+
+ For more information, please see docs/mdev.txt
+
+config FEATURE_MDEV_RENAME
+ bool "Support subdirs/symlinks"
+ default n
+ depends on FEATURE_MDEV_CONF
+ help
+ Add support for renaming devices and creating symlinks.
+
+ For more information, please see docs/mdev.txt
+
+config FEATURE_MDEV_RENAME_REGEXP
+ bool "Support regular expressions substitutions when renaming device"
+ default n
+ depends on FEATURE_MDEV_RENAME
+ help
+ Add support for regular expressions substitutions when renaming
+ device.
+
+config FEATURE_MDEV_EXEC
+ bool "Support command execution at device addition/removal"
+ default n
+ depends on FEATURE_MDEV_CONF
+ help
+ This adds support for an optional field to /etc/mdev.conf for
+ executing commands when devices are created/removed.
+
+ For more information, please see docs/mdev.txt
+
+config FEATURE_MDEV_LOAD_FIRMWARE
+ bool "Support loading of firmwares"
+ default n
+ depends on MDEV
+ help
+ Some devices need to load firmware before they can be usable.
+
+ These devices will request userspace look up the files in
+ /lib/firmware/ and if it exists, send it to the kernel for
+ loading into the hardware.
+
+config MKSWAP
+ bool "mkswap"
+ default n
+ 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_V0
+ bool "Version 0 support"
+ default n
+ depends on MKSWAP
+# depends on MKSWAP && DEPRECATED
+ help
+ Enable support for the old v0 style.
+ If your kernel is older than 2.1.117, then v0 support is the
+ only option.
+
+config MORE
+ bool "more"
+ default n
+ 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 FEATURE_USE_TERMIOS
+ bool "Use termios to manipulate the screen"
+ default y
+ depends on MORE || TOP
+ help
+ This option allows utilities such as 'more' and 'top' to determine
+ the size of the screen. If you leave this disabled, your utilities
+ that display things on the screen will be especially primitive and
+ will be unable to determine the current screen size, and will be
+ unable to move the cursor.
+
+config VOLUMEID
+ bool #No description makes it a hidden option
+ default n
+
+config FEATURE_VOLUMEID_EXT
+ bool "Ext filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+config FEATURE_VOLUMEID_REISERFS
+ bool "Reiser filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+config FEATURE_VOLUMEID_FAT
+ bool "fat filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+config FEATURE_VOLUMEID_HFS
+ bool "hfs filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+config FEATURE_VOLUMEID_JFS
+ bool "jfs filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+### config FEATURE_VOLUMEID_UFS
+### bool "ufs filesystem"
+### default n
+### depends on VOLUMEID
+### help
+### TODO
+
+config FEATURE_VOLUMEID_XFS
+ bool "xfs filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+config FEATURE_VOLUMEID_NTFS
+ bool "ntfs filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+config FEATURE_VOLUMEID_ISO9660
+ bool "iso9660 filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+config FEATURE_VOLUMEID_UDF
+ bool "udf filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+config FEATURE_VOLUMEID_LUKS
+ bool "luks filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+config FEATURE_VOLUMEID_LINUXSWAP
+ bool "linux swap filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+### config FEATURE_VOLUMEID_LVM
+### bool "lvm"
+### default n
+### depends on VOLUMEID
+### help
+### TODO
+
+config FEATURE_VOLUMEID_CRAMFS
+ bool "cramfs filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+### config FEATURE_VOLUMEID_HPFS
+### bool "hpfs filesystem"
+### default n
+### depends on VOLUMEID
+### help
+### TODO
+
+config FEATURE_VOLUMEID_ROMFS
+ bool "romfs filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+config FEATURE_VOLUMEID_SYSV
+ bool "sysv filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+### config FEATURE_VOLUMEID_MINIX
+### bool "minix filesystem"
+### default n
+### depends on VOLUMEID
+### help
+### TODO
+
+### These only detect partition tables - not used (yet?)
+### config FEATURE_VOLUMEID_MAC
+### bool "mac filesystem"
+### default n
+### depends on VOLUMEID
+### help
+### TODO
+###
+### config FEATURE_VOLUMEID_MSDOS
+### bool "msdos filesystem"
+### default n
+### depends on VOLUMEID
+### help
+### TODO
+
+config FEATURE_VOLUMEID_OCFS2
+ bool "ocfs2 filesystem"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+### config FEATURE_VOLUMEID_HIGHPOINTRAID
+### bool "highpoint raid"
+### default n
+### depends on VOLUMEID
+### help
+### TODO
+
+### config FEATURE_VOLUMEID_ISWRAID
+### bool "intel raid"
+### default n
+### depends on VOLUMEID
+### help
+### TODO
+
+### config FEATURE_VOLUMEID_LSIRAID
+### bool "lsi raid"
+### default n
+### depends on VOLUMEID
+### help
+### TODO
+
+### config FEATURE_VOLUMEID_VIARAID
+### bool "via raid"
+### default n
+### depends on VOLUMEID
+### help
+### TODO
+
+### config FEATURE_VOLUMEID_SILICONRAID
+### bool "silicon raid"
+### default n
+### depends on VOLUMEID
+### help
+### TODO
+
+### config FEATURE_VOLUMEID_NVIDIARAID
+### bool "nvidia raid"
+### default n
+### depends on VOLUMEID
+### help
+### TODO
+
+### config FEATURE_VOLUMEID_PROMISERAID
+### bool "promise raid"
+### default n
+### depends on VOLUMEID
+### help
+### TODO
+
+config FEATURE_VOLUMEID_LINUXRAID
+ bool "linuxraid"
+ default n
+ depends on VOLUMEID
+ help
+ TODO
+
+config MOUNT
+ bool "mount"
+ default n
+ 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 n
+ depends on MOUNT
+ help
+ Enable support for faking a file system mount.
+
+config FEATURE_MOUNT_VERBOSE
+ bool "Support option -v"
+ default n
+ 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 specifiying devices by label or UUID"
+ default n
+ 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.
+
+config FEATURE_MOUNT_NFS
+ bool "Support mounting NFS file systems"
+ default n
+ depends on MOUNT
+ select FEATURE_HAVE_RPC
+ select FEATURE_SYSLOG
+ help
+ Enable mounting of NFS file systems.
+
+config FEATURE_MOUNT_CIFS
+ bool "Support mounting CIFS/SMB file systems"
+ default n
+ 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 n
+ 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 n
+ 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 n
+ help
+ Print the device node associated with the filesystem mounted at '/'.
+
+config READPROFILE
+ bool "readprofile"
+ default n
+ help
+ This allows you to parse /proc/profile for basic profiling.
+
+config RTCWAKE
+ bool "rtcwake"
+ default n
+ help
+ Enter a system sleep state until specified wakeup time.
+
+config SCRIPT
+ bool "script"
+ default n
+ help
+ The script makes typescript of terminal session.
+
+config SETARCH
+ bool "setarch"
+ default n
+ 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 n
+ 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 n
+ depends on SWAPONOFF
+ help
+ Enable support for setting swap device priority in swapon.
+
+config SWITCH_ROOT
+ bool "switch_root"
+ default n
+ 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 n
+ 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 n
+ 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 n
+ 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_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 writeable, 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.
+
+endmenu
diff --git a/util-linux/Kbuild b/util-linux/Kbuild
new file mode 100644
index 0000000..2d0fc49
--- /dev/null
+++ b/util-linux/Kbuild
@@ -0,0 +1,37 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+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_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_MDEV) += mdev.o
+lib-$(CONFIG_MKFS_MINIX) += mkfs_minix.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_SETARCH) += setarch.o
+lib-$(CONFIG_SWAPONOFF) += swaponoff.o
+lib-$(CONFIG_SWITCH_ROOT) += switch_root.o
+lib-$(CONFIG_UMOUNT) += umount.o
diff --git a/util-linux/blkid.c b/util-linux/blkid.c
new file mode 100644
index 0000000..ec699d1
--- /dev/null
+++ b/util-linux/blkid.c
@@ -0,0 +1,18 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Print UUIDs on all filesystems
+ *
+ * Copyright (C) 2008 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+#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 UNUSED_PARAM)
+{
+ display_uuid_cache();
+ return 0;
+}
diff --git a/util-linux/dmesg.c b/util-linux/dmesg.c
new file mode 100644
index 0000000..f52026c
--- /dev/null
+++ b/util-linux/dmesg.c
@@ -0,0 +1,67 @@
+/* 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 tarball for details.
+ */
+
+#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;
+ char *buf;
+ char *size, *level;
+ unsigned flags = getopt32(argv, "cs:n:", &size, &level);
+ enum {
+ OPT_c = 1<<0,
+ OPT_s = 1<<1,
+ OPT_n = 1<<2
+ };
+
+ if (flags & OPT_n) {
+ if (klogctl(8, NULL, xatoul_range(level, 0, 10)))
+ bb_perror_msg_and_die("klogctl");
+ return EXIT_SUCCESS;
+ }
+
+ len = (flags & OPT_s) ? xatoul_range(size, 2, INT_MAX) : 16384;
+ buf = xmalloc(len);
+ len = klogctl(3 + (flags & OPT_c), buf, len);
+ if (len < 0)
+ bb_perror_msg_and_die("klogctl");
+ if (len == 0)
+ return EXIT_SUCCESS;
+
+ /* Skip <#> at the start of lines, and make sure we end with a newline. */
+
+ if (ENABLE_FEATURE_DMESG_PRETTY) {
+ int last = '\n';
+ int in = 0;
+
+ do {
+ if (last == '\n' && buf[in] == '<')
+ in += 3;
+ else {
+ last = buf[in++];
+ bb_putchar(last);
+ }
+ } while (in < len);
+ 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/util-linux/fbset.c b/util-linux/fbset.c
new file mode 100644
index 0000000..590918a
--- /dev/null
+++ b/util-linux/fbset.c
@@ -0,0 +1,404 @@
+/* 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 tarball for details.
+ *
+ * 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)
+ */
+
+#include "libbb.h"
+
+#define DEFAULTFBDEV FB_0
+#define DEFAULTFBMODE "/etc/fb.modes"
+
+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_CHANGE = 13,
+
+#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
+};
+
+/* 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 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 },
+ { "n" , 0, CMD_CHANGE },
+#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
+};
+
+#if ENABLE_FEATURE_FBSET_READMODE
+/* taken from linux/fb.h */
+enum {
+ FB_VMODE_INTERLACED = 1, /* interlaced */
+ FB_VMODE_DOUBLE = 2, /* double scan */
+ FB_SYNC_HOR_HIGH_ACT = 1, /* horizontal sync high active */
+ FB_SYNC_VERT_HIGH_ACT = 2, /* vertical sync high active */
+ 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;
+}
+
+static int readmode(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\0",
+ token[0]);
+ switch (i) {
+ case 0:
+ /* FIXME: catastrophic on arches with 64bit ints */
+ sscanf(p, "%d %d %d %d %d",
+ &(base->xres), &(base->yres),
+ &(base->xres_virtual), &(base->yres_virtual),
+ &(base->bits_per_pixel));
+//bb_info_msg("GEO[%s]", p);
+ break;
+ case 1:
+ 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));
+//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;
+ }
+ }
+ return 0;
+}
+#endif
+
+static void setmode(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;
+ if ((int32_t) set->bits_per_pixel > 0)
+ base->bits_per_pixel = set->bits_per_pixel;
+}
+
+static 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_INFO = (1 << 1), ??*/
+ OPT_READMODE = (1 << 2),
+ OPT_ALL = (1 << 9),
+ };
+ struct fb_var_screeninfo var, varset;
+ int fh, i;
+ unsigned options = 0;
+
+ const char *fbdev = DEFAULTFBDEV;
+ const char *modefile = DEFAULTFBMODE;
+ char *thisarg, *mode = NULL;
+
+ memset(&varset, 0xff, sizeof(varset));
+
+ /* 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] == '-') 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_GEOMETRY:
+ varset.xres = xatou32(argv[1]);
+ varset.yres = xatou32(argv[2]);
+ varset.xres_virtual = xatou32(argv[3]);
+ varset.yres_virtual = xatou32(argv[4]);
+ varset.bits_per_pixel = xatou32(argv[5]);
+ break;
+ case CMD_TIMING:
+ varset.pixclock = xatou32(argv[1]);
+ varset.left_margin = xatou32(argv[2]);
+ varset.right_margin = xatou32(argv[3]);
+ varset.upper_margin = xatou32(argv[4]);
+ varset.lower_margin = xatou32(argv[5]);
+ varset.hsync_len = xatou32(argv[6]);
+ varset.vsync_len = xatou32(argv[7]);
+ break;
+ case CMD_ALL:
+ options |= OPT_ALL;
+ break;
+ case CMD_CHANGE:
+ options |= OPT_CHANGE;
+ break;
+#if ENABLE_FEATURE_FBSET_FANCY
+ case CMD_XRES:
+ varset.xres = xatou32(argv[1]);
+ break;
+ case CMD_YRES:
+ varset.yres = xatou32(argv[1]);
+ break;
+ case CMD_DEPTH:
+ varset.bits_per_pixel = xatou32(argv[1]);
+ break;
+#endif
+ }
+ argc -= g_cmdoptions[i].param_count;
+ argv += g_cmdoptions[i].param_count;
+ goto contin;
+ }
+ if (argc != 1)
+ bb_show_usage();
+ mode = *argv;
+ options |= OPT_READMODE;
+ contin: ;
+ }
+
+ fh = xopen(fbdev, O_RDONLY);
+ xioctl(fh, FBIOGET_VSCREENINFO, &var);
+ if (options & OPT_READMODE) {
+#if !ENABLE_FEATURE_FBSET_READMODE
+ bb_show_usage();
+#else
+ if (!readmode(&var, modefile, mode)) {
+ bb_error_msg_and_die("unknown video mode '%s'", mode);
+ }
+#endif
+ }
+
+ setmode(&var, &varset);
+ if (options & OPT_CHANGE) {
+ if (options & OPT_ALL)
+ var.activate = FB_ACTIVATE_ALL;
+ xioctl(fh, FBIOPUT_VSCREENINFO, &var);
+ }
+ showmode(&var);
+ /* Don't close the file, as exiting will take care of that */
+ /* close(fh); */
+
+ return EXIT_SUCCESS;
+}
diff --git a/util-linux/fdformat.c b/util-linux/fdformat.c
new file mode 100644
index 0000000..094d620
--- /dev/null
+++ b/util-linux/fdformat.c
@@ -0,0 +1,130 @@
+/* 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
+ */
+
+#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/util-linux/fdisk.c b/util-linux/fdisk.c
new file mode 100644
index 0000000..b1f0b65
--- /dev/null
+++ b/util-linux/fdisk.c
@@ -0,0 +1,2997 @@
+/* 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 the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#ifndef _LARGEFILE64_SOURCE
+/* For lseek64 */
+#define _LARGEFILE64_SOURCE
+#endif
+#include <assert.h> /* assert */
+#include "libbb.h"
+
+/* Looks like someone forgot to add this to config system */
+#ifndef ENABLE_FEATURE_FDISK_BLKSIZE
+# define ENABLE_FEATURE_FDISK_BLKSIZE 0
+# define USE_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,
+};
+
+
+/* Used for sector numbers. Today's disk sizes make it necessary */
+typedef unsigned long long ullong;
+
+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 %d 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;
+
+static const char unable_to_open[] ALIGN1 = "can't open %s";
+static const char unable_to_read[] ALIGN1 = "can't read from %s";
+static const char unable_to_seek[] ALIGN1 = "can't seek on %s";
+
+enum label_type {
+ LABEL_DOS, LABEL_SUN, LABEL_SGI, LABEL_AIX, LABEL_OSF
+};
+
+#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
+
+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 int get_partition(int warn, int max);
+static void list_types(const char *const *sys);
+static unsigned read_int(unsigned low, unsigned dflt, unsigned high, unsigned base, const char *mesg);
+#endif
+static const char *partition_type(unsigned char type);
+static void get_geometry(void);
+#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 unsigned get_start_sect(const struct partition *p);
+static unsigned get_nr_sects(const struct partition *p);
+
+/*
+ * 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 */
+ ullong offset; /* disk sector number */
+ char *sectorbuffer; /* disk sector contents */
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ char changed; /* boolean */
+#endif
+};
+
+/* 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;
+ ullong extended_offset; /* offset of link pointers */
+ ullong 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? */
+static ullong bb_BLKGETSIZE_sectors(int fd)
+{
+ uint64_t v64;
+ unsigned long longsectors;
+
+ if (ioctl(fd, BLKGETSIZE64, &v64) == 0) {
+ /* Got bytes, convert to 512 byte sectors */
+ return (v64 >> 9);
+ }
+ /* Needs temp of type long */
+ if (ioctl(fd, BLKGETSIZE, &longsectors))
+ longsectors = 0;
+ 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)))
+
+#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
+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);
+}
+
+#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(prompt, line_buffer, sizeof(line_buffer), NULL);
+ 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 && !isgraph(*line_ptr))
+ line_ptr++;
+ return *line_ptr;
+}
+#endif
+
+/*
+ * 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;
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+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;
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+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);
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+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 == 'l' || *line_ptr == 'L') {
+ list_types(sys);
+ continue;
+ }
+ v = bb_strtoul(line_ptr, NULL, 16);
+ if (v > 0xff)
+ /* Bad input also triggers this */
+ continue;
+ return v;
+ }
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+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(ullong secno)
+{
+ secno *= sector_size;
+#if ENABLE_FDISK_SUPPORT_LARGE_DISKS
+ if (lseek64(dev_fd, (off64_t)secno, SEEK_SET) == (off64_t) -1)
+ fdisk_fatal(unable_to_seek);
+#else
+ if (secno > MAXINT(off_t)
+ || lseek(dev_fd, (off_t)secno, SEEK_SET) == (off_t) -1
+ ) {
+ fdisk_fatal(unable_to_seek);
+ }
+#endif
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+write_sector(ullong secno, const void *buf)
+{
+ seek_sector(secno);
+ xwrite(dev_fd, buf, sector_size);
+}
+#endif
+
+
+#include "fdisk_aix.c"
+
+typedef struct {
+ 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 */
+} sun_partition;
+#define sunlabel ((sun_partition *)MBRbuffer)
+STATIC_OSF void bsd_select(void);
+STATIC_OSF void xbsd_print_disklabel(int);
+#include "fdisk_osf.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"
+
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+/* start_sect and nr_sects are stored little endian on all machines */
+/* moreover, they are not aligned correctly */
+static void
+store4_little_endian(unsigned char *cp, unsigned val)
+{
+ cp[0] = val;
+ cp[1] = val >> 8;
+ cp[2] = val >> 16;
+ cp[3] = val >> 24;
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+static unsigned
+read4_little_endian(const unsigned char *cp)
+{
+ return cp[0] + (cp[1] << 8) + (cp[2] << 16) + (cp[3] << 24);
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+set_start_sect(struct partition *p, unsigned start_sect)
+{
+ store4_little_endian(p->start4, start_sect);
+}
+#endif
+
+static unsigned
+get_start_sect(const struct partition *p)
+{
+ return read4_little_endian(p->start4);
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+set_nr_sects(struct partition *p, unsigned nr_sects)
+{
+ store4_little_endian(p->size4, nr_sects);
+}
+#endif
+
+static unsigned
+get_nr_sects(const struct partition *p)
+{
+ return read4_little_endian(p->size4);
+}
+
+/* Allocate a buffer and read a partition table sector */
+static void
+read_pte(struct pte *pe, ullong offset)
+{
+ pe->offset = 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 unsigned
+get_partition_start(const struct pte *pe)
+{
+ return pe->offset + 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 {
+ 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 /* FEATURE_FDISK_WRITABLE */
+
+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";
+}
+
+
+#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');
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+static int
+is_cleared_partition(const struct partition *p)
+{
+ return !(!p || p->boot_ind || p->head || p->sector || p->cyl ||
+ p->sys_ind || p->end_head || p->end_sector || p->end_cyl ||
+ get_start_sect(p) || get_nr_sects(p));
+}
+
+static void
+clear_partition(struct partition *p)
+{
+ if (!p)
+ return;
+ memset(p, 0, sizeof(struct partition));
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+set_partition(int i, int doext, ullong start, ullong stop, int sysid)
+{
+ struct partition *p;
+ ullong offset;
+
+ if (doext) {
+ p = ptes[i].ext_pointer;
+ offset = extended_offset;
+ } else {
+ p = ptes[i].part_table;
+ offset = ptes[i].offset;
+ }
+ p->boot_ind = 0;
+ p->sys_ind = sysid;
+ set_start_sect(p, start - offset);
+ set_nr_sects(p, stop - start + 1);
+ 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);
+ 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 %d.\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 %d\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"
+ " %d\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"
+ " %d\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 (%d)\n", i+1);
+ delete_partition(i);
+ goto remove; /* numbering changed */
+ }
+ }
+#endif
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+create_doslabel(void)
+{
+ int i;
+
+ 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;
+
+ for (i = 510-64; i < 510; i++)
+ MBRbuffer[i] = 0;
+ write_part_table_flag(MBRbuffer);
+ extended_offset = 0;
+ set_all_unchanged();
+ set_changed(0);
+ get_boot(CREATE_EMPTY_DOS);
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+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 %d "
+ "(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.
+ * FIXME: document what each 'what' value will do!
+ * 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 = 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 USE_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_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 or OSF "
+ "disklabel\n");
+#ifdef __sparc__
+ USE_FEATURE_SUN_LABEL(create_sunlabel();)
+#else
+ create_doslabel();
+#endif
+ return 0;
+ }
+ /* TRY_ONLY: */
+ return -1;
+ }
+ created_table:
+#endif /* FEATURE_FDISK_WRITABLE */
+
+
+ USE_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 %d\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 %d will be corrected by w(rite)\n",
+ pe->sectorbuffer[510],
+ pe->sectorbuffer[511],
+ i + 1);
+ USE_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 unsigned
+read_int(unsigned low, unsigned dflt, unsigned high, unsigned base, const char *mesg)
+{
+ unsigned i;
+ 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;
+
+ i = atoi(line_ptr + 1);
+
+ while (isdigit(*++line_ptr))
+ use_default = 0;
+
+ switch (*line_ptr) {
+ case 'c':
+ case 'C':
+ if (!display_in_cyl_units)
+ i *= 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) i * absolute;
+ unit = sector_size * units_per_sector;
+ bytes += unit/2; /* round */
+ bytes /= unit;
+ i = bytes;
+ }
+ if (minus)
+ i = -i;
+ i += base;
+ } else {
+ i = atoi(line_ptr);
+ while (isdigit(*line_ptr)) {
+ line_ptr++;
+ use_default = 0;
+ }
+ }
+ if (use_default) {
+ i = dflt;
+ printf("Using default value %u\n", i);
+ }
+ if (i >= low && i <= high)
+ break;
+ printf("Value is out of range\n");
+ }
+ return i;
+}
+
+static int
+get_partition(int warn, int max)
+{
+ struct pte *pe;
+ int 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 %d has empty type\n", i+1);
+ }
+ }
+ return i;
+}
+
+static int
+get_existing_partition(int warn, int max)
+{
+ int pno = -1;
+ int 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 %d\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, int max)
+{
+ int pno = -1;
+ int 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 %d\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 %d 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(pe) -
+ extended_offset);
+ pe->offset = 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 %d 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 %d "
+ "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 %d has different physical/logical "
+ "beginnings (non-Linux?):\n", partition + 1);
+ printf(" phys=(%d, %d, %d) ", pbc, pbh, pbs);
+ printf("logical=(%d, %d, %d)\n", lbc, lbh, lbs);
+ }
+
+/* Same physical / logical ending? */
+ if (g_cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
+ printf("Partition %d has different physical/logical "
+ "endings:\n", partition + 1);
+ printf(" phys=(%d, %d, %d) ", pec, peh, pes);
+ printf("logical=(%d, %d, %d)\n", lec, leh, les);
+ }
+
+/* Ending on cylinder boundary? */
+ if (peh != (g_heads - 1) || pes != g_sectors) {
+ printf("Partition %i does not end on cylinder boundary\n",
+ partition + 1);
+ }
+}
+
+static void
+list_disk_geometry(void)
+{
+ long long bytes = (total_number_of_sectors << 9);
+ long megabytes = bytes/1000000;
+
+ if (megabytes < 10000)
+ printf("\nDisk %s: %ld MB, %lld bytes\n",
+ disk_device, megabytes, bytes);
+ else
+ printf("\nDisk %s: %ld.%ld GB, %lld bytes\n",
+ disk_device, megabytes/1000, (megabytes/100)%10, bytes);
+ printf("%d heads, %d sectors/track, %d cylinders",
+ g_heads, g_sectors, g_cylinders);
+ if (units_per_sector == 1)
+ printf(", total %llu sectors",
+ total_number_of_sectors / (sector_size/512));
+ printf("\nUnits = %s of %d * %d = %d 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;
+ ullong last_p_start_pos = 0, p_start_pos;
+ int 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(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;
+ ojj = ptes[j+1].offset;
+ if (oj > ojj) {
+ ptes[j].offset = ojj;
+ ptes[j+1].offset = 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;
+ ojj = ptes[j+1].offset;
+ 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_SUN) {
+ sgi_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];
+ ullong psects;
+ ullong 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 %11llu %11llu %11llu%c %2x %s\n",
+ partname(disk_device, i+1, w+2),
+ !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG /* boot flag */
+ ? '*' : '?',
+ (ullong) cround(get_partition_start(pe)), /* start */
+ (ullong) cround(get_partition_start(pe) + psects /* end */
+ - (psects ? 1 : 0)),
+ (ullong) 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: %d heads, %d sectors, %d 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("%2d %02x%4d%4d%5d%4d%4d%5d%11u%11u %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(ullong *first, ullong *last)
+{
+ int 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(pe);
+ last[i] = first[i] + get_nr_sects(p) - 1;
+ }
+ }
+}
+
+static void
+check(int n, unsigned h, unsigned s, unsigned c, ullong start)
+{
+ ullong 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 %d contains sector 0\n", n);
+ if (h >= g_heads)
+ printf("Partition %d: head %d greater than maximum %d\n",
+ n, h + 1, g_heads);
+ if (real_s >= g_sectors)
+ printf("Partition %d: sector %d greater than "
+ "maximum %d\n", n, s, g_sectors);
+ if (real_c >= g_cylinders)
+ printf("Partition %d: cylinder %llu greater than "
+ "maximum %d\n", n, real_c + 1, g_cylinders);
+ if (g_cylinders <= 1024 && start != total)
+ printf("Partition %d: previous sectors %llu disagrees with "
+ "total %llu\n", n, start, total);
+}
+
+static void
+verify(void)
+{
+ int i, j;
+ unsigned total = 1;
+ ullong 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(pe) < first[i])
+ printf("Warning: bad start-of-data in "
+ "partition %d\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 %d overlaps "
+ "partition %d\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];
+ ullong 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 %d "
+ "is empty\n", i + 1);
+ } else if (first[i] < extended_offset || last[i] > e_last) {
+ printf("Logical partition %d not entirely in "
+ "partition %d\n", i + 1, ext_index + 1);
+ }
+ }
+ }
+
+ if (total > g_heads * g_sectors * g_cylinders)
+ printf("Total allocated sectors %d greater than the maximum "
+ "%d\n", total, g_heads * g_sectors * g_cylinders);
+ else {
+ total = g_heads * g_sectors * g_cylinders - total;
+ if (total != 0)
+ printf("%d 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;
+ ullong limit, temp;
+ ullong start, stop = 0;
+ ullong 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 = (ullong) 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)
+ 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 %lld is already allocated\n", temp);
+ temp = start;
+ num_read = 0;
+ }
+ if (!num_read && start == temp) {
+ ullong 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 = start - sector_offset;
+ if (pe->offset == extended_offset) { /* must be corrected */
+ pe->offset++;
+ if (sector_offset == 1)
+ start++;
+ }
+ }
+
+ for (i = 0; i < g_partitions; i++) {
+ struct pte *pe = &ptes[i];
+
+ if (start < pe->offset && limit >= pe->offset)
+ limit = pe->offset - 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, 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 = 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 = 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 == 'p' || c == '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
+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, 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) {
+ int needw = 0;
+
+ for (i = 0; i < 8; i++)
+ if (ptes[i].changed)
+ needw = 1;
+ if (needw)
+ sun_write_table();
+ }
+
+ printf("The partition table has been altered!\n\n");
+ reread_partition_table(1);
+}
+
+static void
+reread_partition_table(int leave)
+{
+ int i;
+
+ printf("Calling ioctl() to re-read partition table\n");
+ sync();
+ /* sleep(2); Huh? */
+ 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);
+ }
+}
+#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(int i)
+{
+ struct pte *pe = &ptes[i];
+ struct partition *p = pe->part_table;
+ ullong new, first;
+
+ if (warn_geometry())
+ return;
+ if (!p->sys_ind || !get_nr_sects(p) || IS_EXTENDED(p->sys_ind)) {
+ printf("Partition %d has no data area\n", i + 1);
+ return;
+ }
+ first = get_partition_start(pe);
+ new = read_int(first, first, first + get_nr_sects(p) - 1, first,
+ "New beginning of data") - pe->offset;
+
+ if (new != get_nr_sects(p)) {
+ first = get_nr_sects(p) + get_start_sect(p) - new;
+ set_nr_sects(p, first);
+ set_start_sect(p, new);
+ pe->changed = 1;
+ }
+}
+
+static void
+xselect(void)
+{
+ char c;
+
+ while (1) {
+ bb_putchar('\n');
+ c = tolower(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();
+}
+
+/* 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], *s;
+ int ma, mi, sz;
+
+ procpt = fopen_or_warn("/proc/partitions", "r");
+
+ while (fgets(line, sizeof(line), procpt)) {
+ if (sscanf(line, " %d %d %d %[^\n ]",
+ &ma, &mi, &sz, ptname) != 4)
+ continue;
+ for (s = ptname; *s; s++)
+ continue;
+ if (isdigit(s[-1]))
+ continue;
+ sprintf(devname, "/dev/%s", ptname);
+ 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, 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" USE_FEATURE_FDISK_BLKSIZE("s"),
+ &sector_size, &user_cylinders, &user_heads, &user_sectors);
+ argc -= optind;
+ argv += optind;
+ if (opt & OPT_b) { // -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 != 1024
+ && sector_size != 2048)
+ 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 (argc <= 0)
+ bb_show_usage();
+ for (j = 0; j < argc; j++) {
+ unsigned long long size;
+ fd = xopen(argv[j], O_RDONLY);
+ size = bb_BLKGETSIZE_sectors(fd) / 2;
+ close(fd);
+ if (argc == 1)
+ printf("%lld\n", size);
+ else
+ printf("%s: %lld\n", argv[j], size);
+ }
+ return 0;
+ }
+#endif
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ if (argc != 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 = tolower(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/util-linux/fdisk_aix.c b/util-linux/fdisk_aix.c
new file mode 100644
index 0000000..83be8a8
--- /dev/null
+++ b/util-linux/fdisk_aix.c
@@ -0,0 +1,73 @@
+#if ENABLE_FEATURE_AIX_LABEL
+/*
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ * This file may be redistributed under
+ * the terms of the GNU Public License.
+ */
+
+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/util-linux/fdisk_osf.c b/util-linux/fdisk_osf.c
new file mode 100644
index 0000000..c50ee9b
--- /dev/null
+++ b/util-linux/fdisk_osf.c
@@ -0,0 +1,1052 @@
+#if ENABLE_FEATURE_OSF_LABEL
+/*
+ * 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.
+ */
+
+
+#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(i386) || defined(__sparc__) || defined(__arm__) \
+ || defined(__m68k__) || defined(__mips__) || defined(__s390__) \
+ || defined(__sh__) || defined(__x86_64__)
+#define BSD_LABELSECTOR 1
+#define BSD_LABELOFFSET 0
+#elif defined(__alpha__) || defined(__powerpc__) || defined(__ia64__) \
+ || defined(__hppa__)
+#define BSD_LABELSECTOR 0
+#define BSD_LABELOFFSET 64
+#elif defined(__s390__) || defined(__s390x__)
+#define BSD_LABELSECTOR 1
+#define BSD_LABELOFFSET 0
+#else
+#error unknown architecture
+#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 %d\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: %d\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: %ld\n", (long) lp->d_secsize);
+ printf("sectors/track: %ld\n", (long) lp->d_nsectors);
+ printf("tracks/cylinder: %ld\n", (long) lp->d_ntracks);
+ printf("sectors/cylinder: %ld\n", (long) lp->d_secpercyl);
+ printf("cylinders: %ld\n", (long) lp->d_ncylinders);
+ printf("rpm: %d\n", lp->d_rpm);
+ printf("interleave: %d\n", lp->d_interleave);
+ printf("trackskew: %d\n", lp->d_trackskew);
+ printf("cylinderskew: %d\n", lp->d_cylskew);
+ printf("headswitch: %ld\t\t# milliseconds\n",
+ (long) lp->d_headswitch);
+ printf("track-to-track seek: %ld\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("%ld ", (long) lp->d_drivedata[j]);
+ }
+ printf("\n%d 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: %8ld%c %8ld%c %8ld%c ",
+ 'a' + i,
+ (long) pp->p_offset / lp->d_secpercyl + 1,
+ (pp->p_offset % lp->d_secpercyl) ? '*' : ' ',
+ (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: %8ld %8ld %8ld ",
+ '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(" %5ld %5ld %5.5s ",
+ (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, "");
+ break;
+ case BSD_FS_BSDFFS:
+ printf(" %5ld %5ld %5d ",
+ (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 == 'y' || c == '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 == 'n')
+ return 0;
+ }
+}
+
+static int
+edit_int(int def, const char *mesg)
+{
+ mesg = xasprintf("%s (%d): ", 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 (%d, maximum is %d)\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
+ 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/util-linux/fdisk_sgi.c b/util-linux/fdisk_sgi.c
new file mode 100644
index 0000000..5a86a68
--- /dev/null
+++ b/util-linux/fdisk_sgi.c
@@ -0,0 +1,886 @@
+#if ENABLE_FEATURE_SGI_LABEL
+
+#define SGI_DEBUG 0
+
+/*
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ * This file may be modified and redistributed under
+ * the terms of the GNU Public License.
+ */
+
+#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): %d heads, %d sectors\n"
+ "%d cylinders, %d physical cylinders\n"
+ "%d extra sects/cyl, interleave %d:1\n"
+ "%s\n"
+ "Units = %s of %d * 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): "
+ "%d heads, %d sectors, %d cylinders\n"
+ "Units = %s of %d * 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(
+ "%2d: %s %4s %9ld %9ld %9ld %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("%2d: %-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 %d\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 %d diskblock large,\n"
+ "but the disk is %d 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=%d\tpartition=%d\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 %d 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 %d 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 %d and %d overlap by %d 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 %8u sectors - sectors %8u-%8u\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("%2d:%12d\t%12d\t%12d\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 %8u sectors - sectors %8u-%8u\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 %d.\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 %d\n", i);
+ if (SGI_DEBUG)
+ printf("ID=%02x\tSTART=%d\tLENGTH=%d\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/util-linux/fdisk_sun.c b/util-linux/fdisk_sun.c
new file mode 100644
index 0000000..d1a436b
--- /dev/null
+++ b/util-linux/fdisk_sun.c
@@ -0,0 +1,727 @@
+#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
+
+/*
+ * fdisksunlabel.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
+ */
+
+
+static int sun_other_endian;
+static int scsi_disk;
+static int 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, uint start, uint 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%d Channel: %02d Id: %02d Lun: %02d\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 %d alt %d hd %d sec %d",
+ 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(uint *starts, uint *lens, uint *start, uint *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 uint *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)
+{
+ uint 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 %d 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 %d overlaps with others in "
+ "sectors %d-%d\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 0-%d\n", starts[array[0]]);
+ for (i = 0; i < 7 && array[i+1] != -1; i++) {
+ printf("Unused gap - sectors %d-%d\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 %d-%d\n", start, stop);
+}
+
+static void
+add_sun_partition(int n, int sys)
+{
+ uint start, stop, stop2;
+ uint 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 %d 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 %d %s covers some other partition.\n"
+"Your entry has been changed to %d %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): %d heads, %d sectors, %d rpm\n"
+ "%d cylinders, %d alternate cylinders, %d physical cylinders\n"
+ "%d extra sects/cyl, interleave %d:1\n"
+ "%s\n"
+ "Units = %s of %d * 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): %d heads, %d sectors, %d cylinders\n"
+ "Units = %s of %d * 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 %9ld %9ld %9ld%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/util-linux/findfs.c b/util-linux/findfs.c
new file mode 100644
index 0000000..5b64399
--- /dev/null
+++ b/util-linux/findfs.c
@@ -0,0 +1,38 @@
+/* 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 tarball for details.
+ */
+
+#include "libbb.h"
+#include "volume_id.h"
+
+int findfs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int findfs_main(int argc, char **argv)
+{
+ char *tmp = NULL;
+
+ if (argc != 2)
+ bb_show_usage();
+
+ if (!strncmp(argv[1], "LABEL=", 6))
+ tmp = get_devname_from_label(argv[1] + 6);
+ else if (!strncmp(argv[1], "UUID=", 5))
+ tmp = get_devname_from_uuid(argv[1] + 5);
+ else if (!strncmp(argv[1], "/dev/", 5)) {
+ /* Just pass a device name right through. This might aid in some scripts
+ being able to call this unconditionally */
+ tmp = argv[1];
+ } else
+ bb_show_usage();
+
+ if (tmp) {
+ puts(tmp);
+ return 0;
+ }
+ return 1;
+}
diff --git a/util-linux/freeramdisk.c b/util-linux/freeramdisk.c
new file mode 100644
index 0000000..bde6afc
--- /dev/null
+++ b/util-linux/freeramdisk.c
@@ -0,0 +1,33 @@
+/* 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 tarball for details.
+ */
+
+#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, char **argv)
+{
+ int fd;
+
+ if (argc != 2) bb_show_usage();
+
+ fd = xopen(argv[1], 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/util-linux/fsck_minix.c b/util-linux/fsck_minix.c
new file mode 100644
index 0000000..78a7c82
--- /dev/null
+++ b/util-linux/fsck_minix.c
@@ -0,0 +1,1309 @@
+/* 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 tarball for details.
+ */
+
+/*
+ * 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 :-).
+ */
+
+#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];
+ USE_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(stdout);
+ c = getchar();
+ if (c == EOF) {
+ if (!def)
+ errors_uncorrected = 1;
+ return def;
+ }
+ c = toupper(c);
+ if (c == 'Y') {
+ def = 1;
+ break;
+ } else if (c == 'N') {
+ def = 0;
+ break;
+ } else if (c == ' ' || c == '\n')
+ 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)
+{
+ FILE *f;
+ struct mntent *mnt;
+ int cont;
+ int fd;
+//XXX:FIXME use find_mount_point()
+ f = setmntent(MOUNTED, "r");
+ if (f == NULL)
+ return;
+ while ((mnt = getmntent(f)) != NULL)
+ if (strcmp(device_name, mnt->mnt_fsname) == 0)
+ break;
+ endmntent(f);
+ if (!mnt)
+ return;
+
+ /*
+ * If the root is mounted read-only, then /etc/mtab is
+ * probably not correct; so we won't issue a warning based on
+ * it.
+ */
+ fd = open(MOUNTED, O_RDWR);
+ if (fd < 0 && errno == EROFS)
+ return;
+ close(fd);
+
+ 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("cannot write superblock");
+}
+
+static void write_tables(void)
+{
+ write_superblock();
+
+ if (IMAPS * BLOCK_SIZE != write(dev_fd, inode_map, IMAPS * BLOCK_SIZE))
+ die("cannot write inode map");
+ if (ZMAPS * BLOCK_SIZE != write(dev_fd, zone_map, ZMAPS * BLOCK_SIZE))
+ die("cannot write zone map");
+ if (INODE_BUFFER_SIZE != write(dev_fd, inode_buffer, INODE_BUFFER_SIZE))
+ die("cannot 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("cannot 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("cannot read inode map");
+ if (ZMAPS * BLOCK_SIZE != read(dev_fd, zone_map, ZMAPS * BLOCK_SIZE))
+ die("cannot read zone map");
+ if (INODE_BUFFER_SIZE != read(dev_fd, inode_buffer, INODE_BUFFER_SIZE))
+ die("cannot 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/util-linux/getopt.c b/util-linux/getopt.c
new file mode 100644
index 0000000..fd67287
--- /dev/null
+++ b/util-linux/getopt.c
@@ -0,0 +1,354 @@
+/* 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 tarball for details.
+ */
+
+/*
+ * 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 in
+ * Removed parse_error(), using bb_error_msg() from Busybox instead
+ * Replaced our_malloc with xmalloc and our_realloc with xrealloc
+ *
+ */
+
+#include <getopt.h>
+#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_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_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_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_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_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_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_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_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)
+{
+ char *optstr = NULL;
+ char *name = NULL;
+ unsigned opt;
+ const char *compatible;
+ char *s_arg;
+#if ENABLE_GETOPT_LONG
+ struct option *long_options = NULL;
+ llist_t *l_arg = NULL;
+#endif
+
+ compatible = getenv("GETOPT_COMPATIBLE"); /* used as yes/no flag */
+
+ if (argc == 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;
+
+ option_mask32 |= OPT_u; /* quoting off */
+ s = xstrdup(argv[1] + strspn(argv[1], "-+"));
+ argv[1] = argv[0];
+ return generate_output(argv+1, argc-1, s, long_options);
+ }
+
+#if !ENABLE_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 */
+ if (!optstr) {
+ if (optind >= argc)
+ bb_error_msg_and_die("missing optstring argument");
+ optstr = argv[optind++];
+ }
+
+ argv[optind-1] = name ? name : argv[0];
+ return generate_output(argv+optind-1, argc-optind+1, optstr, long_options);
+}
diff --git a/util-linux/hexdump.c b/util-linux/hexdump.c
new file mode 100644
index 0000000..48edd70
--- /dev/null
+++ b/util-linux/hexdump.c
@@ -0,0 +1,151 @@
+/* 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 tarball for details.
+ */
+
+#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" USE_FEATURE_HEXDUMP_REVERSE("R");
+
+static const struct suffix_mult suffixes[] = {
+ { "b", 512 },
+ { "k", 1024 },
+ { "m", 1024*1024 },
+ { }
+};
+
+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_u(optarg);
+ } /* else */
+ if (ch == 's') {
+ dumper->dump_skip = xatoul_range_sfx(optarg, 0, 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/util-linux/hwclock.c b/util-linux/hwclock.c
new file mode 100644
index 0000000..3d28364
--- /dev/null
+++ b/util-linux/hwclock.c
@@ -0,0 +1,127 @@
+/* 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 tarball for details.
+*/
+
+#include <sys/utsname.h>
+#include "libbb.h"
+#include "rtc_.h"
+
+#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+# endif
+#endif
+
+static const char *rtcname;
+
+static time_t read_rtc(int utc)
+{
+ time_t ret;
+ int fd;
+
+ fd = rtc_xopen(&rtcname, O_RDONLY);
+ ret = rtc_read_time(fd, utc);
+ close(fd);
+
+ return ret;
+}
+
+static void write_rtc(time_t t, int utc)
+{
+ struct tm tm;
+ int rtc = rtc_xopen(&rtcname, O_WRONLY);
+
+ tm = *(utc ? gmtime(&t) : localtime(&t));
+ tm.tm_isdst = 0;
+
+ xioctl(rtc, RTC_SET_TIME, &tm);
+
+ close(rtc);
+}
+
+static void show_clock(int utc)
+{
+ //struct tm *ptm;
+ time_t t;
+ char *cp;
+
+ t = read_rtc(utc);
+ //ptm = localtime(&t); /* Sets 'tzname[]' */
+
+ cp = ctime(&t);
+ if (cp[0])
+ cp[strlen(cp) - 1] = '\0';
+
+ //printf("%s %.6f seconds %s\n", cp, 0.0, utc ? "" : (ptm->tm_isdst ? tzname[1] : tzname[0]));
+ printf("%s 0.000000 seconds\n", cp);
+}
+
+static void to_sys_clock(int utc)
+{
+ struct timeval tv;
+ const struct timezone tz = { timezone/60 - 60*daylight, 0 };
+
+ tv.tv_sec = read_rtc(utc);
+ tv.tv_usec = 0;
+ if (settimeofday(&tv, &tz))
+ bb_perror_msg_and_die("settimeofday() failed");
+}
+
+static void from_sys_clock(int utc)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ //if (gettimeofday(&tv, NULL))
+ // bb_perror_msg_and_die("gettimeofday() failed");
+ write_rtc(tv.tv_sec, utc);
+}
+
+#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_RTCFILE 0x20
+
+int hwclock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int hwclock_main(int argc UNUSED_PARAM, char **argv)
+{
+ unsigned opt;
+ int utc;
+
+#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
+ static const char hwclock_longopts[] ALIGN1 =
+ "localtime\0" No_argument "l"
+ "utc\0" No_argument "u"
+ "show\0" No_argument "r"
+ "hctosys\0" No_argument "s"
+ "systohc\0" No_argument "w"
+ "file\0" Required_argument "f"
+ ;
+ applet_long_options = hwclock_longopts;
+#endif
+ opt_complementary = "r--ws:w--rs:s--wr:l--u:u--l";
+ opt = getopt32(argv, "lurswf:", &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(utc);
+ else if (opt & HWCLOCK_OPT_SYSTOHC)
+ from_sys_clock(utc);
+ else
+ /* default HWCLOCK_OPT_SHOW */
+ show_clock(utc);
+
+ return 0;
+}
diff --git a/util-linux/ipcrm.c b/util-linux/ipcrm.c
new file mode 100644
index 0000000..5dcda85
--- /dev/null
+++ b/util-linux/ipcrm.c
@@ -0,0 +1,220 @@
+/* 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 tarball for details.
+ */
+
+#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, int argc, char **argv)
+{
+ unsigned long id;
+ int ret = 0; /* silence gcc */
+ int nb_errors = 0;
+ union semun arg;
+
+ arg.val = 0;
+
+ while (argc) {
+ id = bb_strtoul(argv[0], NULL, 10);
+ if (errno || id > INT_MAX) {
+ bb_error_msg("invalid id: %s", argv[0]);
+ nb_errors++;
+ } else {
+ 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("cannot remove id %s", argv[0]);
+ nb_errors++;
+ }
+ }
+ argc--;
+ 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, argc-2, &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/util-linux/ipcs.c b/util-linux/ipcs.c
new file mode 100644
index 0000000..9201257
--- /dev/null
+++ b/util-linux/ipcs.c
@@ -0,0 +1,621 @@
+/* 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 tarball for details.
+ */
+
+/* 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;
+ ulong shm_tot; /* total allocated shm */
+ ulong shm_rss; /* total resident shm */
+ ulong shm_swp; /* total swapped shm */
+ ulong swap_attempts;
+ ulong 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 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 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 = (ushort *) (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 = (ushort *) (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 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/util-linux/losetup.c b/util-linux/losetup.c
new file mode 100644
index 0000000..e224a4d
--- /dev/null
+++ b/util-linux/losetup.c
@@ -0,0 +1,79 @@
+/* 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 tarball for details.
+ */
+
+#include "libbb.h"
+
+int losetup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int losetup_main(int argc, char **argv)
+{
+ char dev[] = LOOP_NAME"0";
+ unsigned opt;
+ char *opt_o;
+ char *s;
+ unsigned long long offset = 0;
+
+ /* max 2 args, all opts are mutually exclusive */
+ opt_complementary = "?2:d--of:o--df:f-do";
+ opt = getopt32(argv, "do:f", &opt_o);
+ argc -= optind;
+ argv += optind;
+
+ if (opt == 0x2) // -o
+ offset = xatoull(opt_o);
+
+ if (opt == 0x4 && argc) // -f does not take any argument
+ bb_show_usage();
+
+ if (opt == 0x1) { // -d
+ /* detach takes exactly one argument */
+ if (argc != 1)
+ bb_show_usage();
+ if (del_loop(argv[0]))
+ bb_simple_perror_msg_and_die(argv[0]);
+ return EXIT_SUCCESS;
+ }
+
+ if (argc == 2) {
+ /* -o or no option */
+ if (set_loop(&argv[0], argv[1], offset) < 0)
+ bb_simple_perror_msg_and_die(argv[0]);
+ return EXIT_SUCCESS;
+ }
+
+ if (argc == 1) {
+ /* -o or no option */
+ 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;
+ }
+
+ /* -o, -f or no option */
+ while (1) {
+ s = query_loop(dev);
+ if (!s) {
+ if (opt == 0x4) {
+ puts(dev);
+ return EXIT_SUCCESS;
+ }
+ } else {
+ if (opt != 0x4)
+ printf("%s: %s\n", dev, s);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(s);
+ }
+
+ if (++dev[sizeof(dev) - 2] > '9')
+ break;
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
new file mode 100644
index 0000000..d8b603e
--- /dev/null
+++ b/util-linux/mdev.c
@@ -0,0 +1,487 @@
+/* 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 GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "xregex.h"
+
+struct globals {
+ int root_major, root_minor;
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define root_major (G.root_major)
+#define root_minor (G.root_minor)
+
+/* Prevent infinite loops in /sys symlinks */
+#define MAX_SYSFS_DEPTH 3
+
+/* We use additional 64+ bytes in make_device() */
+#define SCRATCH_SIZE 80
+
+#if ENABLE_FEATURE_MDEV_RENAME
+/* Builds an alias path.
+ * This function potentionally reallocates the alias parameter.
+ */
+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 */
+ bb_make_directory(alias, 0755, FILEUTILS_RECUR);
+ *dest = '/';
+ if (dest[1] == '\0') { /* ">bar/" => ">bar/device_name" */
+ dest = alias;
+ alias = concat_path_file(alias, device_name);
+ free(dest);
+ }
+ }
+
+ return alias;
+}
+#endif
+
+/* mknod in /dev based on a path like "/sys/block/hda/hda1" */
+/* NB: "mdev -s" may call us many times, do not leak memory/fds! */
+static void make_device(char *path, int delete)
+{
+ const char *device_name;
+ int major, minor, type, len;
+ int mode = 0660;
+#if ENABLE_FEATURE_MDEV_CONF
+ struct bb_uidgid_t ugid = { 0, 0 };
+ parser_t *parser;
+ char *tokens[5];
+#endif
+#if ENABLE_FEATURE_MDEV_EXEC
+ char *command = NULL;
+#endif
+#if ENABLE_FEATURE_MDEV_RENAME
+ char *alias = NULL;
+ char aliaslink = aliaslink; /* for compiler */
+#endif
+ char *dev_maj_min = path + strlen(path);
+
+ /* Force the configuration file settings exactly. */
+ umask(0);
+
+ /* 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 (!delete) {
+ 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, so just try to run script */
+ *dev_maj_min = '\0';
+ } else if (sscanf(dev_maj_min, "%u:%u", &major, &minor) != 2) {
+ major = -1;
+ }
+ }
+
+ /* Determine device name, type, major and minor */
+ device_name = 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/"))
+ type = S_IFBLK;
+
+#if ENABLE_FEATURE_MDEV_CONF
+ parser = config_open2("/etc/mdev.conf", fopen_for_read);
+
+ /* If we have config file, look up user settings */
+ while (config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL)) {
+ regmatch_t off[1 + 9*ENABLE_FEATURE_MDEV_RENAME_REGEXP];
+ char *val;
+
+ /* Fields: regex uid:gid mode [alias] [cmd] */
+
+ /* 1st field: @<numeric maj,min>... */
+ if (tokens[0][0] == '@') {
+ /* @major,minor[-last] */
+ /* (useful when name is ambiguous:
+ * "/sys/class/usb/lp0" and
+ * "/sys/class/printer/lp0") */
+ int cmaj, cmin0, cmin1, sc;
+ if (major < 0)
+ continue; /* no dev, no match */
+ sc = sscanf(tokens[0], "@%u,%u-%u", &cmaj, &cmin0, &cmin1);
+ if (sc < 1 || major != cmaj
+ || (sc == 2 && minor != cmin0)
+ || (sc == 3 && (minor < cmin0 || minor > cmin1))
+ ) {
+ continue; /* no match */
+ }
+ } else { /* ... or regex to match device name */
+ regex_t match;
+ int result;
+
+ /* Is this it? */
+ xregcomp(&match, tokens[0], REG_EXTENDED);
+ result = regexec(&match, device_name, ARRAY_SIZE(off), off, 0);
+ regfree(&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 not this device, skip rest of line */
+ /* (regexec returns whole pattern as "range" 0) */
+ if (result || off[0].rm_so
+ || ((int)off[0].rm_eo != (int)strlen(device_name))
+ ) {
+ continue;
+ }
+ }
+
+ /* This line matches: stop parsing the file
+ * after parsing the rest of fields */
+
+ /* 2nd field: uid:gid - device ownership */
+ parse_chown_usergroup_or_die(&ugid, tokens[1]);
+
+ /* 3rd field: mode - device permissions */
+ mode = strtoul(tokens[2], NULL, 8);
+
+ val = tokens[3];
+ /* 4th field (opt): >alias */
+#if ENABLE_FEATURE_MDEV_RENAME
+ if (!val)
+ break;
+ aliaslink = *val;
+ if (aliaslink == '>' || aliaslink == '=') {
+ char *s;
+#if ENABLE_FEATURE_MDEV_RENAME_REGEXP
+ char *p;
+ unsigned i, n;
+#endif
+ char *a = val;
+ s = strchrnul(val, ' ');
+ val = (s[0] && s[1]) ? s+1 : NULL;
+ s[0] = '\0';
+#if ENABLE_FEATURE_MDEV_RENAME_REGEXP
+ /* substitute %1..9 with off[1..9], if any */
+ n = 0;
+ s = a;
+ while (*s)
+ if (*s++ == '%')
+ n++;
+
+ p = alias = xzalloc(strlen(a) + n * strlen(device_name));
+ s = a + 1;
+ while (*s) {
+ *p = *s;
+ if ('%' == *s) {
+ i = (s[1] - '0');
+ if (i <= 9 && off[i].rm_so >= 0) {
+ n = off[i].rm_eo - off[i].rm_so;
+ strncpy(p, device_name + off[i].rm_so, n);
+ p += n - 1;
+ s++;
+ }
+ }
+ p++;
+ s++;
+ }
+#else
+ alias = xstrdup(a + 1);
+#endif
+ }
+#endif /* ENABLE_FEATURE_MDEV_RENAME */
+
+#if ENABLE_FEATURE_MDEV_EXEC
+ /* The rest (opt): command to run */
+ if (!val)
+ break;
+ {
+ const char *s = "@$*";
+ const char *s2 = strchr(s, *val);
+
+ if (!s2)
+ bb_error_msg_and_die("bad line %u", parser->lineno);
+
+ /* Correlate the position in the "@$*" with the delete
+ * step so that we get the proper behavior:
+ * @cmd: run on create
+ * $cmd: run on delete
+ * *cmd: run on both
+ */
+ if ((s2 - s + 1) /*1/2/3*/ & /*1/2*/ (1 + delete)) {
+ command = xstrdup(val + 1);
+ }
+ }
+#endif
+ /* end of field parsing */
+ break; /* we found matching line, stop */
+ } /* end of "while line is read from /etc/mdev.conf" */
+
+ config_close(parser);
+#endif /* ENABLE_FEATURE_MDEV_CONF */
+
+ if (!delete && major >= 0) {
+
+ if (ENABLE_FEATURE_MDEV_RENAME)
+ unlink(device_name);
+
+ if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST)
+ bb_perror_msg_and_die("mknod %s", device_name);
+
+ if (major == root_major && minor == root_minor)
+ symlink(device_name, "root");
+
+#if ENABLE_FEATURE_MDEV_CONF
+ chown(device_name, ugid.uid, ugid.gid);
+
+#if ENABLE_FEATURE_MDEV_RENAME
+ if (alias) {
+ alias = build_alias(alias, device_name);
+
+ /* move the device, and optionally
+ * make a symlink to moved device node */
+ if (rename(device_name, alias) == 0 && aliaslink == '>')
+ symlink(alias, device_name);
+
+ free(alias);
+ }
+#endif
+#endif
+ }
+
+#if ENABLE_FEATURE_MDEV_EXEC
+ if (command) {
+ /* setenv will leak memory, use putenv/unsetenv/free */
+ char *s = xasprintf("MDEV=%s", device_name);
+ putenv(s);
+ if (system(command) == -1)
+ bb_perror_msg_and_die("can't run '%s'", command);
+ s[4] = '\0';
+ unsetenv(s);
+ free(s);
+ free(command);
+ }
+#endif
+
+ if (delete) {
+ unlink(device_name);
+ /* At creation time, device might have been moved
+ * and a symlink might have been created. Undo that. */
+#if ENABLE_FEATURE_MDEV_RENAME
+ if (alias) {
+ alias = build_alias(alias, device_name);
+ unlink(alias);
+ free(alias);
+ }
+#endif
+ }
+}
+
+/* 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(scratch, 0);
+
+ 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)
+{
+ 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 *const firmware, const char *const sysfs_path)
+{
+ int cnt;
+ int firmware_fd, loading_fd, data_fd;
+
+ /* check for /lib/firmware/$FIRMWARE */
+ xchdir("/lib/firmware");
+ firmware_fd = xopen(firmware, O_RDONLY);
+
+ /* in case we goto out ... */
+ data_fd = -1;
+
+ /* 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 != -1)
+ goto loading;
+ sleep(1);
+ }
+ goto out;
+
+ loading:
+ /* tell kernel we're loading by `echo 1 > /sys/$DEVPATH/loading` */
+ if (full_write(loading_fd, "1", 1) != 1)
+ goto out;
+
+ /* load firmware by `cat /lib/firmware/$FIRMWARE > /sys/$DEVPATH/data */
+ data_fd = open("data", O_WRONLY);
+ if (data_fd == -1)
+ goto out;
+ cnt = bb_copyfd_eof(firmware_fd, data_fd);
+
+ /* tell kernel result by `echo [0|-1] > /sys/$DEVPATH/loading` */
+ 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);
+ close(data_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);
+
+ /* We can be called as hotplug helper */
+ /* Kernel cannot provide suitable stdio fds for us, do it ourself */
+#if 1
+ bb_sanitize_stdio();
+#else
+ /* Debug code */
+ /* Replace LOGFILE by other file or device name if you need */
+#define LOGFILE "/dev/console"
+ /* Just making sure fd 0 is not closed,
+ * we don't really intend to read from it */
+ xmove_fd(xopen("/", O_RDONLY), STDIN_FILENO);
+ xmove_fd(xopen(LOGFILE, O_WRONLY|O_APPEND), STDOUT_FILENO);
+ xmove_fd(xopen(LOGFILE, O_WRONLY|O_APPEND), STDERR_FILENO);
+#endif
+
+ xchdir("/dev");
+
+ if (argv[1] && !strcmp(argv[1], "-s")) {
+ /* Scan:
+ * mdev -s
+ */
+ struct stat st;
+
+ xstat("/", &st);
+ root_major = major(st.st_dev);
+ 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) */
+ recursive_action("/sys/block",
+ ACTION_RECURSE | ACTION_FOLLOWLINKS,
+ fileAction, dirAction, temp, 0);
+ recursive_action("/sys/class",
+ ACTION_RECURSE | ACTION_FOLLOWLINKS,
+ fileAction, dirAction, temp, 0);
+ } else {
+ char *seq;
+ char *action;
+ char *env_path;
+ char seqbuf[sizeof(int)*3 + 2];
+ int seqlen = seqlen; /* for compiler */
+
+ /* Hotplug:
+ * env ACTION=... DEVPATH=... [SEQNUM=...] mdev
+ * ACTION can be "add" or "remove"
+ * DEVPATH is like "/block/sda" or "/class/input/mice"
+ */
+ action = getenv("ACTION");
+ env_path = getenv("DEVPATH");
+ if (!action || !env_path)
+ bb_show_usage();
+
+ seq = getenv("SEQNUM");
+ if (seq) {
+ int timeout = 2000 / 32;
+ do {
+ seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf-1));
+ if (seqlen < 0)
+ break;
+ seqbuf[seqlen] = '\0';
+ if (seqbuf[0] == '\n' /* seed file? */
+ || strcmp(seq, seqbuf) == 0 /* correct idx? */
+ ) {
+ break;
+ }
+ usleep(32*1000);
+ } while (--timeout);
+ }
+
+ snprintf(temp, PATH_MAX, "/sys%s", env_path);
+ if (!strcmp(action, "remove"))
+ make_device(temp, 1);
+ else if (!strcmp(action, "add")) {
+ make_device(temp, 0);
+
+ if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
+ char *fw = getenv("FIRMWARE");
+ if (fw)
+ load_firmware(fw, temp);
+ }
+ }
+
+ if (seq && seqlen >= 0) {
+ xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));
+ }
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ RELEASE_CONFIG_BUFFER(temp);
+
+ return 0;
+}
diff --git a/util-linux/minix.h b/util-linux/minix.h
new file mode 100644
index 0000000..3e2b989
--- /dev/null
+++ b/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[0];
+};
+
+/* 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/util-linux/mkfs_minix.c b/util-linux/mkfs_minix.c
new file mode 100644
index 0000000..b29bf5a
--- /dev/null
+++ b/util-linux/mkfs_minix.c
@@ -0,0 +1,734 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfs.c - make a linux (minix) file-system.
+ *
+ * (C) 1991 Linus Torvalds. This file may be redistributed as per
+ * the Linux copyright.
+ */
+
+/*
+ * 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.
+ */
+
+#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 = "cannot 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 = "cannot write superblock";
+ xwrite(dev_fd, G.superblock_buffer, BLOCK_SIZE);
+
+ msg_eol = "cannot write inode map";
+ xwrite(dev_fd, G.inode_map, SB_IMAPS * BLOCK_SIZE);
+
+ msg_eol = "cannot write zone map";
+ xwrite(dev_fd, G.zone_map, SB_ZMAPS * BLOCK_SIZE);
+
+ msg_eol = "cannot 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(stdout);
+}
+
+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)
+{
+ struct mntent *mp;
+ 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 */
+ mp = find_mount_point(G.device_name, NULL);
+ if (mp && strcmp(G.device_name, mp->mnt_fsname) == 0)
+ bb_error_msg_and_die("%s is mounted on %s; "
+ "refusing to make a filesystem",
+ G.device_name, mp->mnt_dir);
+
+ xmove_fd(xopen(G.device_name, O_RDWR), dev_fd);
+ if (fstat(dev_fd, &statbuf) < 0)
+ bb_error_msg_and_die("cannot stat %s", 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/util-linux/mkswap.c b/util-linux/mkswap.c
new file mode 100644
index 0000000..11c411b
--- /dev/null
+++ b/util-linux/mkswap.c
@@ -0,0 +1,129 @@
+/* vi: set sw=4 ts=4: */
+/* mkswap.c - format swap device (Linux v1 only)
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+#if ENABLE_SELINUX
+static void mkswap_selinux_setcontext(int fd, const char *path)
+{
+ struct stat stbuf;
+
+ if (!is_selinux_enabled())
+ return;
+
+ if (fstat(fd, &stbuf) < 0)
+ bb_perror_msg_and_die("fstat failed");
+ 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
+
+#if 0 /* from Linux 2.6.23 */
+/*
+ * Magic header for a swap area. The first part of the union is
+ * what the swap magic looks like for the old (limited to 128MB)
+ * swap area format, the second part of the union adds - in the
+ * old reserved area - some extra information. Note that the first
+ * kilobyte is reserved for boot loader or disk label stuff...
+ */
+union swap_header {
+ struct {
+ char reserved[PAGE_SIZE - 10];
+ char magic[10]; /* SWAP-SPACE or SWAPSPACE2 */
+ } magic;
+ struct {
+ char bootbits[1024]; /* Space for disklabel etc. */
+ __u32 version; /* second kbyte, word 0 */
+ __u32 last_page; /* 1 */
+ __u32 nr_badpages; /* 2 */
+ unsigned char sws_uuid[16]; /* 3,4,5,6 */
+ unsigned char sws_volume[16]; /* 7,8,9,10 */
+ __u32 padding[117]; /* 11..127 */
+ __u32 badpages[1]; /* 128, total 129 32-bit words */
+ } info;
+};
+#endif
+
+#define NWORDS 129
+#define hdr ((uint32_t*)(&bb_common_bufsiz1))
+
+struct BUG_bufsiz1_is_too_small {
+ char BUG_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, char **argv)
+{
+ int fd, pagesize;
+ off_t len;
+
+ // No options supported.
+
+ if (argc != 2) bb_show_usage();
+
+ // Figure out how big the device is and announce our intentions.
+
+ fd = xopen(argv[1], O_RDWR);
+ /* fdlength was reported to be unreliable - use seek */
+ len = xlseek(fd, 0, SEEK_END);
+#if ENABLE_SELINUX
+ xlseek(fd, 0, SEEK_SET);
+#endif
+ pagesize = getpagesize();
+ printf("Setting up swapspace version 1, size = %"OFF_FMT"u bytes\n",
+ len - pagesize);
+ mkswap_selinux_setcontext(fd, argv[1]);
+
+ // Make a header. hdr is zero-filled so far...
+ hdr[0] = 1;
+ hdr[1] = (len / pagesize) - 1;
+
+ // Write the header. Sync to disk because some kernel versions check
+ // signature on disk (not in cache) during swapon.
+
+ xlseek(fd, 1024, SEEK_SET);
+ 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/util-linux/more.c b/util-linux/more.c
new file mode 100644
index 0000000..cf8e137
--- /dev/null
+++ b/util-linux/more.c
@@ -0,0 +1,205 @@
+/* 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 tarball for details.
+ */
+
+#include "libbb.h"
+#if ENABLE_FEATURE_USE_TERMIOS
+#include <termios.h>
+#endif /* FEATURE_USE_TERMIOS */
+
+
+#if ENABLE_FEATURE_USE_TERMIOS
+
+struct globals {
+ int cin_fileno;
+ struct termios initial_settings;
+ struct termios new_settings;
+};
+#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) tcsetattr(fd, TCSANOW, argp)
+#define getTermSettings(fd, argp) tcgetattr(fd, argp)
+
+static void gotsig(int sig UNUSED_PARAM)
+{
+ bb_putchar('\n');
+ setTermSettings(cin_fileno, &initial_settings);
+ exit(EXIT_FAILURE);
+}
+
+#else /* !FEATURE_USE_TERMIOS */
+#define INIT_G() ((void)0)
+#define setTermSettings(fd, argp) ((void)0)
+#endif /* FEATURE_USE_TERMIOS */
+
+#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 gcc */
+ 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;
+ new_settings.c_lflag &= ~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);
+#endif
+
+ 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) {
+ len += printf("(%d%% of %"OFF_FMT"d bytes)",
+ (int) (ftello(file)*100 / st.st_size),
+ st.st_size);
+ }
+ fflush(stdout);
+
+ /*
+ * 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"); /* up cursor */
+#endif
+ /* 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;
+#endif
+ }
+
+ /* Crudely convert tabs into spaces, which are
+ * a bajillion times easier to deal with. */
+ if (c == '\t') {
+ spaces = CONVERTED_TAB_SIZE - 1;
+ 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);
+ }
+ fclose(file);
+ fflush(stdout);
+ } while (*argv && *++argv);
+ end:
+ setTermSettings(cin_fileno, &initial_settings);
+ return 0;
+}
diff --git a/util-linux/mount.c b/util-linux/mount.c
new file mode 100644
index 0000000..313521a
--- /dev/null
+++ b/util-linux/mount.c
@@ -0,0 +1,1951 @@
+/* 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 tarball for details.
+ */
+
+/* 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.
+*/
+
+#include <mntent.h>
+#include <syslog.h>
+#include "libbb.h"
+
+#if ENABLE_FEATURE_MOUNT_LABEL
+#include "volume_id.h"
+#endif
+
+/* Needed for nfs support only */
+#include <sys/utsname.h>
+#undef TRUE
+#undef FALSE
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+
+#ifndef MS_SILENT
+#define MS_SILENT (1 << 15)
+#endif
+/* Grab more as needed from util-linux's mount/mount_constants.h */
+#ifndef MS_DIRSYNC
+#define MS_DIRSYNC 128 /* Directory modifications are synchronous */
+#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:rwanfvsi"
+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),
+};
+
+#if ENABLE_FEATURE_MTAB_SUPPORT
+#define useMtab (!(option_mask32 & OPT_n))
+#else
+#define useMtab 0
+#endif
+
+#if ENABLE_FEATURE_MOUNT_FAKE
+#define fakeIt (option_mask32 & OPT_f)
+#else
+#define fakeIt 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.
+
+ USE_FEATURE_MOUNT_LOOP(
+ /* "loop" */ 0,
+ )
+
+ USE_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,
+ USE_DESKTOP(/* "user" */ MOUNT_USERS,)
+ USE_DESKTOP(/* "users" */ MOUNT_USERS,)
+ /* "_netdev" */ 0,
+ )
+
+ USE_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,
+ /* "loud" */ ~MS_SILENT,
+
+ // action flags
+ /* "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_SLAVE|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[] =
+ USE_FEATURE_MOUNT_LOOP(
+ "loop" "\0"
+ )
+ USE_FEATURE_MOUNT_FSTAB(
+ "defaults" "\0"
+ /* "quiet" "\0" - do not filter out, vfat wants to see it */
+ "noauto" "\0"
+ "sw" "\0"
+ "swap" "\0"
+ USE_DESKTOP("user" "\0")
+ USE_DESKTOP("users" "\0")
+ "_netdev" "\0"
+ )
+ USE_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"
+ "loud" "\0"
+
+ // action flags
+ "bind" "\0"
+ "move" "\0"
+ "shared" "\0"
+ "slave" "\0"
+ "private" "\0"
+ "unbindable" "\0"
+ "rshared" "\0"
+ "rslave" "\0"
+ "rprivate" "\0"
+ "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];
+
+};
+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 )
+
+
+#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
+
+static int resolve_mount_spec(char **fsname)
+{
+ char *tmp = NULL;
+
+#if ENABLE_FEATURE_MOUNT_LABEL
+ if (!strncmp(*fsname, "UUID=", 5))
+ tmp = get_devname_from_uuid(*fsname + 5);
+ else if (!strncmp(*fsname, "LABEL=", 6))
+ tmp = get_devname_from_label(*fsname + 6);
+#endif
+
+ if (tmp) {
+ *fsname = tmp;
+ return 1;
+ }
+ return 0;
+}
+
+/* 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 return list of unrecognized options if unrecognized!=NULL */
+static long parse_mount_options(char *options, char **unrecognized)
+{
+ 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++) {
+ if (!strcasecmp(option_str, options)) {
+ long fl = mount_options[i];
+ if (fl < 0) flags &= fl;
+ else flags |= fl;
+ break;
+ }
+ option_str += strlen(option_str) + 1;
+ }
+ // If unrecognized not NULL, append unrecognized mount options */
+ if (unrecognized && i == ARRAY_SIZE(mount_options)) {
+ // Add it to strflags, to pass on to kernel
+ i = *unrecognized ? strlen(*unrecognized) : 0;
+ *unrecognized = xrealloc(*unrecognized, i + strlen(options) + 2);
+
+ // Comma separated if it's not the first one
+ if (i) (*unrecognized)[i++] = ',';
+ strcpy((*unrecognized)+i, options);
+ }
+
+ 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 = 0;
+ 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) && isspace(buf[5]))
+ continue;
+ fs = skip_whitespace(buf);
+ if (*fs=='#' || *fs=='*' || !*fs) continue;
+
+ llist_add_to_end(&list, xstrdup(fs));
+ 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, long vfsflags, char *filteropts)
+{
+ int rc = 0;
+
+ if (fakeIt) {
+ 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 (ENABLE_FEATURE_MOUNT_HELPERS && rc) {
+ char *args[6];
+ int errno_save = errno;
+ args[0] = xasprintf("mount.%s", mp->mnt_type);
+ rc = 1;
+ if (filteropts) {
+ args[rc++] = (char *)"-o";
+ args[rc++] = filteropts;
+ }
+ args[rc++] = mp->mnt_fsname;
+ args[rc++] = mp->mnt_dir;
+ args[rc] = NULL;
+ rc = wait4pid(spawn(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 (useMtab && !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_error_msg("no %s", 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;
+ if (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 = 0;
+ 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.
+
+ 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 tarball for details.
+ *
+ * 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.
+ */
+
+/* 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
+
+#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_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 struct {
+ short stat;
+ short errnum;
+} nfs_errtbl[] = {
+ {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
+ {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
+ {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
+ {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
+};
+
+static char *nfs_strerror(int status)
+{
+ int i;
+
+ for (i = 0; nfs_errtbl[i].stat != -1; i++) {
+ if (nfs_errtbl[i].stat == status)
+ return strerror(nfs_errtbl[i].errnum);
+ }
+ return xasprintf("unknown nfs status return value: %d", status);
+}
+
+static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
+{
+ if (!xdr_opaque(xdrs, objp, FHSIZE))
+ return FALSE;
+ return TRUE;
+}
+
+static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
+{
+ if (!xdr_u_int(xdrs, &objp->fhs_status))
+ return FALSE;
+ switch (objp->fhs_status) {
+ case 0:
+ if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
+ return FALSE;
+ break;
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
+{
+ if (!xdr_string(xdrs, objp, MNTPATHLEN))
+ return FALSE;
+ return TRUE;
+}
+
+static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
+{
+ if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
+ return FALSE;
+ return TRUE;
+}
+
+static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
+{
+ if (!xdr_fhandle3(xdrs, &objp->fhandle))
+ return FALSE;
+ if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
+ sizeof (int), (xdrproc_t) xdr_int))
+ return FALSE;
+ return TRUE;
+}
+
+static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
+{
+ if (!xdr_enum(xdrs, (enum_t *) objp))
+ return FALSE;
+ return TRUE;
+}
+
+static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
+{
+ if (!xdr_mountstat3(xdrs, &objp->fhs_status))
+ return FALSE;
+ switch (objp->fhs_status) {
+ case MNT_OK:
+ if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
+ return FALSE;
+ break;
+ default:
+ break;
+ }
+ 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,1,32))
+ nfs_mount_version = 1;
+ else if (kernel_version < KERNEL_VERSION(2,2,18) ||
+ (kernel_version >= KERNEL_VERSION(2,3,0) &&
+ kernel_version < KERNEL_VERSION(2,3,99)))
+ 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 int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
+{
+ CLIENT *mclient;
+ char *hostname;
+ char *pathname;
+ char *mounthost;
+ 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. 4.3.1 likes this combination: */
+ smallint tcp;
+ smallint soft;
+ int intr;
+ int posix;
+ int nocto;
+ int noac;
+ int nordirplus;
+ int nolock;
+
+ 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 ((size_t)hp->h_length > sizeof(struct in_addr)) {
+ bb_error_msg("got bad hp->h_length");
+ hp->h_length = sizeof(struct in_addr);
+ }
+ memcpy(&server_addr.sin_addr,
+ hp->h_addr, hp->h_length);
+ }
+
+ 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;
+ retry = 10000; /* 10000 minutes ~ 1 week */
+ tcp = 0;
+
+ 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;
+ }
+
+ val = xatoi_u(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";
+ 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;
+ 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);
+ 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 ((size_t)hp->h_length > sizeof(struct in_addr)) {
+ bb_error_msg("got bad hp->h_length");
+ hp->h_length = sizeof(struct in_addr);
+ }
+ mount_server_addr.sin_family = AF_INET;
+ memcpy(&mount_server_addr.sin_addr,
+ hp->h_addr, hp->h_length);
+ }
+ }
+
+ /*
+ * 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;
+ }
+ }
+
+ do_mount: /* perform actual mount */
+
+ mp->mnt_type = (char*)"nfs";
+ retval = mount_it_now(mp, vfsflags, (char*)&data);
+ goto ret;
+
+ fail: /* abort */
+
+ 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 */
+
+/* Never called. Call should be optimized out. */
+int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
+
+#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;
+ long vfsflags;
+ char *loopFile = 0, *filteropts = 0;
+ llist_t *fl = 0;
+ struct stat st;
+
+ 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 *s, *p, *args[35];
+ int n = 0;
+// FIXME: does it allow execution of arbitrary commands?!
+// What args[0] can end up with?
+ for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) {
+ if (s[0] == '#' && s[1] != '#') {
+ *s = '\0';
+ args[n++] = p;
+ p = s + 1;
+ }
+ }
+ args[n++] = p;
+ args[n++] = mp->mnt_dir;
+ args[n] = NULL;
+ rc = wait4pid(xspawn(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]
+ ) {
+ len_and_sockaddr *lsa;
+ char *ip, *dotted;
+ char *s;
+
+ rc = 1;
+ // Replace '/' with '\' and verify that unc points to "//server/share".
+
+ for (s = mp->mnt_fsname; *s; ++s)
+ if (*s == '/') *s = '\\';
+
+ // get server IP
+
+ s = strrchr(mp->mnt_fsname, '\\');
+ if (s <= mp->mnt_fsname+1) goto report_error;
+ *s = '\0';
+ lsa = host2sockaddr(mp->mnt_fsname+2, 0);
+ *s = '\\';
+ if (!lsa) goto report_error;
+
+ // insert ip=... option into string flags.
+
+ dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+ ip = xasprintf("ip=%s", dotted);
+ parse_mount_options(ip, &filteropts);
+
+ // compose new unc '\\server-ip\share'
+ // (s => slash after hostname)
+
+ mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
+
+ // lock is required
+ vfsflags |= MS_MANDLOCK;
+
+ mp->mnt_type = (char*)"cifs";
+ rc = mount_it_now(mp, vfsflags, filteropts);
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free(mp->mnt_fsname);
+ free(ip);
+ free(dotted);
+ free(lsa);
+ }
+ goto report_error;
+ }
+
+ // Might this be an NFS filesystem?
+
+ if (ENABLE_FEATURE_MOUNT_NFS
+ && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
+ && strchr(mp->mnt_fsname, ':') != NULL
+ ) {
+ 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) < 0) {
+ if (errno == EPERM || errno == EACCES)
+ bb_error_msg(bb_msg_perm_denied_are_you_root);
+ else
+ bb_perror_msg("cannot 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)))
+ rc = mount_it_now(mp, vfsflags, filteropts);
+ 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) 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;
+}
+
+// Parse options, if necessary parse fstab/mtab, and call singlemount for
+// each directory to be mounted.
+
+static const char must_be_root[] ALIGN1 = "you must be root";
+
+int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mount_main(int argc UNUSED_PARAM, char **argv)
+{
+ char *cmdopts = xstrdup("");
+ char *fstype = NULL;
+ char *storage_path;
+ llist_t *lst_o = NULL;
+ const char *fstabname;
+ FILE *fstab;
+ int i, j, rc = 0;
+ unsigned opt;
+ struct mntent mtpair[2], *mtcur = mtpair;
+ SKIP_DESKTOP(const int nonroot = 0;)
+
+ USE_DESKTOP(int nonroot = ) sanitize_env_if_suid();
+
+ // 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; -v is a counter
+ opt_complementary = "?2o::" USE_FEATURE_MOUNT_VERBOSE(":vv");
+ opt = getopt32(argv, OPTION_STR, &lst_o, &fstype
+ USE_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")) continue;
+
+ if (!fstype || !strcmp(mtpair->mnt_type, fstype))
+ 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(must_be_root);
+ mtpair->mnt_fsname = argv[0];
+ mtpair->mnt_dir = argv[1];
+ mtpair->mnt_type = fstype;
+ mtpair->mnt_opts = cmdopts;
+ if (ENABLE_FEATURE_MOUNT_LABEL) {
+ resolve_mount_spec(&mtpair->mnt_fsname);
+ }
+ rc = singlemount(mtpair, 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"
+
+ i = parse_mount_options(cmdopts, 0); // FIXME: should be "long", not "int"
+ if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
+ bb_error_msg_and_die(must_be_root);
+
+ // If we have a shared subtree flag, don't worry about fstab or mtab.
+ if (ENABLE_FEATURE_MOUNT_FLAGS
+ && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
+ ) {
+ rc = verbose_mount(/*source:*/ "", /*target:*/ argv[0],
+ /*type:*/ "", /*flags:*/ i, /*data:*/ "");
+ if (rc)
+ bb_simple_perror_msg_and_die(argv[0]);
+ return rc;
+ }
+
+ // Open either fstab or mtab
+ fstabname = "/etc/fstab";
+ if (i & 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("cannot 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) &&
+ strcmp(storage_path, mtcur->mnt_fsname) &&
+ strcmp(argv[0], mtcur->mnt_dir) &&
+ strcmp(storage_path, mtcur->mnt_dir)) continue;
+
+ // Remember this entry. Something later may have
+ // overmounted it, and we want the _last_ match.
+ mtcur = mtother;
+
+ // If we're mounting all
+ } else {
+ // Do we need to match a filesystem type?
+ if (fstype && match_fstype(mtcur, fstype))
+ continue;
+
+ // Skip noauto and swap anyway.
+ if (parse_mount_options(mtcur->mnt_opts, 0) & (MOUNT_NOAUTO | MOUNT_SWAP))
+ continue;
+
+ // No, mount -a won't mount anything,
+ // even user mounts, for mere humans
+ if (nonroot)
+ bb_error_msg_and_die(must_be_root);
+
+ // Mount this thing
+ if (ENABLE_FEATURE_MOUNT_LABEL)
+ resolve_mount_spec(&mtpair->mnt_fsname);
+
+ // NFS mounts want this to be xrealloc-able
+ mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
+ if (singlemount(mtcur, 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]) {
+ // 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);
+ if (nonroot) {
+ // fstab must have "users" or "user"
+ if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS))
+ bb_error_msg_and_die(must_be_root);
+ }
+
+ // Mount the last thing we found
+ mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
+ append_mount_options(&(mtcur->mnt_opts), cmdopts);
+ if (ENABLE_FEATURE_MOUNT_LABEL) {
+ resolve_mount_spec(&mtpair->mnt_fsname);
+ }
+ rc = singlemount(mtcur, 0);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(mtcur->mnt_opts);
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ endmntent(fstab);
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free(storage_path);
+ free(cmdopts);
+ }
+ return rc;
+}
diff --git a/util-linux/pivot_root.c b/util-linux/pivot_root.c
new file mode 100644
index 0000000..28af00c
--- /dev/null
+++ b/util-linux/pivot_root.c
@@ -0,0 +1,27 @@
+/* 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 GPL version 2, see file LICENSE in this tarball for details.
+ */
+#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/util-linux/rdate.c b/util-linux/rdate.c
new file mode 100644
index 0000000..0880edf
--- /dev/null
+++ b/util-linux/rdate.c
@@ -0,0 +1,71 @@
+/* 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 GPL v2 or later, see file License for details.
+*/
+
+#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, (void *)&nett, 4) != 4) /* read time from server */
+ bb_error_msg_and_die("%s did not send the complete time", host);
+ 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 long flags;
+
+ opt_complementary = "-1";
+ flags = getopt32(argv, "sp");
+
+ remote_time = askremotedate(argv[optind]);
+
+ if ((flags & 2) == 0) {
+ 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("cannot set time of day");
+ }
+
+ if ((flags & 1) == 0)
+ printf("%s", ctime(&remote_time));
+
+ return EXIT_SUCCESS;
+}
diff --git a/util-linux/rdev.c b/util-linux/rdev.c
new file mode 100644
index 0000000..33abd39
--- /dev/null
+++ b/util-linux/rdev.c
@@ -0,0 +1,24 @@
+/* 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 tarball for details.
+ *
+ */
+
+#include "libbb.h"
+
+int rdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rdev_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+ char const * const root_device = find_block_device("/");
+
+ if (root_device != NULL) {
+ printf("%s /\n", root_device);
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
diff --git a/util-linux/readprofile.c b/util-linux/readprofile.c
new file mode 100644
index 0000000..1f5ba2e
--- /dev/null
+++ b/util-linux/readprofile.c
@@ -0,0 +1,247 @@
+/* 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 tarball for details.
+ */
+
+/*
+ * 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>.
+ */
+
+#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/util-linux/rtcwake.c b/util-linux/rtcwake.c
new file mode 100644
index 0000000..a9766ca
--- /dev/null
+++ b/util-linux/rtcwake.c
@@ -0,0 +1,199 @@
+/*
+ * rtcwake -- enter a system sleep state until specified wakeup time.
+ *
+ * This version was taken from util-linux and scrubbed down for busybox.
+ *
+ * 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.
+ */
+
+#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 time_t rtc_time;
+
+static bool may_wakeup(const char *rtcname)
+{
+ ssize_t ret;
+ char buf[128];
+
+ /* strip the '/dev/' from the rtcname here */
+ if (!strncmp(rtcname, "/dev/", 5))
+ rtcname += 5;
+
+ 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 void setup_alarm(int fd, time_t *wakeup)
+{
+ struct tm *tm;
+ 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.
+ */
+ tm = localtime(wakeup);
+
+ wake.time.tm_sec = tm->tm_sec;
+ wake.time.tm_min = tm->tm_min;
+ wake.time.tm_hour = tm->tm_hour;
+ wake.time.tm_mday = tm->tm_mday;
+ wake.time.tm_mon = tm->tm_mon;
+ wake.time.tm_year = tm->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)
+{
+ 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_GETOPT_LONG
+ 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 = xatoi(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(0);
+ if (sys_time == (time_t)-1)
+ bb_perror_msg_and_die("read system time");
+ rtc_time = rtc_read_time(fd, 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);
+
+ sync();
+ printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time));
+ fflush(stdout);
+ 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/util-linux/script.c b/util-linux/script.c
new file mode 100644
index 0000000..2e103fd
--- /dev/null
+++ b/util-linux/script.c
@@ -0,0 +1,186 @@
+/* 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 tarball for details.
+ */
+
+#include "libbb.h"
+
+static smallint fd_count = 2;
+
+static void handle_sigchld(int sig UNUSED_PARAM)
+{
+ fd_count = 0;
+}
+
+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;
+
+#if ENABLE_GETOPT_LONG
+ 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"
+ ;
+
+ applet_long_options = getopt_longopts;
+#endif
+ opt_complementary = "?1"; /* max one arg */
+ opt = getopt32(argv, "ac:fq", &shell_arg);
+ //argc -= optind;
+ argv += optind;
+ if (argv[0]) {
+ fname = argv[0];
+ }
+ mode = O_CREAT|O_TRUNC|O_WRONLY;
+ if (opt & 1) {
+ mode = O_CREAT|O_APPEND|O_WRONLY;
+ }
+ if (opt & 2) {
+ shell_opt[1] = 'c';
+ }
+ if (!(opt & 8)) { /* not -q */
+ printf("Script started, file is %s\n", fname);
+ }
+ shell = getenv("SHELL");
+ if (shell == NULL) {
+ shell = DEFAULT_SHELL;
+ }
+
+ 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, handle_sigchld);
+
+ /* TODO: SIGWINCH? pass window size changes down to slave? */
+
+ child_pid = vfork();
+ if (child_pid < 0) {
+ bb_perror_msg_and_die("vfork");
+ }
+
+ if (child_pid) {
+ /* parent */
+#define buf bb_common_bufsiz1
+ struct pollfd pfd[2];
+ int outfd, count, loop;
+
+ outfd = xopen(fname, mode);
+ pfd[0].fd = pty;
+ pfd[0].events = POLLIN;
+ pfd[1].fd = 0;
+ pfd[1].events = POLLIN;
+ ndelay_on(pty); /* this descriptor is not shared, can do this */
+ /* ndelay_on(0); - 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) {
+ /* 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) {
+ full_write(STDOUT_FILENO, buf, count);
+ full_write(outfd, buf, count);
+ if (opt & 4) { /* -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 fd_count to 0,
+ * there still can be some buffered output. But not 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 & 8)) /* not -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, NULL);
+ bb_simple_perror_msg_and_die(shell);
+}
diff --git a/util-linux/setarch.c b/util-linux/setarch.c
new file mode 100644
index 0000000..8213382
--- /dev/null
+++ b/util-linux/setarch.c
@@ -0,0 +1,48 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * linux32/linux64 allows for changing uname emulation.
+ *
+ * Copyright 2002 Andi Kleen, SuSE Labs.
+ *
+ * Licensed under GPL v2 or later, see file License for details.
+*/
+
+#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/util-linux/swaponoff.c b/util-linux/swaponoff.c
new file mode 100644
index 0000000..863f773
--- /dev/null
+++ b/util-linux/swaponoff.c
@@ -0,0 +1,102 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini swapon/swapoff implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ */
+
+#include "libbb.h"
+#include <mntent.h>
+#include <sys/swap.h>
+
+#if ENABLE_FEATURE_SWAPON_PRI
+struct globals {
+ int flags;
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define g_flags (G.flags)
+#else
+#define g_flags 0
+#endif
+
+static int swap_enable_disable(char *device)
+{
+ int status;
+ struct stat st;
+
+ 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)
+ err += swap_enable_disable(m->mnt_fsname);
+
+ 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;
+
+#if !ENABLE_FEATURE_SWAPON_PRI
+ ret = getopt32(argv, "a");
+#else
+ 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/util-linux/switch_root.c b/util-linux/switch_root.c
new file mode 100644
index 0000000..21cc992
--- /dev/null
+++ b/util-linux/switch_root.c
@@ -0,0 +1,115 @@
+/* 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 GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include <sys/vfs.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);
+ }
+
+ // It wasn't a directory. Zap it.
+ } else 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 st1, st2;
+ struct statfs stfs;
+ dev_t rootdev;
+
+ // Parse args (-c console)
+ opt_complementary = "-2"; // minimum 2 params
+ getopt32(argv, "+c:", &console); // '+': stop parsing at first non-option
+ argv += optind;
+
+ // Change to new root directory and verify it's a different fs.
+ newroot = *argv++;
+
+ xchdir(newroot);
+ if (lstat(".", &st1) || lstat("/", &st2) || st1.st_dev == st2.st_dev) {
+ bb_error_msg_and_die("bad newroot %s", newroot);
+ }
+ rootdev = st2.st_dev;
+
+ // 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 eat their filesystems.)
+ if (lstat("/init", &st1) || !S_ISREG(st1.st_mode) || statfs("/", &stfs)
+ || (((unsigned)stfs.f_type != RAMFS_MAGIC) && ((unsigned)stfs.f_type != TMPFS_MAGIC))
+ || (getpid() != 1)
+ ) {
+ bb_error_msg_and_die("not rootfs");
+ }
+
+ // Zap everything out of rootdev
+ delete_contents("/", rootdev);
+
+ // Overmount / with newdir and chroot into it. The chdir is needed to
+ // recalculate "." and ".." links.
+ if (mount(".", "/", NULL, MS_MOVE, NULL))
+ bb_error_msg_and_die("error moving root");
+ xchroot(".");
+ xchdir("/");
+
+ // If a new console specified, redirect stdin/stdout/stderr to that.
+ if (console) {
+ close(0);
+ xopen(console, O_RDWR);
+ xdup2(0, 1);
+ xdup2(0, 2);
+ }
+
+ // Exec real init. (This is why we must be pid 1.)
+ execv(argv[0], argv);
+ bb_perror_msg_and_die("bad init %s", argv[0]);
+}
diff --git a/util-linux/umount.c b/util-linux/umount.c
new file mode 100644
index 0000000..0662cea
--- /dev/null
+++ b/util-linux/umount.c
@@ -0,0 +1,173 @@
+/* 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 GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include <mntent.h>
+#include "libbb.h"
+
+#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
+
+/* ignored: -v -d -t -i */
+#define OPTION_STRING "fldnra" "vdt:i"
+#define OPT_FORCE (1 << 0)
+#define OPT_LAZY (1 << 1)
+#define OPT_FREELOOP (1 << 2)
+#define OPT_NO_MTAB (1 << 3)
+#define OPT_REMOUNT (1 << 4)
+#define OPT_ALL (ENABLE_FEATURE_UMOUNT_ALL ? (1 << 5) : 0)
+
+// These constants from linux/fs.h must match OPT_FORCE and OPT_LAZY,
+// otherwise "doForce" trick below won't work!
+//#define MNT_FORCE 0x00000001 /* Attempt to forcibly umount */
+//#define MNT_DETACH 0x00000002 /* Just detach from the tree */
+
+int umount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int umount_main(int argc UNUSED_PARAM, char **argv)
+{
+ int doForce;
+ char *const path = xmalloc(PATH_MAX + 2); /* to save stack */
+ 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;
+ 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, path, PATH_MAX)) {
+ /* Match fstype if passed */
+ if (fstype && match_fstype(&me, fstype))
+ continue;
+ m = xmalloc(sizeof(struct mtab_list));
+ 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;
+
+ // Do we already know what to umount this time through the loop?
+ if (m)
+ safe_strncpy(path, m->dir, PATH_MAX);
+ // 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++;
+ realpath(zapit, path);
+ for (m = mtl; m; m = m->next)
+ if (!strcmp(path, m->dir) || !strcmp(path, m->device))
+ 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_FREELOOP) && 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)
+ if ((opt & OPT_ALL) || !strcmp(path, m->device))
+ break;
+ }
+
+ // 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;
+ }
+ free(path);
+ }
+
+ return status;
+}
diff --git a/util-linux/volume_id/Kbuild b/util-linux/volume_id/Kbuild
new file mode 100644
index 0000000..d78e4ad
--- /dev/null
+++ b/util-linux/volume_id/Kbuild
@@ -0,0 +1,42 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+
+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_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_NTFS) += ntfs.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/util-linux/volume_id/cramfs.c b/util-linux/volume_id/cramfs.c
new file mode 100644
index 0000000..63b0c7c
--- /dev/null
+++ b/util-linux/volume_id/cramfs.c
@@ -0,0 +1,58 @@
+/*
+ * 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;
+ } __attribute__((__packed__)) info;
+ uint8_t name[16];
+} __attribute__((__packed__));
+
+int volume_id_probe_cramfs(struct volume_id *id, uint64_t off)
+{
+ 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);
+// id->type = "cramfs";
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/util-linux/volume_id/ext.c b/util-linux/volume_id/ext.c
new file mode 100644
index 0000000..db29dae
--- /dev/null
+++ b/util-linux/volume_id/ext.c
@@ -0,0 +1,73 @@
+/*
+ * 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 ext2_super_block {
+ uint32_t inodes_count;
+ uint32_t blocks_count;
+ uint32_t r_blocks_count;
+ uint32_t free_blocks_count;
+ uint32_t free_inodes_count;
+ uint32_t first_data_block;
+ uint32_t log_block_size;
+ uint32_t dummy3[7];
+ uint8_t magic[2];
+ uint16_t state;
+ uint32_t dummy5[8];
+ uint32_t feature_compat;
+ uint32_t feature_incompat;
+ uint32_t feature_ro_compat;
+ uint8_t uuid[16];
+ uint8_t volume_name[16];
+} __attribute__((__packed__));
+
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x00000004
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x00000008
+#define EXT_SUPERBLOCK_OFFSET 0x400
+
+int volume_id_probe_ext(struct volume_id *id, uint64_t off)
+{
+ 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->magic[0] != 0123 || es->magic[1] != 0357) {
+ 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, es->volume_name, 16);
+ volume_id_set_uuid(id, es->uuid, UUID_DCE);
+ dbg("ext: label '%s' uuid '%s'", id->label, id->uuid);
+
+// if ((le32_to_cpu(es->feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0)
+// id->type = "ext3";
+// else
+// id->type = "ext2";
+
+ return 0;
+}
diff --git a/util-linux/volume_id/fat.c b/util-linux/volume_id/fat.c
new file mode 100644
index 0000000..816d69d
--- /dev/null
+++ b/util-linux/volume_id/fat.c
@@ -0,0 +1,331 @@
+/*
+ * 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];
+ } __attribute__((__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];
+ } __attribute__((__packed__)) fat32;
+ } __attribute__((__packed__)) type;
+} __attribute__((__packed__));
+
+struct vfat_dir_entry {
+ uint8_t name[11];
+ uint8_t attr;
+ uint16_t time_creat;
+ uint16_t date_creat;
+ uint16_t time_acc;
+ uint16_t date_acc;
+ uint16_t cluster_high;
+ uint16_t time_write;
+ uint16_t date_write;
+ uint16_t cluster_low;
+ uint32_t size;
+} __attribute__((__packed__));
+
+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 volume_id_probe_vfat(struct volume_id *id, uint64_t fat_partition_off)
+{
+ 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 found;
+
+ 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 found;
+
+ 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) {
+ uint32_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 = (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 found;
+
+ 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 found;
+
+ /* 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);
+
+ found:
+// volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+// id->type = "vfat";
+
+ return 0;
+}
diff --git a/util-linux/volume_id/get_devname.c b/util-linux/volume_id/get_devname.c
new file mode 100644
index 0000000..d82808f
--- /dev/null
+++ b/util-linux/volume_id/get_devname.c
@@ -0,0 +1,254 @@
+/* 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 tarball for details.
+ */
+
+#include "volume_id_internal.h"
+
+//#define BLKGETSIZE64 _IOR(0x12,114,size_t)
+
+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 */
+} *uuidCache;
+
+/* 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)
+{
+ 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') {
+ *label = xstrndup(vid->label, sizeof(vid->label));
+ *uuid = xstrndup(vid->uuid, sizeof(vid->uuid));
+ dbg("found label '%s', uuid '%s' on %s", *label, *uuid, device);
+ 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)
+{
+ 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 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)
+{
+ char *uuid = uuid; /* for compiler */
+ char *label = label;
+ int fd;
+
+ if (!S_ISBLK(statbuf->st_mode))
+ return TRUE;
+
+ fd = open(device, O_RDONLY);
+ if (fd < 0)
+ return TRUE;
+
+ /* get_label_uuid() closes fd in all cases (success & failure) */
+ if (get_label_uuid(fd, &label, &uuid) == 0) {
+ /* uuidcache_addentry() takes ownership of all three params */
+ uuidcache_addentry(xstrdup(device), /*ma, mi,*/ label, uuid);
+ }
+ return TRUE;
+}
+
+static void
+uuidcache_init(void)
+{
+ if (uuidCache)
+ return;
+
+ /* 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?) */
+
+ recursive_action("/dev", ACTION_RECURSE,
+ uuidcache_check_device, /* file_action */
+ NULL, /* dir_action */
+ NULL, /* userData */
+ 0 /* depth */);
+}
+
+#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;
+
+ uuidcache_init();
+ uc = uuidCache;
+
+ 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(void)
+{
+ struct uuidCache_s *u;
+
+ uuidcache_init();
+ u = uuidCache;
+ while (u) {
+ printf("%s:", u->device);
+ if (u->label[0])
+ printf(" LABEL=\"%s\"", u->label);
+ if (u->uc_uuid[0])
+ printf(" UUID=\"%s\"", u->uc_uuid);
+ bb_putchar('\n');
+ u = u->next;
+ }
+}
+
+/* Used by mount and findfs */
+
+char *get_devname_from_label(const char *spec)
+{
+ struct uuidCache_s *uc;
+ int spec_len = strlen(spec);
+
+ uuidcache_init();
+ uc = uuidCache;
+ while (uc) {
+// FIXME: empty label ("LABEL=") matches anything??!
+ if (uc->label[0] && strncmp(spec, uc->label, spec_len) == 0) {
+ return xstrdup(uc->device);
+ }
+ uc = uc->next;
+ }
+ return NULL;
+}
+
+char *get_devname_from_uuid(const char *spec)
+{
+ struct uuidCache_s *uc;
+
+ uuidcache_init();
+ uc = uuidCache;
+ 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;
+}
diff --git a/util-linux/volume_id/hfs.c b/util-linux/volume_id/hfs.c
new file mode 100644
index 0000000..79658e4
--- /dev/null
+++ b/util-linux/volume_id/hfs.c
@@ -0,0 +1,291 @@
+/*
+ * 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];
+} __attribute__((__packed__));
+
+struct hfs_mdb {
+ uint8_t signature[2];
+ uint32_t cr_date;
+ uint32_t ls_Mod;
+ uint16_t atrb;
+ uint16_t nm_fls;
+ uint16_t vbm_st;
+ uint16_t alloc_ptr;
+ uint16_t nm_al_blks;
+ uint32_t al_blk_size;
+ uint32_t clp_size;
+ uint16_t al_bl_st;
+ uint32_t nxt_cnid;
+ uint16_t free_bks;
+ uint8_t label_len;
+ uint8_t label[27];
+ uint32_t vol_bkup;
+ uint16_t vol_seq_num;
+ uint32_t wr_cnt;
+ uint32_t xt_clump_size;
+ uint32_t ct_clump_size;
+ uint16_t num_root_dirs;
+ uint32_t file_count;
+ uint32_t dir_count;
+ struct hfs_finder_info finder_info;
+ uint8_t embed_sig[2];
+ uint16_t embed_startblock;
+ uint16_t embed_blockcount;
+} __attribute__((__packed__));
+
+struct hfsplus_bnode_descriptor {
+ uint32_t next;
+ uint32_t prev;
+ uint8_t type;
+ uint8_t height;
+ uint16_t num_recs;
+ uint16_t reserved;
+} __attribute__((__packed__));
+
+struct hfsplus_bheader_record {
+ uint16_t depth;
+ uint32_t root;
+ uint32_t leaf_count;
+ uint32_t leaf_head;
+ uint32_t leaf_tail;
+ uint16_t node_size;
+} __attribute__((__packed__));
+
+struct hfsplus_catalog_key {
+ uint16_t key_len;
+ uint32_t parent_id;
+ uint16_t unicode_len;
+ uint8_t unicode[255 * 2];
+} __attribute__((__packed__));
+
+struct hfsplus_extent {
+ uint32_t start_block;
+ uint32_t block_count;
+} __attribute__((__packed__));
+
+#define HFSPLUS_EXTENT_COUNT 8
+struct hfsplus_fork {
+ uint64_t total_size;
+ uint32_t clump_size;
+ uint32_t total_blocks;
+ struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
+} __attribute__((__packed__));
+
+struct hfsplus_vol_header {
+ uint8_t signature[2];
+ uint16_t version;
+ uint32_t attributes;
+ uint32_t last_mount_vers;
+ uint32_t reserved;
+ uint32_t create_date;
+ uint32_t modify_date;
+ uint32_t backup_date;
+ uint32_t checked_date;
+ uint32_t file_count;
+ uint32_t folder_count;
+ uint32_t blocksize;
+ uint32_t total_blocks;
+ uint32_t free_blocks;
+ uint32_t next_alloc;
+ uint32_t rsrc_clump_sz;
+ uint32_t data_clump_sz;
+ uint32_t next_cnid;
+ uint32_t write_count;
+ uint64_t encodings_bmp;
+ struct hfs_finder_info finder_info;
+ struct hfsplus_fork alloc_file;
+ struct hfsplus_fork ext_file;
+ struct hfsplus_fork cat_file;
+ struct hfsplus_fork attr_file;
+ struct hfsplus_fork start_file;
+} __attribute__((__packed__));
+
+#define HFS_SUPERBLOCK_OFFSET 0x400
+#define HFS_NODE_LEAF 0xff
+#define HFSPLUS_POR_CNID 1
+
+int volume_id_probe_hfs_hfsplus(struct volume_id *id, uint64_t off)
+{
+ 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) ;
+ }
+
+ volume_id_set_uuid(id, hfs->finder_info.id, UUID_HFS);
+// volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+// 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:
+ volume_id_set_uuid(id, hfsplus->finder_info.id, UUID_HFS);
+
+ 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);
+// id->type = "hfsplus";
+
+ return 0;
+}
diff --git a/util-linux/volume_id/iso9660.c b/util-linux/volume_id/iso9660.c
new file mode 100644
index 0000000..c15608c
--- /dev/null
+++ b/util-linux/volume_id/iso9660.c
@@ -0,0 +1,119 @@
+/*
+ * 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];
+} __attribute__((__packed__));
+
+struct high_sierra_volume_descriptor {
+ uint8_t foo[8];
+ uint8_t type;
+ uint8_t id[4];
+ uint8_t version;
+} __attribute__((__packed__));
+
+int volume_id_probe_iso9660(struct volume_id *id, uint64_t off)
+{
+ 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);
+// id->type = "iso9660";
+
+ return 0;
+}
diff --git a/util-linux/volume_id/jfs.c b/util-linux/volume_id/jfs.c
new file mode 100644
index 0000000..63692f9
--- /dev/null
+++ b/util-linux/volume_id/jfs.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 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];
+} __attribute__((__packed__));
+
+#define JFS_SUPERBLOCK_OFFSET 0x8000
+
+int volume_id_probe_jfs(struct volume_id *id, uint64_t off)
+{
+ 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);
+// id->type = "jfs";
+
+ return 0;
+}
diff --git a/util-linux/volume_id/linux_raid.c b/util-linux/volume_id/linux_raid.c
new file mode 100644
index 0000000..a113060
--- /dev/null
+++ b/util-linux/volume_id/linux_raid.c
@@ -0,0 +1,79 @@
+/*
+ * 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;
+} __attribute__((packed));
+
+#define MD_RESERVED_BYTES 0x10000
+#define MD_MAGIC 0xa92b4efc
+
+int volume_id_probe_linux_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+ 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;
+
+ memcpy(uuid, &mdp->set_uuid0, 4);
+ 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);
+// id->type = "linux_raid_member";
+
+ return 0;
+}
diff --git a/util-linux/volume_id/linux_swap.c b/util-linux/volume_id/linux_swap.c
new file mode 100644
index 0000000..e608454
--- /dev/null
+++ b/util-linux/volume_id/linux_swap.c
@@ -0,0 +1,73 @@
+/*
+ * 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];
+} __attribute__((__packed__));
+
+#define LARGEST_PAGESIZE 0x4000
+
+int volume_id_probe_linux_swap(struct volume_id *id, uint64_t off)
+{
+ 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) {
+ 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);
+// id->type = "swap";
+
+ return 0;
+}
diff --git a/util-linux/volume_id/luks.c b/util-linux/volume_id/luks.c
new file mode 100644
index 0000000..b0f0f5b
--- /dev/null
+++ b/util-linux/volume_id/luks.c
@@ -0,0 +1,99 @@
+/*
+ * 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 volume_id_probe_luks(struct volume_id *id, uint64_t off)
+{
+ 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);
+// id->type = "crypto_LUKS";
+
+ return 0;
+}
diff --git a/util-linux/volume_id/ntfs.c b/util-linux/volume_id/ntfs.c
new file mode 100644
index 0000000..7488a41
--- /dev/null
+++ b/util-linux/volume_id/ntfs.c
@@ -0,0 +1,193 @@
+/*
+ * 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;
+} __attribute__((__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;
+} __attribute__((__packed__));
+
+struct file_attribute {
+ uint32_t type;
+ uint32_t len;
+ uint8_t non_resident;
+ uint8_t name_len;
+ uint16_t name_offset;
+ uint16_t flags;
+ uint16_t instance;
+ uint32_t value_len;
+ uint16_t value_offset;
+} __attribute__((__packed__));
+
+struct volume_info {
+ uint64_t reserved;
+ uint8_t major_ver;
+ uint8_t minor_ver;
+} __attribute__((__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 volume_id_probe_ntfs(struct volume_id *id, uint64_t off)
+{
+ 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);
+// id->type = "ntfs";
+
+ return 0;
+}
diff --git a/util-linux/volume_id/ocfs2.c b/util-linux/volume_id/ocfs2.c
new file mode 100644
index 0000000..8bcaac0
--- /dev/null
+++ b/util-linux/volume_id/ocfs2.c
@@ -0,0 +1,105 @@
+/*
+ * 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 */
+} __attribute__((__packed__));
+
+int volume_id_probe_ocfs2(struct volume_id *id, uint64_t off)
+{
+ 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);
+// id->type = "ocfs2";
+ return 0;
+}
diff --git a/util-linux/volume_id/reiserfs.c b/util-linux/volume_id/reiserfs.c
new file mode 100644
index 0000000..d9a3745
--- /dev/null
+++ b/util-linux/volume_id/reiserfs.c
@@ -0,0 +1,112 @@
+/*
+ * 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];
+} __attribute__((__packed__));
+
+struct reiser4_super_block {
+ uint8_t magic[16];
+ uint16_t dummy[2];
+ uint8_t uuid[16];
+ uint8_t label[16];
+ uint64_t dummy2;
+} __attribute__((__packed__));
+
+#define REISERFS1_SUPERBLOCK_OFFSET 0x2000
+#define REISERFS_SUPERBLOCK_OFFSET 0x10000
+
+int volume_id_probe_reiserfs(struct volume_id *id, uint64_t off)
+{
+ 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);
+// id->type = "reiserfs";
+
+ return 0;
+}
diff --git a/util-linux/volume_id/romfs.c b/util-linux/volume_id/romfs.c
new file mode 100644
index 0000000..400bdce
--- /dev/null
+++ b/util-linux/volume_id/romfs.c
@@ -0,0 +1,54 @@
+/*
+ * 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[0];
+} __attribute__((__packed__));
+
+int volume_id_probe_romfs(struct volume_id *id, uint64_t off)
+{
+ 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);
+// id->type = "romfs";
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/util-linux/volume_id/sysv.c b/util-linux/volume_id/sysv.c
new file mode 100644
index 0000000..7671962
--- /dev/null
+++ b/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;
+} __attribute__((__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;
+} __attribute__((__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 volume_id_probe_sysv(struct volume_id *id, uint64_t off)
+{
+ 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);
+// 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);
+// id->type = "xenix";
+ goto found;
+ }
+ }
+
+ return -1;
+
+ found:
+// volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+ return 0;
+}
diff --git a/util-linux/volume_id/udf.c b/util-linux/volume_id/udf.c
new file mode 100644
index 0000000..55e97a7
--- /dev/null
+++ b/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;
+ } __attribute__((__packed__)) tag;
+ union {
+ struct anchor_descriptor {
+ uint32_t length;
+ uint32_t location;
+ } __attribute__((__packed__)) anchor;
+ struct primary_descriptor {
+ uint32_t seq_num;
+ uint32_t desc_num;
+ struct dstring {
+ uint8_t clen;
+ uint8_t c[31];
+ } __attribute__((__packed__)) ident;
+ } __attribute__((__packed__)) primary;
+ } __attribute__((__packed__)) type;
+} __attribute__((__packed__));
+
+struct volume_structure_descriptor {
+ uint8_t type;
+ uint8_t id[5];
+ uint8_t version;
+} __attribute__((__packed__));
+
+#define UDF_VSD_OFFSET 0x8000
+
+int volume_id_probe_udf(struct volume_id *id, uint64_t off)
+{
+ 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);
+// id->type = "udf";
+
+ return 0;
+}
diff --git a/util-linux/volume_id/unused_highpoint.c b/util-linux/volume_id/unused_highpoint.c
new file mode 100644
index 0000000..57c5cad
--- /dev/null
+++ b/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;
+} __attribute__((packed));
+
+struct hpt45x_meta {
+ uint32_t magic;
+} __attribute__((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 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 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/util-linux/volume_id/unused_hpfs.c b/util-linux/volume_id/unused_hpfs.c
new file mode 100644
index 0000000..8b51756
--- /dev/null
+++ b/util-linux/volume_id/unused_hpfs.c
@@ -0,0 +1,49 @@
+/*
+ * 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;
+} __attribute__((__packed__));
+
+#define HPFS_SUPERBLOCK_OFFSET 0x2000
+
+int 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/util-linux/volume_id/unused_isw_raid.c b/util-linux/volume_id/unused_isw_raid.c
new file mode 100644
index 0000000..d928245
--- /dev/null
+++ b/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;
+} __attribute__((packed));
+
+#define ISW_SIGNATURE "Intel Raid ISM Cfg Sig. "
+
+
+int 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/util-linux/volume_id/unused_lsi_raid.c b/util-linux/volume_id/unused_lsi_raid.c
new file mode 100644
index 0000000..730a313
--- /dev/null
+++ b/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];
+} __attribute__((packed));
+
+#define LSI_SIGNATURE "$XIDE$"
+
+int 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/util-linux/volume_id/unused_lvm.c b/util-linux/volume_id/unused_lvm.c
new file mode 100644
index 0000000..caee04b
--- /dev/null
+++ b/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];
+} __attribute__((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];
+} __attribute__((packed));
+
+#define LVM1_SB_OFF 0x400
+
+int 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 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/util-linux/volume_id/unused_mac.c b/util-linux/volume_id/unused_mac.c
new file mode 100644
index 0000000..8eaa173
--- /dev/null
+++ b/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;
+} __attribute__((__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];
+} __attribute__((__packed__));
+
+int 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/util-linux/volume_id/unused_minix.c b/util-linux/volume_id/unused_minix.c
new file mode 100644
index 0000000..2f52093
--- /dev/null
+++ b/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;
+} __attribute__((__packed__));
+
+#define MINIX_SUPERBLOCK_OFFSET 0x400
+
+int 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/util-linux/volume_id/unused_msdos.c b/util-linux/volume_id/unused_msdos.c
new file mode 100644
index 0000000..097ee67
--- /dev/null
+++ b/util-linux/volume_id/unused_msdos.c
@@ -0,0 +1,193 @@
+/*
+ * 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;
+} __attribute__((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 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/util-linux/volume_id/unused_nvidia_raid.c b/util-linux/volume_id/unused_nvidia_raid.c
new file mode 100644
index 0000000..692aad1
--- /dev/null
+++ b/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;
+} __attribute__((packed));
+
+#define NVIDIA_SIGNATURE "NVIDIA"
+
+int 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/util-linux/volume_id/unused_promise_raid.c b/util-linux/volume_id/unused_promise_raid.c
new file mode 100644
index 0000000..75c6f89
--- /dev/null
+++ b/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];
+} __attribute__((packed));
+
+#define PDC_CONFIG_OFF 0x1200
+#define PDC_SIGNATURE "Promise Technology, Inc."
+
+int 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/util-linux/volume_id/unused_silicon_raid.c b/util-linux/volume_id/unused_silicon_raid.c
new file mode 100644
index 0000000..5143112
--- /dev/null
+++ b/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;
+} __attribute__((packed));
+
+#define SILICON_MAGIC 0x2F000000
+
+int 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/util-linux/volume_id/unused_ufs.c b/util-linux/volume_id/unused_ufs.c
new file mode 100644
index 0000000..8693758
--- /dev/null
+++ b/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;
+ } __attribute__((__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];
+ } __attribute__((__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];
+ } __attribute__((__packed__)) fs_cstotal;
+ struct ufs_timeval {
+ int32_t tv_sec;
+ int32_t tv_usec;
+ } __attribute__((__packed__)) fs_time;
+ int64_t fs_size;
+ int64_t fs_dsize;
+ uint64_t fs_csaddr;
+ int64_t fs_pendingblocks;
+ int32_t fs_pendinginodes;
+ } __attribute__((__packed__)) fs_u2;
+ } fs_u11;
+ union {
+ struct {
+ int32_t fs_sparecon[53];
+ int32_t fs_reclaim;
+ int32_t fs_sparecon2[1];
+ int32_t fs_state;
+ uint32_t fs_qbmask[2];
+ uint32_t fs_qfmask[2];
+ } __attribute__((__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];
+ } __attribute__((__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;
+ } __attribute__((__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];
+} __attribute__((__packed__));
+
+#define UFS_MAGIC 0x00011954
+#define UFS2_MAGIC 0x19540119
+#define UFS_MAGIC_FEA 0x00195612
+#define UFS_MAGIC_LFN 0x00095014
+
+int 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/util-linux/volume_id/unused_via_raid.c b/util-linux/volume_id/unused_via_raid.c
new file mode 100644
index 0000000..4332946
--- /dev/null
+++ b/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;
+ } __attribute((packed)) array;
+ uint32_t serial_checksum[8];
+ uint8_t checksum;
+} __attribute__((packed));
+
+#define VIA_SIGNATURE 0xAA55
+
+int 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/util-linux/volume_id/util.c b/util-linux/volume_id/util.c
new file mode 100644
index 0000000..c4d20ba
--- /dev/null
+++ b/util-linux/volume_id/util.c
@@ -0,0 +1,272 @@
+/*
+ * 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) {
+ str[j] = '\0';
+ break;
+ } else if (c < 0x80) {
+ if (j+1 >= len)
+ break;
+ str[j++] = (uint8_t) c;
+ } else if (c < 0x800) {
+ if (j+2 >= len)
+ break;
+ str[j++] = (uint8_t) (0xc0 | (c >> 6));
+ str[j++] = (uint8_t) (0x80 | (c & 0x3f));
+ } else {
+ if (j+3 >= len)
+ break;
+ str[j++] = (uint8_t) (0xe0 | (c >> 12));
+ str[j++] = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
+ str[j++] = (uint8_t) (0x80 | (c & 0x3f));
+ }
+ }
+ 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 = 0;
+
+ switch (format) {
+ case UUID_DOS:
+ count = 4;
+ break;
+ case UUID_NTFS:
+ case UUID_HFS:
+ count = 8;
+ break;
+ case UUID_DCE:
+ count = 16;
+ break;
+ case UUID_DCE_STRING:
+ /* 36 is ok, id->uuid has one extra byte for NUL */
+ count = VOLUME_ID_UUID_SIZE;
+ break;
+ }
+// 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_HFS:
+ sprintf(id->uuid, "%02X%02X%02X%02X%02X%02X%02X%02X",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+ 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)
+{
+ ssize_t buf_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) {
+ if (id->sbbuf == NULL) {
+ id->sbbuf = xmalloc(SB_BUFFER_SIZE);
+ }
+
+ /* check if we need to read */
+ if ((off + len) > id->sbbuf_len) {
+ dbg("read sbbuf len:0x%llx", (unsigned long long) (off + len));
+ if (lseek(id->fd, 0, SEEK_SET) != 0) {
+ dbg("seek(0) failed");
+ return NULL;
+ }
+ buf_len = full_read(id->fd, id->sbbuf, off + len);
+ if (buf_len < 0) {
+ dbg("read failed (%s)", strerror(errno));
+ return NULL;
+ }
+ dbg("got 0x%zx (%zi) bytes", buf_len, buf_len);
+ id->sbbuf_len = buf_len;
+ if ((uint64_t)buf_len < off + len) {
+ dbg("requested 0x%zx bytes, got only 0x%zx bytes", len, buf_len);
+ return NULL;
+ }
+ }
+
+ return &(id->sbbuf[off]);
+ }
+
+ if (len > SEEK_BUFFER_SIZE) {
+ dbg("seek buffer too small %d", SEEK_BUFFER_SIZE);
+ return NULL;
+ }
+
+ /* get seek buffer */
+ if (id->seekbuf == NULL) {
+ id->seekbuf = xmalloc(SEEK_BUFFER_SIZE);
+ }
+
+ /* check if we need to read */
+ if ((off < id->seekbuf_off) || ((off + len) > (id->seekbuf_off + id->seekbuf_len))) {
+ dbg("read seekbuf off:0x%llx len:0x%zx", (unsigned long long) off, len);
+ if (lseek(id->fd, off, SEEK_SET) != off) {
+ dbg("seek(0x%llx) failed", (unsigned long long) off);
+ return NULL;
+ }
+ buf_len = full_read(id->fd, id->seekbuf, len);
+ if (buf_len < 0) {
+ dbg("read failed (%s)", strerror(errno));
+ return NULL;
+ }
+ dbg("got 0x%zx (%zi) bytes", buf_len, buf_len);
+ id->seekbuf_off = off;
+ id->seekbuf_len = buf_len;
+ if ((size_t)buf_len < len) {
+ dbg("requested 0x%zx bytes, got only 0x%zx bytes", len, buf_len);
+ return NULL;
+ }
+ }
+
+ return &(id->seekbuf[off - id->seekbuf_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;
+}
diff --git a/util-linux/volume_id/volume_id.c b/util-linux/volume_id/volume_id.c
new file mode 100644
index 0000000..6852a82
--- /dev/null
+++ b/util-linux/volume_id/volume_id.c
@@ -0,0 +1,238 @@
+/*
+ * 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 (*raid_probe_fptr)(struct volume_id *id, uint64_t off, uint64_t size);
+typedef int (*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_MAC
+ volume_id_probe_mac_partition_map,
+#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_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_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 volume_id_probe_all(struct volume_id *id, uint64_t off, uint64_t size)
+{
+ unsigned i;
+
+ if (id == NULL)
+ return -EINVAL;
+
+ /* 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;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(raid2); i++)
+ if (raid2[i](id, off) == 0)
+ 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;
+
+ /* 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;
+ return -1;
+
+ ret:
+ /* If the filestystem in recognized, we free the allocated buffers,
+ otherwise they will stay in place for the possible next probe call */
+ volume_id_free_buffer(id);
+
+ return 0;
+}
+
+/* open volume by device node */
+struct volume_id *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 *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("cannot mknod(%s)", tmp_node);
+
+ id = volume_id_open_node(tmp_node);
+ unlink(tmp_node);
+ free(tmp_node);
+ return id;
+}
+#endif
+
+void 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/util-linux/volume_id/volume_id_internal.h b/util-linux/volume_id/volume_id_internal.h
new file mode 100644
index 0000000..075ddb3
--- /dev/null
+++ b/util-linux/volume_id/volume_id_internal.h
@@ -0,0 +1,233 @@
+/*
+ * 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"
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility push(hidden)
+#endif
+
+#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 {
+// 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;
+// const char *type;
+
+#ifdef UNUSED_PARTITION_CODE
+ struct volume_id_partition *partitions;
+ size_t partition_count;
+#endif
+
+ int fd;
+ uint8_t *sbbuf;
+ uint8_t *seekbuf;
+ size_t sbbuf_len;
+ uint64_t seekbuf_off;
+ size_t seekbuf_len;
+// int fd_close:1;
+};
+
+struct volume_id *volume_id_open_node(int fd);
+int volume_id_probe_all(struct volume_id *id, uint64_t off, uint64_t size);
+void 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
+
+enum uuid_format {
+ UUID_DCE_STRING,
+ UUID_DCE,
+ UUID_DOS,
+ UUID_NTFS,
+ UUID_HFS,
+};
+
+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 volume_id_probe_highpoint_37x_raid(struct volume_id *id, uint64_t off);
+//int volume_id_probe_highpoint_45x_raid(struct volume_id *id, uint64_t off, uint64_t size);
+
+//int volume_id_probe_intel_software_raid(struct volume_id *id, uint64_t off, uint64_t size);
+
+int volume_id_probe_linux_raid(struct volume_id *id, uint64_t off, uint64_t size);
+
+//int volume_id_probe_lsi_mega_raid(struct volume_id *id, uint64_t off, uint64_t size);
+
+//int volume_id_probe_nvidia_raid(struct volume_id *id, uint64_t off, uint64_t size);
+
+//int volume_id_probe_promise_fasttrack_raid(struct volume_id *id, uint64_t off, uint64_t size);
+
+//int volume_id_probe_silicon_medley_raid(struct volume_id *id, uint64_t off, uint64_t size);
+
+//int volume_id_probe_via_raid(struct volume_id *id, uint64_t off, uint64_t size);
+
+//int volume_id_probe_lvm1(struct volume_id *id, uint64_t off);
+//int volume_id_probe_lvm2(struct volume_id *id, uint64_t off);
+
+/* FS */
+
+int volume_id_probe_cramfs(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_ext(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_vfat(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_hfs_hfsplus(struct volume_id *id, uint64_t off);
+
+//int volume_id_probe_hpfs(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_iso9660(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_jfs(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_linux_swap(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_luks(struct volume_id *id, uint64_t off);
+
+//int volume_id_probe_mac_partition_map(struct volume_id *id, uint64_t off);
+
+//int volume_id_probe_minix(struct volume_id *id, uint64_t off);
+
+//int volume_id_probe_msdos_part_table(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_ntfs(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_ocfs2(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_reiserfs(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_romfs(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_sysv(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_udf(struct volume_id *id, uint64_t off);
+
+//int volume_id_probe_ufs(struct volume_id *id, uint64_t off);
+
+int volume_id_probe_xfs(struct volume_id *id, uint64_t off);
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility pop
+#endif
diff --git a/util-linux/volume_id/xfs.c b/util-linux/volume_id/xfs.c
new file mode 100644
index 0000000..0d90437
--- /dev/null
+++ b/util-linux/volume_id/xfs.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 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;
+} __attribute__((__packed__));
+
+int volume_id_probe_xfs(struct volume_id *id, uint64_t off)
+{
+ 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);
+// id->type = "xfs";
+
+ return 0;
+}