diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-27 07:35:04 +0300 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-27 07:35:04 +0300 |
commit | 8d42f86b146871ae4c4cafd3801a85f381249a14 (patch) | |
tree | b963999fc54eddb65f1929b894f868e24851fc9c /util-linux |
Correcting branch name to be like previous ones
Diffstat (limited to 'util-linux')
30 files changed, 13695 insertions, 0 deletions
diff --git a/util-linux/Config.in b/util-linux/Config.in new file mode 100644 index 000000000..848914d6f --- /dev/null +++ b/util-linux/Config.in @@ -0,0 +1,526 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Linux System Utilities" + +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 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 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. + + http://www.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: call it with -s to populate + /dev from /sys, then "echo /sbin/mdev > /proc/sys/kernel/hotplug" to + have it handle hotplug events afterwards. Device names are taken + from sysfs. + +config FEATURE_MDEV_CONF + bool "Support /etc/mdev.conf" + default n + depends on MDEV + help + The mdev config file contains lines that look like: + + hd[a-z][0-9]* 0:3 660 + + That's device name (with regex match), uid:gid, and permissions. + + Config file parsing stops on the first matching line. If no config + entry is matched, devices are created with default 0:0 660. (Make + the last line match .* to override this.) + +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, consisting + of a special character and a command line to run after creating the + corresponding device(s) and before removing, ala: + + hdc root:cdrom 660 *ln -s $MDEV cdrom + + The $MDEV environment variable is set to the name of the device. + + The special characters and their meanings are: + @ Run after creating the device. + $ Run before removing the device. + * Run both after creating and before removing the device. + + Commands are executed via system() so you need /bin/sh, meaning you + probably want to select a default shell in the Shells menu. + +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 + 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 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_NFS + bool "Support mounting NFS file systems" + default n + depends on MOUNT + depends on 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 READPROFILE + bool "readprofile" + default n + help + This allows you to parse /proc/profile for basic profiling. + +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 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 "umount -a option" + 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 + 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 000000000..cc1d0e05d --- /dev/null +++ b/util-linux/Kbuild @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> +# +# Licensed under the GPL v2, see the file LICENSE in this tarball. + +lib-y:= +lib-$(CONFIG_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_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_READPROFILE) +=readprofile.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/dmesg.c b/util-linux/dmesg.c new file mode 100644 index 000000000..658cddc38 --- /dev/null +++ b/util-linux/dmesg.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * + * dmesg - display/control kernel ring buffer. + * + * Copyright 2006 Rob Landley <rob@landley.net> + * Copyright 2006 Bernhard Fischer <rep.nop@aon.at> + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" +#include <unistd.h> +#include <sys/klog.h> + +int dmesg_main(int argc, char *argv[]) +{ + char *size, *level; + int flags = getopt32(argc, argv, "cs:n:", &size, &level); + + if (flags & 4) { + if (klogctl(8, NULL, xatoul_range(level, 0, 10))) + bb_perror_msg_and_die("klogctl"); + } else { + int len; + char *buf; + + len = (flags & 2) ? xatoul_range(size, 2, INT_MAX) : 16384; + buf = xmalloc(len); + if (0 > (len = klogctl(3 + (flags & 1), buf, len))) + bb_perror_msg_and_die("klogctl"); + + // Skip <#> at the start of lines, and make sure we end with a newline. + + if (ENABLE_FEATURE_DMESG_PRETTY) { + int last = '\n'; + int in; + + for (in = 0; in<len;) { + if (last == '\n' && buf[in] == '<') in += 3; + else putchar(last = buf[in++]); + } + if (last != '\n') putchar('\n'); + } else { + write(1,buf,len); + if (len && buf[len-1]!='\n') putchar('\n'); + } + + if (ENABLE_FEATURE_CLEAN_UP) free(buf); + } + + return 0; +} diff --git a/util-linux/fbset.c b/util-linux/fbset.c new file mode 100644 index 000000000..1aa0a0ac1 --- /dev/null +++ b/util-linux/fbset.c @@ -0,0 +1,407 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini fbset implementation for busybox + * + * Copyright (C) 1999 by Randolph Chung <tausq@debian.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + * This is a from-scratch implementation of fbset; but the de facto fbset + * implementation was a good reference. fbset (original) is released under + * the GPL, and is (c) 1995-1999 by: + * Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be) + */ + +#include "busybox.h" + +#define DEFAULTFBDEV FB_0 +#define DEFAULTFBMODE "/etc/fb.modes" + +enum { + OPT_CHANGE = (1 << 0), + OPT_INFO = (1 << 1), + OPT_READMODE = (1 << 2), + OPT_ALL = (1 << 9), + + 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, + +#ifdef CONFIG_FEATURE_FBSET_FANCY + CMD_XRES = 100, + CMD_YRES = 101, + CMD_VXRES = 102, + CMD_VYRES = 103, + CMD_DEPTH = 104, + CMD_MATCH = 105, + CMD_PIXCLOCK = 106, + CMD_LEFT = 107, + CMD_RIGHT = 108, + CMD_UPPER = 109, + CMD_LOWER = 110, + CMD_HSLEN = 111, + CMD_VSLEN = 112, + CMD_CSYNC = 113, + CMD_GSYNC = 114, + CMD_EXTSYNC = 115, + CMD_BCAST = 116, + CMD_RGBA = 117, + CMD_STEP = 118, + CMD_MOVE = 119, +#endif +}; + +static unsigned int g_options = 0; + +/* 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[10]; + const unsigned char param_count; + const unsigned char code; +} g_cmdoptions[] = { + { "-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 }, +#ifdef CONFIG_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 + { "", 0, 0 } +}; + +#ifdef CONFIG_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 + +static int readmode(struct fb_var_screeninfo *base, const char *fn, + const char *mode) +{ +#ifdef CONFIG_FEATURE_FBSET_READMODE + FILE *f; + char buf[256]; + char *p = buf; + + f = xfopen(fn, "r"); + while (!feof(f)) { + fgets(buf, sizeof(buf), f); + if (!(p = strstr(buf, "mode ")) && !(p = strstr(buf, "mode\t"))) + continue; + p += 5; + if (!(p = strstr(buf, mode))) + continue; + p += strlen(mode); + if (!isspace(*p) && (*p != 0) && (*p != '"') + && (*p != '\r') && (*p != '\n')) + continue; /* almost, but not quite */ + + while (!feof(f)) { + fgets(buf, sizeof(buf), f); + if ((p = strstr(buf, "geometry "))) { + p += 9; + /* 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)); + } else if ((p = strstr(buf, "timings "))) { + p += 8; + sscanf(p, "%d %d %d %d %d %d %d", + &(base->pixclock), + &(base->left_margin), &(base->right_margin), + &(base->upper_margin), &(base->lower_margin), + &(base->hsync_len), &(base->vsync_len)); + } else if ((p = strstr(buf, "laced "))) { + //p += 6; + if (strstr(buf, "false")) { + base->vmode &= ~FB_VMODE_INTERLACED; + } else { + base->vmode |= FB_VMODE_INTERLACED; + } + } else if ((p = strstr(buf, "double "))) { + //p += 7; + if (strstr(buf, "false")) { + base->vmode &= ~FB_VMODE_DOUBLE; + } else { + base->vmode |= FB_VMODE_DOUBLE; + } + } else if ((p = strstr(buf, "vsync "))) { + //p += 6; + if (strstr(buf, "low")) { + base->sync &= ~FB_SYNC_VERT_HIGH_ACT; + } else { + base->sync |= FB_SYNC_VERT_HIGH_ACT; + } + } else if ((p = strstr(buf, "hsync "))) { + //p += 6; + if (strstr(buf, "low")) { + base->sync &= ~FB_SYNC_HOR_HIGH_ACT; + } else { + base->sync |= FB_SYNC_HOR_HIGH_ACT; + } + } else if ((p = strstr(buf, "csync "))) { + //p += 6; + if (strstr(buf, "low")) { + base->sync &= ~FB_SYNC_COMP_HIGH_ACT; + } else { + base->sync |= FB_SYNC_COMP_HIGH_ACT; + } + } else if ((p = strstr(buf, "extsync "))) { + //p += 8; + if (strstr(buf, "false")) { + base->sync &= ~FB_SYNC_EXT; + } else { + base->sync |= FB_SYNC_EXT; + } + } + + if (strstr(buf, "endmode")) + return 1; + } + } +#else + bb_error_msg("mode reading not compiled in"); +#endif + return 0; +} + +static inline void setmode(struct fb_var_screeninfo *base, + struct fb_var_screeninfo *set) +{ + if ((int) set->xres > 0) + base->xres = set->xres; + if ((int) set->yres > 0) + base->yres = set->yres; + if ((int) set->xres_virtual > 0) + base->xres_virtual = set->xres_virtual; + if ((int) set->yres_virtual > 0) + base->yres_virtual = set->yres_virtual; + if ((int) set->bits_per_pixel > 0) + base->bits_per_pixel = set->bits_per_pixel; +} + +static inline 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" +#ifdef CONFIG_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), +#ifdef CONFIG_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); +} + +#ifdef STANDALONE +int main(int argc, char **argv) +#else +int fbset_main(int argc, char **argv) +#endif +{ + struct fb_var_screeninfo var, varset; + int fh, i; + char *fbdev = DEFAULTFBDEV; + 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); argc--, argv++) { + for (i = 0; g_cmdoptions[i].name[0]; i++) { + if (strcmp(thisarg, g_cmdoptions[i].name)) + continue; + if (argc-1 < 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: + g_options |= OPT_ALL; + break; + case CMD_CHANGE: + g_options |= OPT_CHANGE; + break; +#ifdef CONFIG_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; + break; + } + if (!g_cmdoptions[i].name[0]) { + if (argc != 1) + bb_show_usage(); + mode = *argv; + g_options |= OPT_READMODE; + } + } + + fh = xopen(fbdev, O_RDONLY); + if (ioctl(fh, FBIOGET_VSCREENINFO, &var)) + bb_perror_msg_and_die("ioctl(%sT_VSCREENINFO)", "GE"); + if (g_options & OPT_READMODE) { + if (!readmode(&var, modefile, mode)) { + bb_error_msg_and_die("unknown video mode '%s'", mode); + } + } + + setmode(&var, &varset); + if (g_options & OPT_CHANGE) { + if (g_options & OPT_ALL) + var.activate = FB_ACTIVATE_ALL; + if (ioctl(fh, FBIOPUT_VSCREENINFO, &var)) + bb_perror_msg_and_die("ioctl(%sT_VSCREENINFO)", "PU"); + } + 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 000000000..0242d8d3a --- /dev/null +++ b/util-linux/fdformat.c @@ -0,0 +1,143 @@ +/* vi: set sw=4 ts=4: */ +/* fdformat.c - Low-level formats a floppy disk - Werner Almesberger */ + +/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL> + * - added Native Language Support + * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br> + * - more i18n/nls translatable strings marked + * + * 5 July 2003 -- modified for Busybox by Erik Andersen + */ + +#include "busybox.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. */ + +static void xioctl(int fd, int request, void *argp, const char *string) +{ + if (ioctl(fd, request, argp) < 0) { + bb_perror_msg_and_die(string); + } +} + +int fdformat_main(int argc,char **argv) +{ + int fd, n, cyl, read_bytes, verify; + unsigned char *data; + struct stat st; + struct floppy_struct param; + struct format_descr descr; + + if (argc < 2) { + bb_show_usage(); + } + verify = !getopt32(argc, 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, "FDGETPRM"); + + 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, "FDFMTBEG"); + + /* n == track */ + for (n = 0; n < param.track; n++) { + descr.head = 0; + descr.track = n; + xioctl(fd, FDFMTTRK, &descr, "FDFMTTRK"); + printf("%3d\b\b\b", n); + if (param.head == 2) { + descr.head = 1; + xioctl(fd, FDFMTTRK, &descr, "FDFMTTRK"); + } + } + + xioctl(fd, FDFMTEND, NULL, "FDFMTEND"); + 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 000000000..2f87f1c60 --- /dev/null +++ b/util-linux/fdisk.c @@ -0,0 +1,3043 @@ +/* vi: set sw=4 ts=4: */ +/* fdisk.c -- Partition table manipulator for Linux. + * + * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk) + * Copyright (C) 2001,2002 Vladimir Oleynik <dzo@simtreas.ru> (initial bb port) + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include <assert.h> /* assert */ +#include "busybox.h" +#define _(x) x + +/* 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 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 + +#define IS_EXTENDED(i) \ + ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED) + +#define SIZE(a) (sizeof(a)/sizeof((a)[0])) + +#define cround(n) (display_in_cyl_units ? ((n)/units_per_sector)+1 : (n)) +#define scround(x) (((x)+units_per_sector-1)/units_per_sector) + +struct hd_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + unsigned long start; +}; + +#define HDIO_GETGEO 0x0301 /* get device geometry */ + +struct systypes { + const char *name; +}; + +static unsigned sector_size = DEFAULT_SECTOR_SIZE; +static unsigned user_set_sector_size; +static unsigned sector_offset = 1; + +/* + * Raw disk label. For DOS-type partition tables the MBR, + * with descriptions of the primary partitions. + */ +#if (MAX_SECTOR_SIZE) > (BUFSIZ+1) +static char MBRbuffer[MAX_SECTOR_SIZE]; +#else +# define MBRbuffer bb_common_bufsiz1 +#endif + +#if ENABLE_FEATURE_OSF_LABEL +static int possibly_osf_label; +#endif + +static unsigned heads, sectors, cylinders; +static void update_units(void); + + +/* + * return partition name - uses static storage unless buf is supplied + */ +static const char * +partname(const char *dev, int pno, int lth) +{ + static char buffer[80]; + const char *p; + int w, wp; + int bufsiz; + char *bufp; + + bufp = buffer; + bufsiz = sizeof(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; +} + +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 */ +} ATTRIBUTE_PACKED; + +enum failure { + ioctl_error, unable_to_open, unable_to_read, unable_to_seek, + unable_to_write +}; + +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 { fdisk, require, try_only, create_empty_dos, create_empty_sun }; + +static enum label_type current_label_type; + +static const char *disk_device; +static int fd; /* the disk */ +static int partitions = 4; /* maximum partition + 1 */ +static int display_in_cyl_units = 1; +static unsigned units_per_sector = 1; +#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 struct systypes *sys); +static unsigned read_int(unsigned low, unsigned dflt, unsigned high, unsigned base, char *mesg); +#endif +static const char *partition_type(unsigned char type); +static void fdisk_fatal(enum failure why) ATTRIBUTE_NORETURN; +static void get_geometry(void); +static int get_boot(enum action what); + +#define PLURAL 0 +#define SINGULAR 1 + +#define hex_val(c) ({ \ + char _c = (c); \ + isdigit(_c) ? _c - '0' : \ + tolower(_c) + 10 - 'a'; \ + }) + + +#define LINE_LENGTH 800 +#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) { \ + s = sector % sectors + 1; \ + sector /= sectors; \ + h = sector % heads; \ + sector /= heads; \ + c = sector & 0xff; \ + s |= (sector >> 2) & 0xc0; \ + } + + +static int32_t get_start_sect(const struct partition *p); +static int32_t 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. + */ +static struct pte { + struct partition *part_table; /* points into sectorbuffer */ + struct partition *ext_pointer; /* points into sectorbuffer */ +#if ENABLE_FEATURE_FDISK_WRITABLE + char changed; /* boolean */ +#endif + off_t offset; /* disk sector number */ + char *sectorbuffer; /* disk sector contents */ +} ptes[MAXIMUM_PARTS]; + + +#if ENABLE_FEATURE_FDISK_WRITABLE +static void +set_all_unchanged(void) +{ + int i; + + for (i = 0; i < MAXIMUM_PARTS; i++) + ptes[i].changed = 0; +} + +extern inline void +set_changed(int i) +{ + ptes[i].changed = 1; +} +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + +extern 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"); + else + 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 +extern inline void +write_part_table_flag(char *b) +{ + b[510] = 0x55; + b[511] = 0xaa; +} + +static char line_buffer[LINE_LENGTH]; +static char *line_ptr; + +/* read line; return 0 or first char */ +static int +read_line(void) +{ + fflush(stdout); /* requested by niles@scyld.com */ + line_ptr = line_buffer; + if (!fgets(line_buffer, LINE_LENGTH, stdin)) { + /* error or eof */ + bb_error_msg_and_die("\ngot EOF, exiting"); + } + while (*line_ptr && !isgraph(*line_ptr)) + line_ptr++; + return *line_ptr; +} + +static char +read_nonempty(const char *mesg) +{ + do { + fputs(mesg, stdout); + } while (!read_line()); + return *line_ptr; +} + +static char +read_maybe_empty(const char *mesg) +{ + fputs(mesg, stdout); + if (!read_line()) { + line_ptr = line_buffer; + *line_ptr = '\n'; + line_ptr[1] = 0; + } + return *line_ptr; +} + +static int +read_hex(const struct systypes *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 (errno || v > 0xff) continue; + return v; + } +} +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + +#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) +#define SUNOS_SWAP 3 +#define SUN_WHOLE_DISK 5 +STATIC_OSF void bsd_select(void); +STATIC_OSF void xbsd_print_disklabel(int); +#include "fdisk_osf.c" + +#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 +#if defined(CONFIG_FEATURE_SGI_LABEL) || defined(CONFIG_FEATURE_SUN_LABEL) +static uint16_t +__swap16(uint16_t x) +{ + return (x << 8) | (x >> 8); +} + +static uint32_t +__swap32(uint32_t x) +{ + return (x << 24) | + ((x & 0xFF00) << 8) | + ((x & 0xFF0000) >> 8) | + (x >> 24); +} +#endif + +STATIC_SGI const struct systypes 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); +STATIC_SGI void sgi_set_xcyl(void); +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 struct systypes 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 sun_set_xcyl(void); +STATIC_SUN void add_sun_partition(int n, int sys); +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); +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" + +/* DOS partition types */ + +static const struct systypes 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 Win95 FAT32 (LBA)" }, + { "\x1e" "Hidden Win95 FAT16 (LBA)" }, + { "\x3c" "PartitionMagic 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" "IBM 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 + { 0 } +}; + + +#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 /* CONFIG_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 int32_t +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, int32_t nr_sects) +{ + store4_little_endian(p->size4, nr_sects); +} +#endif + +static int32_t +get_nr_sects(const struct partition *p) +{ + return read4_little_endian(p->size4); +} + +/* normally O_RDWR, -l option gives O_RDONLY */ +static int type_open = O_RDWR; + + +static int ext_index; /* the prime extended partition */ +static int listing; /* no aborts for fdisk -l */ +static int dos_compatible_flag = ~0; +#if ENABLE_FEATURE_FDISK_WRITABLE +static int dos_changed; +static int nowarn; /* no warnings for fdisk -l/-s */ +#endif + + + +static unsigned user_cylinders, user_heads, user_sectors; +static unsigned pt_heads, pt_sectors; +static unsigned kern_heads, kern_sectors; + +static off_t extended_offset; /* offset of link pointers */ + +static unsigned long long total_number_of_sectors; + + +static jmp_buf listingbuf; + +static void fdisk_fatal(enum failure why) +{ + const char *message; + + if (listing) { + close(fd); + longjmp(listingbuf, 1); + } + + switch (why) { + case unable_to_open: + message = "\nUnable to open %s"; + break; + case unable_to_read: + message = "\nUnable to read %s"; + break; + case unable_to_seek: + message = "\nUnable to seek on %s"; + break; + case unable_to_write: + message = "\nUnable to write %s"; + break; + case ioctl_error: + message = "\nBLKGETSIZE ioctl failed on %s"; + break; + default: + message = "\nFatal error"; + } + + bb_error_msg_and_die(message, disk_device); +} + +static void +seek_sector(off_t secno) +{ + off_t offset = secno * sector_size; + if (lseek(fd, offset, SEEK_SET) == (off_t) -1) + fdisk_fatal(unable_to_seek); +} + +#if ENABLE_FEATURE_FDISK_WRITABLE +static void +write_sector(off_t secno, char *buf) +{ + seek_sector(secno); + if (write(fd, buf, sector_size) != sector_size) + fdisk_fatal(unable_to_write); +} +#endif + +/* Allocate a buffer and read a partition table sector */ +static void +read_pte(struct pte *pe, off_t offset) +{ + pe->offset = offset; + pe->sectorbuffer = (char *) xmalloc(sector_size); + seek_sector(offset); + if (read(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". + */ +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); +} + +static void +menu(void) +{ + if (LABEL_IS_SUN) { + puts(_("Command action")); + puts(_("\ta\ttoggle a read only flag")); /* sun */ + puts(_("\tb\tedit bsd disklabel")); + puts(_("\tc\ttoggle the mountable flag")); /* sun */ + puts(_("\td\tdelete a partition")); + puts(_("\tl\tlist known partition types")); + puts(_("\tm\tprint this menu")); + puts(_("\tn\tadd a new partition")); + puts(_("\to\tcreate a new empty DOS partition table")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */ + puts(_("\tt\tchange a partition's system id")); + puts(_("\tu\tchange display/entry units")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); +#if ENABLE_FEATURE_FDISK_ADVANCED + puts(_("\tx\textra functionality (experts only)")); +#endif + } else + if (LABEL_IS_SGI) { + puts(_("Command action")); + puts(_("\ta\tselect bootable partition")); /* sgi flavour */ + puts(_("\tb\tedit bootfile entry")); /* sgi */ + puts(_("\tc\tselect sgi swap partition")); /* sgi flavour */ + puts(_("\td\tdelete a partition")); + puts(_("\tl\tlist known partition types")); + puts(_("\tm\tprint this menu")); + puts(_("\tn\tadd a new partition")); + puts(_("\to\tcreate a new empty DOS partition table")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */ + puts(_("\tt\tchange a partition's system id")); + puts(_("\tu\tchange display/entry units")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); + } else + if (LABEL_IS_AIX) { + puts(_("Command action")); + puts(_("\tm\tprint this menu")); + puts(_("\to\tcreate a new empty DOS partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */ + } else + { + puts(_("Command action")); + puts(_("\ta\ttoggle a bootable flag")); + puts(_("\tb\tedit bsd disklabel")); + puts(_("\tc\ttoggle the dos compatibility flag")); + puts(_("\td\tdelete a partition")); + puts(_("\tl\tlist known partition types")); + puts(_("\tm\tprint this menu")); + puts(_("\tn\tadd a new partition")); + puts(_("\to\tcreate a new empty DOS partition table")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */ + puts(_("\tt\tchange a partition's system id")); + puts(_("\tu\tchange display/entry units")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); +#if ENABLE_FEATURE_FDISK_ADVANCED + puts(_("\tx\textra functionality (experts only)")); +#endif + } +} +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + + +#if ENABLE_FEATURE_FDISK_ADVANCED +static void +xmenu(void) +{ + if (LABEL_IS_SUN) { + puts(_("Command action")); + puts(_("\ta\tchange number of alternate cylinders")); /*sun*/ + puts(_("\tc\tchange number of cylinders")); + puts(_("\td\tprint the raw data in the partition table")); + puts(_("\te\tchange number of extra sectors per cylinder"));/*sun*/ + puts(_("\th\tchange number of heads")); + puts(_("\ti\tchange interleave factor")); /*sun*/ + puts(_("\to\tchange rotation speed (rpm)")); /*sun*/ + puts(_("\tm\tprint this menu")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\tr\treturn to main menu")); + puts(_("\ts\tchange number of sectors/track")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); + puts(_("\ty\tchange number of physical cylinders")); /*sun*/ + } else + if (LABEL_IS_SGI) { + puts(_("Command action")); + puts(_("\tb\tmove beginning of data in a partition")); /* !sun */ + puts(_("\tc\tchange number of cylinders")); + puts(_("\td\tprint the raw data in the partition table")); + puts(_("\te\tlist extended partitions")); /* !sun */ + puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */ + puts(_("\th\tchange number of heads")); + puts(_("\tm\tprint this menu")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\tr\treturn to main menu")); + puts(_("\ts\tchange number of sectors/track")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); + } else + if (LABEL_IS_AIX) { + puts(_("Command action")); + puts(_("\tb\tmove beginning of data in a partition")); /* !sun */ + puts(_("\tc\tchange number of cylinders")); + puts(_("\td\tprint the raw data in the partition table")); + puts(_("\te\tlist extended partitions")); /* !sun */ + puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */ + puts(_("\th\tchange number of heads")); + puts(_("\tm\tprint this menu")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\tr\treturn to main menu")); + puts(_("\ts\tchange number of sectors/track")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); + } else { + puts(_("Command action")); + puts(_("\tb\tmove beginning of data in a partition")); /* !sun */ + puts(_("\tc\tchange number of cylinders")); + puts(_("\td\tprint the raw data in the partition table")); + puts(_("\te\tlist extended partitions")); /* !sun */ + puts(_("\tf\tfix partition order")); /* !sun, !aix, !sgi */ +#if ENABLE_FEATURE_SGI_LABEL + puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */ +#endif + puts(_("\th\tchange number of heads")); + puts(_("\tm\tprint this menu")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\tr\treturn to main menu")); + puts(_("\ts\tchange number of sectors/track")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); + } +} +#endif /* ADVANCED mode */ + +#if ENABLE_FEATURE_FDISK_WRITABLE +static const struct systypes * +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 /* CONFIG_FEATURE_FDISK_WRITABLE */ + +static const char *partition_type(unsigned char type) +{ + int i; + const struct systypes *types = get_sys_types(); + + for (i = 0; types[i].name; i++) + if ((unsigned char )types[i].name[0] == type) + return types[i].name + 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); +} + +void list_types(const struct systypes *sys) +{ + unsigned last[4], done = 0, next = 0, size; + int i; + + for (i = 0; sys[i].name; i++); + size = i; + + for (i = 3; i >= 0; i--) + last[3 - i] = done += (size + i - done) / (i + 1); + i = done = 0; + + do { + printf("%c%2x %-15.15s", i ? ' ' : '\n', + (unsigned char)sys[next].name[0], + partition_type((unsigned char)sys[next].name[0])); + next = last[i++] + done; + if (i > 3 || next >= last[i]) { + i = 0; + next = ++done; + } + } while (done < last[0]); + putchar('\n'); +} +#endif /* CONFIG_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, off_t start, off_t stop, int sysid) +{ + struct partition *p; + off_t 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/(sectors*heads) > 1023)) + start = heads*sectors*1024 - 1; + set_hsc(p->head, p->sector, p->cyl, start); + if (dos_compatible_flag && (stop/(sectors*heads) > 1023)) + stop = heads*sectors*1024 - 1; + set_hsc(p->end_head, p->end_sector, p->end_cyl, stop); + ptes[i].changed = 1; +} +#endif + +static int +test_c(const char **m, const char *mesg) +{ + int val = 0; + if (!*m) + printf(_("You must set")); + else { + printf(" %s", *m); + val = 1; + } + *m = mesg; + return val; +} + +static int +warn_geometry(void) +{ + const char *m = NULL; + int prev = 0; + + if (!heads) + prev = test_c(&m, _("heads")); + if (!sectors) + prev = test_c(&m, _("sectors")); + if (!cylinders) + prev = test_c(&m, _("cylinders")); + if (!m) + return 0; + + printf("%s%s.\n" +#if ENABLE_FEATURE_FDISK_WRITABLE + "You can do this from the extra functions menu.\n" +#endif + , prev ? _(" and ") : " ", m); + + return 1; +} + +static void update_units(void) +{ + int cyl_units = heads * 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 && 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"), + 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[partitions]; + + if (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[partitions-1]; +#if ENABLE_FEATURE_FDISK_WRITABLE + printf(_("Warning: deleting partitions after %d\n"), + 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"), 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"), 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; + partitions++; + } + +#if ENABLE_FEATURE_FDISK_WRITABLE + /* remove empty links */ + remove: + for (i = 4; i < partitions; i++) { + struct pte *pe = &ptes[i]; + + if (!get_nr_sects(pe->part_table) && + (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( + _("Building a new DOS disklabel. Changes will remain in memory only,\n" + "until you decide to write them. After that, of course, the previous\n" + "content won't be recoverable.\n\n")); + + current_label_type = label_dos; + +#if ENABLE_FEATURE_OSF_LABEL + possibly_osf_label = 0; +#endif + 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 /* CONFIG_FEATURE_FDISK_WRITABLE */ + +static void +get_sectorsize(void) +{ + if (!user_set_sector_size) { + int arg; + if (ioctl(fd, BLKSSZGET, &arg) == 0) + sector_size = arg; + if (sector_size != DEFAULT_SECTOR_SIZE) + printf(_("Note: sector size is %d (not %d)\n"), + sector_size, DEFAULT_SECTOR_SIZE); + } +} + +static void +get_kernel_geometry(void) +{ + struct hd_geometry geometry; + + if (!ioctl(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; + unsigned long long bytes; /* really u64 */ + + get_sectorsize(); + sec_fac = sector_size / 512; +#if ENABLE_FEATURE_SUN_LABEL + guess_device_type(); +#endif + heads = cylinders = sectors = 0; + kern_heads = kern_sectors = 0; + pt_heads = pt_sectors = 0; + + get_kernel_geometry(); + get_partition_table_geometry(); + + heads = user_heads ? user_heads : + pt_heads ? pt_heads : + kern_heads ? kern_heads : 255; + sectors = user_sectors ? user_sectors : + pt_sectors ? pt_sectors : + kern_sectors ? kern_sectors : 63; + if (ioctl(fd, BLKGETSIZE64, &bytes) == 0) { + /* got bytes */ + } else { + unsigned long longsectors; + + if (ioctl(fd, BLKGETSIZE, &longsectors)) + longsectors = 0; + bytes = ((unsigned long long) longsectors) << 9; + } + + total_number_of_sectors = (bytes >> 9); + + sector_offset = 1; + if (dos_compatible_flag) + sector_offset = sectors; + + cylinders = total_number_of_sectors / (heads * sectors * sec_fac); + if (!cylinders) + cylinders = user_cylinders; +} + +/* + * Read MBR. Returns: + * -1: no 0xaa55 flag present (possibly entire disk BSD) + * 0: found or created label + * 1: I/O error + */ +static int +get_boot(enum action what) +{ + int i; + + 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_SUN_LABEL + if (what == create_empty_sun && check_sun_label()) + return 0; +#endif + + memset(MBRbuffer, 0, 512); + +#if ENABLE_FEATURE_FDISK_WRITABLE + if (what == create_empty_dos) + goto got_dos_table; /* skip reading disk */ + + if ((fd = open(disk_device, type_open)) < 0) { + if ((fd = open(disk_device, O_RDONLY)) < 0) { + if (what == try_only) + return 1; + fdisk_fatal(unable_to_open); + } else + printf(_("You will not be able to write " + "the partition table.\n")); + } + + if (512 != read(fd, MBRbuffer, 512)) { + if (what == try_only) + return 1; + fdisk_fatal(unable_to_read); + } +#else + if ((fd = open(disk_device, O_RDONLY)) < 0) + return 1; + if (512 != read(fd, MBRbuffer, 512)) + return 1; +#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 + got_dos_table: +#endif + + if (!valid_part_table_flag(MBRbuffer)) { +#ifndef CONFIG_FEATURE_FDISK_WRITABLE + return -1; +#else + switch (what) { + case fdisk: + printf(_("Device contains neither a valid DOS " + "partition table, nor Sun, SGI or OSF " + "disklabel\n")); +#ifdef __sparc__ +#if ENABLE_FEATURE_SUN_LABEL + create_sunlabel(); +#endif +#else + create_doslabel(); +#endif + return 0; + case try_only: + return -1; + case create_empty_dos: +#if ENABLE_FEATURE_SUN_LABEL + case create_empty_sun: +#endif + break; + default: + bb_error_msg_and_die(_("internal error")); + } +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + } + +#if ENABLE_FEATURE_FDISK_WRITABLE + warn_cylinders(); +#endif + warn_geometry(); + + for (i = 0; i < 4; i++) { + struct pte *pe = &ptes[i]; + + if (IS_EXTENDED(pe->part_table->sys_ind)) { + if (partitions != 4) + printf(_("Ignoring extra extended " + "partition %d\n"), i + 1); + else + read_extended(i); + } + } + + for (i = 3; i < 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); +#if ENABLE_FEATURE_FDISK_WRITABLE + pe->changed = 1; +#endif + } + } + + 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, 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 *= heads * 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) { + unsigned long long bytes; + unsigned long unit; + + bytes = (unsigned long long) 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) + printf(_("Using default value %u\n"), i = dflt); + if (i >= low && i <= high) + break; + else + 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 = ~dos_compatible_flag; + if (dos_compatible_flag) { + sector_offset = 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) { + 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 */ + --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 (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 (partitions > 5) { + partitions--; + while (i < 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, partitions); + } else { + i = get_partition(0, 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. You can delete\n" + "a partition using the 'd' command.\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" + "Delete it first.\n")); + break; + } + } + + if (sys < 256) { + 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")); + 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")); + } + 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 /* CONFIG_FEATURE_FDISK_WRITABLE */ + + +/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993, + * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross, + * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S. + * Lubkin Oct. 1991). */ + +static void +long2chs(ulong ls, unsigned *c, unsigned *h, unsigned *s) +{ + int spc = heads * sectors; + + *c = ls / spc; + ls = ls % spc; + *h = ls / sectors; + *s = ls % 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 (!heads || !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) */ + long2chs(get_start_sect(p), &lbc, &lbh, &lbs); + +/* compute logical ending (c, h, s) */ + long2chs(get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les); + +/* Same physical / logical beginning? */ + if (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 (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 != (heads - 1) || pes != 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"), + heads, sectors, 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; + off_t last_p_start_pos = 0, p_start_pos; + int i, last_i = 0; + + for (i = 0 ; i < partitions; i++) { + if (i == 4) { + last_i = 4; + last_p_start_pos = 0; + } + pe = &ptes[i]; + if ((p = pe->part_table)->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 < 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 < 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 < 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(_("Nothing to do. Ordering is correct already.\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 < partitions; i++) { + const struct pte *pe = &ptes[i]; + off_t psects; + off_t pblocks; + unsigned podd; + + p = pe->part_table; + if (!p || is_cleared_partition(p)) + continue; + + psects = get_nr_sects(p); + pblocks = psects; + podd = 0; + + if (sector_size < 1024) { + pblocks /= (1024 / sector_size); + podd = psects % (1024 / sector_size); + } + if (sector_size > 1024) + pblocks *= (sector_size / 1024); + + printf("%s %c %11llu %11llu %11llu%c %2x %s\n", + partname(disk_device, i+1, w+2), + !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG /* boot flag */ + ? '*' : '?', + (unsigned long long) cround(get_partition_start(pe)), /* start */ + (unsigned long long) cround(get_partition_start(pe) + psects /* end */ + - (psects ? 1 : 0)), + (unsigned long long) 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, heads, sectors, cylinders); + printf(_("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n")); + for (i = 0 ; i < 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(off_t *first, off_t *last) +{ + int i; + const struct pte *pe = &ptes[0]; + const struct partition *p; + + for (i = 0; i < 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, off_t start) +{ + off_t total, real_s, real_c; + + real_s = sector(s) - 1; + real_c = cylinder(s, c); + total = (real_c * sectors + real_s) * heads + h; + if (!total) + printf(_("Warning: partition %d contains sector 0\n"), n); + if (h >= heads) + printf(_("Partition %d: head %d greater than maximum %d\n"), + n, h + 1, heads); + if (real_s >= sectors) + printf(_("Partition %d: sector %d greater than " + "maximum %d\n"), n, s, sectors); + if (real_c >= cylinders) + printf(_("Partitions %d: cylinder %llu greater than " + "maximum %d\n"), n, (unsigned long long)real_c + 1, cylinders); + if (cylinders <= 1024 && start != total) + printf(_("Partition %d: previous sectors %llu disagrees with " + "total %llu\n"), n, (unsigned long long)start, (unsigned long long)total); +} + +static void +verify(void) +{ + int i, j; + unsigned total = 1; + off_t first[partitions], last[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 < 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]; + off_t e_last = get_start_sect(pex->part_table) + + get_nr_sects(pex->part_table) - 1; + + for (i = 4; i < partitions; i++) { + total++; + p = ptes[i].part_table; + if (!p->sys_ind) { + if (i != 4 || i + 1 < 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 > heads * sectors * cylinders) + printf(_("Total allocated sectors %d greater than the maximum " + "%d\n"), total, heads * sectors * cylinders); + else if ((total = heads * sectors * cylinders - 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; + long long llimit; + off_t start, stop = 0, limit, temp, + first[partitions], last[partitions]; + + if (p && p->sys_ind) { + printf(_("Partition %d is already defined. Delete " + "it before re-adding it.\n"), n + 1); + return; + } + fill_bounds(first, last); + if (n < 4) { + start = sector_offset; + if (display_in_cyl_units || !total_number_of_sectors) + llimit = heads * sectors * cylinders - 1; + else + llimit = total_number_of_sectors - 1; + limit = llimit; + if (limit != llimit) + limit = 0x7fffffff; + 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 < 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 < 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 %"OFF_FMT"d is already allocated\n"), temp); + temp = start; + num_read = 0; + } + if (!num_read && start == temp) { + off_t saved_start; + + saved_start = start; + start = read_int(cround(saved_start), cround(saved_start), cround(limit), + 0, mesg); + if (display_in_cyl_units) { + start = (start - 1) * units_per_sector; + if (start < saved_start) start = saved_start; + } + num_read = 1; + } + } while (start != temp || !num_read); + if (n > 4) { /* NOT for fifth partition */ + struct pte *pe = &ptes[n]; + + pe->offset = start - sector_offset; + if (pe->offset == extended_offset) { /* must be corrected */ + pe->offset++; + if (sector_offset == 1) + start++; + } + } + + for (i = 0; i < 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) + 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; + partitions = 5; + } +} + +static void +add_logical(void) +{ + if (partitions > 5 || ptes[4].part_table->sys_ind) { + struct pte *pe = &ptes[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; + partitions++; + } + add_partition(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, partitions), LINUX_NATIVE); + return; + } + if (LABEL_IS_SGI) { + sgi_add_partition(get_partition(0, partitions), LINUX_NATIVE); + return; + } + if (LABEL_IS_AIX) { + printf(_("\tSorry - this fdisk cannot handle AIX disk labels." + "\n\tIf you want to add DOS-type partitions, create" + "\n\ta new empty DOS partition table first. (Use o.)" + "\n\tWARNING: " + "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 && 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[LINE_LENGTH]; + snprintf(line, sizeof(line), "%s\n %s\n p primary " + "partition (1-4)\n", + "Command action", (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; + } + else if (c == 'l' && extended_offset) { + add_logical(); + return; + } + else if (c == 'e' && !extended_offset) { + i = get_nonexisting_partition(0, 4); + if (i >= 0) + add_partition(i, EXTENDED); + return; + } + else + 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 < 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 error = 0; + int i; + + printf(_("Calling ioctl() to re-read partition table.\n")); + sync(); + sleep(2); + if ((i = ioctl(fd, BLKRRPART)) != 0) { + error = errno; + } else { + /* some kernel versions (1.2.x) seem to have trouble + rereading the partition table, but if asked to do it + twice, the second time works. - biro@yggdrasil.com */ + sync(); + sleep(2); + if ((i = ioctl(fd, BLKRRPART)) != 0) + error = errno; + } + + if (i) { + printf(_("\nWARNING: Re-reading the partition table " + "failed with error %d: %s.\n" + "The kernel still uses the old table.\n" + "The new table will be used " + "at the next reboot.\n"), + error, strerror(error)); + } + + 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")); + + if (leave) { + close(fd); + + printf(_("Syncing disks.\n")); + sync(); + sleep(4); /* for sync() */ + exit(!!i); + } +} +#endif /* CONFIG_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) { + puts(""); + l = -1; + } + } + if (l > 0) + puts(""); + puts(""); +} + + +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 < partitions; i++) + print_buffer(ptes[i].sectorbuffer); + } +} + +static void +move_begin(int i) +{ + struct pte *pe = &ptes[i]; + struct partition *p = pe->part_table; + off_t 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) { + 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, partitions)); + break; + case 'c': + user_cylinders = cylinders = + read_int(1, cylinders, 1048576, 0, + _("Number of cylinders")); + if (LABEL_IS_SUN) + sun_set_ncyl(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 = heads = read_int(1, 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': + close(fd); + puts(""); + exit(0); + case 'r': + return; + case 's': + user_sectors = sectors = read_int(1, sectors, 63, 0, + _("Number of sectors")); + if (dos_compatible_flag) { + sector_offset = 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(buf, "r"); + 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 +try(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; + if ((fd = open(disk_device, type_open)) >= 0) { + gb = get_boot(try_only); + if (gb > 0) { /* I/O error */ + close(fd); + } else if (gb < 0) { /* no DOS signature */ + list_disk_geometry(); + if (LABEL_IS_AIX) { + return; + } +#if ENABLE_FEATURE_OSF_LABEL + if (btrydev(device) < 0) +#endif + printf(_("Disk %s doesn't contain a valid " + "partition table\n"), device); + close(fd); + } else { + close(fd); + list_table(0); +#if ENABLE_FEATURE_FDISK_WRITABLE + if (!LABEL_IS_SUN && partitions > 4){ + delete_partition(ext_index); + } +#endif + } + } else { + /* Ignore other errors, since we try IDE + and SCSI hard disks which may not be + installed on the system. */ + if (errno == EACCES) { + printf(_("Cannot open %s\n"), device); + return; + } + } +} + +/* for fdisk -l: try all things in /proc/partitions + that look like a partition name (do not end in a digit) */ +static void +tryprocpt(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++); + if (isdigit(s[-1])) + continue; + sprintf(devname, "/dev/%s", ptname); + try(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) +{ + char *str_b, *str_C, *str_H, *str_S; + unsigned opt; + int c; + /* + * fdisk -v + * fdisk -l [-b sectorsize] [-u] device ... + * fdisk -s [partition] ... + * fdisk [-b sectorsize] [-u] device + * + * Options -C, -H, -S set the geometry. + */ + 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, + }; + opt = getopt32(argc, argv, "b:C:H:lS:u" USE_FEATURE_FDISK_BLKSIZE("s"), + &str_b, &str_C, &str_H, &str_S); + 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. + */ + sector_size = xatoi_u(str_b); + if (sector_size != 512 && sector_size != 1024 && + sector_size != 2048) + bb_show_usage(); + sector_offset = 2; + user_set_sector_size = 1; + } + if (opt & OPT_C) user_cylinders = xatoi_u(str_C); // -C + if (opt & OPT_H) { // -H + user_heads = xatoi_u(str_H); + if (user_heads <= 0 || user_heads >= 256) + user_heads = 0; + } + //if (opt & OPT_l) // -l + if (opt & OPT_S) { // -S + user_sectors = xatoi_u(str_S); + if (user_sectors <= 0 || user_sectors >= 64) + user_sectors = 0; + } + if (opt & OPT_u) display_in_cyl_units = 0; // -u + //if (opt & OPT_s) // -s + + if (user_set_sector_size && argc != 1) + printf(_("Warning: the -b (set sector size) option should" + " be used with one specified device\n")); + +#if ENABLE_FEATURE_FDISK_WRITABLE + if (opt & OPT_l) { + nowarn = 1; +#endif + type_open = O_RDONLY; + if (argc > 0) { + int k; +#if __GNUC__ + /* avoid gcc warning: + variable `k' might be clobbered by `longjmp' */ + (void)&k; +#endif + listing = 1; + for (k = 0; k < argc; k++) + try(argv[k], 1); + } else { + /* we no longer have default device names */ + /* but, we can use /proc/partitions instead */ + tryprocpt(); + } + return 0; +#if ENABLE_FEATURE_FDISK_WRITABLE + } +#endif + +#if ENABLE_FEATURE_FDISK_BLKSIZE + if (opt & OPT_s) { + long size; + int j; + + nowarn = 1; + type_open = O_RDONLY; + + if (argc <= 0) + bb_show_usage(); + + for (j = 0; j < argc; j++) { + disk_device = argv[j]; + fd = open(disk_device, type_open); + if (fd < 0) + fdisk_fatal(unable_to_open); + if (ioctl(fd, BLKGETSIZE, &size)) + fdisk_fatal(ioctl_error); + close(fd); + if (argc == 1) + printf("%ld\n", size/2); + else + printf("%s: %ld\n", argv[j], size/2); + } + return 0; + } +#endif + +#if ENABLE_FEATURE_FDISK_WRITABLE + if (argc != 1) + bb_show_usage(); + + disk_device = argv[0]; + get_boot(fdisk); + + 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) { + putchar('\n'); + c = tolower(read_nonempty(_("Command (m for help): "))); + switch (c) { + case 'a': + if (LABEL_IS_DOS) + toggle_active(get_partition(1, partitions)); + else if (LABEL_IS_SUN) + toggle_sunflags(get_partition(1, partitions), + 0x01); + else if (LABEL_IS_SGI) + sgi_set_bootpartition( + get_partition(1, 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, partitions), + 0x10); + else if (LABEL_IS_SGI) + sgi_set_swappartition( + get_partition(1, 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, partitions); + } else { + j = get_partition(1, 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': + close(fd); + puts(""); + 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 /* CONFIG_FEATURE_FDISK_WRITABLE */ +} diff --git a/util-linux/fdisk_aix.c b/util-linux/fdisk_aix.c new file mode 100644 index 000000000..a3d5fe15f --- /dev/null +++ b/util-linux/fdisk_aix.c @@ -0,0 +1,76 @@ +#ifdef CONFIG_FEATURE_AIX_LABEL +/* + * Copyright (C) Andreas Neuper, Sep 1998. + * This file may be redistributed under + * the terms of the GNU Public License. + */ + +typedef struct { + unsigned int magic; /* expect AIX_LABEL_MAGIC */ + unsigned int fillbytes1[124]; + unsigned int physical_volume_id; + unsigned int fillbytes2[124]; +} aix_partition; + +#define AIX_LABEL_MAGIC 0xc9c2d4c1 +#define AIX_LABEL_MAGIC_SWAPPED 0xc1d4c2c9 +#define AIX_INFO_MAGIC 0x00072959 +#define AIX_INFO_MAGIC_SWAPPED 0x59290700 + +#define aixlabel ((aix_partition *)MBRbuffer) + + +/* + Changes: + * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br> + * Internationalization + * + * 2003-03-20 Phillip Kesling <pkesling@sgi.com> + * Some fixes +*/ + +static int aix_other_endian; +static short aix_volumes = 1; + +/* + * only dealing with free blocks here + */ + +static void +aix_info(void) +{ + puts( + _("\n\tThere is a valid AIX label on this disk.\n" + "\tUnfortunately Linux cannot handle these\n" + "\tdisks at the moment. Nevertheless some\n" + "\tadvice:\n" + "\t1. fdisk will destroy its contents on write.\n" + "\t2. Be sure that this disk is NOT a still vital\n" + "\t part of a volume group. (Otherwise you may\n" + "\t erase the other disks as well, if unmirrored.)\n" + "\t3. Before deleting this physical volume be sure\n" + "\t to remove the disk logically from your AIX\n" + "\t machine. (Otherwise you become an AIXpert).") + ); +} + +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; + 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 000000000..bff2371e4 --- /dev/null +++ b/util-linux/fdisk_osf.c @@ -0,0 +1,1046 @@ +#ifdef CONFIG_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 +}; +#define BSD_DKMAXTYPES (sizeof(xbsd_dktypenames) / sizeof(xbsd_dktypenames[0]) - 1) + +/* + * 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 struct systypes 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 } +}; +#define BSD_FSMAXTYPES (SIZE(xbsd_fstypes)-1) + + +/* + * flags shared by various drives: + */ +#define BSD_D_REMOVABLE 0x01 /* removable media */ +#define BSD_D_ECC 0x02 /* supports ECC */ +#define BSD_D_BADSECT 0x04 /* supports bad sector forw. */ +#define BSD_D_RAMDISK 0x08 /* disk emulator */ +#define BSD_D_CHAIN 0x10 /* can do back-back transfers */ +#define BSD_D_DOSPART 0x20 /* within MSDOS partition */ + +/* + Changes: + 19990319 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - i18n/nls + + 20000101 - David Huggins-Daines <dhuggins@linuxcare.com> - Better + support for OSF/1 disklabels on Alpha. + Also fixed unaligned accesses in alpha_bootblock_checksum() +*/ + +static int possibly_osf_label; + +#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 u_short xbsd_dkcksum(struct xbsd_disklabel *lp); +static int xbsd_initlabel(struct partition *p, struct xbsd_disklabel *d); +static int xbsd_readlabel(struct partition *p, struct xbsd_disklabel *d); +static int xbsd_writelabel(struct partition *p, struct xbsd_disklabel *d); + +#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 + +#if defined (__alpha__) +/* We access this through a uint64_t * when checksumming */ +static char disklabelbuffer[BSD_BBSIZE] ATTRIBUTE_ALIGNED(8); +#else +static char disklabelbuffer[BSD_BBSIZE]; +#endif + +static struct xbsd_disklabel xbsd_dlabel; + +#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, &xbsd_dlabel) == 0) + return 0; + return 1; +} + +static int +btrydev(const char * dev) +{ + if (xbsd_readlabel (NULL, &xbsd_dlabel) == 0) + return -1; + printf(_("\nBSD label for device: %s\n"), dev); + xbsd_print_disklabel (0); + return 0; +} + +static void +bmenu(void) +{ + puts (_("Command action")); + puts (_("\td\tdelete a BSD partition")); + puts (_("\te\tedit drive data")); + puts (_("\ti\tinstall bootstrap")); + puts (_("\tl\tlist known filesystem types")); + puts (_("\tm\tprint this menu")); + puts (_("\tn\tadd a new BSD partition")); + puts (_("\tp\tprint BSD partition table")); + puts (_("\tq\tquit without saving changes")); + puts (_("\tr\treturn to main menu")); + puts (_("\ts\tshow complete disklabel")); + puts (_("\tt\tchange a partition's filesystem id")); + puts (_("\tu\tchange units (cylinders/sectors)")); + puts (_("\tw\twrite disklabel to disk")); +#if !defined (__alpha__) + puts (_("\tx\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) { + fprintf(stderr, _("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, &xbsd_dlabel) == 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, &xbsd_dlabel) == 0) + if (xbsd_create_disklabel() == 0) + exit (EXIT_SUCCESS); + +#endif + + while (1) { + 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': + close(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: + bmenu(); + 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) { +#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 < BSD_DKMAXTYPES) + 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:")); + if (lp->d_flags & BSD_D_REMOVABLE) + printf(_(" removable")); + if (lp->d_flags & BSD_D_ECC) + printf(_(" ecc")); + if (lp->d_flags & BSD_D_BADSECT) + printf(_(" badsect")); + puts(""); + /* 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 < BSD_FSMAXTYPES) + printf("%8.8s", xbsd_fstypes[pp->p_fstype].name); + 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; + } + puts(""); + } + } +} + +static void +xbsd_write_disklabel(void) +{ +#if defined (__alpha__) + printf(_("Writing disklabel to %s.\n"), disk_device); + xbsd_writelabel(NULL, &xbsd_dlabel); +#else + printf(_("Writing disklabel to %s.\n"), + partname(disk_device, xbsd_part_index + 1, 0)); + xbsd_writelabel(xbsd_part, &xbsd_dlabel); +#endif + reread_partition_table(0); /* no exit yet */ +} + +static int +xbsd_create_disklabel(void) +{ + char c; + +#if defined (__alpha__) + fprintf(stderr, _("%s contains no disklabel.\n"), disk_device); +#else + fprintf(stderr, _("%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, &xbsd_dlabel +#else + xbsd_part, &xbsd_dlabel/* not used, xbsd_part_index*/ +#endif + ) == 1) { + xbsd_print_disklabel (1); + return 1; + } else + return 0; + } else if (c == 'n') + return 0; + } +} + +static int +edit_int(int def, char *mesg) +{ + do { + fputs(mesg, stdout); + printf(" (%d): ", def); + if (!read_line()) + return def; + } while (!isdigit(*line_ptr)); + return atoi(line_ptr); +} + +static void +xbsd_edit_disklabel(void) +{ + struct xbsd_disklabel *d; + + d = &xbsd_dlabel; + +#if defined (__alpha__) || defined (__ia64__) + d->d_secsize = (u_long) edit_int((u_long) d->d_secsize ,_("bytes/sector")); + d->d_nsectors = (u_long) edit_int((u_long) d->d_nsectors ,_("sectors/track")); + d->d_ntracks = (u_long) edit_int((u_long) d->d_ntracks ,_("tracks/cylinder")); + d->d_ncylinders = (u_long) edit_int((u_long) d->d_ncylinders ,_("cylinders")); +#endif + + /* d->d_secpercyl can be != d->d_nsectors * d->d_ntracks */ + while (1) { + d->d_secpercyl = (u_long) edit_int((u_long) 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 = (u_short) edit_int((u_short) d->d_rpm ,_("rpm")); + d->d_interleave = (u_short) edit_int((u_short) d->d_interleave,_("interleave")); + d->d_trackskew = (u_short) edit_int((u_short) d->d_trackskew ,_("trackskew")); + d->d_cylskew = (u_short) edit_int((u_short) d->d_cylskew ,_("cylinderskew")); + d->d_headswitch = (u_long) edit_int((u_long) d->d_headswitch ,_("headswitch")); + d->d_trkseek = (u_long) edit_int((u_long) 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; + + if ((fdb = open (path, O_RDONLY)) < 0) { + perror(path); + return 0; + } + if (read(fdb, ptr, size) < 0) { + perror(path); + close(fdb); + return 0; + } + printf(" ... %s\n", path); + close(fdb); + return 1; +} + +static void +sync_disks(void) +{ + printf(_("\nSyncing disks.\n")); + sync(); + sleep(4); /* What? */ +} + +static void +xbsd_write_bootstrap(void) +{ + char *bootdir = BSD_LINUX_BOOTDIR; + char path[MAXPATHLEN]; + 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"; + + printf(_("Bootstrap: %sboot -> boot%s (%s): "), + dkbasename, dkbasename, dkbasename); + if (read_line()) { + line_ptr[strlen(line_ptr)-1] = '\0'; + 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) { + fprintf(stderr, _("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 + + if (lseek(fd, sector * SECTOR_SIZE, SEEK_SET) == -1) + fdisk_fatal(unable_to_seek); + if (BSD_BBSIZE != write(fd, disklabelbuffer, BSD_BBSIZE)) + fdisk_fatal(unable_to_write); + +#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[256]; + 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) { + fprintf(stderr, _("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) { + fprintf(stderr, _("This partition already exists.\n")); + return 0; + } + + return 1; +} + +static void +xbsd_list_types(void) +{ + list_types(xbsd_fstypes); +} + +static u_short +xbsd_dkcksum(struct xbsd_disklabel *lp) +{ + u_short *start, *end; + u_short sum = 0; + + start = (u_short *) lp; + end = (u_short *) &lp->d_partitions[lp->d_npartitions]; + while (start < end) + sum ^= *start++; + return sum; +} + +static int +xbsd_initlabel(struct partition *p, struct xbsd_disklabel *d) +{ + 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 = sectors; /* sectors/track */ + d->d_ntracks = heads; /* tracks/cylinder (heads) */ + d->d_ncylinders = cylinders; + d->d_secpercyl = sectors * 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 + the 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 + the whole disk */ + pp->p_offset = 0; + pp->p_size = d->d_secperunit; + pp->p_fstype = BSD_FS_UNUSED; +#elif defined (__alpha__) + 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; + + /* p is used only to get the starting sector */ +#if !defined (__alpha__) + sector = (p ? get_start_sect(p) : 0); +#elif defined (__alpha__) + sector = 0; +#endif + + if (lseek(fd, sector * SECTOR_SIZE, SEEK_SET) == -1) + fdisk_fatal(unable_to_seek); + if (BSD_BBSIZE != read(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) + fprintf(stderr, _("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) +{ + 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); + if (lseek(fd, 0, SEEK_SET) == -1) + fdisk_fatal(unable_to_seek); + if (BSD_BBSIZE != write(fd, disklabelbuffer, BSD_BBSIZE)) + fdisk_fatal(unable_to_write); +#else + if (lseek(fd, sector * SECTOR_SIZE + BSD_LABELOFFSET, SEEK_SET) == -1) + fdisk_fatal(unable_to_seek); + if (sizeof(struct xbsd_disklabel) != write(fd, d, sizeof(struct xbsd_disklabel))) + fdisk_fatal(unable_to_write); +#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, 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__) + +#if !defined(__GLIBC__) +typedef unsigned long long uint64_t; +#endif + +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__ */ + +#endif /* OSF_LABEL */ diff --git a/util-linux/fdisk_sgi.c b/util-linux/fdisk_sgi.c new file mode 100644 index 000000000..548a70bdc --- /dev/null +++ b/util-linux/fdisk_sgi.c @@ -0,0 +1,885 @@ +#ifdef CONFIG_FEATURE_SGI_LABEL + +/* + * Copyright (C) Andreas Neuper, Sep 1998. + * This file may be modified and redistributed under + * the terms of the GNU Public License. + */ + +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 ? __swap16(x) : (uint16_t)(x)) +#define SGI_SSWAP32(x) (sgi_other_endian ? __swap32(x) : (uint32_t)(x)) + +#define sgilabel ((sgi_partition *)MBRbuffer) +#define sgiparam (sgilabel->devparam) + +/* + * + * fdisksgilabel.c + * + * Copyright (C) Andreas Neuper, Sep 1998. + * This file may be modified and redistributed under + * the terms of the GNU Public License. + * + * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br> + * Internationalization + */ + + +static int sgi_other_endian; +static int debug; +static short sgi_volumes = 1; + +/* + * 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 struct systypes 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; +} + +static int +check_sgi_label(void) +{ + if (sizeof(sgilabel) > 512) { + fprintf(stderr, + _("According to MIPS Computer Systems, Inc the " + "Label must not contain more than 512 bytes\n")); + exit(1); + } + + 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))) { + fprintf(stderr, + _("Detected sgi disklabel with wrong checksum.\n")); + } + update_units(); + current_label_type = label_sgi; + 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, heads, sectors, 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, heads, sectors, cylinders, + str_units(PLURAL), units_per_sector ); + } + + w = strlen(disk_device); + wd = strlen(_("Device")); + if (w < wd) + w = wd; + + printf(_("----- partitions -----\n" + "Pt# %*s Info Start End Sectors Id System\n"), + w + 2, _("Device")); + for (i = 0 ; i < partitions; i++) { + if( sgi_get_num_sectors(i) || 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 heads * sectors * 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; + } else { + if (strlen(aFile) > 16) { + printf(_("\n\tName of Bootfile too long: " + "16 bytes maximum.\n")); + return 0; + } else { + if (aFile[0] != '/') { + printf(_("\n\tBootfile must have a " + "fully qualified pathname.\n")); + return 0; + } + } + } + if (strncmp(aFile, (char*)sgilabel->boot_file, 16)) { + printf(_("\n\tBe aware, that the bootfile is not checked for existence.\n\t" + "SGI'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); + + if (lseek(fd, 0, SEEK_SET) < 0) + fdisk_fatal(unable_to_seek); + if (write(fd, sgilabel, SECTOR_SIZE) != SECTOR_SIZE) + fdisk_fatal(unable_to_write); + 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); + if (lseek(fd, infostartblock*SECTOR_SIZE, SEEK_SET) < 0) + fdisk_fatal(unable_to_seek); + if (write(fd, info, SECTOR_SIZE) != SECTOR_SIZE) + fdisk_fatal(unable_to_write); + 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 (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 (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 (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 (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(_("The Partition %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-%u\n"), + sgi_get_start_sector(Index[i]) - start, + start, sgi_get_start_sector(Index[i])-1); + gap += sgi_get_start_sector(Index[i]) - start; + add2freelist(start, sgi_get_start_sector(Index[i])); + } + start = sgi_get_start_sector(Index[i]) + + sgi_get_num_sectors(Index[i]); + if (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-%u\n"), + lastblock - start, start, lastblock-1); + gap += lastblock - start; + add2freelist(start, lastblock); + } + /* + * Done with arithmetics + * Go for details now + */ + if (verbose) { + if (!sgi_get_num_sectors(sgi_get_bootpartition())) { + printf(_("\nThe boot partition does not exist.\n")); + } + if (!sgi_get_num_sectors(sgi_get_swappartition())) { + printf(_("\nThe swap partition does not exist.\n")); + } else { + if ((sgi_get_sysid(sgi_get_swappartition()) != SGI_SWAP) + && (sgi_get_sysid(sgi_get_swappartition()) != LINUX_SWAP)) + printf(_("\nThe swap partition has no swap type.\n")); + } + if (sgi_check_bootfile("/unix")) + printf(_("\tYou have chosen an unusual boot file name.\n")); + } + return (gap > 0) ? 1 : (gap == 0) ? 0 : -1; +} + +static int +sgi_gaps(void) +{ + /* + * returned value is: + * = 0 : disk is properly filled to the rim + * < 0 : there is an overlap + * > 0 : there is still some vacant space + */ + return verify_sgi(0); +} + +static void +sgi_change_sysid(int i, int sys) +{ + if( sgi_get_num_sectors(i) == 0 ) { /* caught already before, ... */ + printf(_("Sorry You may change the Tag of non-empty partitions.\n")); + return; + } + if (((sys != SGI_ENTIRE_DISK) && (sys != SGI_VOLHDR)) + && (sgi_get_start_sector(i) < 1) ) { + read_maybe_empty( + _("It is highly recommended that the partition at offset 0\n" + "is of type \"SGI volhdr\", the IRIX system will rely on it to\n" + "retrieve from its directory standalone tools like sash and fx.\n" + "Only the \"SGI volume\" entire disk section may violate this.\n" + "Type YES if you are sure about tagging this partition differently.\n")); + if (strcmp(line_ptr, _("YES\n"))) + 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(_("Do You know, You got a partition overlap on the disk?\n")); +} + +static void +sgi_set_entire(void) +{ + int n; + + for (n = 10; n < 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 < 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 (heads * sectors * 5 < sgi_get_lastblock()) + sgi_set_partition(n, 0, heads * 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(_("Partition %d is already defined. Delete " + "it before re-adding it.\n"), 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) { + printf(_("You will get a partition overlap on the disk. " + "Fix it first!\n")); + } else + break; + } + 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); +} + +#ifdef CONFIG_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 */ + + fprintf( stderr, + _("Building a new SGI disklabel. Changes will remain in memory only,\n" + "until you decide to write them. After that, of course, the previous\n" + "content will be unrecoverably lost.\n\n")); + + sgi_other_endian = (BYTE_ORDER == LITTLE_ENDIAN); + res = ioctl(fd, BLKGETSIZE, &longsectors); + if (!ioctl(fd, HDIO_GETGEO, &geometry)) { + heads = geometry.heads; + sectors = geometry.sectors; + if (res == 0) { + /* the get device size ioctl was successful */ + cylinders = longsectors / (heads * sectors); + cylinders /= sec_fac; + } else { + /* otherwise print error and use truncated version */ + cylinders = geometry.cylinders; + fprintf(stderr, + _("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, 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 (debug) + printf(_("ID=%02x\tSTART=%d\tLENGTH=%d\n"), + old[i].sysid, old[i].start, old[i].nsect); + } + } + } + + memset(MBRbuffer, 0, sizeof(MBRbuffer)); + sgilabel->magic = SGI_SSWAP32(SGI_LABEL_MAGIC); + sgilabel->boot_part = SGI_SSWAP16(0); + sgilabel->swap_part = SGI_SSWAP16(1); + + /* sizeof(sgilabel->boot_file) = 16 > 6 */ + memset(sgilabel->boot_file, 0, 16); + strcpy((char*)sgilabel->boot_file, "/unix"); + + sgilabel->devparam.skew = (0); + sgilabel->devparam.gap1 = (0); + sgilabel->devparam.gap2 = (0); + sgilabel->devparam.sparecyl = (0); + sgilabel->devparam.pcylcount = SGI_SSWAP16(geometry.cylinders); + sgilabel->devparam.head_vol0 = SGI_SSWAP16(0); + sgilabel->devparam.ntrks = SGI_SSWAP16(geometry.heads); + /* tracks/cylinder (heads) */ + sgilabel->devparam.cmd_tag_queue_depth = (0); + sgilabel->devparam.unused0 = (0); + sgilabel->devparam.unused1 = SGI_SSWAP16(0); + sgilabel->devparam.nsect = SGI_SSWAP16(geometry.sectors); + /* sectors/track */ + 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; + 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 /* CONFIG_FEATURE_FDISK_ADVANCED */ + +/* _____________________________________________________________ + */ + +static sgiinfo * +fill_sgiinfo(void) +{ + sgiinfo *info = calloc(1, 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 000000000..1e8f2e525 --- /dev/null +++ b/util-linux/fdisk_sun.c @@ -0,0 +1,729 @@ +#ifdef CONFIG_FEATURE_SUN_LABEL + +#define SUN_LABEL_MAGIC 0xDABE +#define SUN_LABEL_MAGIC_SWAPPED 0xBEDA +#define SUN_SSWAP16(x) (sun_other_endian ? __swap16(x) : (uint16_t)(x)) +#define SUN_SSWAP32(x) (sun_other_endian ? __swap32(x) : (uint32_t)(x)) + +/* Copied from linux/major.h */ +#define FLOPPY_MAJOR 2 + +#define SCSI_IOCTL_GET_IDLUN 0x5382 + +/* + * fdisksunlabel.c + * + * I think this is mostly, or entirely, due to + * Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996 + * + * Merged with fdisk for other architectures, aeb, June 1998. + * + * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br> + * Internationalization + */ + + +static int sun_other_endian; +static int scsi_disk; +static int floppy; + +#ifndef IDE0_MAJOR +#define IDE0_MAJOR 3 +#endif +#ifndef IDE1_MAJOR +#define IDE1_MAJOR 22 +#endif + +static void +guess_device_type(void) +{ + struct stat bootstat; + + if (fstat(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 struct systypes 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 / (heads * 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) { + fprintf(stderr,_("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 { + heads = SUN_SSWAP16(sunlabel->ntrks); + cylinders = SUN_SSWAP16(sunlabel->ncyl); + sectors = SUN_SSWAP16(sunlabel->nsect); + } + update_units(); + current_label_type = label_sun; + 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(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("/proc/scsi/scsi", "r"); + 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 < 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 int ndiv; + int i; + unsigned char c; + const struct sun_predefined_drives *p = NULL; + + fprintf(stderr, + _("Building a new sun disklabel. Changes will remain in memory only,\n" + "until you decide to write them. After that, of course, the previous\n" + "content won't be recoverable.\n\n")); + sun_other_endian = BB_LITTLE_ENDIAN; + memset(MBRbuffer, 0, sizeof(MBRbuffer)); + sunlabel->magic = SUN_SSWAP16(SUN_LABEL_MAGIC); + if (!floppy) { + puts(_("Drive type\n" + " ? auto configure\n" + " 0 custom (with hardware detected defaults)")); + for (i = 0; i < 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 >= 'a' && c < 'a' + SIZE(sun_drives)) { + p = sun_drives + c - 'a'; + break; + } else if (c >= 'A' && c < 'A' + SIZE(sun_drives)) { + p = sun_drives + c - 'A'; + break; + } else if (c == '0') { + break; + } else if (c == '?' && scsi_disk) { + p = sun_autoconfigure_scsi(); + if (!p) + printf(_("Autoconfigure failed.\n")); + else + break; + } + } + } + if (!p || floppy) { + if (!ioctl(fd, HDIO_GETGEO, &geometry)) { + heads = geometry.heads; + sectors = geometry.sectors; + cylinders = geometry.cylinders; + } else { + heads = 0; + sectors = 0; + cylinders = 0; + } + if (floppy) { + sunlabel->nacyl = 0; + sunlabel->pcylcount = SUN_SSWAP16(cylinders); + sunlabel->rspeed = SUN_SSWAP16(300); + sunlabel->ilfact = SUN_SSWAP16(1); + sunlabel->sparecyl = 0; + } else { + heads = read_int(1,heads,1024,0,_("Heads")); + sectors = read_int(1,sectors,1024,0,_("Sectors/track")); + if (cylinders) + cylinders = read_int(1,cylinders-2,65535,0,_("Cylinders")); + else + 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,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,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); + cylinders = p->ncyl; + heads = p->ntrks; + 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")), + cylinders, SUN_SSWAP16(sunlabel->nacyl), heads, sectors); + + sunlabel->ntrks = SUN_SSWAP16(heads); + sunlabel->nsect = SUN_SSWAP16(sectors); + sunlabel->ncyl = SUN_SSWAP16(cylinders); + if (floppy) + set_sun_partition(0, 0, cylinders * heads * sectors, LINUX_NATIVE); + else { + if (cylinders * heads * sectors >= 150 * 2048) { + ndiv = cylinders - (50 * 2048 / (heads * sectors)); /* 50M swap */ + } else + ndiv = cylinders * 2 / 3; + set_sun_partition(0, 0, ndiv * heads * sectors, LINUX_NATIVE); + set_sun_partition(1, ndiv * heads * sectors, cylinders * heads * sectors, LINUX_SWAP); + sunlabel->infos[1].flags |= 0x01; /* Not mountable */ + } + set_sun_partition(2, 0, cylinders * heads * 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 = cylinders * heads * sectors; + for (i = 0; i < 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) * heads * 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] % (heads * 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,SIZE(array),sizeof(array[0]), + (int (*)(const void *,const void *)) verify_sun_cmp); + if (array[0] == -1) { + printf(_("No partitions defined\n")); + return; + } + stop = cylinders * heads * 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(_("Partition %d is already defined. Delete " + "it before re-adding it.\n"), 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.\nDelete " + "some/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 + heads * sectors - 1) / (heads * 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 < partitions; i++) + if (lens[i] && starts[i] <= first && starts[i] + lens[i] > first) + break; + if (i < partitions && !whole_disk) { + if (n == 2 && !first) { + whole_disk = 1; + break; + } + printf(_("Sector %d is already allocated\n"), first); + } else + break; + } + stop = cylinders * heads * sectors; + stop2 = stop; + for (i = 0; i < 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, but your value\n" + "%d %s covers some other partition. " + "Your entry has been changed\n" + "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)) == heads * sectors * 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, heads, sectors, SUN_SSWAP16(sunlabel->rspeed), + 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, heads, sectors, cylinders, + str_units(PLURAL), units_per_sector); + + printf(_("%*s Flag Start End Blocks Id System\n"), + w + 1, _("Device")); + for (i = 0 ; i < partitions; i++) { + if (sunlabel->partitions[i].num_sectors) { + uint32_t start = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * 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 */ + } + } +} + +#ifdef CONFIG_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), 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 /* CONFIG_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; + if (lseek(fd, 0, SEEK_SET) < 0) + fdisk_fatal(unable_to_seek); + if (write(fd, sunlabel, SECTOR_SIZE) != SECTOR_SIZE) + fdisk_fatal(unable_to_write); +} +#endif /* SUN_LABEL */ diff --git a/util-linux/freeramdisk.c b/util-linux/freeramdisk.c new file mode 100644 index 000000000..2293d3ee6 --- /dev/null +++ b/util-linux/freeramdisk.c @@ -0,0 +1,34 @@ +/* vi: set sw=4 ts=4: */ +/* + * freeramdisk and fdflush implementations for busybox + * + * Copyright (C) 2000 and written by Emanuele Caratti <wiz@iol.it> + * Adjusted a bit by Erik Andersen <andersen@codepoet.org> + * Unified with fdflush by Tito Ragusa <farmatito@tiscali.it> + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" + +/* From <linux/fd.h> */ +#define FDFLUSH _IO(2,0x4b) + +int freeramdisk_main(int argc, char **argv) +{ + int result; + int fd; + + if (argc != 2) bb_show_usage(); + + fd = xopen(argv[1], O_RDWR); + + // Act like freeramdisk, fdflush, or both depending on configuration. + result = ioctl(fd, (ENABLE_FREERAMDISK && applet_name[1]=='r') + || !ENABLE_FDFLUSH ? BLKFLSBUF : FDFLUSH); + + if (ENABLE_FEATURE_CLEAN_UP) close(fd); + + if (result) bb_perror_msg_and_die("%s", argv[1]); + return EXIT_SUCCESS; +} diff --git a/util-linux/fsck_minix.c b/util-linux/fsck_minix.c new file mode 100644 index 000000000..9c831bd59 --- /dev/null +++ b/util-linux/fsck_minix.c @@ -0,0 +1,1417 @@ +/* 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 + * super-block information + * + * Sat Oct 9 11:17:11 1993, faith@cs.unc.edu: make exit status conform + * to that required by fsutil + * + * Mon Jan 3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu) + * Added support for file system valid flag. Also + * added program_version variable and output of + * program name and version number when program + * is executed. + * + * 30.10.94 - added support for v2 filesystem + * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de) + * + * 10.12.94 - added test to prevent checking of mounted fs adapted + * from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck + * program. (Daniel Quinlan, quinlan@yggdrasil.com) + * + * 01.07.96 - Fixed the v2 fs stuff to use the right #defines and such + * for modern libcs (janl@math.uio.no, Nicolai Langfeldt) + * + * 02.07.96 - Added C bit fiddling routines from rmk@ecs.soton.ac.uk + * (Russell King). He made them for ARM. It would seem + * that the ARM is powerful enough to do this in C whereas + * i386 and m64k must use assembly to get it fast >:-) + * This should make minix fsck system-independent. + * (janl@math.uio.no, Nicolai Langfeldt) + * + * 04.11.96 - Added minor fixes from Andreas Schwab to avoid compiler + * warnings. Added mc68k bitops from + * Joerg Dorchain <dorchain@mpi-sb.mpg.de>. + * + * 06.11.96 - Added v2 code submitted by Joerg Dorchain, but written by + * Andreas Schwab. + * + * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org> + * - added Native Language Support + * + * + * I've had no time to add comments - hopefully the function names + * are comments enough. As with all file system checkers, this assumes + * the file system is quiescent - don't use it on a mounted device + * unless you can be sure nobody is writing to it (and remember that the + * kernel can write to it when it searches for files). + * + * Usage: fsck [-larvsm] device + * -l for a listing of all the filenames + * -a for automatic repairs (not implemented) + * -r for repairs (interactive) (not implemented) + * -v for verbose (tells how many files) + * -s for super-block 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 "busybox.h" +#include <mntent.h> + +/* + * This is the original minix inode layout on disk. + * Note the 8-bit gid and atime and ctime. + */ +struct minix_inode { + uint16_t i_mode; + uint16_t i_uid; + uint32_t i_size; + uint32_t i_time; + uint8_t i_gid; + uint8_t i_nlinks; + uint16_t i_zone[9]; +}; + +/* + * 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]; +}; + +enum { + MINIX_ROOT_INO = 1, + MINIX_LINK_MAX = 250, + MINIX2_LINK_MAX = 65530, + + MINIX_I_MAP_SLOTS = 8, + MINIX_Z_MAP_SLOTS = 64, + MINIX_SUPER_MAGIC = 0x137F, /* original minix fs */ + MINIX_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. */ + + MINIX_INODES_PER_BLOCK = ((BLOCK_SIZE)/(sizeof (struct minix_inode))), + MINIX2_INODES_PER_BLOCK = ((BLOCK_SIZE)/(sizeof (struct minix2_inode))), + + MINIX_V1 = 0x0001, /* original minix fs */ + MINIX_V2 = 0x0002 /* minix V2 fs */ +}; + +#define INODE_VERSION(inode) inode->i_sb->u.minix_sb.s_version + +/* + * minix super-block data on disk + */ +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; +}; + +struct minix_dir_entry { + uint16_t inode; + char name[0]; +}; + + +#define NAME_MAX 255 /* # chars in a file name */ + +#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode))) + +#ifndef BLKGETSIZE +#define BLKGETSIZE _IO(0x12,96) /* return device size */ +#endif + +#ifndef __linux__ +#define volatile +#endif + +enum { ROOT_INO = 1 }; + +#define UPPER(size,n) ((size+((n)-1))/(n)) +#define INODE_SIZE (sizeof(struct minix_inode)) +#ifdef CONFIG_FEATURE_MINIX2 +#define INODE_SIZE2 (sizeof(struct minix2_inode)) +#define INODE_BLOCKS UPPER(INODES, (version2 ? MINIX2_INODES_PER_BLOCK \ + : MINIX_INODES_PER_BLOCK)) +#else +#define INODE_BLOCKS UPPER(INODES, (MINIX_INODES_PER_BLOCK)) +#endif +#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE) + +#define BITS_PER_BLOCK (BLOCK_SIZE<<3) + +static char *program_version = "1.2 - 11/11/96"; +static char *device_name; +static int IN; +static int repair, automatic, verbose, list, show, warn_mode, force; +static int directory, regular, blockdev, chardev, links, symlinks, total; + +static int changed; /* flags if the filesystem has been changed */ +static int errors_uncorrected; /* flag if some error was not corrected */ +static int dirsize = 16; +static int namelen = 14; +static struct termios termios; +static int termios_set; + +static char *inode_buffer; +#define Inode (((struct minix_inode *) inode_buffer)-1) +#define Inode2 (((struct minix2_inode *) inode_buffer)-1) +static char super_block_buffer[BLOCK_SIZE]; + +#define Super (*(struct minix_super_block *)super_block_buffer) +#define INODES ((unsigned long)Super.s_ninodes) +#ifdef CONFIG_FEATURE_MINIX2 +static int version2; +#define ZONES ((unsigned long)(version2 ? Super.s_zones : Super.s_nzones)) +#else +#define ZONES ((unsigned long)(Super.s_nzones)) +#endif +#define IMAPS ((unsigned long)Super.s_imap_blocks) +#define ZMAPS ((unsigned long)Super.s_zmap_blocks) +#define FIRSTZONE ((unsigned long)Super.s_firstdatazone) +#define ZONESIZE ((unsigned long)Super.s_log_zone_size) +#define MAXSIZE ((unsigned long)Super.s_max_size) +#define MAGIC (Super.s_magic) +#define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS) + +static char *inode_map; +static char *zone_map; + +static unsigned char *inode_count; +static unsigned char *zone_count; + +static void recursive_check(unsigned int ino); +#ifdef CONFIG_FEATURE_MINIX2 +static void recursive_check2(unsigned int ino); +#endif + +static int bit(char *a, unsigned int i) +{ + return (a[i >> 3] & (1<<(i & 7))) != 0; +} +#define inode_in_use(x) (bit(inode_map,(x))) +#define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1)) + +#define mark_inode(x) (setbit(inode_map,(x)),changed=1) +#define unmark_inode(x) (clrbit(inode_map,(x)),changed=1) + +#define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1),changed=1) +#define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1),changed=1) + +static void leave(int) ATTRIBUTE_NORETURN; +static void leave(int status) +{ + if (termios_set) + tcsetattr(0, TCSANOW, &termios); + exit(status); +} + +static void die(const char *str) +{ + bb_error_msg("%s", str); + leave(8); +} + +/* File-name data */ +enum { MAX_DEPTH = 32 }; +static int name_depth; +static char *current_name; +static char *name_component[MAX_DEPTH+1]; + +/* Wed Feb 9 15:17:06 MST 2000 */ +/* dynamically allocate name_list (instead of making it static) */ +static void alloc_current_name(void) +{ + current_name = xmalloc(MAX_DEPTH * (BUFSIZ + 1)); + current_name[0] = '/'; + current_name[1] = '\0'; + name_component[0] = ¤t_name[0]; +} + +#ifdef CONFIG_FEATURE_CLEAN_UP +/* execute this atexit() to deallocate name_list[] */ +/* piptigger was here */ +static void free_current_name(void) +{ + free(current_name); +} +#endif + +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 (!repair) { + puts(""); + errors_uncorrected = 1; + return 0; + } + if (automatic) { + puts(""); + if (!def) + errors_uncorrected = 1; + return def; + } + printf(def ? "%s (y/n)? " : "%s (n/y)? ", string); + for (;;) { + fflush(stdout); + if ((c = getchar()) == 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; + + if ((f = setmntent(MOUNTED, "r")) == 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; + else + 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(0); + } + return; +} + +/* + * 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, int *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, int *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 int nr, char *addr) +{ + if (!nr) { + memset(addr, 0, BLOCK_SIZE); + return; + } + if (BLOCK_SIZE * nr != lseek(IN, BLOCK_SIZE * nr, SEEK_SET)) { + printf("%s: cannot seek to block in file '%s'\n", + bb_msg_read_error, current_name); + errors_uncorrected = 1; + memset(addr, 0, BLOCK_SIZE); + } else if (BLOCK_SIZE != read(IN, addr, BLOCK_SIZE)) { + printf("%s: bad block in file '%s'\n", + bb_msg_read_error, current_name); + errors_uncorrected = 1; + memset(addr, 0, BLOCK_SIZE); + } +} + +/* + * write_block writes block nr to disk. + */ +static void write_block(unsigned int nr, char *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; + } + if (BLOCK_SIZE * nr != lseek(IN, BLOCK_SIZE * nr, SEEK_SET)) + die("Seek failed in write_block"); + if (BLOCK_SIZE != write(IN, addr, BLOCK_SIZE)) { + printf("%s: bad block in file '%s'\n", + bb_msg_write_error, 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 minix_inode *inode, unsigned int blknr) +{ + uint16_t ind[BLOCK_SIZE >> 1]; + uint16_t dind[BLOCK_SIZE >> 1]; + int blk_chg, block, result; + + 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); + read_block(block, (char *) ind); + blk_chg = 0; + result = check_zone_nr(blknr + ind, &blk_chg); + if (blk_chg) + write_block(block, (char *) ind); + return result; + } + blknr -= 512; + block = check_zone_nr(inode->i_zone + 8, &changed); + read_block(block, (char *) dind); + blk_chg = 0; + result = check_zone_nr(dind + (blknr / 512), &blk_chg); + if (blk_chg) + write_block(block, (char *) dind); + block = result; + read_block(block, (char *) ind); + blk_chg = 0; + result = check_zone_nr(ind + (blknr % 512), &blk_chg); + if (blk_chg) + write_block(block, (char *) ind); + return result; +} + +#ifdef CONFIG_FEATURE_MINIX2 +static int map_block2(struct minix2_inode *inode, unsigned int blknr) +{ + uint32_t ind[BLOCK_SIZE >> 2]; + uint32_t dind[BLOCK_SIZE >> 2]; + uint32_t tind[BLOCK_SIZE >> 2]; + int blk_chg, block, result; + + 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); + read_block(block, (char *) ind); + blk_chg = 0; + result = check_zone_nr2(blknr + ind, &blk_chg); + if (blk_chg) + write_block(block, (char *) ind); + return result; + } + blknr -= 256; + if (blknr >= 256 * 256) { + block = check_zone_nr2(inode->i_zone + 8, &changed); + read_block(block, (char *) dind); + blk_chg = 0; + result = check_zone_nr2(dind + blknr / 256, &blk_chg); + if (blk_chg) + write_block(block, (char *) dind); + block = result; + read_block(block, (char *) ind); + blk_chg = 0; + result = check_zone_nr2(ind + blknr % 256, &blk_chg); + if (blk_chg) + write_block(block, (char *) ind); + return result; + } + blknr -= 256 * 256; + block = check_zone_nr2(inode->i_zone + 9, &changed); + read_block(block, (char *) tind); + blk_chg = 0; + result = check_zone_nr2(tind + blknr / (256 * 256), &blk_chg); + if (blk_chg) + write_block(block, (char *) tind); + block = result; + read_block(block, (char *) dind); + blk_chg = 0; + result = check_zone_nr2(dind + (blknr / 256) % 256, &blk_chg); + if (blk_chg) + write_block(block, (char *) dind); + block = result; + read_block(block, (char *) ind); + blk_chg = 0; + result = check_zone_nr2(ind + blknr % 256, &blk_chg); + if (blk_chg) + write_block(block, (char *) ind); + return result; +} +#endif + +static void write_super_block(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; + if (errors_uncorrected) + Super.s_state |= MINIX_ERROR_FS; + else + Super.s_state &= ~MINIX_ERROR_FS; + + if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET)) + die("Seek failed in write_super_block"); + if (BLOCK_SIZE != write(IN, super_block_buffer, BLOCK_SIZE)) + die("Unable to write super-block"); +} + +static void write_tables(void) +{ + write_super_block(); + + if (IMAPS * BLOCK_SIZE != write(IN, inode_map, IMAPS * BLOCK_SIZE)) + die("Unable to write inode map"); + if (ZMAPS * BLOCK_SIZE != write(IN, zone_map, ZMAPS * BLOCK_SIZE)) + die("Unable to write zone map"); + if (INODE_BUFFER_SIZE != write(IN, inode_buffer, INODE_BUFFER_SIZE)) + die("Unable to write inodes"); +} + +static void get_dirsize(void) +{ + int block; + char blk[BLOCK_SIZE]; + int size; + +#ifdef CONFIG_FEATURE_MINIX2 + if (version2) + block = Inode2[ROOT_INO].i_zone[0]; + else +#endif + block = Inode[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) +{ + if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET)) + die("Seek failed"); + if (BLOCK_SIZE != read(IN, super_block_buffer, BLOCK_SIZE)) + die("Unable to read super block"); + /* already initialized to: + namelen = 14; + dirsize = 16; + version2 = 0; + */ + if (MAGIC == MINIX_SUPER_MAGIC) { + } else if (MAGIC == MINIX_SUPER_MAGIC2) { + namelen = 30; + dirsize = 32; +#ifdef CONFIG_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 super-block"); + 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 super-block"); + if (ZMAPS * BLOCK_SIZE * 8 < ZONES - FIRSTZONE + 1) + die("Bad s_zmap_blocks field in super-block"); +} + +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(IN, inode_map, IMAPS * BLOCK_SIZE)) + die("Unable to read inode map"); + if (ZMAPS * BLOCK_SIZE != read(IN, zone_map, ZMAPS * BLOCK_SIZE)) + die("Unable to read zone map"); + if (INODE_BUFFER_SIZE != read(IN, inode_buffer, INODE_BUFFER_SIZE)) + die("Unable to read inodes"); + if (NORM_FIRSTZONE != FIRSTZONE) { + printf("Warning: Firstzone!=Norm_firstzone\n"); + errors_uncorrected = 1; + } + get_dirsize(); + if (show) { + printf("%ld inodes\n" + "%ld blocks\n" + "Firstdatazone=%ld (%ld)\n" + "Zonesize=%d\n" + "Maxsize=%ld\n" + "Filesystem state=%d\n" + "namelen=%d\n\n", + INODES, + ZONES, + FIRSTZONE, NORM_FIRSTZONE, + BLOCK_SIZE << ZONESIZE, + MAXSIZE, + Super.s_state, + namelen); + } +} + +static struct minix_inode *get_inode(unsigned int nr) +{ + struct minix_inode *inode; + + if (!nr || nr > INODES) + return NULL; + total++; + inode = Inode + nr; + 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 (repair) { + if (ask("Mark as 'in use'", 1)) + mark_inode(nr); + } else { + errors_uncorrected = 1; + } + } + if (S_ISDIR(inode->i_mode)) + directory++; + else if (S_ISREG(inode->i_mode)) + regular++; + else if (S_ISCHR(inode->i_mode)) + chardev++; + else if (S_ISBLK(inode->i_mode)) + blockdev++; + else if (S_ISLNK(inode->i_mode)) + symlinks++; + else if (S_ISSOCK(inode->i_mode)); + else if (S_ISFIFO(inode->i_mode)); + else { + printf("%s has mode %05o\n", current_name, inode->i_mode); + } + + } else + links++; + if (!++inode_count[nr]) { + printf("Warning: inode count too big\n"); + inode_count[nr]--; + errors_uncorrected = 1; + } + return inode; +} + +#ifdef CONFIG_FEATURE_MINIX2 +static struct minix2_inode *get_inode2(unsigned int nr) +{ + struct minix2_inode *inode; + + if (!nr || nr > INODES) + return NULL; + total++; + inode = Inode2 + nr; + 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 (repair) { + if (ask("Mark as 'in use'", 1)) + mark_inode(nr); + else + errors_uncorrected = 1; + } + } + if (S_ISDIR(inode->i_mode)) + directory++; + else if (S_ISREG(inode->i_mode)) + regular++; + else if (S_ISCHR(inode->i_mode)) + chardev++; + else if (S_ISBLK(inode->i_mode)) + blockdev++; + else if (S_ISLNK(inode->i_mode)) + symlinks++; + else if (S_ISSOCK(inode->i_mode)); + else if (S_ISFIFO(inode->i_mode)); + else { + printf("%s has mode %05o\n", current_name, inode->i_mode); + } + } else + links++; + if (!++inode_count[nr]) { + printf("Warning: inode count too big\n"); + inode_count[nr]--; + errors_uncorrected = 1; + } + return inode; +} +#endif + +static void check_root(void) +{ + struct minix_inode *inode = Inode + ROOT_INO; + + if (!inode || !S_ISDIR(inode->i_mode)) + die("Root inode isn't a directory"); +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void check_root2(void) +{ + struct minix2_inode *inode = Inode2 + ROOT_INO; + + if (!inode || !S_ISDIR(inode->i_mode)) + die("Root inode isn't a directory"); +} +#endif + +static int add_zone(uint16_t *znr, int *corrected) +{ + int result; + int block; + + result = 0; + block = check_zone_nr(znr, corrected); + if (!block) + return 0; + if (zone_count[block]) { + printf("Already used block is reused in file '%s'. ", + current_name); + if (ask("Clear", 1)) { + *znr = 0; + block = 0; + *corrected = 1; + return 0; + } + } + 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; +} + +#ifdef CONFIG_FEATURE_MINIX2 +static int add_zone2(uint32_t *znr, int *corrected) +{ + int result; + int block; + + result = 0; + block = check_zone_nr2(znr, corrected); + if (!block) + return 0; + if (zone_count[block]) { + printf("Already used block is reused in file '%s'. ", + current_name); + if (ask("Clear", 1)) { + *znr = 0; + block = 0; + *corrected = 1; + return 0; + } + } + 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; +} +#endif + +static void add_zone_ind(uint16_t *znr, int *corrected) +{ + static char blk[BLOCK_SIZE]; + int i, chg_blk = 0; + int block; + + block = add_zone(znr, corrected); + if (!block) + return; + read_block(block, blk); + for (i = 0; i < (BLOCK_SIZE >> 1); i++) + add_zone(i + (uint16_t *) blk, &chg_blk); + if (chg_blk) + write_block(block, blk); +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void add_zone_ind2(uint32_t *znr, int *corrected) +{ + static char blk[BLOCK_SIZE]; + int i, chg_blk = 0; + int block; + + block = add_zone2(znr, corrected); + if (!block) + return; + read_block(block, blk); + for (i = 0; i < BLOCK_SIZE >> 2; i++) + add_zone2(i + (uint32_t *) blk, &chg_blk); + if (chg_blk) + write_block(block, blk); +} +#endif + +static void add_zone_dind(uint16_t *znr, int *corrected) +{ + static char blk[BLOCK_SIZE]; + int i, blk_chg = 0; + int block; + + block = add_zone(znr, corrected); + if (!block) + return; + read_block(block, blk); + for (i = 0; i < (BLOCK_SIZE >> 1); i++) + add_zone_ind(i + (uint16_t *) blk, &blk_chg); + if (blk_chg) + write_block(block, blk); +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void add_zone_dind2(uint32_t *znr, int *corrected) +{ + static char blk[BLOCK_SIZE]; + int i, blk_chg = 0; + int block; + + block = add_zone2(znr, corrected); + if (!block) + return; + read_block(block, blk); + for (i = 0; i < BLOCK_SIZE >> 2; i++) + add_zone_ind2(i + (uint32_t *) blk, &blk_chg); + if (blk_chg) + write_block(block, blk); +} + +static void add_zone_tind2(uint32_t *znr, int *corrected) +{ + static char blk[BLOCK_SIZE]; + int i, blk_chg = 0; + int block; + + block = add_zone2(znr, corrected); + if (!block) + return; + read_block(block, blk); + for (i = 0; i < BLOCK_SIZE >> 2; i++) + add_zone_dind2(i + (uint32_t *) blk, &blk_chg); + if (blk_chg) + write_block(block, blk); +} +#endif + +static void check_zones(unsigned int i) +{ + struct minix_inode *inode; + + if (!i || i > INODES) + return; + if (inode_count[i] > 1) /* have we counted this file already? */ + return; + inode = Inode + 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); +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void check_zones2(unsigned int 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 minix_inode *dir, unsigned int offset) +{ + static char blk[BLOCK_SIZE]; + struct minix_inode *inode; + int ino; + char *name; + int block; + + block = map_block(dir, offset / BLOCK_SIZE); + read_block(block, blk); + name = 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, blk); + } + ino = 0; + } + push_filename(name); + inode = get_inode(ino); + pop_filename(); + if (!offset) { + if (!inode || strcmp(".", name)) { + printf("%s: bad directory: '.' isn't first\n", current_name); + errors_uncorrected = 1; + } else + return; + } + if (offset == dirsize) { + if (!inode || strcmp("..", name)) { + printf("%s: bad directory: '..' isn't second\n", current_name); + errors_uncorrected = 1; + } else + return; + } + if (!inode) + return; + push_filename(name); + if (list) { + if (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(); + return; +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void check_file2(struct minix2_inode *dir, unsigned int offset) +{ + static char blk[BLOCK_SIZE]; + struct minix2_inode *inode; + int ino; + char *name; + int block; + + block = map_block2(dir, offset / BLOCK_SIZE); + read_block(block, blk); + name = 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, blk); + } + ino = 0; + } + push_filename(name); + inode = get_inode2(ino); + pop_filename(); + if (!offset) { + if (!inode || strcmp(".", name)) { + printf("%s: bad directory: '.' isn't first\n", current_name); + errors_uncorrected = 1; + } else + return; + } + if (offset == dirsize) { + if (!inode || strcmp("..", name)) { + printf("%s: bad directory: '..' isn't second\n", current_name); + errors_uncorrected = 1; + } else + return; + } + if (!inode) + return; + push_filename(name); + if (list) { + if (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(); + return; +} +#endif + +static void recursive_check(unsigned int ino) +{ + struct minix_inode *dir; + unsigned int offset; + + dir = Inode + 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); +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void recursive_check2(unsigned int ino) +{ + struct minix2_inode *dir; + unsigned int 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[1024]; + + if (BLOCK_SIZE * i != lseek(IN, BLOCK_SIZE * i, SEEK_SET)) + die("Seek failed in bad_zone"); + return (BLOCK_SIZE != read(IN, buffer, BLOCK_SIZE)); +} + +static void check_counts(void) +{ + int i; + + for (i = 1; i <= INODES; i++) { + if (warn_mode && Inode[i].i_mode && !inode_in_use(i)) { + printf("Inode %d has non-zero mode. ", i); + if (ask("Clear", 1)) { + Inode[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 (Inode[i].i_nlinks != inode_count[i]) { + printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ", + i, Inode[i].i_mode, Inode[i].i_nlinks, inode_count[i]); + if (ask("Set i_nlinks to count", 1)) { + Inode[i].i_nlinks = inode_count[i]; + changed = 1; + } + } + } + for (i = FIRSTZONE; i < ZONES; i++) { + if (zone_in_use(i) == 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]); + } +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void check_counts2(void) +{ + int i; + + for (i = 1; i <= INODES; i++) { + if (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) == 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(ROOT_INO); + recursive_check(ROOT_INO); + check_counts(); +} + +#ifdef CONFIG_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(ROOT_INO); + recursive_check2(ROOT_INO); + check_counts2(); +} +#endif + +int fsck_minix_main(int argc, char **argv) +{ + struct termios tmp; + int retcode = 0; + + alloc_current_name(); +#ifdef CONFIG_FEATURE_CLEAN_UP + /* Don't bother to free memory. Exit does + * that automagically, so we can save a few bytes */ + atexit(free_current_name); +#endif + + if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE) + die("Bad inode size"); +#ifdef CONFIG_FEATURE_MINIX2 + if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) + die("Bad v2 inode size"); +#endif + while (argc-- > 1) { + argv++; + if (argv[0][0] != '-') { + if (device_name) + bb_show_usage(); + else + device_name = argv[0]; + } else + while (*++argv[0]) + switch (argv[0][0]) { + case 'l': + list = 1; + break; + case 'a': + automatic = 1; + repair = 1; + break; + case 'r': + automatic = 0; + repair = 1; + break; + case 'v': + verbose = 1; + break; + case 's': + show = 1; + break; + case 'm': + warn_mode = 1; + break; + case 'f': + force = 1; + break; + default: + bb_show_usage(); + } + } + if (!device_name) + bb_show_usage(); + check_mount(); /* trying to check a mounted filesystem? */ + if (repair && !automatic) { + if (!isatty(0) || !isatty(1)) + die("Need terminal for interactive repairs"); + } + IN = open(device_name, repair ? O_RDWR : O_RDONLY); + if (IN < 0){ + printf("Unable to open device '%s'\n", device_name); + leave(8); + } + 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, program_version); + if (!(Super.s_state & MINIX_ERROR_FS) && + (Super.s_state & MINIX_VALID_FS) && !force) { + if (repair) + printf("%s is clean, check is skipped\n", device_name); + return retcode; + } else if (force) + printf("Forcing filesystem check on %s\n", device_name); + else if (repair) + printf("Filesystem on %s is dirty, needs checking\n", + device_name); + + read_tables(); + + if (repair && !automatic) { + tcgetattr(0, &termios); + tmp = termios; + tmp.c_lflag &= ~(ICANON | ECHO); + tcsetattr(0, TCSANOW, &tmp); + termios_set = 1; + } +#ifdef CONFIG_FEATURE_MINIX2 + if (version2) { + check_root2(); + check2(); + } else +#endif + { + check_root(); + check(); + } + if (verbose) { + int i, free_cnt; + + for (i = 1, free_cnt = 0; i <= INODES; i++) + if (!inode_in_use(i)) + free_cnt++; + printf("\n%6ld inodes used (%ld%%)\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("%6ld zones used (%ld%%)\n\n" + "%6d regular files\n" + "%6d directories\n" + "%6d character device files\n" + "%6d block device files\n" + "%6d links\n" + "%6d symbolic links\n" + "------\n" + "%6d 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 (repair) + write_super_block(); + + if (repair && !automatic) + tcsetattr(0, TCSANOW, &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 000000000..64f568aa9 --- /dev/null +++ b/util-linux/getopt.c @@ -0,0 +1,365 @@ +/* vi: set sw=4 ts=4: */ +/* + * getopt.c - Enhanced implementation of BSD getopt(1) + * Copyright (c) 1997, 1998, 1999, 2000 Frodo Looijaard <frodol@dds.nl> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* + * Version 1.0-b4: Tue Sep 23 1997. First public release. + * Version 1.0: Wed Nov 19 1997. + * Bumped up the version number to 1.0 + * Fixed minor typo (CSH instead of TCSH) + * Version 1.0.1: Tue Jun 3 1998 + * Fixed sizeof instead of strlen bug + * Bumped up the version number to 1.0.1 + * Version 1.0.2: Thu Jun 11 1998 (not present) + * Fixed gcc-2.8.1 warnings + * Fixed --version/-V option (not present) + * Version 1.0.5: Tue Jun 22 1999 + * Make -u option work (not present) + * Version 1.0.6: Tue Jun 27 2000 + * No important changes + * Version 1.1.0: Tue Jun 30 2000 + * Added NLS support (partly written by Arkadiusz Mi<B6>kiewicz + * <misiek@misiek.eu.org>) + * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org> + * Removed --version/-V and --help/-h in + * Removed parse_error(), using bb_error_msg() from Busybox instead + * Replaced our_malloc with xmalloc and our_realloc with xrealloc + * + */ + +#include "busybox.h" +#include <getopt.h> + +/* NON_OPT is the code that is returned when a non-option is found in '+' + mode */ +enum { + NON_OPT = 1, +/* LONG_OPT is the code that is returned when a long option is found. */ + LONG_OPT = 2 +}; + +/* The shells recognized. */ +typedef enum {BASH,TCSH} shell_t; + + +/* Some global variables that tells us how to parse. */ +static shell_t shell=BASH; /* The shell we generate output for. */ +static int quiet_errors; /* 0 is not quiet. */ +static int quiet_output; /* 0 is not quiet. */ +static int quote=1; /* 1 is do quote. */ +static int alternative; /* 0 is getopt_long, 1 is getopt_long_only */ + +/* Function prototypes */ +static const char *normalize(const char *arg); +static int generate_output(char * argv[],int argc,const char *optstr, + const struct option *longopts); +static void add_long_options(char *options); +static void add_longopt(const char *name,int has_arg); +static void set_shell(const char *new_shell); + + +/* + * 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. + */ +const char *normalize(const char *arg) +{ + static char *BUFFER=NULL; + const char *argptr=arg; + char *bufptr; + + free(BUFFER); + + 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 (*argptr) { + if (*argptr == '\'') { + /* Quote: replace it with: '\'' */ + *bufptr++='\''; + *bufptr++='\\'; + *bufptr++='\''; + *bufptr++='\''; + } else if (shell==TCSH && *argptr=='!') { + /* Exclamation mark: replace it with: \! */ + *bufptr++='\''; + *bufptr++='\\'; + *bufptr++='!'; + *bufptr++='\''; + } else if (shell==TCSH && *argptr=='\n') { + /* Newline: replace it with: \n */ + *bufptr++='\\'; + *bufptr++='n'; + } else if (shell==TCSH && isspace(*argptr)) { + /* Non-newline whitespace: replace it with \<ws> */ + *bufptr++='\''; + *bufptr++='\\'; + *bufptr++=*argptr; + *bufptr++='\''; + } else + /* Just copy */ + *bufptr++=*argptr; + argptr++; + } + *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. + */ +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; + int longindex; + const char *charptr; + + if (quiet_errors) /* No error reporting from getopt(3) */ + opterr=0; + optind=0; /* Reset getopt(3) */ + + while ((opt = (alternative? + getopt_long_only(argc,argv,optstr,longopts,&longindex): + getopt_long(argc,argv,optstr,longopts,&longindex))) + != EOF) + if (opt == '?' || opt == ':' ) + exit_code = 1; + else if (!quiet_output) { + if (opt == LONG_OPT) { + printf(" --%s",longopts[longindex].name); + if (longopts[longindex].has_arg) + printf(" %s", + normalize(optarg?optarg:"")); + } else 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++])); + puts(""); + } + return exit_code; +} + +static struct option *long_options; +static int long_options_length; /* Length of array */ +static int long_options_nr; /* Nr of used elements in array */ +enum { LONG_OPTIONS_INCR = 10 }; +#define init_longopt() add_longopt(NULL,0) + +/* Register a long option. The contents of name is copied. */ +void add_longopt(const char *name, int has_arg) +{ + if (!name) { /* init */ + free(long_options); + long_options=NULL; + long_options_length=0; + long_options_nr=0; + } + + if (long_options_nr == long_options_length) { + long_options_length += LONG_OPTIONS_INCR; + long_options=xrealloc(long_options, + sizeof(struct option) * + long_options_length); + } + + long_options[long_options_nr].name=NULL; + long_options[long_options_nr].has_arg=0; + long_options[long_options_nr].flag=NULL; + long_options[long_options_nr].val=0; + + if (long_options_nr) { /* Not for init! */ + long_options[long_options_nr-1].has_arg=has_arg; + long_options[long_options_nr-1].flag=NULL; + long_options[long_options_nr-1].val=LONG_OPT; + long_options[long_options_nr-1].name=xstrdup(name); + } + long_options_nr++; +} + + +/* + * Register several long options. options is a string of long options, + * separated by commas or whitespace. + * This nukes options! + */ +void add_long_options(char *options) +{ + int arg_opt, tlen; + char *tokptr=strtok(options,", \t\n"); + while (tokptr) { + arg_opt=no_argument; + tlen=strlen(tokptr); + if (tlen > 0) { + if (tokptr[tlen-1] == ':') { + if (tlen > 1 && tokptr[tlen-2] == ':') { + tokptr[tlen-2]='\0'; + tlen -= 2; + arg_opt=optional_argument; + } else { + tokptr[tlen-1]='\0'; + tlen -= 1; + arg_opt=required_argument; + } + if (tlen == 0) + bb_error_msg("empty long option after -l or --long argument"); + } + add_longopt(tokptr,arg_opt); + } + tokptr=strtok(NULL,", \t\n"); + } +} + +void set_shell(const char *new_shell) +{ + if (!strcmp(new_shell,"bash")) + shell=BASH; + else if (!strcmp(new_shell,"tcsh")) + shell=TCSH; + else if (!strcmp(new_shell,"sh")) + shell=BASH; + else if (!strcmp(new_shell,"csh")) + shell=TCSH; + else + bb_error_msg("unknown shell after -s or --shell argument"); +} + + +/* 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 + */ + +static const struct option longopts[]= +{ + {"options",required_argument,NULL,'o'}, + {"longoptions",required_argument,NULL,'l'}, + {"quiet",no_argument,NULL,'q'}, + {"quiet-output",no_argument,NULL,'Q'}, + {"shell",required_argument,NULL,'s'}, + {"test",no_argument,NULL,'T'}, + {"unquoted",no_argument,NULL,'u'}, + {"alternative",no_argument,NULL,'a'}, + {"name",required_argument,NULL,'n'}, + {NULL,0,NULL,0} +}; + +/* Stop scanning as soon as a non-option argument is found! */ +static const char shortopts[]="+ao:l:n:qQs:Tu"; + + +int getopt_main(int argc, char *argv[]) +{ + const char *optstr = NULL; + char *name = NULL; + int opt; + int compatible=0; + + init_longopt(); + + if (getenv("GETOPT_COMPATIBLE")) + compatible=1; + + if (argc == 1) { + if (compatible) { + /* For some reason, the original getopt gave no error + when there were no arguments. */ + printf(" --\n"); + return 0; + } else + bb_error_msg_and_die("missing optstring argument"); + } + + if (argv[1][0] != '-' || compatible) { + char *s; + + quote=0; + s=xmalloc(strlen(argv[1])+1); + strcpy(s,argv[1]+strspn(argv[1],"-+")); + argv[1]=argv[0]; + return generate_output(argv+1,argc-1,s,long_options); + } + + while ((opt = getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF) + switch (opt) { + case 'a': + alternative=1; + break; + case 'o': + optstr = optarg; + break; + case 'l': + add_long_options(optarg); + break; + case 'n': + name = optarg; + break; + case 'q': + quiet_errors=1; + break; + case 'Q': + quiet_output=1; + break; + case 's': + set_shell(optarg); + break; + case 'T': + return 4; + case 'u': + quote=0; + break; + default: + bb_show_usage(); + } + + if (!optstr) { + if (optind >= argc) + bb_error_msg_and_die("missing optstring argument"); + else optstr=argv[optind++]; + } + if (name) + argv[optind-1]=name; + else + argv[optind-1]=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 000000000..5cb245feb --- /dev/null +++ b/util-linux/hexdump.c @@ -0,0 +1,101 @@ +/* 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 "busybox.h" +#include <getopt.h> +#include "dump.h" + +static void bb_dump_addfile(char *name) +{ + char *p; + FILE *fp; + char *buf; + + fp = xfopen(name, "r"); + + while ((buf = xmalloc_getline(fp)) != NULL) { + p = skip_whitespace(buf); + + if (*p && (*p != '#')) { + bb_dump_add(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[] = "\"%07.7_Ax\n\""; + +static const char hexdump_opts[] = "bcdoxCe:f:n:s:v"; + +static const struct suffix_mult suffixes[] = { + {"b", 512 }, + {"k", 1024 }, + {"m", 1024*1024 }, + {NULL, 0 } +}; + +int hexdump_main(int argc, char **argv) +{ + const char *p; + int ch; + + bb_dump_vflag = FIRST; + bb_dump_length = -1; + + 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(add_first); + bb_dump_add(add_strings[(int)(p - hexdump_opts)]); + } else if (ch == 'C') { + bb_dump_add("\"%08.8_Ax\n\""); + bb_dump_add("\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" "); + bb_dump_add("\" |\" 16/1 \"%_p\" \"|\\n\""); + } else { + /* Save a little bit of space below by omitting the 'else's. */ + if (ch == 'e') { + bb_dump_add(optarg); + } /* else */ + if (ch == 'f') { + bb_dump_addfile(optarg); + } /* else */ + if (ch == 'n') { + bb_dump_length = xatoi_u(optarg); + } /* else */ + if (ch == 's') { + bb_dump_skip = xatoul_range_sfx(optarg, 0, LONG_MAX, suffixes); + } /* else */ + if (ch == 'v') { + bb_dump_vflag = ALL; + } + } + } + + if (!bb_dump_fshead) { + bb_dump_add(add_first); + bb_dump_add("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\""); + } + + argv += optind; + + return bb_dump_dump(argv); +} diff --git a/util-linux/hwclock.c b/util-linux/hwclock.c new file mode 100644 index 000000000..8fcd8c99c --- /dev/null +++ b/util-linux/hwclock.c @@ -0,0 +1,213 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini hwclock implementation for busybox + * + * Copyright (C) 2002 Robert Griebl <griebl@gmx.de> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. +*/ + + +#include <sys/ioctl.h> +#include <sys/utsname.h> +#include <getopt.h> +#include "busybox.h" + +/* Copied from linux/rtc.h to eliminate the kernel dependency */ +struct linux_rtc_time { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +#define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */ +#define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */ + +#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS +# ifndef _GNU_SOURCE +# define _GNU_SOURCE +# endif +#endif + +static int xopen_rtc(int flags) +{ + int rtc; + rtc = open("/dev/rtc", flags); + if (rtc < 0) { + rtc = open("/dev/misc/rtc", flags); + if (rtc < 0) + bb_perror_msg_and_die("cannot access RTC"); + } + return rtc; +} + +static time_t read_rtc(int utc) +{ + struct tm tm; + char *oldtz = 0; + time_t t = 0; + int rtc = xopen_rtc(O_RDONLY); + + memset(&tm, 0, sizeof(struct tm)); + if (ioctl(rtc, RTC_RD_TIME, &tm) < 0 ) + bb_perror_msg_and_die("cannot read time from RTC"); + tm.tm_isdst = -1; /* not known */ + + close(rtc); + + if (utc) { + oldtz = getenv("TZ"); + setenv("TZ", "UTC 0", 1); + tzset(); + } + + t = mktime(&tm); + + if (utc) { + if (oldtz) + setenv("TZ", oldtz, 1); + else + unsetenv("TZ"); + tzset(); + } + return t; +} + +static void write_rtc(time_t t, int utc) +{ + struct tm tm; + int rtc = xopen_rtc(O_WRONLY); + + tm = *(utc ? gmtime(&t) : localtime(&t)); + tm.tm_isdst = 0; + + if (ioctl(rtc, RTC_SET_TIME, &tm) < 0) + bb_perror_msg_and_die("cannot set the RTC time"); + + close(rtc); +} + +static int show_clock(int utc) +{ + struct tm *ptm; + time_t t; + RESERVE_CONFIG_BUFFER(buffer, 64); + + t = read_rtc(utc); + ptm = localtime(&t); /* Sets 'tzname[]' */ + + safe_strncpy(buffer, ctime(&t), 64); + if (buffer[0]) + buffer[strlen(buffer) - 1] = 0; + + //printf("%s %.6f seconds %s\n", buffer, 0.0, utc ? "" : (ptm->tm_isdst ? tzname[1] : tzname[0])); + printf( "%s %.6f seconds\n", buffer, 0.0); + RELEASE_CONFIG_BUFFER(buffer); + + return 0; +} + +static int to_sys_clock(int utc) +{ + struct timeval tv = { 0, 0 }; + const struct timezone tz = { timezone/60 - 60*daylight, 0 }; + + tv.tv_sec = read_rtc(utc); + + if (settimeofday(&tv, &tz)) + bb_perror_msg_and_die("settimeofday() failed"); + + return 0; +} + +static int from_sys_clock(int utc) +{ + struct timeval tv = { 0, 0 }; + struct timezone tz = { 0, 0 }; + + if (gettimeofday(&tv, &tz)) + bb_perror_msg_and_die("gettimeofday() failed"); + + write_rtc(tv.tv_sec, utc); + return 0; +} + +#ifdef CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS +# define ADJTIME_PATH "/var/lib/hwclock/adjtime" +#else +# define ADJTIME_PATH "/etc/adjtime" +#endif +static int check_utc(void) +{ + int utc = 0; + FILE *f = fopen(ADJTIME_PATH, "r"); + + if (f) { + RESERVE_CONFIG_BUFFER(buffer, 128); + + while (fgets(buffer, sizeof(buffer), f)) { + int len = strlen(buffer); + + while (len && isspace(buffer[len - 1])) + len--; + + buffer[len] = 0; + + if (strncmp(buffer, "UTC", 3) == 0 ) { + utc = 1; + break; + } + } + fclose(f); + RELEASE_CONFIG_BUFFER(buffer); + } + return 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 + +int hwclock_main(int argc, char **argv ) +{ + unsigned opt; + int utc; + +#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS + static const struct option hwclock_long_options[] = { + { "localtime", 0, 0, 'l' }, + { "utc", 0, 0, 'u' }, + { "show", 0, 0, 'r' }, + { "hctosys", 0, 0, 's' }, + { "systohc", 0, 0, 'w' }, + { 0, 0, 0, 0 } + }; + applet_long_options = hwclock_long_options; +#endif + opt_complementary = "?:r--ws:w--rs:s--wr:l--u:u--l"; + opt = getopt32(argc, argv, "lursw"); + + /* 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 = check_utc(); + + if (opt & HWCLOCK_OPT_HCTOSYS) { + return to_sys_clock(utc); + } + else if (opt & HWCLOCK_OPT_SYSTOHC) { + return from_sys_clock(utc); + } else { + /* default HWCLOCK_OPT_SHOW */ + return show_clock(utc); + } +} diff --git a/util-linux/ipcrm.c b/util-linux/ipcrm.c new file mode 100644 index 000000000..507e58fe3 --- /dev/null +++ b/util-linux/ipcrm.c @@ -0,0 +1,217 @@ +/* vi: set sw=4 ts=4: */ +/* + * ipcrm.c - utility to allow removal of IPC objects and data structures. + * + * 01 Sept 2004 - Rodney Radford <rradford@mindspring.com> + * Adapted for busybox from util-linux-2.12a. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" + +/* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */ +/* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */ +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/msg.h> +#include <sys/sem.h> + +#if defined (__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) +/* union semun is defined by including <sys/sem.h> */ +#else +/* according to X/OPEN we have to define it ourselves */ +union semun { + int val; + struct semid_ds *buf; + unsigned short int *array; + struct seminfo *__buf; +}; +#endif + +#ifndef CONFIG_IPCRM_DROP_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 /* #ifndef CONFIG_IPCRM_DROP_LEGACY */ + + +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; +#ifndef CONFIG_IPCRM_DROP_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(1); + printf("resource(s) deleted\n"); + return 0; + } + } +#endif /* #ifndef CONFIG_IPCRM_DROP_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) { + char *errmsg; + const char * const what = "key"; + + 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, what, 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) { + 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 000000000..b81d07d6d --- /dev/null +++ b/util-linux/ipcs.c @@ -0,0 +1,630 @@ +/* vi: set sw=4 ts=4: */ +/* + * ipcs.c -- provides information on allocated ipc resources. + * + * 01 Sept 2004 - Rodney Radford <rradford@mindspring.com> + * Adapted for busybox from util-linux-2.12a. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" +#include <errno.h> +#include <time.h> +#include <pwd.h> +#include <grp.h> + +/* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */ +/* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */ +/* X/OPEN tells us to use <sys/{types,ipc,shm}.h> for shmctl() */ +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/sem.h> +#include <sys/msg.h> +#include <sys/shm.h> + + + +/*-------------------------------------------------------------------*/ +/* SHM_DEST and SHM_LOCKED are defined in kernel headers, + but inside #ifdef __KERNEL__ ... #endif */ +#ifndef SHM_DEST +/* shm_mode upper byte flags */ +#define SHM_DEST 01000 /* segment will be destroyed on last detach */ +#define SHM_LOCKED 02000 /* segment will not be swapped */ +#endif + +/* For older kernels the same holds for the defines below */ +#ifndef MSG_STAT +#define MSG_STAT 11 +#define MSG_INFO 12 +#endif + +#ifndef SHM_STAT +#define SHM_STAT 13 +#define SHM_INFO 14 +struct shm_info { + int used_ids; + ulong shm_tot; /* total allocated shm */ + ulong shm_rss; /* total resident shm */ + ulong shm_swp; /* total swapped shm */ + ulong swap_attempts; + ulong swap_successes; +}; +#endif + +#ifndef SEM_STAT +#define SEM_STAT 18 +#define SEM_INFO 19 +#endif + +/* Some versions of libc only define IPC_INFO when __USE_GNU is defined. */ +#ifndef IPC_INFO +#define IPC_INFO 3 +#endif +/*-------------------------------------------------------------------*/ + +/* The last arg of semctl is a union semun, but where is it defined? + X/OPEN tells us to define it ourselves, but until recently + Linux include files would also define it. */ +#if defined (__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) +/* union semun is defined by including <sys/sem.h> */ +#else +/* according to X/OPEN we have to define it ourselves */ +union semun { + int val; + struct semid_ds *buf; + unsigned short int *array; + struct seminfo *__buf; +}; +#endif + +/* X/OPEN (Jan 1987) does not define fields key, seq in struct ipc_perm; + libc 4/5 does not mention struct ipc_term at all, but includes + <linux/ipc.h>, which defines a struct ipc_perm with such fields. + glibc-1.09 has no support for sysv ipc. + glibc 2 uses __key, __seq */ +#if defined (__GNU_LIBRARY__) && __GNU_LIBRARY__ > 1 +#define KEY __key +#else +#define KEY key +#endif + +#define LIMITS 1 +#define STATUS 2 +#define CREATOR 3 +#define TIME 4 +#define PID 5 + +static char format; + +static void print_perms(int id, struct ipc_perm *ipcp) +{ + struct passwd *pw; + struct group *gr; + + printf("%-10d %-10o", id, ipcp->mode & 0777); + + if ((pw = getpwuid(ipcp->cuid))) + printf(" %-10s", pw->pw_name); + else + printf(" %-10d", ipcp->cuid); + if ((gr = getgrgid(ipcp->cgid))) + printf(" %-10s", gr->gr_name); + else + printf(" %-10d", ipcp->cgid); + + if ((pw = getpwuid(ipcp->uid))) + printf(" %-10s", pw->pw_name); + else + printf(" %-10d", ipcp->uid); + if ((gr = getgrgid(ipcp->gid))) + 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); + } + puts(""); +} + +int ipcs_main(int argc, 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(argc, 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(0); + } + if (flags & flag_sem) { + print_sem(id); + fflush_stdout_and_exit(0); + } + if (flags & flag_msg) { + print_msg(id); + fflush_stdout_and_exit(0); + } + bb_show_usage(); + } + + if (!(flags & (flag_shm | flag_msg | flag_sem))) + flags |= flag_msg | flag_shm | flag_sem; + puts(""); + + if (flags & flag_shm) { + do_shm(); + puts(""); + } + if (flags & flag_sem) { + do_sem(); + puts(""); + } + if (flags & flag_msg) { + do_msg(); + puts(""); + } + fflush_stdout_and_exit(0); +} diff --git a/util-linux/losetup.c b/util-linux/losetup.c new file mode 100644 index 000000000..c7eb85a91 --- /dev/null +++ b/util-linux/losetup.c @@ -0,0 +1,64 @@ +/* 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 <getopt.h> +#include <stdlib.h> + +#include "busybox.h" + +int losetup_main(int argc, char **argv) +{ + unsigned opt; + char *opt_o; + unsigned long long offset = 0; + + opt = getopt32(argc, argv, "do:", &opt_o); + argc -= optind; + argv += optind; + + if (opt == 0x3) // -d + -o (illegal) + bb_show_usage(); + + if (opt == 0x1) { // -d + /* detach takes exactly one argument */ + if (argc != 1) + bb_show_usage(); + if (!del_loop(argv[0])) + return EXIT_SUCCESS; + bb_perror_nomsg_and_die(); + } + + if (opt == 0x2) // -o + offset = xatoull(opt_o); + + /* -o or no option */ + + if (argc == 2) { + if (set_loop(&argv[0], argv[1], offset) < 0) + bb_perror_nomsg_and_die(); + } else if (argc == 1) { + char *s = query_loop(argv[0]); + if (!s) bb_perror_nomsg_and_die(); + printf("%s: %s\n", argv[0], s); + if (ENABLE_FEATURE_CLEAN_UP) free(s); + } else { + char dev[sizeof(LOOP_NAME"0")] = LOOP_NAME"0"; + char c; + for (c = '0'; c <= '9'; ++c) { + char *s; + dev[sizeof(LOOP_NAME"0")-2] = c; + s = query_loop(dev); + if (s) { + printf("%s: %s\n", dev, s); + if (ENABLE_FEATURE_CLEAN_UP) free(s); + } + } + } + return EXIT_SUCCESS; +} diff --git a/util-linux/mdev.c b/util-linux/mdev.c new file mode 100644 index 000000000..957316d52 --- /dev/null +++ b/util-linux/mdev.c @@ -0,0 +1,268 @@ +/* vi: set sw=4 ts=4: */ +/* + * + * mdev - Mini udev for busybox + * + * Copyright 2005 Rob Landley <rob@landley.net> + * Copyright 2005 Frank Sorenson <frank@tuxrocks.com> + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" +#include "xregex.h" + +#define DEV_PATH "/dev" + +struct mdev_globals +{ + int root_major, root_minor; +} mdev_globals; + +#define bbg mdev_globals + +/* mknod in /dev based on a path like "/sys/block/hda/hda1" */ +static void make_device(char *path, int delete) +{ + char *device_name; + int major, minor, type, len; + int mode = 0660; + uid_t uid = 0; + gid_t gid = 0; + char *temp = path + strlen(path); + char *command = NULL; + + /* 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. */ + + if (!delete) { + strcat(path, "/dev"); + len = open_read_close(path, temp + 1, 64); + *temp++ = 0; + if (len < 1) return; + } + + /* Determine device name, type, major and minor */ + + device_name = strrchr(path, '/') + 1; + type = path[5]=='c' ? S_IFCHR : S_IFBLK; + + /* If we have a config file, look up permissions for this device */ + + if (ENABLE_FEATURE_MDEV_CONF) { + char *conf, *pos, *end; + int line, fd; + + /* mmap the config file */ + fd = open("/etc/mdev.conf", O_RDONLY); + if (fd < 0) + goto end_parse; + len = xlseek(fd, 0, SEEK_END); + conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + if (!conf) + goto end_parse; + + line = 0; + /* Loop through lines in mmaped file*/ + for (pos=conf; pos-conf<len;) { + int field; + char *end2; + + line++; + /* find end of this line */ + for (end=pos; end-conf<len && *end!='\n'; end++) + ; + + /* Three fields: regex, uid:gid, mode */ + for (field=0; field < (3 + ENABLE_FEATURE_MDEV_EXEC); + field++) + { + /* Skip whitespace */ + while (pos<end && isspace(*pos)) pos++; + if (pos==end || *pos=='#') break; + for (end2=pos; + end2<end && !isspace(*end2) && *end2!='#'; end2++) + ; + + if (field == 0) { + /* Regex to match this device */ + + char *regex = strndupa(pos, end2-pos); + regex_t match; + regmatch_t off; + int result; + + /* Is this it? */ + xregcomp(&match,regex, REG_EXTENDED); + result = regexec(&match, device_name, 1, &off, 0); + regfree(&match); + + /* If not this device, skip rest of line */ + if (result || off.rm_so + || off.rm_eo != strlen(device_name)) + break; + } + if (field == 1) { + /* uid:gid */ + + char *s, *s2; + + /* Find : */ + for (s=pos; s<end2 && *s!=':'; s++) + ; + if (s == end2) break; + + /* Parse UID */ + uid = strtoul(pos, &s2, 10); + if (s != s2) { + struct passwd *pass; + pass = getpwnam(strndupa(pos, s-pos)); + if (!pass) break; + uid = pass->pw_uid; + } + s++; + /* parse GID */ + gid = strtoul(s, &s2, 10); + if (end2 != s2) { + struct group *grp; + grp = getgrnam(strndupa(s, end2-s)); + if (!grp) break; + gid = grp->gr_gid; + } + } + if (field == 2) { + /* mode */ + + mode = strtoul(pos, &pos, 8); + if (pos != end2) break; + } + if (ENABLE_FEATURE_MDEV_EXEC && field == 3) { + // Command to run + char *s = "@$*", *s2; + s2 = strchr(s, *pos++); + if (!s2) { + // Force error + field = 1; + break; + } + if ((s2-s+1) & (1<<delete)) + command = xstrndup(pos, end-pos); + } + + pos = end2; + } + + /* Did everything parse happily? */ + + if (field > 2) break; + if (field) bb_error_msg_and_die("bad line %d",line); + + /* Next line */ + pos = ++end; + } + munmap(conf, len); + end_parse: /* nothing */ ; + } + + umask(0); + if (!delete) { + if (sscanf(temp, "%d:%d", &major, &minor) != 2) return; + if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST) + bb_perror_msg_and_die("mknod %s", device_name); + + if (major == bbg.root_major && minor == bbg.root_minor) + symlink(device_name, "root"); + + if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid); + } + if (command) { + int rc; + char *s; + + s = xasprintf("MDEV=%s", device_name); + putenv(s); + rc = system(command); + s[4] = 0; + putenv(s); + free(s); + free(command); + if (rc == -1) bb_perror_msg_and_die("cannot run %s", command); + } + if (delete) unlink(device_name); +} + +/* Recursive search of /sys/block or /sys/class. path must be a writeable + * buffer of size PATH_MAX containing the directory string to start at. */ + +static void find_dev(char *path) +{ + DIR *dir; + size_t len = strlen(path); + struct dirent *entry; + + dir = opendir(path); + if (dir == NULL) + return; + + while ((entry = readdir(dir)) != NULL) { + struct stat st; + + /* Skip "." and ".." (also skips hidden files, which is ok) */ + + if (entry->d_name[0] == '.') + continue; + + // uClibc doesn't fill out entry->d_type reliably. so we use lstat(). + + snprintf(path+len, PATH_MAX-len, "/%s", entry->d_name); + if (!lstat(path, &st) && S_ISDIR(st.st_mode)) find_dev(path); + path[len] = 0; + + /* If there's a dev entry, mknod it */ + + if (!strcmp(entry->d_name, "dev")) make_device(path, 0); + } + + closedir(dir); +} + +int mdev_main(int argc, char *argv[]) +{ + char *action; + char *env_path; + RESERVE_CONFIG_BUFFER(temp,PATH_MAX); + + xchdir(DEV_PATH); + + /* Scan */ + + if (argc == 2 && !strcmp(argv[1],"-s")) { + struct stat st; + + xstat("/", &st); + bbg.root_major = major(st.st_dev); + bbg.root_minor = minor(st.st_dev); + strcpy(temp,"/sys/block"); + find_dev(temp); + strcpy(temp,"/sys/class"); + find_dev(temp); + + /* Hotplug */ + + } else { + action = getenv("ACTION"); + env_path = getenv("DEVPATH"); + if (!action || !env_path) + bb_show_usage(); + + sprintf(temp, "/sys%s", env_path); + if (!strcmp(action, "add")) make_device(temp,0); + else if (!strcmp(action, "remove")) make_device(temp,1); + } + + if (ENABLE_FEATURE_CLEAN_UP) RELEASE_CONFIG_BUFFER(temp); + return 0; +} diff --git a/util-linux/mkfs_minix.c b/util-linux/mkfs_minix.c new file mode 100644 index 000000000..af19da68c --- /dev/null +++ b/util-linux/mkfs_minix.c @@ -0,0 +1,762 @@ +/* vi: set sw=4 ts=4: */ +/* + * mkfs.c - make a linux (minix) file-system. + * + * (C) 1991 Linus Torvalds. This file may be redistributed as per + * the Linux copyright. + */ + +/* + * DD.MM.YY + * + * 24.11.91 - Time began. Used the fsck sources to get started. + * + * 25.11.91 - Corrected some bugs. Added support for ".badblocks" + * The algorithm for ".badblocks" is a bit weird, but + * it should work. Oh, well. + * + * 25.01.92 - Added the -l option for getting the list of bad blocks + * out of a named file. (Dave Rivers, rivers@ponds.uucp) + * + * 28.02.92 - Added %-information when using -c. + * + * 28.02.93 - Added support for other namelengths than the original + * 14 characters so that I can test the new kernel routines.. + * + * 09.10.93 - Make exit status conform to that required by fsutil + * (Rik Faith, faith@cs.unc.edu) + * + * 31.10.93 - Added inode request feature, for backup floppies: use + * 32 inodes, for a news partition use more. + * (Scott Heavner, sdh@po.cwru.edu) + * + * 03.01.94 - Added support for file system valid flag. + * (Dr. Wettstein, greg%wind.uucp@plains.nodak.edu) + * + * 30.10.94 - added support for v2 filesystem + * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de) + * + * 09.11.94 - Added test to prevent overwrite of mounted fs adapted + * from Theodore Ts'o's (tytso@athena.mit.edu) mke2fs + * program. (Daniel Quinlan, quinlan@yggdrasil.com) + * + * 03.20.95 - Clear first 512 bytes of filesystem to make certain that + * the filesystem is not misidentified as a MS-DOS FAT filesystem. + * (Daniel Quinlan, quinlan@yggdrasil.com) + * + * 02.07.96 - Added small patch from Russell King to make the program a + * good deal more portable (janl@math.uio.no) + * + * Usage: mkfs [-c | -l filename ] [-v] [-nXX] [-iXX] device [size-in-blocks] + * + * -c for readability checking (SLOW!) + * -l for getting a list of bad blocks from a file. + * -n for namelength (currently the kernel only uses 14 or 30) + * -i for number of inodes + * -v for v2 filesystem + * + * The device may be a block device or a image of one, but this isn't + * enforced (but it's not much fun on a character device :-). + * + * Modified for BusyBox by Erik Andersen <andersen@debian.org> -- + * removed getopt based parser and added a hand rolled one. + */ + +#include "busybox.h" +#include <mntent.h> + +#define DEBUG 0 + +/* If debugging, store the very same times/uids/gids for image consistency */ +#if DEBUG +# define CUR_TIME 0 +# define GETUID 0 +# define GETGID 0 +#else +# define CUR_TIME time(NULL) +# define GETUID getuid() +# define GETGID getgid() +#endif + +/* + * 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 super-block data on disk + */ +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; +}; + +struct minix_dir_entry { + uint16_t inode; + char name[0]; +}; + +/* Believe it or not, but mount.h has this one */ +#undef BLOCK_SIZE +enum { + BLOCK_SIZE = 1024, + BITS_PER_BLOCK = BLOCK_SIZE << 3, + + MINIX_ROOT_INO = 1, + MINIX_BAD_INO = 2, + MAX_GOOD_BLOCKS = 512, + + 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), + + TEST_BUFFER_BLOCKS = 16, +}; + +#if ENABLE_FEATURE_MINIX2 +static int version2; +#else +enum { version2 = 0 }; +#endif + +static char *device_name; +static int dev_fd = -1; +static uint32_t total_blocks; +static int badblocks; +/* default (changed to 30, per Linus's suggestion, Sun Nov 21 08:05:07 1993) */ +static int namelen = 30; +static int dirsize = 32; +static int magic = MINIX1_SUPER_MAGIC2; + +static char root_block[BLOCK_SIZE]; +static char super_block_buffer[BLOCK_SIZE]; +static char boot_block_buffer[512]; +static char *inode_buffer; + +static char *inode_map; +static char *zone_map; + +static int used_good_blocks; +static unsigned short good_blocks_table[MAX_GOOD_BLOCKS]; +static unsigned long req_nr_inodes; + +extern inline unsigned div_roundup(unsigned size, unsigned n) +{ + return (size + n-1) / n; +} + +#define INODE_BUF1 (((struct minix1_inode*)inode_buffer) - 1) +#define INODE_BUF2 (((struct minix2_inode*)inode_buffer) - 1) + +#define SB (*(struct minix_super_block*)super_block_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) + +static int bit(const char* a, unsigned i) +{ + return a[i >> 3] & (1<<(i & 7)); +} + +/* Note: do not assume 0/1, it is 0/nonzero */ +#define inode_in_use(x) bit(inode_map,(x)) +#define zone_in_use(x) bit(zone_map,(x)-SB_FIRSTZONE+1) + +#define mark_inode(x) setbit(inode_map,(x)) +#define unmark_inode(x) clrbit(inode_map,(x)) +#define mark_zone(x) setbit(zone_map,(x)-SB_FIRSTZONE+1) +#define unmark_zone(x) clrbit(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 super block 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, 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, super_block_buffer, BLOCK_SIZE); + + msg_eol = "cannot write inode map"; + xwrite(dev_fd, inode_map, SB_IMAPS * BLOCK_SIZE); + + msg_eol = "cannot write zone map"; + xwrite(dev_fd, zone_map, SB_ZMAPS * BLOCK_SIZE); + + msg_eol = "cannot write inodes"; + xwrite(dev_fd, 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 (used_good_blocks + 1 >= MAX_GOOD_BLOCKS) + bb_error_msg_and_die("too many bad blocks"); + if (used_good_blocks) + blk = good_blocks_table[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"); + good_blocks_table[used_good_blocks] = blk; + used_good_blocks++; + return blk; +} + +static void mark_good_blocks(void) +{ + int blk; + + for (blk = 0; blk < used_good_blocks; blk++) + mark_zone(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; + unsigned short ind_block[BLOCK_SIZE >> 1]; + unsigned short dind_block[BLOCK_SIZE >> 1]; + +#define NEXT_BAD (zone = next(zone)) + + if (!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 = 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); +} + +#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; + unsigned long ind_block[BLOCK_SIZE >> 2]; + unsigned long dind_block[BLOCK_SIZE >> 2]; + + if (!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 = 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); +} +#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 (badblocks) + inode->i_size = 3 * dirsize; + else { + root_block[2 * dirsize] = '\0'; + root_block[2 * dirsize + 1] = '\0'; + inode->i_size = 2 * 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], 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 (badblocks) + inode->i_size = 3 * dirsize; + else { + root_block[2 * dirsize] = '\0'; + root_block[2 * dirsize + 1] = '\0'; + inode->i_size = 2 * 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], root_block); +} +#endif + +static void setup_tables(void) +{ + unsigned long inodes; + unsigned norm_firstzone; + unsigned sb_zmaps; + unsigned i; + + memset(super_block_buffer, 0, BLOCK_SIZE); + memset(boot_block_buffer, 0, 512); + SB_MAGIC = magic; + SB_ZONE_SIZE = 0; + SB_MAXSIZE = version2 ? 0x7fffffff : (7 + 512 + 512 * 512) * 1024; + if (version2) + SB.s_zones = total_blocks; + else + SB.s_nzones = total_blocks; + + /* some magic nrs: 1 inode / 3 blocks */ + if (req_nr_inodes == 0) + inodes = total_blocks / 3; + else + inodes = 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(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; + inode_map = xmalloc(SB_IMAPS * BLOCK_SIZE); + zone_map = xmalloc(SB_ZMAPS * BLOCK_SIZE); + memset(inode_map, 0xff, SB_IMAPS * BLOCK_SIZE); + memset(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); + 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); +} + +/* + * Perform a test of a block; return the number of + * blocks readable/writable. + */ +static long do_check(char *buffer, int try, unsigned current_block) +{ + long 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; + if (got & (BLOCK_SIZE - 1)) { + printf("Weird values in do_check: probably bugs\n"); + } + got /= BLOCK_SIZE; + return got; +} + +static unsigned currently_testing; + +static void alarm_intr(int alnum) +{ + if (currently_testing >= SB_ZONES) + return; + signal(SIGALRM, alarm_intr); + alarm(5); + if (!currently_testing) + return; + printf("%d ...", currently_testing); + fflush(stdout); +} + +static void check_blocks(void) +{ + int try, got; + /* buffer[] was the biggest static in entire bbox */ + char *buffer = xmalloc(BLOCK_SIZE * TEST_BUFFER_BLOCKS); + + currently_testing = 0; + signal(SIGALRM, alarm_intr); + alarm(5); + while (currently_testing < SB_ZONES) { + msg_eol = "seek failed in check_blocks"; + xlseek(dev_fd, currently_testing * BLOCK_SIZE, SEEK_SET); + msg_eol = "\n"; + try = TEST_BUFFER_BLOCKS; + if (currently_testing + try > SB_ZONES) + try = SB_ZONES - currently_testing; + got = do_check(buffer, try, currently_testing); + currently_testing += got; + if (got == try) + continue; + if (currently_testing < SB_FIRSTZONE) + bb_error_msg_and_die("bad blocks before data-area: cannot make fs"); + mark_zone(currently_testing); + badblocks++; + currently_testing++; + } + free(buffer); + printf("%d bad block(s)\n", badblocks); +} + +static void get_list_blocks(char *filename) +{ + FILE *listfile; + unsigned long blockno; + + listfile = xfopen(filename, "r"); + while (!feof(listfile)) { + fscanf(listfile, "%ld\n", &blockno); + mark_zone(blockno); + badblocks++; + } + printf("%d bad block(s)\n", badblocks); +} + +int mkfs_minix_main(int argc, char **argv) +{ + struct mntent *mp; + unsigned opt; + char *tmp; + struct stat statbuf; + char *str_i, *str_n; + char *listfile = NULL; + + 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 = getopt32(argc, argv, "ci:l:n:v", &str_i, &listfile, &str_n); + argv += optind; + //if (opt & 1) -c + if (opt & 2) req_nr_inodes = xatoul(str_i); // -i + //if (opt & 4) -l + if (opt & 8) { // -n + namelen = xatoi_u(str_n); + if (namelen == 14) magic = MINIX1_SUPER_MAGIC; + else if (namelen == 30) magic = MINIX1_SUPER_MAGIC2; + else bb_show_usage(); + dirsize = namelen + 2; + } + if (opt & 0x10) { // -v +#if ENABLE_FEATURE_MINIX2 + version2 = 1; +#else + bb_error_msg_and_die("%s: not compiled with minix v2 support", + device_name); +#endif + } + + device_name = *argv++; + if (!device_name) + bb_show_usage(); + if (*argv) + total_blocks = xatou32(*argv); + else + total_blocks = get_size(device_name) / 1024; + + if (total_blocks < 10) + bb_error_msg_and_die("must have at least 10 blocks"); + + if (version2) { + magic = MINIX2_SUPER_MAGIC2; + if (namelen == 14) + magic = MINIX2_SUPER_MAGIC; + } else if (total_blocks > 65535) + total_blocks = 65535; + + /* Check if it is mounted */ + mp = find_mount_point(device_name, NULL); + if (mp && strcmp(device_name, mp->mnt_fsname) == 0) + bb_error_msg_and_die("%s is mounted on %s; " + "refusing to make a filesystem", + device_name, mp->mnt_dir); + + dev_fd = xopen(device_name, O_RDWR); + if (fstat(dev_fd, &statbuf) < 0) + bb_error_msg_and_die("cannot stat %s", 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'", device_name); +#endif + + tmp = root_block; + *(short *) tmp = 1; + strcpy(tmp + 2, "."); + tmp += dirsize; + *(short *) tmp = 1; + strcpy(tmp + 2, ".."); + tmp += 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 000000000..7baa3ecfb --- /dev/null +++ b/util-linux/mkswap.c @@ -0,0 +1,47 @@ +/* vi: set sw=4 ts=4: */ +/* mkswap.c - format swap device (Linux v1 only) + * + * Copyright 2006 Rob Landley <rob@landley.net> + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" + +int mkswap_main(int argc, char *argv[]) +{ + int fd, pagesize; + off_t len; + unsigned int hdr[129]; + + // 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); + len = fdlength(fd); + pagesize = getpagesize(); + printf("Setting up swapspace version 1, size = %"OFF_FMT"d bytes\n", + len - pagesize); + + // Make a header. + + memset(hdr, 0, sizeof(hdr)); + 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, sizeof(hdr)); + 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 000000000..d048ace92 --- /dev/null +++ b/util-linux/more.c @@ -0,0 +1,177 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini more implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>. + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Latest version blended together by Erik Andersen <andersen@codepoet.org>, + * based on the original more implementation by Bruce, and code from the + * Debian boot-floppies team. + * + * Termios corrects by Vladimir Oleynik <dzo@simtreas.ru> + * + * Licensed under GPLv2 or later, see file License in this tarball for details. + */ + +#include "busybox.h" + + +#if ENABLE_FEATURE_USE_TERMIOS +static int cin_fileno; +#include <termios.h> +#define setTermSettings(fd, argp) tcsetattr(fd, TCSANOW, argp) +#define getTermSettings(fd, argp) tcgetattr(fd, argp); + +static struct termios initial_settings, new_settings; + +static void set_tty_to_initial_mode(void) +{ + setTermSettings(cin_fileno, &initial_settings); +} + +static void gotsig(int sig) +{ + putchar('\n'); + exit(EXIT_FAILURE); +} +#endif /* FEATURE_USE_TERMIOS */ + + +int more_main(int argc, char **argv) +{ + int c, lines, input = 0; + int please_display_more_prompt = 0; + struct stat st; + FILE *file; + FILE *cin; + int len, page_height; + int terminal_width; + int terminal_height; + + argc--; + argv++; + + cin = stdin; + /* use input from terminal unless we do "more >outfile" */ + if (isatty(STDOUT_FILENO)) { + cin = fopen(CURRENT_TTY, "r"); + if (!cin) + cin = xfopen(CONSOLE_DEV, "r"); + please_display_more_prompt = 2; +#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); + atexit(set_tty_to_initial_mode); + signal(SIGINT, gotsig); + signal(SIGQUIT, gotsig); + signal(SIGTERM, gotsig); +#endif + } + + do { + file = stdin; + if (argc != 0) { + file = fopen_or_warn(*argv, "r"); + if (!file) + goto loop; + } + + st.st_size = 0; + fstat(fileno(file), &st); + + please_display_more_prompt &= ~1; + + get_terminal_width_height(fileno(cin), &terminal_width, &terminal_height); + if (terminal_height > 4) + terminal_height -= 2; + if (terminal_width > 0) + terminal_width -= 1; + + len = 0; + lines = 0; + page_height = terminal_height; + while ((c = getc(file)) != EOF) { + + if ((please_display_more_prompt & 3) == 3) { + len = printf("--More-- "); + if (file != stdin && 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. + */ + input = getc(cin); +#if !ENABLE_FEATURE_USE_TERMIOS + printf("\033[A"); /* up cursor */ +#endif + /* Erase the "More" message */ + printf("\r%*s\r", len, ""); + len = 0; + lines = 0; + page_height = terminal_height; + please_display_more_prompt &= ~1; + + if (input == 'q') + goto end; + } + + /* + * 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. + */ + if (c == '\n') { + /* increment by just one line if we are at + * the end of this line */ + if (input == '\n') + please_display_more_prompt |= 1; + /* Adjust the terminal height for any overlap, so that + * no lines get lost off the top. */ + if (len >= terminal_width) { + int quot, rem; + quot = len / terminal_width; + rem = len - (quot * terminal_width); + if (quot) { + if (rem) + page_height -= quot; + else + page_height -= (quot - 1); + } + } + if (++lines >= page_height) { + please_display_more_prompt |= 1; + } + len = 0; + } + /* + * If we just read a newline from the file being 'mored' and any + * key other than a return is hit, scroll by one page + */ + putc(c, stdout); + len++; + } + fclose(file); + fflush(stdout); + loop: + argv++; + } while (--argc > 0); + end: + return 0; +} diff --git a/util-linux/mount.c b/util-linux/mount.c new file mode 100644 index 000000000..f8ae1df19 --- /dev/null +++ b/util-linux/mount.c @@ -0,0 +1,1712 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mount implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>. + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* Design notes: There is no spec for mount. Remind me to write one. + + mount_main() calls singlemount() which calls mount_it_now(). + + mount_main() can loop through /etc/fstab for mount -a + singlemount() can loop through /etc/filesystems for fstype detection. + mount_it_now() does the actual mount. +*/ + +#include "busybox.h" +#include <mntent.h> + +/* Needed for nfs support only... */ +#include <syslog.h> +#include <sys/utsname.h> +#undef TRUE +#undef FALSE +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <rpc/pmap_clnt.h> + + +// 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), +}; +// 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 */ + +struct { + char *name; + long flags; +} static 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}, + {"swap", MOUNT_SWAP}, + USE_DESKTOP({"user", MOUNT_USERS},) + USE_DESKTOP({"users", MOUNT_USERS},) + ) + + 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}, + {"async", ~MS_SYNCHRONOUS}, + {"atime", ~MS_NOATIME}, + {"noatime", MS_NOATIME}, + {"diratime", ~MS_NODIRATIME}, + {"nodiratime", MS_NODIRATIME}, + {"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 +}; + +#define VECTOR_SIZE(v) (sizeof(v) / sizeof((v)[0])) + +/* Append mount options to string */ +static void append_mount_options(char **oldopts, 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 int parse_mount_options(char *options, char **unrecognized) +{ + int flags = MS_SILENT; + + // Loop through options + for (;;) { + int i; + char *comma = strchr(options, ','); + + if (comma) *comma = 0; + + // Find this option in mount_options + for (i = 0; i < VECTOR_SIZE(mount_options); i++) { + if (!strcasecmp(mount_options[i].name, options)) { + long fl = mount_options[i].flags; + if (fl < 0) flags &= fl; + else flags |= fl; + break; + } + } + // If unrecognized not NULL, append unrecognized mount options */ + if (unrecognized && i == VECTOR_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); + } + + // Advance to next option, or finish + if (comma) { + *comma = ','; + options = ++comma; + } else break; + } + + return flags; +} + +// Return a list of all block device backed filesystems + +static llist_t *get_block_backed_filesystems(void) +{ + static const char *const filesystems[] = { + "/etc/filesystems", + "/proc/filesystems", + 0 + }; + char *fs, *buf; + llist_t *list = 0; + int i; + FILE *f; + + for (i = 0; filesystems[i]; i++) { + f = fopen(filesystems[i], "r"); + if (!f) continue; + + while ((buf = xmalloc_getline(f)) != 0) { + 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; +} + +llist_t *fslist = 0; + +#if ENABLE_FEATURE_CLEAN_UP +static void delete_block_backed_filesystems(void) +{ + llist_free(fslist, free); +} +#else +void delete_block_backed_filesystems(void); +#endif + +#if ENABLE_FEATURE_MTAB_SUPPORT +static int useMtab = 1; +static int fakeIt; +#else +#define useMtab 0 +#define fakeIt 0 +#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, int vfsflags, char *filteropts) +{ + int rc = 0; + + if (fakeIt) goto mtab; + + // Mount, with fallback to read-only if necessary. + + for (;;) { + rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type, + vfsflags, filteropts); + if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS)) + break; + 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 (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) { + char *fsname; + FILE *mountTable = setmntent(bb_path_mtab_file, "a+"); + 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].flags != MS_REMOUNT; i++) + if (mount_options[i].flags > 0 && (mount_options[i].flags & vfsflags)) + append_mount_options(&(mp->mnt_opts), mount_options[i].name); + + // 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 = "bind"; + } + mp->mnt_freq = mp->mnt_passno = 0; + + // Write and close. + + addmntent(mountTable, mp); + endmntent(mountTable); + if (ENABLE_FEATURE_CLEAN_UP) { + free(mp->mnt_dir); + free(fsname); + } + } + ret: + return rc; +} + +#if ENABLE_FEATURE_MOUNT_NFS + +/* + * Linux NFS mount + * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com> + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + * + * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port + * numbers to be specified on the command line. + * + * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>: + * Omit the call to connect() for Linux version 1.3.11 or later. + * + * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com> + * Implemented the "bg", "fg" and "retry" mount options for NFS. + * + * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org> + * - added Native Language Support + * + * Modified by Olaf Kirch and Trond Myklebust for new NFS code, + * plus NFSv3 stuff. + */ + +/* This is just a warning of a common mistake. Possibly this should be a + * uclibc faq entry rather than in busybox... */ +#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) +#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support." +#endif + +#define MOUNTPORT 635 +#define MNTPATHLEN 1024 +#define MNTNAMLEN 255 +#define FHSIZE 32 +#define FHSIZE3 64 + +typedef char fhandle[FHSIZE]; + +typedef struct { + unsigned int fhandle3_len; + char *fhandle3_val; +} fhandle3; + +enum mountstat3 { + MNT_OK = 0, + MNT3ERR_PERM = 1, + MNT3ERR_NOENT = 2, + MNT3ERR_IO = 5, + MNT3ERR_ACCES = 13, + MNT3ERR_NOTDIR = 20, + MNT3ERR_INVAL = 22, + MNT3ERR_NAMETOOLONG = 63, + MNT3ERR_NOTSUPP = 10004, + MNT3ERR_SERVERFAULT = 10006, +}; +typedef enum mountstat3 mountstat3; + +struct fhstatus { + unsigned int fhs_status; + union { + fhandle fhs_fhandle; + } fhstatus_u; +}; +typedef struct fhstatus fhstatus; + +struct mountres3_ok { + fhandle3 fhandle; + struct { + unsigned int auth_flavours_len; + char *auth_flavours_val; + } auth_flavours; +}; +typedef struct mountres3_ok mountres3_ok; + +struct mountres3 { + mountstat3 fhs_status; + union { + mountres3_ok mountinfo; + } mountres3_u; +}; +typedef struct mountres3 mountres3; + +typedef char *dirpath; + +typedef char *name; + +typedef struct mountbody *mountlist; + +struct mountbody { + name ml_hostname; + dirpath ml_directory; + mountlist ml_next; +}; +typedef struct mountbody mountbody; + +typedef struct groupnode *groups; + +struct groupnode { + name gr_name; + groups gr_next; +}; +typedef struct groupnode groupnode; + +typedef struct exportnode *exports; + +struct exportnode { + dirpath ex_dir; + groups ex_groups; + exports ex_next; +}; +typedef struct exportnode exportnode; + +struct ppathcnf { + int pc_link_max; + short pc_max_canon; + short pc_max_input; + short pc_name_max; + short pc_path_max; + short pc_pipe_buf; + u_char 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 */ +}; + + +/* + * We need to translate between nfs status return values and + * the local errno values which may not be the same. + * + * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno: + * "after #include <errno.h> the symbol errno is reserved for any use, + * it cannot even be used as a struct tag or field name". + */ + +#ifndef EDQUOT +#define EDQUOT ENOSPC +#endif + +// Convert each NFSERR_BLAH into EBLAH + +static const struct { + int stat; + int 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; + static char buf[sizeof("unknown nfs status return value: ") + sizeof(int)*3]; + + for (i = 0; nfs_errtbl[i].stat != -1; i++) { + if (nfs_errtbl[i].stat == status) + return strerror(nfs_errtbl[i].errnum); + } + sprintf(buf, "unknown nfs status return value: %d", status); + return buf; +} + +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) + +/* + * nfs_mount_version according to the sources seen at compile time. + */ +static int nfs_mount_version; +static int kernel_version; + +/* + * 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) +{ + if (kernel_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 struct pmap * +get_mountport(struct sockaddr_in *server_addr, + long unsigned prog, + long unsigned version, + long unsigned proto, + long unsigned port) +{ + struct pmaplist *pmap; + static struct pmap p = {0, 0, 0, 0}; + + server_addr->sin_port = PMAPPORT; + pmap = pmap_getmaps(server_addr); + + if (version > MAX_NFSPROT) + version = MAX_NFSPROT; + if (!prog) + prog = MOUNTPROG; + p.pm_prog = prog; + p.pm_vers = version; + p.pm_prot = proto; + p.pm_port = port; + + while (pmap) { + if (pmap->pml_map.pm_prog != prog) + goto next; + if (!version && p.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 && p.pm_prot && pmap->pml_map.pm_prot != proto) || + (port && pmap->pml_map.pm_port != port)) + goto next; + memcpy(&p, &pmap->pml_map, sizeof(p)); +next: + pmap = pmap->pml_next; + } + if (!p.pm_vers) + p.pm_vers = MOUNTVERS; + if (!p.pm_port) + p.pm_port = MOUNTPORT; + if (!p.pm_prot) + p.pm_prot = IPPROTO_TCP; + return &p; +} + +static int daemonize(void) +{ + int fd; + int pid = fork(); + if (pid < 0) /* error */ + return -errno; + if (pid > 0) /* parent */ + return 0; + /* child */ + fd = xopen(bb_dev_null, O_RDWR); + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) close(fd); + setsid(); + openlog(applet_name, LOG_PID, LOG_DAEMON); + logmode = LOGMODE_SYSLOG; + return 1; +} + +// TODO +static inline int we_saw_this_host_before(const char *hostname) +{ + 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, int 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; + int bg; + int soft; + int intr; + int posix; + int nocto; + int noac; + int nolock; + int retry; + int tcp; + int mountprog; + int mountvers; + int nfsprog; + int nfsvers; + int retval; + + find_kernel_nfs_mount_version(); + + daemonized = 0; + mounthost = NULL; + retval = ETIMEDOUT; + msock = fsock = -1; + mclient = NULL; + + /* NB: hostname, mounthost, filteropts must be free()d prior to return */ + + filteropts = xstrdup(filteropts); /* going to trash it later... */ + + hostname = xstrdup(mp->mnt_fsname); + /* mount_main() guarantees that ':' is there */ + s = strchr(hostname, ':'); + pathname = s + 1; + *s = '\0'; + /* Ignore all but first hostname in replicated mounts + until they can be fully supported. (mack@sgi.com) */ + s = strchr(hostname, ','); + if (s) { + *s = '\0'; + bb_error_msg("warning: multiple hostnames not supported"); + } + + server_addr.sin_family = AF_INET; + if (!inet_aton(hostname, &server_addr.sin_addr)) { + hp = gethostbyname(hostname); + if (hp == NULL) { + bb_herror_msg("%s", hostname); + goto fail; + } + if (hp->h_length > 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; + + bg = 0; + soft = 0; + intr = 0; + posix = 0; + nocto = 0; + nolock = 0; + noac = 0; + retry = 10000; /* 10000 minutes ~ 1 week */ + tcp = 0; + + mountprog = MOUNTPROG; + mountvers = 0; + port = 0; + mountport = 0; + nfsprog = 100003; + nfsvers = 0; + + /* parse options */ + + for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) { + char *opteq = strchr(opt, '='); + if (opteq) { + const char *const options[] = { + /* 0 */ "rsize", + /* 1 */ "wsize", + /* 2 */ "timeo", + /* 3 */ "retrans", + /* 4 */ "acregmin", + /* 5 */ "acregmax", + /* 6 */ "acdirmin", + /* 7 */ "acdirmax", + /* 8 */ "actimeo", + /* 9 */ "retry", + /* 10 */ "port", + /* 11 */ "mountport", + /* 12 */ "mounthost", + /* 13 */ "mountprog", + /* 14 */ "mountvers", + /* 15 */ "nfsprog", + /* 16 */ "nfsvers", + /* 17 */ "vers", + /* 18 */ "proto", + /* 19 */ "namlen", + /* 20 */ "addr", + NULL + }; + int val = xatoi_u(opteq + 1); + *opteq = '\0'; + switch (index_in_str_array(options, opt)) { + case 0: // "rsize" + data.rsize = val; + break; + case 1: // "wsize" + data.wsize = val; + break; + case 2: // "timeo" + data.timeo = val; + break; + case 3: // "retrans" + data.retrans = val; + break; + case 4: // "acregmin" + data.acregmin = val; + break; + case 5: // "acregmax" + data.acregmax = val; + break; + case 6: // "acdirmin" + data.acdirmin = val; + break; + case 7: // "acdirmax" + data.acdirmax = val; + break; + case 8: // "actimeo" + data.acregmin = val; + data.acregmax = val; + data.acdirmin = val; + data.acdirmax = val; + break; + case 9: // "retry" + retry = val; + break; + case 10: // "port" + port = val; + break; + case 11: // "mountport" + mountport = val; + break; + case 12: // "mounthost" + mounthost = xstrndup(opteq+1, + strcspn(opteq+1," \t\n\r,")); + break; + case 13: // "mountprog" + mountprog = val; + break; + case 14: // "mountvers" + mountvers = val; + break; + case 15: // "nfsprog" + nfsprog = val; + break; + case 16: // "nfsvers" + case 17: // "vers" + nfsvers = val; + break; + case 18: // "proto" + if (!strncmp(opteq+1, "tcp", 3)) + tcp = 1; + else if (!strncmp(opteq+1, "udp", 3)) + tcp = 0; + else + bb_error_msg("warning: unrecognized proto= option"); + break; + case 19: // "namlen" + if (nfs_mount_version >= 2) + data.namlen = val; + else + bb_error_msg("warning: option namlen is not supported\n"); + break; + case 20: // "addr" - ignore + break; + default: + bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val); + goto fail; + } + } + else { + const char *const options[] = { + "bg", + "fg", + "soft", + "hard", + "intr", + "posix", + "cto", + "ac", + "tcp", + "udp", + "lock", + NULL + }; + int val = 1; + if (!strncmp(opt, "no", 2)) { + val = 0; + opt += 2; + } + switch (index_in_str_array(options, opt)) { + case 0: // "bg" + bg = val; + break; + case 1: // "fg" + bg = !val; + 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; + 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); + 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(); /* parent or error */ + 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; + } else { + if (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; + 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); + + pm_mnt = get_mountport(&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 = 0; + } + 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 = 0; + close(msock); + } + + /* 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); + + 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 */ + 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 = "nfs"; + retval = mount_it_now(mp, vfsflags, (char*)&data); + goto ret; + +fail: /* abort */ + + if (msock != -1) { + if (mclient) { + auth_destroy(mclient->cl_auth); + clnt_destroy(mclient); + } + close(msock); + } + if (fsock != -1) + 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, int 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, 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")) mp->mnt_type = 0; + + // Might this be an CIFS filesystem? + + if (ENABLE_FEATURE_MOUNT_CIFS && + (!mp->mnt_type || !strcmp(mp->mnt_type,"cifs")) && + (mp->mnt_fsname[0]==mp->mnt_fsname[1] && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\'))) + { + struct hostent *he; + char ip[32], *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; + he = gethostbyname(mp->mnt_fsname+2); + *s = '\\'; + if (!he) goto report_error; + + // Insert ip=... option into string flags. (NOTE: Add IPv6 support.) + + sprintf(ip, "ip=%d.%d.%d.%d", he->h_addr[0], he->h_addr[1], + he->h_addr[2], he->h_addr[3]); + parse_mount_options(ip, &filteropts); + + // compose new unc '\\server-ip\share' + + mp->mnt_fsname = xasprintf("\\\\%s%s", ip+3, + strchr(mp->mnt_fsname+2,'\\')); + + // lock is required + vfsflags |= MS_MANDLOCK; + + mp->mnt_type = "cifs"; + rc = mount_it_now(mp, vfsflags, filteropts); + if (ENABLE_FEATURE_CLEAN_UP) free(mp->mnt_fsname); + 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.) + + if (!lstat(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 = 0; + switch (set_loop(&(mp->mnt_fsname), loopFile, 0)) { + case 0: + case 1: + break; + default: + bb_error_msg( errno == EPERM || errno == EACCES + ? bb_msg_perm_denied_are_you_root + : "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); + + // Loop through filesystem types until mount succeeds or we run out + + else { + + /* 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 (rc && errno == EBUSY && ignore_busy) rc = 0; + if (rc < 0) + /* perror here sometimes says "mounting ... on ... failed: Success" */ + bb_error_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. + +const char must_be_root[] = "you must be root"; + +int mount_main(int argc, char **argv) +{ + enum { OPT_ALL = 0x10 }; + + char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0; + char *opt_o; + 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 = (getuid() != 0);) + + /* parse long options, like --bind and --move. Note that -o option + * and --option are synonymous. Yes, this means --remount,rw works. */ + + for (i = j = 0; i < argc; i++) { + if (argv[i][0] == '-' && argv[i][1] == '-') { + append_mount_options(&cmdopts, argv[i]+2); + } else argv[j++] = argv[i]; + } + argv[j] = 0; + argc = j; + + // Parse remaining options + + opt = getopt32(argc, argv, "o:t:rwanfvs", &opt_o, &fstype); + if (opt & 0x1) append_mount_options(&cmdopts, opt_o); // -o + //if (opt & 0x2) // -t + if (opt & 0x4) append_mount_options(&cmdopts, "ro"); // -r + if (opt & 0x8) append_mount_options(&cmdopts, "rw"); // -w + //if (opt & 0x10) // -a + if (opt & 0x20) USE_FEATURE_MTAB_SUPPORT(useMtab = 0); // -n + if (opt & 0x40) USE_FEATURE_MTAB_SUPPORT(fakeIt = 1); // -f + //if (opt & 0x80) // -v: verbose (ignore) + //if (opt & 0x100) // -s: sloppy (ignore) + argv += optind; + argc -= optind; + + // Three or more non-option arguments? Die with a usage message. + + if (argc > 2) bb_show_usage(); + + // If we have no arguments, show currently mounted filesystems + + if (!argc) { + if (!(opt & OPT_ALL)) { + 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, bb_common_bufsiz1, + sizeof(bb_common_bufsiz1))) + { + // Don't show rootfs. FIXME: why?? + 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; + } + } else storage_path = bb_simplify_path(argv[0]); + + // 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 (argc == 2) { + 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; + rc = singlemount(mtpair, 0); + goto clean_up; + } + + i = parse_mount_options(cmdopts, 0); + 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 = mount("", argv[0], "", i, ""); + if (rc) bb_perror_msg_and_die("%s", argv[0]); + goto clean_up; + } + + // Open either fstab or mtab + + fstabname = "/etc/fstab"; + if (i & MS_REMOUNT) { + 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 *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair); + + // Get next fstab entry + + if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1 + + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0), + sizeof(bb_common_bufsiz1)/2)) + { + // Were we looking for something specific? + + if (argc) { + + // If we didn't find anything, complain. + + if (!mtnext->mnt_fsname) + bb_error_msg_and_die("can't find %s in %s", + argv[0], fstabname); + + mtcur = mtnext; + 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); + rc = singlemount(mtcur, 0); + free(mtcur->mnt_opts); + } + goto clean_up; + } + + /* If we're trying to mount something specific and this isn't it, + * skip it. Note we must match both the exact text in fstab (ala + * "proc") or a full path from root */ + + if (argc) { + + // 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 = mtnext; + + // If we're mounting all. + + } else { + // Do we need to match a filesystem type? + // TODO: support "-t type1,type2"; "-t notype1,type2" + + if (fstype && strcmp(mtcur->mnt_type, 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 (singlemount(mtcur, 1)) { + /* Count number of failed mounts */ + rc++; + } + } + } + if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab); + +clean_up: + + 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 000000000..bd02302c7 --- /dev/null +++ b/util-linux/pivot_root.c @@ -0,0 +1,24 @@ +/* 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 "busybox.h" + +extern int pivot_root(const char * new_root,const char * put_old); + +int pivot_root_main(int argc, char **argv) +{ + if (argc != 3) + bb_show_usage(); + + if (pivot_root(argv[1],argv[2]) < 0) + bb_perror_msg_and_die("pivot_root"); + + return EXIT_SUCCESS; +} diff --git a/util-linux/rdate.c b/util-linux/rdate.c new file mode 100644 index 000000000..12105953d --- /dev/null +++ b/util-linux/rdate.c @@ -0,0 +1,86 @@ +/* vi: set sw=4 ts=4: */ +/* + * The Rdate command will ask a time server for the RFC 868 time + * and optionally set the system time. + * + * by Sterling Huxley <sterling@europa.com> + * + * Licensed under GPL v2 or later, see file License for details. +*/ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> + +#include "busybox.h" + + +static const int RFC_868_BIAS = 2208988800UL; + +static void socket_timeout(int sig) +{ + bb_error_msg_and_die("timeout connecting to time server"); +} + +static time_t askremotedate(const char *host) +{ + unsigned long nett; + struct sockaddr_in s_in; + int fd; + + bb_lookup_host(&s_in, host); + s_in.sin_port = bb_lookup_port("time", "tcp", 37); + + /* Add a timeout for dead or inaccessible servers */ + alarm(10); + signal(SIGALRM, socket_timeout); + + fd = xconnect_tcp_v4(&s_in); + + 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) +{ + time_t remote_time; + unsigned long flags; + + opt_complementary = "-1"; + flags = getopt32(argc, 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/readprofile.c b/util-linux/readprofile.c new file mode 100644 index 000000000..dd810f021 --- /dev/null +++ b/util-linux/readprofile.c @@ -0,0 +1,236 @@ +/* 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 Mi¶kiewicz <misiek@pld.ORG.PL> + * - added Native Language Support + * 1999-09-01 Stephane Eranian <eranian@cello.hpl.hp.com> + * - 64bit clean patch + * 3Feb2001 Andrew Morton <andrewm@uow.edu.au> + * - -M option to write profile multiplier. + * 2001-11-07 Werner Almesberger <wa@almesberger.net> + * - byte order auto-detection and -n option + * 2001-11-09 Werner Almesberger <wa@almesberger.net> + * - skip step size (index 0) + * 2002-03-09 John Levon <moz@compsoc.man.ac.uk> + * - make maplineno do something + * 2002-11-28 Mads Martin Joergensen + + * - also try /boot/System.map-`uname -r` + * 2003-04-09 Werner Almesberger <wa@almesberger.net> + * - fixed off-by eight error and improved heuristics in byte order detection + * 2003-08-12 Nikita Danilov <Nikita@Namesys.COM> + * - added -s option; example of use: + * "readprofile -s -m /boot/System.map-test | grep __d_lookup | sort -n -k3" + * + * Taken from util-linux and adapted for busybox by + * Paul Mundt <lethal@linux-sh.org>. + */ + +#include "busybox.h" +#include <sys/utsname.h> + +#define S_LEN 128 + +/* These are the defaults */ +static const char defaultmap[] = "/boot/System.map"; +static const char defaultpro[] = "/proc/profile"; + +int readprofile_main(int argc, char **argv) +{ + FILE *map; + const char *mapFile, *proFile, *mult = 0; + 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 optAll = 0, optInfo = 0, optReset = 0; + int optVerbose = 0, optNative = 0; + int optBins = 0, optSub = 0; + int maplineno = 1; + int header_printed; + +#define next (current^1) + + proFile = defaultpro; + mapFile = defaultmap; + + opt_complementary = "nn:aa:bb:ss:ii:rr:vv"; + getopt32(argc, argv, "M:m:p:nabsirv", + &mult, &mapFile, &proFile, + &optNative, &optAll, &optBins, &optSub, + &optInfo, &optReset, &optVerbose); + + if (optReset || mult) { + int multiplier, fd, to_write; + + /* + * When writing the multiplier, if the length of the write is + * not sizeof(int), the multiplier is not changed + */ + if (mult) { + multiplier = xatoi_u(mult); + to_write = sizeof(int); + } else { + multiplier = 0; + to_write = 1; /* sth different from sizeof(int) */ + } + + fd = xopen(defaultpro, O_WRONLY); + + if (full_write(fd, &multiplier, to_write) != to_write) + bb_perror_msg_and_die("error writing %s", defaultpro); + + close(fd); + return EXIT_SUCCESS; + } + + /* + * Use an fd for the profiling buffer, to skip stdio overhead + */ + len = INT_MAX; + buf = xmalloc_open_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(mapFile, "r"); + + 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/setarch.c b/util-linux/setarch.c new file mode 100644 index 000000000..d7e1c0917 --- /dev/null +++ b/util-linux/setarch.c @@ -0,0 +1,52 @@ +/* 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 <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <sys/personality.h> + +#include "busybox.h" + +int setarch_main(int ATTRIBUTE_UNUSED argc, char **argv) +{ + int pers = -1; + + /* 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" + */ +retry: + if (argv[0][5] == '6') /* linux64 */ + pers = PER_LINUX; + else if (argv[0][5] == '3') /* linux32 */ + pers = PER_LINUX32; + else if (pers == -1 && argv[1] != NULL) { + pers = PER_LINUX32; + ++argv; + goto retry; + } + + /* make user actually gave us something to do */ + ++argv; + if (argv[0] == NULL) + bb_show_usage(); + + /* Try to set personality */ + if (personality(pers) >= 0) { + + /* Try to execute the program */ + execvp(argv[0], argv); + } + + bb_perror_msg_and_die("%s", argv[0]); +} diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c new file mode 100644 index 000000000..8434d121c --- /dev/null +++ b/util-linux/swaponoff.c @@ -0,0 +1,77 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini swapon/swapoff implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Licensed under the GPL version 2, see the file LICENSE in this tarball. + */ + +#include "busybox.h" +#include <mntent.h> +#include <sys/swap.h> + + +static int swap_enable_disable(char *device) +{ + int status; + struct stat st; + + xstat(device, &st); + + /* test for holes */ + if (S_ISREG(st.st_mode)) + if (st.st_blocks * 512 < st.st_size) + bb_error_msg_and_die("swap file has holes"); + + if (applet_name[5] == 'n') + status = swapon(device, 0); + else + status = swapoff(device); + + if (status != 0) { + bb_perror_msg("%s", 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; +} + +#define DO_ALL 0x01 + +int swap_on_off_main(int argc, char **argv) +{ + int ret; + + if (argc == 1) + bb_show_usage(); + + ret = getopt32(argc, argv, "a"); + if (ret & DO_ALL) + return do_em_all(); + + ret = 0; + while (*++argv) + ret += swap_enable_disable(*argv); + return ret; +} diff --git a/util-linux/switch_root.c b/util-linux/switch_root.c new file mode 100644 index 000000000..4c23f69da --- /dev/null +++ b/util-linux/switch_root.c @@ -0,0 +1,122 @@ +/* vi: set sw=4 ts=4: */ +/* Copyright 2005 Rob Landley <rob@landley.net> + * + * Switch from rootfs to another filesystem as the root of the mount tree. + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" +#include <sys/vfs.h> + + +// Make up for header deficiencies. + +#ifndef RAMFS_MAGIC +#define RAMFS_MAGIC 0x858458f6 +#endif + +#ifndef TMPFS_MAGIC +#define TMPFS_MAGIC 0x01021994 +#endif + +#ifndef MS_MOVE +#define MS_MOVE 8192 +#endif + +dev_t rootdev; + +// Recursively delete contents of rootfs. + +static void delete_contents(char *directory) +{ + 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)) { + if((dir = opendir(directory))) { + while ((d = readdir(dir))) { + char *newdir=d->d_name; + + // Skip . and .. + if(*newdir=='.' && (!newdir[1] || (newdir[1]=='.' && !newdir[2]))) + continue; + + // Recurse to delete contents + newdir = alloca(strlen(directory) + strlen(d->d_name) + 2); + sprintf(newdir, "%s/%s", directory, d->d_name); + delete_contents(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[]) +{ + char *newroot, *console=NULL; + struct stat st1, st2; + struct statfs stfs; + + // Parse args (-c console) + + opt_complementary = "-2"; + getopt32(argc, argv, "c:", &console); + + // Change to new root directory and verify it's a different fs. + + newroot=argv[optind++]; + + if (chdir(newroot) || 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 filesystemss.) + + if (lstat("/init", &st1) || !S_ISREG(st1.st_mode) || statfs("/", &stfs) || + (stfs.f_type != RAMFS_MAGIC && stfs.f_type != TMPFS_MAGIC) || + getpid() != 1) + { + bb_error_msg_and_die("not rootfs"); + } + + // Zap everything out of rootdev + + delete_contents("/"); + + // Overmount / with newdir and chroot into it. The chdir is needed to + // recalculate "." and ".." links. + + if (mount(".", "/", NULL, MS_MOVE, NULL) || chroot(".") || chdir("/")) + bb_error_msg_and_die("moving root"); + + // If a new console specified, redirect stdin/stdout/stderr to that. + + if (console) { + close(0); + if(open(console, O_RDWR) < 0) + bb_error_msg_and_die("bad console '%s'", console); + dup2(0, 1); + dup2(0, 2); + } + + // Exec real init. (This is why we must be pid 1.) + execv(argv[optind], argv+optind); + bb_error_msg_and_die("bad init '%s'", argv[optind]); +} diff --git a/util-linux/umount.c b/util-linux/umount.c new file mode 100644 index 000000000..6ba72aed1 --- /dev/null +++ b/util-linux/umount.c @@ -0,0 +1,151 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini umount implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * Copyright (C) 2005 by Rob Landley <rob@landley.net> + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" +#include <mntent.h> +#include <getopt.h> + +#define OPTION_STRING "flDnravd" +#define OPT_FORCE 1 +#define OPT_LAZY 2 +#define OPT_DONTFREELOOP 4 +#define OPT_NO_MTAB 8 +#define OPT_REMOUNT 16 +#define OPT_ALL (ENABLE_FEATURE_UMOUNT_ALL ? 32 : 0) + +int umount_main(int argc, char **argv) +{ + int doForce; + char path[2*PATH_MAX]; + struct mntent me; + FILE *fp; + int status = EXIT_SUCCESS; + unsigned opt; + struct mtab_list { + char *dir; + char *device; + struct mtab_list *next; + } *mtl, *m; + + /* Parse any options */ + + opt = getopt32(argc, argv, OPTION_STRING); + + 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 = 0; + + /* 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("cannot open %s", bb_path_mtab_file); + } else { + while (getmntent_r(fp, &me, path, sizeof(path))) { + 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)) { + m = 0; + if (!argc) bb_show_usage(); + } + + // 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; + // Get next command line argument (and look it up in mtab list) + else if (!argc--) break; + else { + 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 (curstat) + bb_error_msg("forced umount of %s failed!", zapit); + } + + // If still can't umount, maybe remount read-only? + if (curstat && (opt & OPT_REMOUNT) && errno == EBUSY && m) { + curstat = mount(m->device, zapit, NULL, MS_REMOUNT|MS_RDONLY, NULL); + bb_error_msg(curstat ? "cannot remount %s read-only" : + "%s busy - remounted read-only", m->device); + } + + if (curstat) { + status = EXIT_FAILURE; + bb_perror_msg("cannot umount %s", 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_DONTFREELOOP) && 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. + while (m && (m = m->next)) + 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; + } + } + + return status; +} |