From 73b16af8feec390afbabd9356d6e5e83c0390838 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 15 May 2015 10:20:47 +0200 Subject: busybox: imported from http://www.busybox.net/downloads/busybox-1.13.3.tar.bz2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bjørn Mork --- util-linux/Config.in | 845 ++++++++ util-linux/Kbuild | 37 + util-linux/blkid.c | 18 + util-linux/dmesg.c | 67 + util-linux/fbset.c | 404 ++++ util-linux/fdformat.c | 130 ++ util-linux/fdisk.c | 2997 ++++++++++++++++++++++++++++ util-linux/fdisk_aix.c | 73 + util-linux/fdisk_osf.c | 1052 ++++++++++ util-linux/fdisk_sgi.c | 886 ++++++++ util-linux/fdisk_sun.c | 727 +++++++ util-linux/findfs.c | 38 + util-linux/freeramdisk.c | 33 + util-linux/fsck_minix.c | 1309 ++++++++++++ util-linux/getopt.c | 354 ++++ util-linux/hexdump.c | 151 ++ util-linux/hwclock.c | 127 ++ util-linux/ipcrm.c | 220 ++ util-linux/ipcs.c | 621 ++++++ util-linux/losetup.c | 79 + util-linux/mdev.c | 487 +++++ util-linux/minix.h | 98 + util-linux/mkfs_minix.c | 734 +++++++ util-linux/mkswap.c | 129 ++ util-linux/more.c | 205 ++ util-linux/mount.c | 1951 ++++++++++++++++++ util-linux/pivot_root.c | 27 + util-linux/rdate.c | 71 + util-linux/rdev.c | 24 + util-linux/readprofile.c | 247 +++ util-linux/rtcwake.c | 199 ++ util-linux/script.c | 186 ++ util-linux/setarch.c | 48 + util-linux/swaponoff.c | 102 + util-linux/switch_root.c | 115 ++ util-linux/umount.c | 173 ++ util-linux/volume_id/Kbuild | 42 + util-linux/volume_id/cramfs.c | 58 + util-linux/volume_id/ext.c | 73 + util-linux/volume_id/fat.c | 331 +++ util-linux/volume_id/get_devname.c | 254 +++ util-linux/volume_id/hfs.c | 291 +++ util-linux/volume_id/iso9660.c | 119 ++ util-linux/volume_id/jfs.c | 59 + util-linux/volume_id/linux_raid.c | 79 + util-linux/volume_id/linux_swap.c | 73 + util-linux/volume_id/luks.c | 99 + util-linux/volume_id/ntfs.c | 193 ++ util-linux/volume_id/ocfs2.c | 105 + util-linux/volume_id/reiserfs.c | 112 ++ util-linux/volume_id/romfs.c | 54 + util-linux/volume_id/sysv.c | 125 ++ util-linux/volume_id/udf.c | 172 ++ util-linux/volume_id/unused_highpoint.c | 86 + util-linux/volume_id/unused_hpfs.c | 49 + util-linux/volume_id/unused_isw_raid.c | 58 + util-linux/volume_id/unused_lsi_raid.c | 52 + util-linux/volume_id/unused_lvm.c | 87 + util-linux/volume_id/unused_mac.c | 123 ++ util-linux/volume_id/unused_minix.c | 75 + util-linux/volume_id/unused_msdos.c | 193 ++ util-linux/volume_id/unused_nvidia_raid.c | 56 + util-linux/volume_id/unused_promise_raid.c | 63 + util-linux/volume_id/unused_silicon_raid.c | 69 + util-linux/volume_id/unused_ufs.c | 206 ++ util-linux/volume_id/unused_via_raid.c | 68 + util-linux/volume_id/util.c | 272 +++ util-linux/volume_id/volume_id.c | 238 +++ util-linux/volume_id/volume_id_internal.h | 233 +++ util-linux/volume_id/xfs.c | 59 + 70 files changed, 19190 insertions(+) create mode 100644 util-linux/Config.in create mode 100644 util-linux/Kbuild create mode 100644 util-linux/blkid.c create mode 100644 util-linux/dmesg.c create mode 100644 util-linux/fbset.c create mode 100644 util-linux/fdformat.c create mode 100644 util-linux/fdisk.c create mode 100644 util-linux/fdisk_aix.c create mode 100644 util-linux/fdisk_osf.c create mode 100644 util-linux/fdisk_sgi.c create mode 100644 util-linux/fdisk_sun.c create mode 100644 util-linux/findfs.c create mode 100644 util-linux/freeramdisk.c create mode 100644 util-linux/fsck_minix.c create mode 100644 util-linux/getopt.c create mode 100644 util-linux/hexdump.c create mode 100644 util-linux/hwclock.c create mode 100644 util-linux/ipcrm.c create mode 100644 util-linux/ipcs.c create mode 100644 util-linux/losetup.c create mode 100644 util-linux/mdev.c create mode 100644 util-linux/minix.h create mode 100644 util-linux/mkfs_minix.c create mode 100644 util-linux/mkswap.c create mode 100644 util-linux/more.c create mode 100644 util-linux/mount.c create mode 100644 util-linux/pivot_root.c create mode 100644 util-linux/rdate.c create mode 100644 util-linux/rdev.c create mode 100644 util-linux/readprofile.c create mode 100644 util-linux/rtcwake.c create mode 100644 util-linux/script.c create mode 100644 util-linux/setarch.c create mode 100644 util-linux/swaponoff.c create mode 100644 util-linux/switch_root.c create mode 100644 util-linux/umount.c create mode 100644 util-linux/volume_id/Kbuild create mode 100644 util-linux/volume_id/cramfs.c create mode 100644 util-linux/volume_id/ext.c create mode 100644 util-linux/volume_id/fat.c create mode 100644 util-linux/volume_id/get_devname.c create mode 100644 util-linux/volume_id/hfs.c create mode 100644 util-linux/volume_id/iso9660.c create mode 100644 util-linux/volume_id/jfs.c create mode 100644 util-linux/volume_id/linux_raid.c create mode 100644 util-linux/volume_id/linux_swap.c create mode 100644 util-linux/volume_id/luks.c create mode 100644 util-linux/volume_id/ntfs.c create mode 100644 util-linux/volume_id/ocfs2.c create mode 100644 util-linux/volume_id/reiserfs.c create mode 100644 util-linux/volume_id/romfs.c create mode 100644 util-linux/volume_id/sysv.c create mode 100644 util-linux/volume_id/udf.c create mode 100644 util-linux/volume_id/unused_highpoint.c create mode 100644 util-linux/volume_id/unused_hpfs.c create mode 100644 util-linux/volume_id/unused_isw_raid.c create mode 100644 util-linux/volume_id/unused_lsi_raid.c create mode 100644 util-linux/volume_id/unused_lvm.c create mode 100644 util-linux/volume_id/unused_mac.c create mode 100644 util-linux/volume_id/unused_minix.c create mode 100644 util-linux/volume_id/unused_msdos.c create mode 100644 util-linux/volume_id/unused_nvidia_raid.c create mode 100644 util-linux/volume_id/unused_promise_raid.c create mode 100644 util-linux/volume_id/unused_silicon_raid.c create mode 100644 util-linux/volume_id/unused_ufs.c create mode 100644 util-linux/volume_id/unused_via_raid.c create mode 100644 util-linux/volume_id/util.c create mode 100644 util-linux/volume_id/volume_id.c create mode 100644 util-linux/volume_id/volume_id_internal.h create mode 100644 util-linux/volume_id/xfs.c (limited to 'util-linux') 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 +# +# 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 + * Copyright 2006 Bernhard Reutner-Fischer + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +#include +#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 + * + * 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, ¶m); + + 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 (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 */ +#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"), + §or_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 + * Internationalization + * + * 2003-03-20 Phillip Kesling + * 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 - i18n/nls + + 20000101 - David Huggins-Daines - 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 + * 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 + * 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 + * 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 + * Adjusted a bit by Erik Andersen + * Unified with fdflush by Tito Ragusa + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* From */ +#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 . + * + * 06.11.96 - Added v2 code submitted by Joerg Dorchain, but written by + * Andreas Schwab. + * + * 1999-02-22 Arkadiusz Mickiewicz + * - 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 +#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] = ¤t_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 + * + * 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 + * ) + * Ported to Busybox - Alfred M. Szmidt + * 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 +#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 \ */ + *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 + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. +*/ + +#include +#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 + * 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 for semctl() */ +/* X/OPEN tells us to use for msgctl() */ +#include +#include +#include +#include + +#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) +/* union semun is defined by including */ +#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 + * 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 for semctl() */ +/* X/OPEN tells us to use for msgctl() */ +/* X/OPEN tells us to use for shmctl() */ +#include +#include +#include +#include +#include + +#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 */ +#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 + , 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 + * Copyright 2005 Frank Sorenson + * + * 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: @... */ + 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 -- + * removed getopt based parser and added a hand rolled one. + */ + +#include "libbb.h" +#include + +#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 + * + * 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 . + * Copyright (C) 1999-2004 by Erik Andersen + * + * Latest version blended together by Erik Andersen , + * based on the original more implementation by Bruce, and code from the + * Debian boot-floppies team. + * + * Termios corrects by Vladimir Oleynik + * + * Licensed under GPLv2 or later, see file License in this tarball for details. + */ + +#include "libbb.h" +#if ENABLE_FEATURE_USE_TERMIOS +#include +#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 . + * Copyright (C) 1999-2004 by Erik Andersen + * Copyright (C) 2005-2006 by Rob Landley + * + * 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 +#include +#include "libbb.h" + +#if ENABLE_FEATURE_MOUNT_LABEL +#include "volume_id.h" +#endif + +/* Needed for nfs support only */ +#include +#undef TRUE +#undef FALSE +#include +#include +#include + +#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. + 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 + * + * 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 : + * Omit the call to connect() for Linux version 1.3.11 or later. + * + * Wed Oct 1 23:55:28 1997: Dick Streefland + * Implemented the "bg", "fg" and "retry" mount options for NFS. + * + * 1999-02-22 Arkadiusz Mickiewicz + * - 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 : change errno: + * "after #include 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: " */ + 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 + * + * 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(¤t_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 + * + * 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 + * - added Native Language Support + * 1999-09-01 Stephane Eranian + * - 64bit clean patch + * 3Feb2001 Andrew Morton + * - -M option to write profile multiplier. + * 2001-11-07 Werner Almesberger + * - byte order auto-detection and -n option + * 2001-11-09 Werner Almesberger + * - skip step size (index 0) + * 2002-03-09 John Levon + * - make maplineno do something + * 2002-11-28 Mads Martin Joergensen + + * - also try /boot/System.map-`uname -r` + * 2003-04-09 Werner Almesberger + * - fixed off-by eight error and improved heuristics in byte order detection + * 2003-08-12 Nikita Danilov + * - 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 . + */ + +#include "libbb.h" +#include + +#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 + +#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 + * + * Licensed under the GPL version 2, see the file LICENSE in this tarball. + */ + +#include "libbb.h" +#include +#include + +#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 + * + * 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 + +// 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 + * Copyright (C) 2005 by Rob Landley + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +#include +#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 +# +# 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 + * + * 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 + * + * 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 + * + * 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 + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * Copyright (C) 2005 Tobias Klauser + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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; +} -- cgit v1.2.3