summaryrefslogtreecommitdiffstats
path: root/disk-utils
diff options
context:
space:
mode:
Diffstat (limited to 'disk-utils')
-rw-r--r--disk-utils/Makemodule.am231
-rw-r--r--disk-utils/addpart.840
-rw-r--r--disk-utils/addpart.c66
-rw-r--r--disk-utils/blockdev.893
-rw-r--r--disk-utils/blockdev.c506
-rw-r--r--disk-utils/cfdisk.8214
-rw-r--r--disk-utils/cfdisk.c2788
-rw-r--r--disk-utils/cramfs.h114
-rw-r--r--disk-utils/cramfs_common.c109
-rw-r--r--disk-utils/delpart.827
-rw-r--r--disk-utils/delpart.c65
-rw-r--r--disk-utils/fdformat.879
-rw-r--r--disk-utils/fdformat.c256
-rw-r--r--disk-utils/fdisk-list.c543
-rw-r--r--disk-utils/fdisk-list.h47
-rw-r--r--disk-utils/fdisk-menu.c1112
-rw-r--r--disk-utils/fdisk.8392
-rw-r--r--disk-utils/fdisk.c1180
-rw-r--r--disk-utils/fdisk.h56
-rw-r--r--disk-utils/fsck.8418
-rw-r--r--disk-utils/fsck.c1688
-rw-r--r--disk-utils/fsck.cramfs.861
-rw-r--r--disk-utils/fsck.cramfs.c715
-rw-r--r--disk-utils/fsck.minix.8167
-rw-r--r--disk-utils/fsck.minix.c1435
-rw-r--r--disk-utils/isosize.855
-rw-r--r--disk-utils/isosize.c167
-rw-r--r--disk-utils/minix_programs.h118
-rw-r--r--disk-utils/mkfs.894
-rw-r--r--disk-utils/mkfs.bfs.858
-rw-r--r--disk-utils/mkfs.bfs.c301
-rw-r--r--disk-utils/mkfs.c134
-rw-r--r--disk-utils/mkfs.cramfs.890
-rw-r--r--disk-utils/mkfs.cramfs.c925
-rw-r--r--disk-utils/mkfs.minix.889
-rw-r--r--disk-utils/mkfs.minix.c840
-rw-r--r--disk-utils/mkswap.8158
-rw-r--r--disk-utils/mkswap.c563
-rw-r--r--disk-utils/partx.8198
-rw-r--r--disk-utils/partx.c1071
-rw-r--r--disk-utils/raw.899
-rw-r--r--disk-utils/raw.c276
-rw-r--r--disk-utils/resizepart.838
-rw-r--r--disk-utils/resizepart.c118
-rw-r--r--disk-utils/sfdisk.8661
-rw-r--r--disk-utils/sfdisk.c2438
-rw-r--r--disk-utils/swaplabel.869
-rw-r--r--disk-utils/swaplabel.c194
48 files changed, 21156 insertions, 0 deletions
diff --git a/disk-utils/Makemodule.am b/disk-utils/Makemodule.am
new file mode 100644
index 0000000..b39aff8
--- /dev/null
+++ b/disk-utils/Makemodule.am
@@ -0,0 +1,231 @@
+
+if BUILD_MINIX
+sbin_PROGRAMS += fsck.minix
+dist_man_MANS += disk-utils/fsck.minix.8
+fsck_minix_SOURCES = \
+ disk-utils/fsck.minix.c \
+ disk-utils/minix_programs.h \
+ lib/ismounted.c
+fsck_minix_LDADD = $(LDADD) libcommon.la
+
+sbin_PROGRAMS += mkfs.minix
+dist_man_MANS += disk-utils/mkfs.minix.8
+mkfs_minix_SOURCES = \
+ disk-utils/minix_programs.h \
+ disk-utils/mkfs.minix.c \
+ lib/ismounted.c
+mkfs_minix_LDADD = $(LDADD) libcommon.la
+
+check_PROGRAMS += test_mkfs_minix
+test_mkfs_minix_SOURCES = $(mkfs_minix_SOURCES)
+test_mkfs_minix_LDADD = $(mkfs_minix_LDADD)
+test_mkfs_minix_CFLAGS = $(AM_CFLAGS) -DTEST_SCRIPT
+endif
+
+
+if BUILD_MKFS
+sbin_PROGRAMS += mkfs
+dist_man_MANS += disk-utils/mkfs.8
+mkfs_SOURCES = disk-utils/mkfs.c
+endif
+
+
+if BUILD_ISOSIZE
+usrbin_exec_PROGRAMS += isosize
+dist_man_MANS += disk-utils/isosize.8
+isosize_SOURCES = disk-utils/isosize.c
+isosize_LDADD = $(LDADD) libcommon.la
+endif
+
+
+if BUILD_BFS
+sbin_PROGRAMS += mkfs.bfs
+dist_man_MANS += disk-utils/mkfs.bfs.8
+mkfs_bfs_SOURCES = \
+ disk-utils/mkfs.bfs.c
+mkfs_bfs_LDADD = $(LDADD) libcommon.la
+endif
+
+
+if BUILD_MKSWAP
+sbin_PROGRAMS += mkswap
+dist_man_MANS += disk-utils/mkswap.8
+mkswap_SOURCES = \
+ disk-utils/mkswap.c \
+ lib/ismounted.c
+mkswap_LDADD = $(LDADD) libcommon.la
+
+mkswap_CFLAGS = $(AM_CFLAGS)
+if BUILD_LIBUUID
+mkswap_CFLAGS += -I$(ul_libuuid_incdir)
+mkswap_LDADD += libuuid.la
+endif
+if BUILD_LIBBLKID
+mkswap_CFLAGS += -I$(ul_libblkid_incdir)
+mkswap_LDADD += libblkid.la
+endif
+if HAVE_SELINUX
+mkswap_LDADD += -lselinux
+endif
+endif # BUILD_MKSWAP
+
+
+if BUILD_SWAPLABEL
+sbin_PROGRAMS += swaplabel
+dist_man_MANS += disk-utils/swaplabel.8
+swaplabel_SOURCES = \
+ disk-utils/swaplabel.c \
+ lib/swapprober.c \
+ include/swapprober.h
+
+swaplabel_CFLAGS = $(AM_CFLAGS) -I$(ul_libblkid_incdir)
+swaplabel_LDADD = $(LDADD) libblkid.la libcommon.la
+
+if BUILD_LIBUUID
+swaplabel_LDADD += libuuid.la
+swaplabel_CFLAGS += -I$(ul_libuuid_incdir)
+endif
+endif #BUILD_SWAPLABEL
+
+
+if BUILD_FSCK
+sbin_PROGRAMS += fsck
+dist_man_MANS += disk-utils/fsck.8
+fsck_SOURCES = disk-utils/fsck.c lib/monotonic.c
+fsck_LDADD = $(LDADD) libmount.la libblkid.la libcommon.la $(REALTIME_LIBS)
+fsck_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir) -I$(ul_libblkid_incdir)
+endif
+
+
+if BUILD_RAW
+sbin_PROGRAMS += raw
+dist_man_MANS += disk-utils/raw.8
+raw_SOURCES = disk-utils/raw.c
+endif
+
+
+if BUILD_CRAMFS
+cramfs_common_sources = disk-utils/cramfs.h disk-utils/cramfs_common.c
+sbin_PROGRAMS += fsck.cramfs
+fsck_cramfs_SOURCES = disk-utils/fsck.cramfs.c $(cramfs_common_sources)
+fsck_cramfs_LDADD = $(LDADD) -lz libcommon.la
+dist_man_MANS += disk-utils/fsck.cramfs.8
+
+sbin_PROGRAMS += mkfs.cramfs
+mkfs_cramfs_SOURCES = disk-utils/mkfs.cramfs.c $(cramfs_common_sources)
+mkfs_cramfs_LDADD = $(LDADD) -lz libcommon.la
+dist_man_MANS += disk-utils/mkfs.cramfs.8
+endif
+
+if BUILD_FDFORMAT
+usrsbin_exec_PROGRAMS += fdformat
+dist_man_MANS += disk-utils/fdformat.8
+fdformat_SOURCES = disk-utils/fdformat.c
+fdformat_LDADD = $(LDADD) libcommon.la
+endif
+
+if BUILD_BLOCKDEV
+sbin_PROGRAMS += blockdev
+dist_man_MANS += disk-utils/blockdev.8
+blockdev_SOURCES = disk-utils/blockdev.c
+blockdev_LDADD = $(LDADD) libcommon.la
+endif
+
+
+if BUILD_FDISK
+sbin_PROGRAMS += fdisk
+dist_man_MANS += disk-utils/fdisk.8
+fdisk_SOURCES = \
+ disk-utils/fdisk.c \
+ disk-utils/fdisk.h \
+ disk-utils/fdisk-menu.c \
+ disk-utils/fdisk-list.c \
+ disk-utils/fdisk-list.h
+
+fdisk_LDADD = $(LDADD) libcommon.la libfdisk.la \
+ libsmartcols.la libtcolors.la $(READLINE_LIBS)
+fdisk_CFLAGS = $(AM_CFLAGS) -I$(ul_libfdisk_incdir) -I$(ul_libsmartcols_incdir)
+
+if HAVE_STATIC_FDISK
+sbin_PROGRAMS += fdisk.static
+fdisk_static_SOURCES = $(fdisk_SOURCES)
+fdisk_static_LDFLAGS = -all-static
+fdisk_static_CFLAGS = $(fdisk_CFLAGS)
+fdisk_static_LDADD = $(fdisk_LDADD) $(READLINE_LIBS_STATIC)
+endif
+endif # BUILD_FDISK
+
+
+if BUILD_SFDISK
+sbin_PROGRAMS += sfdisk
+dist_man_MANS += disk-utils/sfdisk.8
+sfdisk_SOURCES = \
+ disk-utils/sfdisk.c \
+ disk-utils/fdisk-list.c \
+ disk-utils/fdisk-list.h
+
+sfdisk_LDADD = $(LDADD) libcommon.la libfdisk.la \
+ libsmartcols.la libtcolors.la $(READLINE_LIBS)
+sfdisk_CFLAGS = $(AM_CFLAGS) -I$(ul_libfdisk_incdir) -I$(ul_libsmartcols_incdir)
+
+if HAVE_STATIC_SFDISK
+sbin_PROGRAMS += sfdisk.static
+sfdisk_static_SOURCES = $(sfdisk_SOURCES)
+sfdisk_static_LDFLAGS = -all-static
+sfdisk_static_CFLAGS = $(sfdisk_CFLAGS)
+sfdisk_static_LDADD = $(sfdisk_LDADD) $(READLINE_LIBS_STATIC)
+endif
+endif # BUILD_SFDISK
+
+
+if BUILD_CFDISK
+sbin_PROGRAMS += cfdisk
+dist_man_MANS += disk-utils/cfdisk.8
+cfdisk_SOURCES = disk-utils/cfdisk.c
+cfdisk_LDADD = \
+ $(LDADD) \
+ libsmartcols.la \
+ libcommon.la \
+ libfdisk.la \
+ libtcolors.la
+cfdisk_CFLAGS = \
+ $(AM_CFLAGS) \
+ -I$(ul_libfdisk_incdir) \
+ -I$(ul_libsmartcols_incdir)
+
+if BUILD_LIBMOUNT
+cfdisk_CFLAGS += -I$(ul_libmount_incdir)
+cfdisk_LDADD += libmount.la
+endif
+
+if HAVE_SLANG
+cfdisk_LDADD += -lslang
+else
+cfdisk_CFLAGS += $(NCURSES_CFLAGS)
+cfdisk_LDADD += $(NCURSES_LIBS)
+endif
+endif # BUILD_CFDISK
+
+
+if BUILD_PARTX
+usrsbin_exec_PROGRAMS += partx addpart delpart resizepart
+dist_man_MANS += \
+ disk-utils/addpart.8 \
+ disk-utils/delpart.8 \
+ disk-utils/resizepart.8 \
+ disk-utils/partx.8
+
+addpart_SOURCES = disk-utils/addpart.c
+addpart_LDADD = $(LDADD) libcommon.la
+
+delpart_SOURCES = disk-utils/delpart.c
+delpart_LDADD = $(LDADD) libcommon.la
+
+resizepart_SOURCES = disk-utils/resizepart.c
+resizepart_LDADD = $(LDADD) libcommon.la
+
+partx_SOURCES = disk-utils/partx.c
+partx_CFLAGS = $(AM_CFLAGS) -I$(ul_libblkid_incdir) -I$(ul_libsmartcols_incdir)
+partx_LDADD = $(LDADD) libblkid.la libcommon.la libsmartcols.la
+
+endif # BUILD_PARTX
diff --git a/disk-utils/addpart.8 b/disk-utils/addpart.8
new file mode 100644
index 0000000..97d11ad
--- /dev/null
+++ b/disk-utils/addpart.8
@@ -0,0 +1,40 @@
+.\" addpart.8 -- man page for addpart
+.\" Copyright 2007 Karel Zak <kzak@redhat.com>
+.\" Copyright 2007 Red Hat, Inc.
+.\" May be distributed under the GNU General Public License
+.TH ADDPART 8 "January 2015" "util-linux" "System Administration"
+.SH NAME
+addpart \- tell the kernel about the existence of a partition
+.SH SYNOPSIS
+.B addpart
+.I device partition start length
+.SH DESCRIPTION
+.B addpart
+tells the Linux kernel about the existence of the specified partition.
+The command is a simple wrapper around the "add partition" ioctl.
+
+This command doesn't manipulate partitions on a block device.
+
+.SH PARAMETERS
+.TP
+.I device
+The disk device.
+.TP
+.I partition
+The partition number.
+.TP
+.I start
+The beginning of the partition (in 512-byte sectors).
+.TP
+.I length
+The length of the partition (in 512-byte sectors).
+
+.SH SEE ALSO
+.BR delpart (8),
+.BR fdisk (8),
+.BR parted (8),
+.BR partprobe (8),
+.BR partx (8)
+.SH AVAILABILITY
+The addpart command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/addpart.c b/disk-utils/addpart.c
new file mode 100644
index 0000000..33abb36
--- /dev/null
+++ b/disk-utils/addpart.c
@@ -0,0 +1,66 @@
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "c.h"
+#include "nls.h"
+#include "partx.h"
+#include "strutils.h"
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s <disk device> <partition number> <start> <length>\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Tell the kernel about the existence of a specified partition.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ printf(USAGE_HELP_OPTIONS(16));
+ printf(USAGE_MAN_TAIL("addpart(8)"));
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int c, fd;
+
+ static const struct option longopts[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0},
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ while ((c = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
+ switch (c) {
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ if (argc != 5) {
+ warnx(_("not enough arguments"));
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ if ((fd = open(argv[1], O_RDONLY)) < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), argv[1]);
+
+ if (partx_add_partition(fd,
+ strtou32_or_err(argv[2], _("invalid partition number argument")),
+ strtou64_or_err(argv[3], _("invalid start argument")),
+ strtou64_or_err(argv[4], _("invalid length argument"))))
+ err(EXIT_FAILURE, _("failed to add partition"));
+
+ return EXIT_SUCCESS;
+}
diff --git a/disk-utils/blockdev.8 b/disk-utils/blockdev.8
new file mode 100644
index 0000000..b38720f
--- /dev/null
+++ b/disk-utils/blockdev.8
@@ -0,0 +1,93 @@
+.\" Copyright 1998 Andries E. Brouwer (aeb@cwi.nl)
+.\" Copyright 2007 Karel Zak <kzak@redhat.com>
+.\"
+.\" May be distributed under the GNU General Public License
+.TH BLOCKDEV 8 "August 2010" "util-linux" "System Administration"
+.SH NAME
+blockdev \- call block device ioctls from the command line
+.SH SYNOPSIS
+.B blockdev
+.RB [ \-q ]
+.RB [ \-v ]
+.I command
+.RI [ command \&...\&]
+.I device
+.RI [ device \&...\&]
+.br
+.B blockdev
+.B \-\-report
+.RI [ device \&...\&]
+.br
+.B blockdev
+.BR \-h | \-V
+.SH DESCRIPTION
+The utility
+.B blockdev
+allows one to call block device ioctls from the command line.
+.SH OPTIONS
+.IP "\fB\-q\fP"
+Be quiet.
+.IP "\fB\-v\fP"
+Be verbose.
+.IP "\fB\-\-report\fP"
+Print a report for the specified device. It is possible to give multiple
+devices. If none is given, all devices which appear in /proc/partitions are
+shown. Note that the partition StartSec is in 512-byte sectors.
+.IP "\fB\-h\fR, \fB\-\-help\fR"
+Display help text and exit.
+.IP "\fB\-V\fR, \fB\-\-version\fR"
+Print version and exit.
+.SH COMMANDS
+It is possible to give multiple devices and multiple commands.
+.IP "\fB\-\-flushbufs\fP"
+Flush buffers.
+.IP "\fB\-\-getalignoff\fP"
+Get alignment offset.
+.IP "\fB\-\-getbsz\fP"
+Print blocksize in bytes. This size does not describe device topology. It's
+size used internally by kernel and it maybe modified (for example) by
+filesystem driver on mount.
+.IP "\fB\-\-getdiscardzeroes\fP"
+Get discard zeroes support status.
+.IP "\fB\-\-getfra\fP"
+Get filesystem readahead in 512-byte sectors.
+.IP "\fB\-\-getiomin\fP"
+Get minimum I/O size.
+.IP "\fB\-\-getioopt\fP"
+Get optimal I/O size.
+.IP "\fB\-\-getmaxsect\fP"
+Get max sectors per request
+.IP "\fB\-\-getpbsz\fP"
+Get physical block (sector) size.
+.IP "\fB\-\-getra\fP"
+Print readahead (in 512-byte sectors).
+.IP "\fB\-\-getro\fP"
+Get read-only. Print 1 if the device is read-only, 0 otherwise.
+.IP "\fB\-\-getsize64\fP"
+Print device size in bytes.
+.IP "\fB\-\-getsize\fP"
+Print device size (32-bit!) in sectors. Deprecated in favor of the \-\-getsz option.
+.IP "\fB\-\-getss\fP"
+Print logical sector size in bytes \(en usually 512.
+.IP "\fB\-\-getsz\fP"
+Get size in 512-byte sectors.
+.IP "\fB\-\-rereadpt\fP"
+Reread partition table
+.IP "\fB\-\-setbsz\fP \fIbytes\fP"
+Set blocksize. Note that the block size is specific to the current file
+descriptor opening the block device, so the change of block size only persists
+for as long as blockdev has the device open, and is lost once blockdev exits.
+.IP "\fB\-\-setfra\fP \fIsectors\fP"
+Set filesystem readahead (same like \-\-setra on 2.6 kernels).
+.IP "\fB\-\-setra\fP \fIsectors\fP"
+Set readahead (in 512-byte sectors).
+.IP "\fB\-\-setro\fP"
+Set read-only. The currently active access to the device may not be affected by the change. For example
+filesystem already mounted in read-write mode will not be affected. The change applies after remount.
+.IP "\fB\-\-setrw\fP"
+Set read-write.
+.SH AUTHORS
+blockdev was written by Andries E.\& Brouwer and rewritten by Karel Zak.
+.SH AVAILABILITY
+The blockdev command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/blockdev.c b/disk-utils/blockdev.c
new file mode 100644
index 0000000..f425d15
--- /dev/null
+++ b/disk-utils/blockdev.c
@@ -0,0 +1,506 @@
+/*
+ * blockdev.c --- Do various simple block device ioctls from the command line
+ * aeb, 991028
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include "c.h"
+#include "nls.h"
+#include "blkdev.h"
+#include "pathnames.h"
+#include "closestream.h"
+#include "sysfs.h"
+
+struct bdc {
+ long ioc; /* ioctl code */
+ const char *iocname; /* ioctl name (e.g. BLKROSET) */
+ long argval; /* default argument */
+
+ const char *name; /* --setfoo */
+ const char *argname; /* argument name or NULL */
+
+ const char *help;
+
+ int argtype;
+ int flags;
+};
+
+/* command flags */
+enum {
+ FL_NOPTR = (1 << 1), /* does not assume pointer (ARG_INT only)*/
+ FL_NORESULT = (1 << 2) /* does not return any data */
+};
+
+/* ioctl argument types */
+enum {
+ ARG_NONE,
+ ARG_USHRT,
+ ARG_INT,
+ ARG_UINT,
+ ARG_LONG,
+ ARG_ULONG,
+ ARG_LLONG,
+ ARG_ULLONG
+};
+
+#define IOCTL_ENTRY( io ) .ioc = io, .iocname = # io
+
+static const struct bdc bdcms[] =
+{
+ {
+ IOCTL_ENTRY(BLKROSET),
+ .name = "--setro",
+ .argtype = ARG_INT,
+ .argval = 1,
+ .flags = FL_NORESULT,
+ .help = N_("set read-only")
+ },{
+ IOCTL_ENTRY(BLKROSET),
+ .name = "--setrw",
+ .argtype = ARG_INT,
+ .argval = 0,
+ .flags = FL_NORESULT,
+ .help = N_("set read-write")
+ },{
+ IOCTL_ENTRY(BLKROGET),
+ .name = "--getro",
+ .argtype = ARG_INT,
+ .argval = -1,
+ .help = N_("get read-only")
+ },{
+ IOCTL_ENTRY(BLKDISCARDZEROES),
+ .name = "--getdiscardzeroes",
+ .argtype = ARG_UINT,
+ .argval = -1,
+ .help = N_("get discard zeroes support status")
+ },{
+ IOCTL_ENTRY(BLKSSZGET),
+ .name = "--getss",
+ .argtype = ARG_INT,
+ .argval = -1,
+ .help = N_("get logical block (sector) size")
+ },{
+ IOCTL_ENTRY(BLKPBSZGET),
+ .name = "--getpbsz",
+ .argtype = ARG_UINT,
+ .argval = -1,
+ .help = N_("get physical block (sector) size")
+ },{
+ IOCTL_ENTRY(BLKIOMIN),
+ .name = "--getiomin",
+ .argtype = ARG_UINT,
+ .argval = -1,
+ .help = N_("get minimum I/O size")
+ },{
+ IOCTL_ENTRY(BLKIOOPT),
+ .name = "--getioopt",
+ .argtype = ARG_UINT,
+ .argval = -1,
+ .help = N_("get optimal I/O size")
+ },{
+ IOCTL_ENTRY(BLKALIGNOFF),
+ .name = "--getalignoff",
+ .argtype = ARG_INT,
+ .argval = -1,
+ .help = N_("get alignment offset in bytes")
+ },{
+ IOCTL_ENTRY(BLKSECTGET),
+ .name = "--getmaxsect",
+ .argtype = ARG_USHRT,
+ .argval = -1,
+ .help = N_("get max sectors per request")
+ },{
+ IOCTL_ENTRY(BLKBSZGET),
+ .name = "--getbsz",
+ .argtype = ARG_INT,
+ .argval = -1,
+ .help = N_("get blocksize")
+ },{
+ IOCTL_ENTRY(BLKBSZSET),
+ .name = "--setbsz",
+ .argname = "<bytes>",
+ .argtype = ARG_INT,
+ .flags = FL_NORESULT,
+ .help = N_("set blocksize on file descriptor opening the block device")
+ },{
+ IOCTL_ENTRY(BLKGETSIZE),
+ .name = "--getsize",
+ .argtype = ARG_ULONG,
+ .argval = -1,
+ .help = N_("get 32-bit sector count (deprecated, use --getsz)")
+ },{
+ IOCTL_ENTRY(BLKGETSIZE64),
+ .name = "--getsize64",
+ .argtype = ARG_ULLONG,
+ .argval = -1,
+ .help = N_("get size in bytes")
+ },{
+ IOCTL_ENTRY(BLKRASET),
+ .name = "--setra",
+ .argname = "<sectors>",
+ .argtype = ARG_INT,
+ .flags = FL_NOPTR | FL_NORESULT,
+ .help = N_("set readahead")
+ },{
+ IOCTL_ENTRY(BLKRAGET),
+ .name = "--getra",
+ .argtype = ARG_LONG,
+ .argval = -1,
+ .help = N_("get readahead")
+ },{
+ IOCTL_ENTRY(BLKFRASET),
+ .name = "--setfra",
+ .argname = "<sectors>",
+ .argtype = ARG_INT,
+ .flags = FL_NOPTR | FL_NORESULT,
+ .help = N_("set filesystem readahead")
+ },{
+ IOCTL_ENTRY(BLKFRAGET),
+ .name = "--getfra",
+ .argtype = ARG_LONG,
+ .argval = -1,
+ .help = N_("get filesystem readahead")
+ },{
+ IOCTL_ENTRY(BLKFLSBUF),
+ .name = "--flushbufs",
+ .help = N_("flush buffers")
+ },{
+ IOCTL_ENTRY(BLKRRPART),
+ .name = "--rereadpt",
+ .help = N_("reread partition table")
+ }
+};
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ size_t i;
+
+ fputs(USAGE_HEADER, stdout);
+ printf(_(
+ " %1$s [-v|-q] commands devices\n"
+ " %1$s --report [devices]\n"
+ " %1$s -h|-V\n"
+ ), program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, stdout);
+ puts( _("Call block device ioctls from the command line."));
+
+ fputs(USAGE_OPTIONS, stdout);
+ puts( _(" -q quiet mode"));
+ puts( _(" -v verbose mode"));
+ puts( _(" --report print report for specified (or all) devices"));
+ fputs(USAGE_SEPARATOR, stdout);
+ printf(USAGE_HELP_OPTIONS(16));
+
+ fputs(USAGE_SEPARATOR, stdout);
+ puts( _("Available commands:"));
+ printf(_(" %-25s get size in 512-byte sectors\n"), "--getsz");
+ for (i = 0; i < ARRAY_SIZE(bdcms); i++) {
+ if (bdcms[i].argname)
+ printf(" %s %-*s %s\n", bdcms[i].name,
+ (int)(24 - strlen(bdcms[i].name)),
+ bdcms[i].argname, _(bdcms[i].help));
+ else
+ printf(" %-25s %s\n", bdcms[i].name,
+ _(bdcms[i].help));
+ }
+
+ printf(USAGE_MAN_TAIL("blockdev(8)"));
+ exit(EXIT_SUCCESS);
+}
+
+static int find_cmd(char *s)
+{
+ size_t j;
+
+ for (j = 0; j < ARRAY_SIZE(bdcms); j++)
+ if (!strcmp(s, bdcms[j].name))
+ return j;
+ return -1;
+}
+
+static void do_commands(int fd, char **argv, int d);
+static void report_header(void);
+static void report_device(char *device, int quiet);
+static void report_all_devices(void);
+
+int main(int argc, char **argv)
+{
+ int fd, d, j, k;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ if (argc < 2) {
+ warnx(_("not enough arguments"));
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ /* -V not together with commands */
+ if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version"))
+ print_version(EXIT_SUCCESS);
+ if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
+ usage();
+
+ /* --report not together with other commands */
+ if (!strcmp(argv[1], "--report")) {
+ report_header();
+ if (argc > 2) {
+ for (d = 2; d < argc; d++)
+ report_device(argv[d], 0);
+ } else {
+ report_all_devices();
+ }
+ return EXIT_SUCCESS;
+ }
+
+ /* do each of the commands on each of the devices */
+ /* devices start after last command */
+ for (d = 1; d < argc; d++) {
+ j = find_cmd(argv[d]);
+ if (j >= 0) {
+ if (bdcms[j].argname)
+ d++;
+ continue;
+ }
+ if (!strcmp(argv[d], "--getsz"))
+ continue;
+ if (!strcmp(argv[d], "--")) {
+ d++;
+ break;
+ }
+ if (argv[d][0] != '-')
+ break;
+ }
+
+ if (d >= argc) {
+ warnx(_("no device specified"));
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ for (k = d; k < argc; k++) {
+ fd = open(argv[k], O_RDONLY, 0);
+ if (fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), argv[k]);
+ do_commands(fd, argv, d);
+ close(fd);
+ }
+ return EXIT_SUCCESS;
+}
+
+static void do_commands(int fd, char **argv, int d)
+{
+ int res, i, j;
+ int iarg = 0;
+ unsigned int uarg = 0;
+ unsigned short huarg = 0;
+ long larg = 0;
+ long long llarg = 0;
+ unsigned long lu = 0;
+ unsigned long long llu = 0;
+ int verbose = 0;
+
+ for (i = 1; i < d; i++) {
+ if (!strcmp(argv[i], "-v")) {
+ verbose = 1;
+ continue;
+ }
+ if (!strcmp(argv[i], "-q")) {
+ verbose = 0;
+ continue;
+ }
+
+ if (!strcmp(argv[i], "--getsz")) {
+ res = blkdev_get_sectors(fd, &llu);
+ if (res == 0)
+ printf("%lld\n", llu);
+ else
+ errx(EXIT_FAILURE,
+ _("could not get device size"));
+ continue;
+ }
+
+ j = find_cmd(argv[i]);
+ if (j == -1) {
+ warnx(_("Unknown command: %s"), argv[i]);
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ switch (bdcms[j].argtype) {
+ default:
+ case ARG_NONE:
+ res = ioctl(fd, bdcms[j].ioc, 0);
+ break;
+ case ARG_USHRT:
+ huarg = bdcms[j].argval;
+ res = ioctl(fd, bdcms[j].ioc, &huarg);
+ break;
+ case ARG_INT:
+ if (bdcms[j].argname) {
+ if (i == d - 1) {
+ warnx(_("%s requires an argument"),
+ bdcms[j].name);
+ errtryhelp(EXIT_FAILURE);
+ }
+ iarg = atoi(argv[++i]);
+ } else
+ iarg = bdcms[j].argval;
+
+ res = bdcms[j].flags & FL_NOPTR ?
+ ioctl(fd, bdcms[j].ioc, iarg) :
+ ioctl(fd, bdcms[j].ioc, &iarg);
+ break;
+ case ARG_UINT:
+ uarg = bdcms[j].argval;
+ res = ioctl(fd, bdcms[j].ioc, &uarg);
+ break;
+ case ARG_LONG:
+ larg = bdcms[j].argval;
+ res = ioctl(fd, bdcms[j].ioc, &larg);
+ break;
+ case ARG_LLONG:
+ llarg = bdcms[j].argval;
+ res = ioctl(fd, bdcms[j].ioc, &llarg);
+ break;
+ case ARG_ULONG:
+ lu = bdcms[j].argval;
+ res = ioctl(fd, bdcms[j].ioc, &lu);
+ break;
+ case ARG_ULLONG:
+ llu = bdcms[j].argval;
+ res = ioctl(fd, bdcms[j].ioc, &llu);
+ break;
+ }
+
+ if (res == -1) {
+ warn(_("ioctl error on %s"), bdcms[j].iocname);
+ if (verbose)
+ printf(_("%s failed.\n"), _(bdcms[j].help));
+ exit(EXIT_FAILURE);
+ }
+
+ if (bdcms[j].argtype == ARG_NONE ||
+ (bdcms[j].flags & FL_NORESULT)) {
+ if (verbose)
+ printf(_("%s succeeded.\n"), _(bdcms[j].help));
+ continue;
+ }
+
+ if (verbose)
+ printf("%s: ", _(bdcms[j].help));
+
+ switch (bdcms[j].argtype) {
+ case ARG_USHRT:
+ printf("%hu\n", huarg);
+ break;
+ case ARG_INT:
+ printf("%d\n", iarg);
+ break;
+ case ARG_UINT:
+ printf("%u\n", uarg);
+ break;
+ case ARG_LONG:
+ printf("%ld\n", larg);
+ break;
+ case ARG_LLONG:
+ printf("%lld\n", llarg);
+ break;
+ case ARG_ULONG:
+ printf("%lu\n", lu);
+ break;
+ case ARG_ULLONG:
+ printf("%llu\n", llu);
+ break;
+ }
+ }
+}
+
+static void report_all_devices(void)
+{
+ FILE *procpt;
+ char line[200];
+ char ptname[200 + 1];
+ char device[210];
+ int ma, mi, sz;
+
+ procpt = fopen(_PATH_PROC_PARTITIONS, "r");
+ if (!procpt)
+ err(EXIT_FAILURE, _("cannot open %s"), _PATH_PROC_PARTITIONS);
+
+ while (fgets(line, sizeof(line), procpt)) {
+ if (sscanf(line, " %d %d %d %200[^\n ]",
+ &ma, &mi, &sz, ptname) != 4)
+ continue;
+
+ sprintf(device, "/dev/%s", ptname);
+ report_device(device, 1);
+ }
+
+ fclose(procpt);
+}
+
+static void report_device(char *device, int quiet)
+{
+ int fd;
+ int ro, ssz, bsz;
+ long ra;
+ unsigned long long bytes;
+ uint64_t start = 0;
+ char start_str[11] = { "\0" };
+ struct stat st;
+
+ fd = open(device, O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ if (!quiet)
+ warn(_("cannot open %s"), device);
+ return;
+ }
+
+ ro = ssz = bsz = 0;
+ ra = 0;
+ if (fstat(fd, &st) == 0) {
+ dev_t disk;
+ struct path_cxt *pc;
+
+ pc = ul_new_sysfs_path(st.st_rdev, NULL, NULL);
+ if (pc &&
+ sysfs_blkdev_get_wholedisk(pc, NULL, 0, &disk) == 0 &&
+ disk != st.st_rdev) {
+
+ if (ul_path_read_u64(pc, &start, "start") != 0)
+ /* TRANSLATORS: Start sector not available. Max. 10 letters. */
+ sprintf(start_str, "%10s", _("N/A"));
+ }
+ ul_unref_path(pc);
+ }
+ if (!*start_str)
+ sprintf(start_str, "%10ju", start);
+
+ if (ioctl(fd, BLKROGET, &ro) == 0 &&
+ ioctl(fd, BLKRAGET, &ra) == 0 &&
+ ioctl(fd, BLKSSZGET, &ssz) == 0 &&
+ ioctl(fd, BLKBSZGET, &bsz) == 0 &&
+ blkdev_get_size(fd, &bytes) == 0) {
+ printf("%s %5ld %5d %5d %s %15lld   %s\n",
+ ro ? "ro" : "rw", ra, ssz, bsz, start_str, bytes, device);
+ } else {
+ if (!quiet)
+ warnx(_("ioctl error on %s"), device);
+ }
+
+ close(fd);
+}
+
+static void report_header(void)
+{
+ printf(_("RO RA SSZ BSZ StartSec Size Device\n"));
+}
diff --git a/disk-utils/cfdisk.8 b/disk-utils/cfdisk.8
new file mode 100644
index 0000000..a61aad7
--- /dev/null
+++ b/disk-utils/cfdisk.8
@@ -0,0 +1,214 @@
+.\" cfdisk.8 -- man page for cfdisk
+.\" Copyright 1994 Kevin E. Martin (martin@cs.unc.edu)
+.\" Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one.
+.\"
+.TH CFDISK 8 "March 2014" "util-linux" "System Administration"
+.SH NAME
+cfdisk \- display or manipulate a disk partition table
+.SH SYNOPSIS
+.B cfdisk
+[options]
+.RI [ device ]
+.SH DESCRIPTION
+.B cfdisk
+is a curses-based program for partitioning any block device.
+The default device is
+.IR /dev/sda .
+
+Note that
+.B cfdisk
+provides basic partitioning functionality with a user-friendly interface.
+If you need advanced features, use
+.BR fdisk (8)
+instead.
+
+Since version 2.25
+.B cfdisk
+supports MBR (DOS), GPT, SUN and SGI disk labels, but no longer provides any
+functionality for CHS (Cylinder-Head-Sector) addressing. CHS has
+never been important for Linux, and this addressing concept does not make any
+sense for new devices.
+
+Since version 2.25
+.B cfdisk
+also does not provide a 'print' command any more.
+This functionality is provided by the utilities
+.BR partx (8)
+and
+.BR lsblk (8)
+in a very comfortable and rich way.
+
+If you want to remove an old partition table from a device, use
+.BR wipefs (8).
+
+.SH OPTIONS
+.TP
+.BR \-h , " \-\-help"
+Display help text and exit.
+.TP
+.BR \-L , " \-\-color" [ = \fIwhen\fR]
+Colorize the output. The optional argument \fIwhen\fP
+can be \fBauto\fR, \fBnever\fR or \fBalways\fR. If the \fIwhen\fR argument is omitted,
+it defaults to \fBauto\fR. The colors can be disabled, for the current built-in default
+see \fB\-\-help\fR output. See also the COLORS section.
+.TP
+\fB\-\-lock\fR[=\fImode\fR]
+Use exclusive BSD lock for device or file it operates. The optional argument
+\fImode\fP can be \fByes\fR, \fBno\fR (or 1 and 0) or \fBnonblock\fR. If the \fImode\fR
+argument is omitted, it defaults to \fB"yes"\fR. This option overwrites
+environment variable \fB$LOCK_BLOCK_DEVICE\fR. The default is not to use any
+lock at all, but it's recommended to avoid collisions with udevd or other
+tools.
+.TP
+.BR \-V , " \-\-version"
+Display version information and exit.
+.TP
+.BR \-z , " \-\-zero"
+Start with an in-memory zeroed partition table. This option does not zero the
+partition table on the disk; rather, it simply starts the program without
+reading the existing partition table. This option allows you to create a new
+partition table from scratch or from an sfdisk-compatible script.
+
+.SH COMMANDS
+The commands for
+.B cfdisk
+can be entered by pressing the corresponding key (pressing
+.I Enter
+after the command is not necessary). Here is a list of the available
+commands:
+.TP
+.B b
+Toggle the bootable flag of the current partition. This allows you to
+select which primary partition is bootable on the drive. This command may not
+be available for all partition label types.
+.TP
+.B d
+Delete the current partition. This will convert the current partition
+into free space and merge it with any free space immediately
+surrounding the current partition. A partition already marked as free
+space or marked as unusable cannot be deleted.
+.TP
+.B h
+Show the help screen.
+.TP
+.B n
+Create a new partition from free space.
+.B cfdisk
+then prompts you for the size of the partition you want to create.
+The default size is equal to the entire available free space at the current
+position.
+
+The size may be followed by a multiplicative suffix: KiB (=1024),
+MiB (=1024*1024), and so on for GiB, TiB, PiB, EiB, ZiB and YiB
+(the "iB" is optional, e.g., "K" has the same meaning as "KiB").
+.TP
+.B q
+Quit the program. This will exit the program without writing any data to
+the disk.
+.TP
+.B s
+Sort the partitions in ascending start-sector order. When deleting and
+adding partitions, it is likely that the numbering of the partitions will
+no longer match their order on the disk. This command restores that match.
+.TP
+.B t
+Change the partition type. By default, new partitions are created as
+.I Linux
+partitions.
+.TP
+.B u
+Dump the current in-memory partition table to an sfdisk-compatible script file.
+.sp
+The script files are compatible between \fBcfdisk\fR, \fBfdisk\fR, \fBsfdisk\fR
+and other libfdisk applications. For more details see
+.BR sfdisk (8).
+.sp
+It is also possible to load an sfdisk-script into \fBcfdisk\fR if there is
+no partition table on the device or when you start \fBcfdisk\fR with the
+\fB--zero\fR command-line option.
+.TP
+.B W
+Write the partition table to disk (you must enter an uppercase W). Since
+this might destroy data on the disk, you must either confirm or deny
+the write by entering `yes' or `no'. If you enter `yes',
+.B cfdisk
+will write the partition table to disk and then tell the kernel to re-read the
+partition table from the disk.
+
+The re-reading of the partition table does not always work. In such a
+case you need to inform the kernel about any new partitions by using
+.BR partprobe (8)
+or
+.BR partx (8),
+or by rebooting the system.
+.TP
+.B x
+Toggle extra information about a partition.
+.TP
+.IR "Up Arrow" , " Down Arrow"
+Move the cursor to the previous or next partition. If there are more
+partitions than can be displayed on a screen, you can display the next
+(previous) set of partitions by moving down (up) at the last (first)
+partition displayed on the screen.
+.TP
+.IR "Left Arrow" , " Right Arrow"
+Select the preceding or the next menu item. Hitting \fIEnter\fR will
+execute the currently selected item.
+
+.PP
+All commands can be entered with either uppercase or lowercase
+letters (except for
+.BR W rite).
+When in a submenu or at a prompt, you can hit the
+.I Esc
+key to return to the main menu.
+
+.SH COLORS
+Implicit coloring can be disabled by creating the empty file
+.IR /etc/terminal-colors.d/cfdisk.disable .
+
+See
+.BR terminal-colors.d (5)
+for more details about colorization configuration.
+
+.B cfdisk
+does not support color customization with a color-scheme file.
+
+.SH ENVIRONMENT
+.IP CFDISK_DEBUG=all
+enables cfdisk debug output.
+.IP LIBFDISK_DEBUG=all
+enables libfdisk debug output.
+.IP LIBBLKID_DEBUG=all
+enables libblkid debug output.
+.IP LIBSMARTCOLS_DEBUG=all
+enables libsmartcols debug output.
+.IP LIBSMARTCOLS_DEBUG_PADDING=on
+use visible padding characters. Requires enabled LIBSMARTCOLS_DEBUG.
+.IP LOCK_BLOCK_DEVICE=<mode>
+use exclusive BSD lock. The mode is "1" or "0". See \fB\-\-lock\fR for more details.
+
+.SH AUTHORS
+Karel Zak <kzak@redhat.com>
+.PP
+The current cfdisk implementation is based on the original cfdisk
+from Kevin E. Martin (martin@cs.unc.edu).
+
+.SH SEE ALSO
+.BR fdisk (8),
+.BR parted (8),
+.BR partprobe (8),
+.BR partx (8),
+.BR sfdisk (8)
+.SH AVAILABILITY
+The cfdisk command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/cfdisk.c b/disk-utils/cfdisk.c
new file mode 100644
index 0000000..d96b6e9
--- /dev/null
+++ b/disk-utils/cfdisk.c
@@ -0,0 +1,2788 @@
+/*
+ * cfdisk.c - Display or manipulate a disk partition table.
+ *
+ * Copyright (C) 2014-2015 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu)
+ *
+ * The original cfdisk was inspired by the fdisk program
+ * by A. V. Le Blanc (leblanc@mcc.ac.uk.
+ *
+ * cfdisk is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <assert.h>
+#include <libsmartcols.h>
+#include <sys/ioctl.h>
+#include <libfdisk.h>
+
+#ifdef HAVE_LIBMOUNT
+# include <libmount.h> /* keep it optional for non-linux systems */
+#endif
+
+#ifdef HAVE_SLANG_H
+# include <slang.h>
+#elif defined(HAVE_SLANG_SLANG_H)
+# include <slang/slang.h>
+#endif
+
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 500 /* for inclusion of get_wch */
+#endif
+
+#ifdef HAVE_SLCURSES_H
+# include <slcurses.h>
+#elif defined(HAVE_SLANG_SLCURSES_H)
+# include <slang/slcurses.h>
+#elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR)
+# include <ncursesw/ncurses.h>
+#elif defined(HAVE_NCURSES_H)
+# include <ncurses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+# include <ncurses/ncurses.h>
+#endif
+
+#ifdef HAVE_WIDECHAR
+# include <wctype.h>
+# include <wchar.h>
+#endif
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "strutils.h"
+#include "xalloc.h"
+#include "mbsalign.h"
+#include "mbsedit.h"
+#include "colors.h"
+#include "debug.h"
+#include "list.h"
+#include "blkdev.h"
+
+static const char *default_disks[] = {
+#ifdef __GNU__
+ "/dev/hd0",
+ "/dev/sd0",
+#elif defined(__FreeBSD__)
+ "/dev/ad0",
+ "/dev/da0",
+#else
+ "/dev/sda",
+ "/dev/vda",
+ "/dev/hda",
+#endif
+};
+
+#define ARROW_CURSOR_STRING ">> "
+#define ARROW_CURSOR_DUMMY " "
+#define ARROW_CURSOR_WIDTH (sizeof(ARROW_CURSOR_STRING) - 1)
+
+/* vertical menu */
+#define MENU_V_SPADDING 1 /* space around menu item string */
+
+/* horizontal menu */
+#define MENU_H_SPADDING 0 /* space around menu item string */
+#define MENU_H_BETWEEN 2 /* space between menu items */
+#define MENU_H_PRESTR "["
+#define MENU_H_POSTSTR "]"
+
+#define MENU_TITLE_PADDING 3
+
+#define MENU_H_PRESTR_SZ (sizeof(MENU_H_PRESTR) - 1)
+#define MENU_H_POSTSTR_SZ (sizeof(MENU_H_POSTSTR) - 1)
+
+#define TABLE_START_LINE 4
+#define MENU_START_LINE (ui_lines - 4) /* The menu maybe use two lines */
+#define INFO_LINE (ui_lines - 2)
+#define WARN_LINE INFO_LINE
+#define HINT_LINE (ui_lines - 1)
+
+#define CFDISK_ERR_ESC 5000
+
+#ifndef KEY_ESC
+# define KEY_ESC '\033'
+#endif
+#ifndef KEY_DELETE
+# define KEY_DELETE '\177'
+#endif
+#ifndef KEY_DC
+# define KEY_DC 0423
+#endif
+
+
+/* colors */
+enum {
+ CFDISK_CL_NONE = 0,
+ CFDISK_CL_WARNING,
+ CFDISK_CL_FREESPACE,
+ CFDISK_CL_INFO
+};
+static const int color_pairs[][2] = {
+ /* color foreground, background */
+ [CFDISK_CL_WARNING] = { COLOR_RED, -1 },
+ [CFDISK_CL_FREESPACE] = { COLOR_GREEN, -1 },
+ [CFDISK_CL_INFO] = { COLOR_BLUE, -1 }
+};
+
+struct cfdisk;
+
+static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx);
+static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf, int key, size_t *idx);
+static struct cfdisk_menu *menu_push(struct cfdisk *cf, struct cfdisk_menuitem *item);
+static struct cfdisk_menu *menu_pop(struct cfdisk *cf);
+static void menu_refresh_size(struct cfdisk *cf);
+
+static int ui_end(void);
+static int ui_refresh(struct cfdisk *cf);
+static void ui_warnx(const char *fmt, ...);
+static void ui_warn(const char *fmt, ...);
+static void ui_info(const char *fmt, ...);
+static void ui_draw_menu(struct cfdisk *cf);
+static int ui_menu_move(struct cfdisk *cf, int key);
+static void ui_menu_resize(struct cfdisk *cf);
+
+static int ui_get_size(struct cfdisk *cf, const char *prompt, uint64_t *res,
+ uint64_t low, uint64_t up, int *expsize);
+
+static int ui_enabled;
+static volatile sig_atomic_t sig_resize;
+static volatile sig_atomic_t sig_die;
+
+/* ncurses LINES and COLS may be actual variables or *macros*, but we need
+ * something portable and writable */
+static size_t ui_lines;
+static size_t ui_cols;
+
+/* menu item */
+struct cfdisk_menuitem {
+ int key; /* keyboard shortcut */
+ const char *name; /* item name */
+ const char *desc; /* item description (hint) */
+ void *userdata;
+};
+
+/* menu */
+struct cfdisk_menu {
+ char *title; /* optional menu title */
+ struct cfdisk_menuitem *items; /* array with menu items */
+ char *ignore;/* string with keys to ignore */
+ size_t width; /* maximal width of the menu item */
+ size_t nitems; /* number of the active menu items */
+ size_t page_sz;/* when menu longer than screen */
+ size_t idx; /* the current menu item */
+ int prefkey;/* preferred menu item */
+ struct cfdisk_menu *prev;
+
+ /* @ignore keys generator */
+ int (*ignore_cb) (struct cfdisk *, char *, size_t);
+
+ unsigned int vertical : 1; /* enable vertical mode */
+};
+
+/* main menu */
+static struct cfdisk_menuitem main_menuitems[] = {
+ { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
+ { 'd', N_("Delete"), N_("Delete the current partition") },
+ { 'r', N_("Resize"), N_("Reduce or enlarge the current partition") },
+ { 'n', N_("New"), N_("Create new partition from free space") },
+ { 'q', N_("Quit"), N_("Quit program without writing changes") },
+ { 't', N_("Type"), N_("Change the partition type") },
+ { 'h', N_("Help"), N_("Print help screen") },
+ { 's', N_("Sort"), N_("Fix partitions order") },
+ { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
+ { 'u', N_("Dump"), N_("Dump partition table to sfdisk compatible script file") },
+ { 0, NULL, NULL }
+};
+
+/* extra partinfo in name:value pairs */
+struct cfdisk_extra {
+ char *name;
+ char *data;
+
+ struct list_head exs;
+};
+
+/* line and extra partinfo list_head */
+struct cfdisk_line {
+ char *data; /* line data */
+ struct libscols_table *extra; /* extra info ('X') */
+ WINDOW *w; /* window with extra info */
+};
+
+/* top level control struct */
+struct cfdisk {
+ struct fdisk_context *cxt; /* libfdisk context */
+ struct fdisk_table *table; /* partition table */
+ struct fdisk_table *original_layout; /* original on-disk PT */
+
+ struct cfdisk_menu *menu; /* the current menu */
+
+ int *fields; /* output columns IDs */
+ size_t nfields; /* number of columns IDs */
+
+ char *linesbuf; /* table as string */
+ size_t linesbufsz; /* size of the tb_buf */
+
+ struct cfdisk_line *lines; /* list of lines */
+
+ size_t nlines; /* number of lines */
+ size_t lines_idx; /* current line <0..N>, exclude header */
+ size_t page_sz;
+
+ unsigned int nwrites; /* fdisk_write_disklabel() counter */
+
+ WINDOW *act_win; /* the window currently on the screen */
+
+#ifdef HAVE_LIBMOUNT
+ struct libmnt_table *mtab;
+ struct libmnt_table *fstab;
+ struct libmnt_cache *mntcache;
+#endif
+ unsigned int wrong_order :1, /* PT not in right order */
+ zero_start :1, /* ignore existing partition table */
+ device_is_used : 1, /* don't use re-read ioctl */
+ show_extra :1; /* show extra partinfo */
+};
+
+
+/*
+ * let's use include/debug.h stuff for cfdisk too
+ */
+static UL_DEBUG_DEFINE_MASK(cfdisk);
+UL_DEBUG_DEFINE_MASKNAMES(cfdisk) = UL_DEBUG_EMPTY_MASKNAMES;
+
+#define CFDISK_DEBUG_INIT (1 << 1)
+#define CFDISK_DEBUG_UI (1 << 2)
+#define CFDISK_DEBUG_MENU (1 << 3)
+#define CFDISK_DEBUG_MISC (1 << 4)
+#define CFDISK_DEBUG_TABLE (1 << 5)
+#define CFDISK_DEBUG_ALL 0xFFFF
+
+#define DBG(m, x) __UL_DBG(cfdisk, CFDISK_DEBUG_, m, x)
+
+static void cfdisk_init_debug(void)
+{
+ __UL_INIT_DEBUG_FROM_ENV(cfdisk, CFDISK_DEBUG_, 0, CFDISK_DEBUG);
+}
+
+/* Initialize output columns -- we follow libfdisk fields (usually specific
+ * to the label type.
+ */
+static int cols_init(struct cfdisk *cf)
+{
+ assert(cf);
+
+ free(cf->fields);
+ cf->fields = NULL;
+ cf->nfields = 0;
+
+ return fdisk_label_get_fields_ids(NULL, cf->cxt, &cf->fields, &cf->nfields);
+}
+
+static void die_on_signal(void)
+{
+ DBG(MISC, ul_debug("die on signal."));
+ ui_end();
+ exit(EXIT_FAILURE);
+}
+
+static void resize(void)
+{
+ struct winsize ws;
+
+ if (ioctl(fileno(stdout), TIOCGWINSZ, &ws) != -1
+ && ws.ws_row && ws.ws_col) {
+ ui_lines = ws.ws_row;
+ ui_cols = ws.ws_col;
+#if HAVE_RESIZETERM
+ resizeterm(ws.ws_row, ws.ws_col);
+#endif
+ clearok(stdscr, TRUE);
+ }
+ touchwin(stdscr);
+
+ DBG(UI, ul_debug("ui: resize refresh ui_cols=%zu, ui_lines=%zu",
+ ui_cols, ui_lines));
+ sig_resize = 0;
+}
+
+/* Reads partition in tree-like order from scols
+ */
+static int partition_from_scols(struct fdisk_table *tb,
+ struct libscols_line *ln)
+{
+ struct fdisk_partition *pa = scols_line_get_userdata(ln);
+
+ fdisk_table_add_partition(tb, pa);
+ fdisk_unref_partition(pa);
+
+ if (scols_line_has_children(ln)) {
+ struct libscols_line *chln;
+ struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
+
+ if (!itr)
+ return -EINVAL;
+ while (scols_line_next_child(ln, itr, &chln) == 0)
+ partition_from_scols(tb, chln);
+ scols_free_iter(itr);
+ }
+ return 0;
+}
+
+static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb)
+{
+ struct fdisk_partition *pa;
+ struct fdisk_label *lb;
+ struct fdisk_iter *itr;
+ struct libscols_table *table = NULL;
+ struct libscols_iter *s_itr = NULL;
+ char *res = NULL;
+ size_t i;
+ int tree = 0;
+ struct libscols_line *ln, *ln_cont = NULL;
+
+ DBG(TABLE, ul_debug("convert to string"));
+
+ assert(cf);
+ assert(cf->cxt);
+ assert(cf->fields);
+ assert(tb);
+
+ lb = fdisk_get_label(cf->cxt, NULL);
+ assert(lb);
+
+ itr = fdisk_new_iter(FDISK_ITER_FORWARD);
+ if (!itr)
+ goto done;
+
+ /* get container (e.g. extended partition) */
+ while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
+ if (fdisk_partition_is_nested(pa)) {
+ DBG(TABLE, ul_debug("nested detected, using tree"));
+ tree = SCOLS_FL_TREE;
+ break;
+ }
+ }
+
+ table = scols_new_table();
+ if (!table)
+ goto done;
+ scols_table_enable_maxout(table, 1);
+ scols_table_enable_nowrap(table, 1);
+
+#if !defined(HAVE_LIBNCURSESW) || !defined(HAVE_WIDECHAR)
+ scols_table_enable_ascii(table, 1);
+#endif
+
+ /* headers */
+ for (i = 0; i < cf->nfields; i++) {
+ int fl = 0;
+ const struct fdisk_field *field =
+ fdisk_label_get_field(lb, cf->fields[i]);
+ if (!field)
+ continue;
+
+ if (fdisk_field_is_number(field))
+ fl |= SCOLS_FL_RIGHT;
+ if (fdisk_field_get_id(field) == FDISK_FIELD_TYPE)
+ fl |= SCOLS_FL_TRUNC;
+ if (tree && fdisk_field_get_id(field) == FDISK_FIELD_DEVICE)
+ fl |= SCOLS_FL_TREE;
+
+ if (!scols_table_new_column(table,
+ _(fdisk_field_get_name(field)),
+ fdisk_field_get_width(field), fl))
+ goto done;
+ }
+
+ /* data */
+ fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
+
+ while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
+ struct libscols_line *parent = fdisk_partition_is_nested(pa) ? ln_cont : NULL;
+
+ ln = scols_table_new_line(table, parent);
+ if (!ln)
+ goto done;
+ for (i = 0; i < cf->nfields; i++) {
+ char *cdata = NULL;
+
+ if (fdisk_partition_to_string(pa, cf->cxt,
+ cf->fields[i], &cdata))
+ continue;
+ scols_line_refer_data(ln, i, cdata);
+ }
+ if (tree && fdisk_partition_is_container(pa))
+ ln_cont = ln;
+
+ scols_line_set_userdata(ln, (void *) pa);
+ fdisk_ref_partition(pa);
+ }
+
+ if (scols_table_is_empty(table))
+ goto done;
+
+ scols_table_reduce_termwidth(table, ARROW_CURSOR_WIDTH);
+ scols_print_table_to_string(table, &res);
+
+ /* scols_* code might reorder lines, let's reorder @tb according to the
+ * final output (it's no problem because partitions are addressed by
+ * parno stored within struct fdisk_partition) */
+
+ /* remove all */
+ fdisk_reset_table(tb);
+
+ s_itr = scols_new_iter(SCOLS_ITER_FORWARD);
+ if (!s_itr)
+ goto done;
+
+ /* add all in the right order (don't forget the output is tree) */
+ while (scols_table_next_line(table, s_itr, &ln) == 0) {
+ if (scols_line_get_parent(ln))
+ continue;
+ if (partition_from_scols(tb, ln))
+ break;
+ }
+done:
+ scols_unref_table(table);
+ scols_free_iter(s_itr);
+ fdisk_free_iter(itr);
+
+ return res;
+}
+
+static void cfdisk_free_lines(struct cfdisk *cf)
+{
+ size_t i = 0;
+ while(i < cf->nlines) {
+ scols_unref_table(cf->lines[i].extra);
+
+ DBG(UI, ul_debug("delete window: %p",
+ cf->lines[i].w));
+
+ if (cf->lines[i].w)
+ delwin(cf->lines[i].w);
+ cf->lines[i].w = NULL;
+ ++i;
+ }
+ cf->act_win = NULL;
+ free(cf->lines);
+ cf->lines = NULL;
+}
+/*
+ * Read data about partitions from libfdisk and prepare output lines.
+ */
+static int lines_refresh(struct cfdisk *cf)
+{
+ int rc;
+ char *p;
+ size_t i;
+
+ assert(cf);
+
+ DBG(TABLE, ul_debug("refreshing buffer"));
+
+ free(cf->linesbuf);
+ cfdisk_free_lines(cf);
+ cf->linesbuf = NULL;
+ cf->linesbufsz = 0;
+ cf->lines = NULL;
+ cf->nlines = 0;
+
+ fdisk_unref_table(cf->table);
+ cf->table = NULL;
+
+ /* read partitions and free spaces into cf->table */
+ rc = fdisk_get_partitions(cf->cxt, &cf->table);
+ if (!rc)
+ rc = fdisk_get_freespaces(cf->cxt, &cf->table);
+ if (rc)
+ return rc;
+
+ cf->linesbuf = table_to_string(cf, cf->table);
+ if (!cf->linesbuf)
+ return -ENOMEM;
+
+ cf->linesbufsz = strlen(cf->linesbuf);
+ cf->nlines = fdisk_table_get_nents(cf->table) + 1; /* 1 for header line */
+ cf->page_sz = 0;
+ cf->wrong_order = fdisk_table_wrong_order(cf->table) ? 1 : 0;
+
+ if (MENU_START_LINE - TABLE_START_LINE < cf->nlines)
+ cf->page_sz = MENU_START_LINE - TABLE_START_LINE - 1;
+
+ cf->lines = xcalloc(cf->nlines, sizeof(struct cfdisk_line));
+
+ for (p = cf->linesbuf, i = 0; p && i < cf->nlines; i++) {
+ cf->lines[i].data = p;
+ p = strchr(p, '\n');
+ if (p) {
+ *p = '\0';
+ p++;
+ }
+ cf->lines[i].extra = scols_new_table();
+ scols_table_enable_noheadings(cf->lines[i].extra, 1);
+ scols_table_new_column(cf->lines[i].extra, NULL, 0, SCOLS_FL_RIGHT);
+ scols_table_new_column(cf->lines[i].extra, NULL, 0, SCOLS_FL_TRUNC);
+ }
+
+ return 0;
+}
+
+static struct fdisk_partition *get_current_partition(struct cfdisk *cf)
+{
+ assert(cf);
+ assert(cf->table);
+
+ return fdisk_table_get_partition(cf->table, cf->lines_idx);
+}
+
+static int is_freespace(struct cfdisk *cf, size_t i)
+{
+ struct fdisk_partition *pa;
+
+ assert(cf);
+ assert(cf->table);
+
+ pa = fdisk_table_get_partition(cf->table, i);
+ return fdisk_partition_is_freespace(pa);
+}
+
+/* converts libfdisk FDISK_ASKTYPE_MENU to cfdisk menu and returns user's
+ * response back to libfdisk
+ */
+static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf)
+{
+ struct cfdisk_menuitem *d, *cm;
+ int key;
+ size_t i = 0, nitems;
+ const char *name, *desc;
+
+ assert(ask);
+ assert(cf);
+
+ /* create cfdisk menu according to libfdisk ask-menu, note that the
+ * last cm[] item has to be empty -- so nitems + 1 */
+ nitems = fdisk_ask_menu_get_nitems(ask);
+ cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
+
+ for (i = 0; i < nitems; i++) {
+ if (fdisk_ask_menu_get_item(ask, i, &key, &name, &desc))
+ break;
+ cm[i].key = key;
+ cm[i].desc = desc;
+ cm[i].name = name;
+ }
+
+ /* make the new menu active */
+ menu_push(cf, cm);
+ ui_draw_menu(cf);
+ refresh();
+
+ /* wait for keys */
+ while (!sig_die) {
+ key = getch();
+
+ if (sig_die)
+ break;
+ if (sig_resize)
+ ui_menu_resize(cf);
+ if (ui_menu_move(cf, key) == 0)
+ continue;
+
+ switch (key) {
+ case KEY_ENTER:
+ case '\n':
+ case '\r':
+ d = menu_get_menuitem(cf, cf->menu->idx);
+ if (d)
+ fdisk_ask_menu_set_result(ask, d->key);
+ menu_pop(cf);
+ free(cm);
+ return 0;
+ }
+ }
+
+ if (sig_die)
+ die_on_signal();
+
+ menu_pop(cf);
+ free(cm);
+ return -1;
+}
+
+/* libfdisk callback
+ */
+static int ask_callback(struct fdisk_context *cxt __attribute__((__unused__)),
+ struct fdisk_ask *ask,
+ void *data __attribute__((__unused__)))
+{
+ int rc = 0;
+
+ assert(ask);
+
+ switch(fdisk_ask_get_type(ask)) {
+ case FDISK_ASKTYPE_INFO:
+ ui_info(fdisk_ask_print_get_mesg(ask));
+ break;
+ case FDISK_ASKTYPE_WARNX:
+ ui_warnx(fdisk_ask_print_get_mesg(ask));
+ break;
+ case FDISK_ASKTYPE_WARN:
+ ui_warn(fdisk_ask_print_get_mesg(ask));
+ break;
+ case FDISK_ASKTYPE_MENU:
+ ask_menu(ask, (struct cfdisk *) data);
+ break;
+ default:
+ ui_warnx(_("internal error: unsupported dialog type %d"),
+ fdisk_ask_get_type(ask));
+ return -EINVAL;
+ }
+ return rc;
+}
+
+static int ui_end(void)
+{
+ if (!ui_enabled)
+ return -EINVAL;
+
+#if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
+ SLsmg_gotorc(ui_lines - 1, 0);
+ SLsmg_refresh();
+#else
+ mvcur(0, ui_cols - 1, ui_lines-1, 0);
+#endif
+ curs_set(1);
+ nl();
+ endwin();
+ printf("\n");
+ ui_enabled = 0;
+ return 0;
+}
+
+static void ui_vprint_center(size_t line, int attrs, const char *fmt, va_list ap)
+{
+ size_t width;
+ char *buf = NULL;
+
+ move(line, 0);
+ clrtoeol();
+
+ xvasprintf(&buf, fmt, ap);
+
+ width = mbs_safe_width(buf);
+ if (width > (size_t) ui_cols) {
+ char *p = strrchr(buf + ui_cols, ' ');
+ if (!p)
+ p = buf + ui_cols;
+ *p = '\0';
+ if (line + 1 >= ui_lines)
+ line--;
+ attron(attrs);
+ mvaddstr(line, 0, buf);
+ mvaddstr(line + 1, 0, p+1);
+ attroff(attrs);
+ } else {
+ attron(attrs);
+ mvaddstr(line, (ui_cols - width) / 2, buf);
+ attroff(attrs);
+ }
+ free(buf);
+}
+
+static void ui_center(size_t line, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ ui_vprint_center(line, 0, fmt, ap);
+ va_end(ap);
+}
+
+static void ui_warnx(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (ui_enabled)
+ ui_vprint_center(WARN_LINE,
+ colors_wanted() ? COLOR_PAIR(CFDISK_CL_WARNING) : 0,
+ fmt, ap);
+ else {
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ }
+ va_end(ap);
+}
+
+static void ui_warn(const char *fmt, ...)
+{
+ char *fmt_m;
+ va_list ap;
+
+ xasprintf(&fmt_m, "%s: %m", fmt);
+
+ va_start(ap, fmt);
+ if (ui_enabled)
+ ui_vprint_center(WARN_LINE,
+ colors_wanted() ? COLOR_PAIR(CFDISK_CL_WARNING) : 0,
+ fmt_m, ap);
+ else {
+ vfprintf(stderr, fmt_m, ap);
+ fputc('\n', stderr);
+ }
+ va_end(ap);
+ free(fmt_m);
+}
+
+static void ui_clean_warn(void)
+{
+ move(WARN_LINE, 0);
+ clrtoeol();
+}
+
+static int __attribute__((__noreturn__)) ui_err(int rc, const char *fmt, ...)
+ {
+ va_list ap;
+ ui_end();
+
+ va_start(ap, fmt);
+ fprintf(stderr, "%s: ", program_invocation_short_name);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(errno));
+ va_end(ap);
+
+ exit(rc);
+}
+
+static int __attribute__((__noreturn__)) ui_errx(int rc, const char *fmt, ...)
+ {
+ va_list ap;
+ ui_end();
+
+ va_start(ap, fmt);
+ fprintf(stderr, "%s: ", program_invocation_short_name);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+
+ exit(rc);
+}
+
+static void ui_info(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (ui_enabled)
+ ui_vprint_center(INFO_LINE,
+ colors_wanted() ? COLOR_PAIR(CFDISK_CL_INFO) : 0,
+ fmt, ap);
+ else {
+ vfprintf(stdout, fmt, ap);
+ fputc('\n', stdout);
+ }
+ va_end(ap);
+}
+
+static void ui_clean_info(void)
+{
+ move(INFO_LINE, 0);
+ clrtoeol();
+}
+
+static void ui_hint(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (ui_enabled)
+ ui_vprint_center(HINT_LINE, A_BOLD, fmt, ap);
+ else {
+ vfprintf(stdout, fmt, ap);
+ fputc('\n', stdout);
+ }
+ va_end(ap);
+}
+
+static void ui_clean_hint(void)
+{
+ move(HINT_LINE, 0);
+ clrtoeol();
+}
+
+
+static void sig_handler_die(int dummy __attribute__((__unused__)))
+{
+ sig_die = 1;
+}
+
+static void sig_handler_resize(int dummy __attribute__((__unused__)))
+{
+ sig_resize = 1;
+}
+
+static void menu_refresh_size(struct cfdisk *cf)
+{
+ if (cf->menu && cf->menu->nitems)
+ cf->menu->page_sz = (cf->menu->nitems / (ui_lines - 4)) ? ui_lines - 4 : 0;
+}
+
+static void menu_update_ignore(struct cfdisk *cf)
+{
+ char ignore[128] = { 0 };
+ int i = 0;
+ struct cfdisk_menu *m;
+ struct cfdisk_menuitem *d, *org = NULL;
+ size_t idx;
+
+ assert(cf);
+ assert(cf->menu);
+ assert(cf->menu->ignore_cb);
+
+ m = cf->menu;
+ DBG(MENU, ul_debug("update menu ignored keys"));
+
+ i = m->ignore_cb(cf, ignore, sizeof(ignore));
+ ignore[i] = '\0';
+
+ /* return if no change */
+ if ((!m->ignore && !*ignore)
+ || (m->ignore && *ignore && strcmp(m->ignore, ignore) == 0)) {
+ return;
+ }
+
+ if (!m->prefkey)
+ org = menu_get_menuitem(cf, m->idx);
+
+ free(m->ignore);
+ m->ignore = xstrdup(ignore);
+ m->nitems = 0;
+
+ for (d = m->items; d->name; d++) {
+ if (m->ignore && strchr(m->ignore, d->key))
+ continue;
+ m->nitems++;
+ }
+
+ DBG(MENU, ul_debug("update menu preferred keys"));
+
+ /* refresh menu index to be at the same menuitem or go to the first */
+ if (org && menu_get_menuitem_by_key(cf, org->key, &idx))
+ m->idx = idx;
+ else if (m->prefkey && menu_get_menuitem_by_key(cf, m->prefkey, &idx))
+ m->idx = idx;
+ else
+ m->idx = 0;
+
+ menu_refresh_size(cf);
+}
+
+static struct cfdisk_menu *menu_push(
+ struct cfdisk *cf,
+ struct cfdisk_menuitem *items)
+{
+ struct cfdisk_menu *m = xcalloc(1, sizeof(*m));
+ struct cfdisk_menuitem *d;
+
+ assert(cf);
+
+ DBG(MENU, ul_debug("new menu"));
+
+ m->prev = cf->menu;
+ m->items = items;
+
+ for (d = m->items; d->name; d++) {
+ const char *name = _(d->name);
+ size_t len = mbs_safe_width(name);
+ if (len > m->width)
+ m->width = len;
+ m->nitems++;
+ }
+
+ cf->menu = m;
+
+ menu_refresh_size(cf);
+ return m;
+}
+
+static struct cfdisk_menu *menu_pop(struct cfdisk *cf)
+{
+ struct cfdisk_menu *m = NULL;
+
+ assert(cf);
+
+ DBG(MENU, ul_debug("pop menu"));
+
+ if (cf->menu) {
+ m = cf->menu->prev;
+ free(cf->menu->ignore);
+ free(cf->menu->title);
+ free(cf->menu);
+ }
+ cf->menu = m;
+ return cf->menu;
+}
+
+static void menu_set_title(struct cfdisk_menu *m, const char *title)
+{
+ char *str = NULL;
+
+ if (title) {
+ size_t len = mbs_safe_width(title);
+ if (len + MENU_TITLE_PADDING > m->width)
+ m->width = len + MENU_TITLE_PADDING;
+ str = xstrdup(title);
+ }
+ m->title = str;
+}
+
+
+static int ui_init(struct cfdisk *cf __attribute__((__unused__)))
+{
+ struct sigaction sa;
+
+ DBG(UI, ul_debug("init"));
+
+ /* setup SIGCHLD handler */
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = sig_handler_die;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ sa.sa_handler = sig_handler_resize;
+ sigaction(SIGWINCH, &sa, NULL);
+
+ ui_enabled = 1;
+ initscr();
+
+#ifdef HAVE_USE_DEFAULT_COLORS
+ if (colors_wanted() && has_colors()) {
+ size_t i;
+
+ start_color();
+ use_default_colors();
+ for (i = 1; i < ARRAY_SIZE(color_pairs); i++) /* yeah, start from 1! */
+ init_pair(i, color_pairs[i][0], color_pairs[i][1]);
+ }
+#else
+ colors_off();
+#endif
+
+ cbreak();
+ noecho();
+ nonl();
+ curs_set(0);
+ keypad(stdscr, TRUE);
+
+ return 0;
+}
+
+/* "[ string ]" */
+#define MENU_H_ITEMWIDTH(m) ( MENU_H_PRESTR_SZ \
+ + MENU_H_SPADDING \
+ + (m)->width \
+ + MENU_H_SPADDING \
+ + MENU_H_POSTSTR_SZ)
+
+#define MENU_V_ITEMWIDTH(m) (MENU_V_SPADDING + (m)->width + MENU_V_SPADDING)
+
+
+static size_t menuitem_get_line(struct cfdisk *cf, size_t idx)
+{
+ struct cfdisk_menu *m = cf->menu;
+
+ if (m->vertical) {
+ if (!m->page_sz) /* small menu */
+ return (ui_lines - (cf->menu->nitems + 1)) / 2 + idx;
+ return (idx % m->page_sz) + 1;
+ }
+
+ {
+ size_t len = MENU_H_ITEMWIDTH(m) + MENU_H_BETWEEN; /** item width */
+ size_t items = ui_cols / len; /* items per line */
+
+ if (items == 0)
+ return 0;
+ return MENU_START_LINE + ((idx / items));
+ }
+}
+
+static int menuitem_get_column(struct cfdisk *cf, size_t idx)
+{
+ if (cf->menu->vertical) {
+ size_t nc = MENU_V_ITEMWIDTH(cf->menu);
+ if ((size_t) ui_cols <= nc)
+ return 0;
+ return (ui_cols - nc) / 2;
+ }
+
+ {
+ size_t len = MENU_H_ITEMWIDTH(cf->menu) + MENU_H_BETWEEN; /* item width */
+ size_t items = ui_cols / len; /* items per line */
+ size_t extra = items < cf->menu->nitems ? /* extra space on line */
+ ui_cols % len : /* - multi-line menu */
+ ui_cols - (cf->menu->nitems * len); /* - one line menu */
+
+ if (items == 0)
+ return 0; /* hmm... no space */
+
+ extra += MENU_H_BETWEEN; /* add padding after last item to extra */
+
+ if (idx < items)
+ return (idx * len) + (extra / 2);
+ return ((idx % items) * len) + (extra / 2);
+ }
+}
+
+static int menuitem_on_page(struct cfdisk *cf, size_t idx)
+{
+ struct cfdisk_menu *m = cf->menu;
+
+ if (m->page_sz == 0 ||
+ m->idx / m->page_sz == idx / m->page_sz)
+ return 1;
+ return 0;
+}
+
+static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx)
+{
+ struct cfdisk_menuitem *d;
+ size_t i;
+
+ for (i = 0, d = cf->menu->items; d->name; d++) {
+ if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
+ continue;
+ if (i++ == idx)
+ return d;
+ }
+
+ return NULL;
+}
+
+static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf,
+ int key, size_t *idx)
+{
+ struct cfdisk_menuitem *d;
+
+ for (*idx = 0, d = cf->menu->items; d->name; d++) {
+ if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
+ continue;
+ if (key == d->key)
+ return d;
+ (*idx)++;
+ }
+
+ return NULL;
+}
+
+static void ui_draw_menuitem(struct cfdisk *cf,
+ struct cfdisk_menuitem *d,
+ size_t idx)
+{
+ char *buf, *ptr;
+ const char *name;
+ size_t width;
+ const size_t buf_sz = 80 * MB_CUR_MAX;
+ int ln, cl, vert = cf->menu->vertical;
+
+ if (!menuitem_on_page(cf, idx))
+ return; /* no visible item */
+ ln = menuitem_get_line(cf, idx);
+ cl = menuitem_get_column(cf, idx);
+
+ ptr = buf = xmalloc(buf_sz);
+ /* string width */
+ if (vert) {
+ width = cf->menu->width + MENU_V_SPADDING;
+ memset(ptr, ' ', MENU_V_SPADDING);
+ ptr += MENU_V_SPADDING;
+ } else
+ width = MENU_H_SPADDING + cf->menu->width + MENU_H_SPADDING;
+
+ name = _(d->name);
+ mbsalign(name, ptr, buf_sz, &width,
+ vert ? MBS_ALIGN_LEFT : MBS_ALIGN_CENTER,
+ 0);
+
+ DBG(MENU, ul_debug("menuitem: cl=%d, ln=%d, item='%s'",
+ cl, ln, buf));
+
+ if (vert) {
+ mvaddch(ln, cl - 1, ACS_VLINE);
+ mvaddch(ln, cl + MENU_V_ITEMWIDTH(cf->menu), ACS_VLINE);
+ }
+
+ if (cf->menu->idx == idx)
+ standout();
+
+ if (vert)
+ mvprintw(ln, cl, "%s", buf);
+ else
+ mvprintw(ln, cl, "%s%s%s", MENU_H_PRESTR, buf, MENU_H_POSTSTR);
+ free(buf);
+
+ if (cf->menu->idx == idx) {
+ standend();
+ if (d->desc)
+ ui_hint(_(d->desc));
+ }
+}
+
+static void ui_clean_menu(struct cfdisk *cf)
+{
+ size_t i;
+ size_t lastline;
+ struct cfdisk_menu *m = cf->menu;
+ size_t ln = menuitem_get_line(cf, 0);
+
+ if (m->vertical)
+ lastline = ln + (m->page_sz ? m->page_sz : m->nitems);
+ else
+ lastline = menuitem_get_line(cf, m->nitems);
+
+ for (i = ln; i <= lastline; i++) {
+ move(i, 0);
+ clrtoeol();
+ DBG(MENU, ul_debug("clean_menu: line %zu", i));
+ }
+ if (m->vertical) {
+ move(ln - 1, 0);
+ clrtoeol();
+ }
+ ui_clean_hint();
+}
+
+static void ui_draw_menu(struct cfdisk *cf)
+{
+ struct cfdisk_menuitem *d;
+ struct cfdisk_menu *m;
+ size_t i = 0;
+ size_t ln = menuitem_get_line(cf, 0);
+ size_t nlines;
+
+ assert(cf);
+ assert(cf->menu);
+
+ DBG(MENU, ul_debug("draw start"));
+
+ ui_clean_menu(cf);
+ m = cf->menu;
+
+ if (m->vertical)
+ nlines = m->page_sz ? m->page_sz : m->nitems;
+ else
+ nlines = menuitem_get_line(cf, m->nitems);
+
+ if (m->ignore_cb)
+ menu_update_ignore(cf);
+ i = 0;
+ while ((d = menu_get_menuitem(cf, i)))
+ ui_draw_menuitem(cf, d, i++);
+
+ if (m->vertical) {
+ size_t cl = menuitem_get_column(cf, 0);
+ size_t curpg = m->page_sz ? m->idx / m->page_sz : 0;
+
+ /* corners and horizontal lines */
+ mvaddch(ln - 1, cl - 1, ACS_ULCORNER);
+ mvaddch(ln + nlines, cl - 1, ACS_LLCORNER);
+
+ for (i = 0; i < MENU_V_ITEMWIDTH(m); i++) {
+ mvaddch(ln - 1, cl + i, ACS_HLINE);
+ mvaddch(ln + nlines, cl + i, ACS_HLINE);
+ }
+
+ mvaddch(ln - 1, cl + i, ACS_URCORNER);
+ mvaddch(ln + nlines, cl + i, ACS_LRCORNER);
+
+ /* draw also lines around empty lines on last page */
+ if (m->page_sz &&
+ m->nitems / m->page_sz == m->idx / m->page_sz) {
+ for (i = m->nitems % m->page_sz + 1; i <= m->page_sz; i++) {
+ mvaddch(i, cl - 1, ACS_VLINE);
+ mvaddch(i, cl + MENU_V_ITEMWIDTH(m), ACS_VLINE);
+ }
+ }
+ if (m->title) {
+ attron(A_BOLD);
+ mvprintw(ln - 1, cl, " %s ", m->title);
+ attroff(A_BOLD);
+ }
+ if (curpg != 0)
+ mvaddch(ln - 1, cl + MENU_V_ITEMWIDTH(m) - 2, ACS_UARROW);
+ if (m->page_sz && curpg < m->nitems / m->page_sz)
+ mvaddch(ln + nlines, cl + MENU_V_ITEMWIDTH(m) - 2, ACS_DARROW);
+ }
+
+ DBG(MENU, ul_debug("draw end."));
+}
+
+inline static int extra_insert_pair(struct cfdisk_line *l, const char *name, const char *data)
+{
+ struct libscols_line *lsl;
+ int rc;
+
+ assert(l);
+ assert(l->extra);
+
+ if (!data || !*data)
+ return 0;
+
+ lsl = scols_table_new_line(l->extra, NULL);
+ if (!lsl)
+ return -ENOMEM;
+
+ rc = scols_line_set_data(lsl, 0, name);
+ if (!rc)
+ rc = scols_line_set_data(lsl, 1, data);
+
+ return rc;
+}
+
+#ifndef HAVE_LIBMOUNT
+static char *get_mountpoint( struct cfdisk *cf __attribute__((unused)),
+ const char *tagname __attribute__((unused)),
+ const char *tagdata __attribute__((unused)))
+{
+ return NULL;
+}
+#else
+static char *get_mountpoint(struct cfdisk *cf, const char *tagname, const char *tagdata)
+{
+ struct libmnt_fs *fs = NULL;
+ char *target = NULL;
+ int mounted = 0;
+
+ assert(tagname);
+ assert(tagdata);
+
+ DBG(UI, ul_debug("asking for mountpoint [%s=%s]", tagname, tagdata));
+
+ if (!cf->mntcache)
+ cf->mntcache = mnt_new_cache();
+
+ /* 1st try between mounted filesystems */
+ if (!cf->mtab) {
+ cf->mtab = mnt_new_table();
+ if (cf->mtab) {
+ mnt_table_set_cache(cf->mtab, cf->mntcache);
+ mnt_table_parse_mtab(cf->mtab, NULL);
+ }
+ }
+
+ if (cf->mtab)
+ fs = mnt_table_find_tag(cf->mtab, tagname, tagdata, MNT_ITER_FORWARD);
+
+ /* 2nd try fstab */
+ if (!fs) {
+ if (!cf->fstab) {
+ cf->fstab = mnt_new_table();
+ if (cf->fstab) {
+ mnt_table_set_cache(cf->fstab, cf->mntcache);
+ if (mnt_table_parse_fstab(cf->fstab, NULL) != 0) {
+ mnt_unref_table(cf->fstab);
+ cf->fstab = NULL;
+ }
+ }
+ }
+ if (cf->fstab)
+ fs = mnt_table_find_tag(cf->fstab, tagname, tagdata, MNT_ITER_FORWARD);
+ } else
+ mounted = 1;
+
+ if (fs) {
+ if (mounted)
+ xasprintf(&target, _("%s (mounted)"), mnt_fs_get_target(fs));
+ else
+ target = xstrdup(mnt_fs_get_target(fs));
+ }
+
+ return target;
+}
+#endif /* HAVE_LIBMOUNT */
+
+static void extra_prepare_data(struct cfdisk *cf)
+{
+ struct fdisk_partition *pa = get_current_partition(cf);
+ struct cfdisk_line *l = &cf->lines[cf->lines_idx];
+ char *data = NULL;
+ char *mountpoint = NULL;
+
+ DBG(UI, ul_debug("preparing extra data"));
+
+ /* string data should not equal an empty string */
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_NAME, &data) && data) {
+ extra_insert_pair(l, _("Partition name:"), data);
+ if (!mountpoint)
+ mountpoint = get_mountpoint(cf, "PARTLABEL", data);
+ free(data);
+ }
+
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_UUID, &data) && data) {
+ extra_insert_pair(l, _("Partition UUID:"), data);
+ if (!mountpoint)
+ mountpoint = get_mountpoint(cf, "PARTUUID", data);
+ free(data);
+ }
+
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_TYPE, &data) && data) {
+ char *code = NULL, *type = NULL;
+
+ fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_TYPEID, &code);
+ xasprintf(&type, "%s (%s)", data, code);
+
+ extra_insert_pair(l, _("Partition type:"), type);
+ free(data);
+ free(code);
+ free(type);
+ }
+
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_ATTR, &data) && data) {
+ extra_insert_pair(l, _("Attributes:"), data);
+ free(data);
+ }
+
+ /* for numeric data, only show non-zero rows */
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_BSIZE, &data) && data) {
+ if (atoi(data))
+ extra_insert_pair(l, "BSIZE:", data);
+ free(data);
+ }
+
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_CPG, &data) && data) {
+ if (atoi(data))
+ extra_insert_pair(l, "CPG:", data);
+ free(data);
+ }
+
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSIZE, &data) && data) {
+ if (atoi(data))
+ extra_insert_pair(l, "FSIZE:", data);
+ free(data);
+ }
+
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSUUID, &data) && data) {
+ extra_insert_pair(l, _("Filesystem UUID:"), data);
+ if (!mountpoint)
+ mountpoint = get_mountpoint(cf, "UUID", data);
+ free(data);
+ }
+
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSLABEL, &data) && data) {
+ extra_insert_pair(l, _("Filesystem LABEL:"), data);
+ if (!mountpoint)
+ mountpoint = get_mountpoint(cf, "LABEL", data);
+ free(data);
+ }
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSTYPE, &data) && data) {
+ extra_insert_pair(l, _("Filesystem:"), data);
+ free(data);
+ }
+
+ if (mountpoint) {
+ extra_insert_pair(l, _("Mountpoint:"), mountpoint);
+ free(mountpoint);
+ }
+}
+
+static int ui_draw_extra(struct cfdisk *cf)
+{
+ WINDOW *win_ex;
+ int wline = 1;
+ struct cfdisk_line *ln = &cf->lines[cf->lines_idx];
+ char *tbstr = NULL, *end;
+ int win_ex_start_line, win_height, tblen;
+ int ndatalines;
+
+ if (!cf->show_extra)
+ return 0;
+
+ DBG(UI, ul_debug("draw extra"));
+
+ assert(ln->extra);
+
+ if (cf->act_win) {
+ wclear(cf->act_win);
+ touchwin(stdscr);
+ }
+
+ if (scols_table_is_empty(ln->extra)) {
+ extra_prepare_data(cf);
+ if (scols_table_is_empty(ln->extra))
+ return 0;
+ }
+
+ ndatalines = fdisk_table_get_nents(cf->table) + 1;
+
+ /* nents + header + one free line */
+ win_ex_start_line = TABLE_START_LINE + ndatalines;
+ win_height = MENU_START_LINE - win_ex_start_line;
+ tblen = scols_table_get_nlines(ln->extra);
+
+ /* we can't get a single line of data under the partlist*/
+ if (win_height < 3)
+ return 1;
+
+ /* number of data lines + 2 for top/bottom lines */
+ win_height = win_height < tblen + 2 ? win_height : tblen + 2;
+
+ if ((size_t) win_ex_start_line + win_height + 1 < MENU_START_LINE)
+ win_ex_start_line = MENU_START_LINE - win_height;
+
+ win_ex = subwin(stdscr, win_height, ui_cols - 2, win_ex_start_line, 1);
+
+ scols_table_reduce_termwidth(ln->extra, 4);
+ scols_print_table_to_string(ln->extra, &tbstr);
+
+ end = tbstr;
+ while ((end = strchr(end, '\n')))
+ *end++ = '\0';
+
+ box(win_ex, 0, 0);
+
+ end = tbstr;
+ while (--win_height > 1) {
+ mvwaddstr(win_ex, wline++, 1 /* window column*/, tbstr);
+ tbstr += strlen(tbstr) + 1;
+ }
+ free(end);
+
+ if (ln->w)
+ delwin(ln->w);
+
+ DBG(UI, ul_debug("draw window: %p", win_ex));
+ touchwin(stdscr);
+ wrefresh(win_ex);
+
+ cf->act_win = ln->w = win_ex;
+ return 0;
+}
+
+static void ui_menu_goto(struct cfdisk *cf, int where)
+{
+ struct cfdisk_menuitem *d;
+ size_t old;
+
+ /* stop and begin/end for vertical menus */
+ if (cf->menu->vertical) {
+ if (where < 0)
+ where = 0;
+ else if (where > (int) cf->menu->nitems - 1)
+ where = cf->menu->nitems - 1;
+ } else {
+ /* continue from begin/end */
+ if (where < 0)
+ where = cf->menu->nitems - 1;
+ else if ((size_t) where > cf->menu->nitems - 1)
+ where = 0;
+ }
+ if ((size_t) where == cf->menu->idx)
+ return;
+
+ ui_clean_info();
+
+ old = cf->menu->idx;
+ cf->menu->idx = where;
+
+ if (!menuitem_on_page(cf, old)) {
+ ui_draw_menu(cf);
+ return;
+ }
+
+ d = menu_get_menuitem(cf, old);
+ ui_draw_menuitem(cf, d, old);
+
+ d = menu_get_menuitem(cf, where);
+ ui_draw_menuitem(cf, d, where);
+
+}
+
+static int ui_menu_move(struct cfdisk *cf, int key)
+{
+ struct cfdisk_menu *m;
+
+ assert(cf);
+ assert(cf->menu);
+
+ if (key == (int) ERR)
+ return 0; /* ignore errors */
+
+ m = cf->menu;
+
+ DBG(MENU, ul_debug("menu move key >%c<.", key));
+
+ if (m->vertical)
+ {
+ switch (key) {
+ case KEY_DOWN:
+ case '\016': /* ^N */
+ case 'j': /* Vi-like alternative */
+ ui_menu_goto(cf, m->idx + 1);
+ return 0;
+ case KEY_UP:
+ case '\020': /* ^P */
+ case 'k': /* Vi-like alternative */
+ ui_menu_goto(cf, (int) m->idx - 1);
+ return 0;
+ case KEY_PPAGE:
+ if (m->page_sz) {
+ ui_menu_goto(cf, (int) m->idx - m->page_sz);
+ return 0;
+ }
+ /* fallthrough */
+ case KEY_HOME:
+ ui_menu_goto(cf, 0);
+ return 0;
+ case KEY_NPAGE:
+ if (m->page_sz) {
+ ui_menu_goto(cf, m->idx + m->page_sz);
+ return 0;
+ }
+ /* fallthrough */
+ case KEY_END:
+ ui_menu_goto(cf, m->nitems);
+ return 0;
+ }
+ } else {
+ switch (key) {
+ case KEY_RIGHT:
+ case '\t':
+ ui_menu_goto(cf, m->idx + 1);
+ return 0;
+ case KEY_LEFT:
+#ifdef KEY_BTAB
+ case KEY_BTAB:
+#endif
+ ui_menu_goto(cf, (int) m->idx - 1);
+ return 0;
+ }
+ }
+
+ if (key == '\014') { /* ^L refresh */
+ ui_menu_resize(cf);
+ return 0;
+ }
+
+ DBG(MENU, ul_debug(" no menu move key"));
+ return 1;
+}
+
+/* but don't call me from ui_run(), this is for pop-up menus only */
+static void ui_menu_resize(struct cfdisk *cf)
+{
+ DBG(MENU, ul_debug("menu resize/refresh"));
+ resize();
+ ui_clean_menu(cf);
+ menu_refresh_size(cf);
+ ui_draw_menu(cf);
+ refresh();
+}
+
+static int partition_on_page(struct cfdisk *cf, size_t i)
+{
+ if (cf->page_sz == 0 ||
+ cf->lines_idx / cf->page_sz == i / cf->page_sz)
+ return 1;
+ return 0;
+}
+
+static void ui_draw_partition(struct cfdisk *cf, size_t i)
+{
+ int ln = TABLE_START_LINE + 1 + i; /* skip table header */
+ int cl = ARROW_CURSOR_WIDTH; /* we need extra space for cursor */
+ int cur = cf->lines_idx == i;
+ size_t curpg = 0;
+
+ if (cf->page_sz) {
+ if (!partition_on_page(cf, i))
+ return;
+ ln = TABLE_START_LINE + (i % cf->page_sz) + 1;
+ curpg = cf->lines_idx / cf->page_sz;
+ }
+
+ DBG(UI, ul_debug(
+ "draw partition %zu [page_sz=%zu, "
+ "line=%d, idx=%zu]",
+ i, cf->page_sz, ln, cf->lines_idx));
+
+ if (cur) {
+ attron(A_REVERSE);
+ mvaddstr(ln, 0, ARROW_CURSOR_STRING);
+ mvaddstr(ln, cl, cf->lines[i + 1].data);
+ attroff(A_REVERSE);
+ } else {
+ int at = 0;
+
+ if (colors_wanted() && is_freespace(cf, i)) {
+ attron(COLOR_PAIR(CFDISK_CL_FREESPACE));
+ at = 1;
+ }
+ mvaddstr(ln, 0, ARROW_CURSOR_DUMMY);
+ mvaddstr(ln, cl, cf->lines[i + 1].data);
+ if (at)
+ attroff(COLOR_PAIR(CFDISK_CL_FREESPACE));
+ }
+
+ if ((size_t) ln == MENU_START_LINE - 1 &&
+ cf->page_sz && curpg < cf->nlines / cf->page_sz) {
+ if (cur)
+ attron(A_REVERSE);
+ mvaddch(ln, ui_cols - 1, ACS_DARROW);
+ mvaddch(ln, 0, ACS_DARROW);
+ if (cur)
+ attroff(A_REVERSE);
+ }
+
+}
+
+static int ui_draw_table(struct cfdisk *cf)
+{
+ int cl = ARROW_CURSOR_WIDTH;
+ size_t i, nparts = fdisk_table_get_nents(cf->table);
+ size_t curpg = cf->page_sz ? cf->lines_idx / cf->page_sz : 0;
+
+ DBG(UI, ul_debug("draw table"));
+
+ for (i = TABLE_START_LINE; i <= TABLE_START_LINE + cf->page_sz; i++) {
+ move(i, 0);
+ clrtoeol();
+ }
+
+ if (nparts == 0 || (size_t) cf->lines_idx > nparts - 1)
+ cf->lines_idx = nparts ? nparts - 1 : 0;
+
+ /* print header */
+ attron(A_BOLD);
+ mvaddstr(TABLE_START_LINE, cl, cf->lines[0].data);
+ attroff(A_BOLD);
+
+ /* print partitions */
+ for (i = 0; i < nparts; i++)
+ ui_draw_partition(cf, i);
+
+ if (curpg != 0) {
+ mvaddch(TABLE_START_LINE, ui_cols - 1, ACS_UARROW);
+ mvaddch(TABLE_START_LINE, 0, ACS_UARROW);
+ }
+ if (cf->page_sz && curpg < cf->nlines / cf->page_sz) {
+ mvaddch(MENU_START_LINE - 1, ui_cols - 1, ACS_DARROW);
+ mvaddch(MENU_START_LINE - 1, 0, ACS_DARROW);
+ }
+ return 0;
+}
+
+static int ui_table_goto(struct cfdisk *cf, int where)
+{
+ size_t old;
+ size_t nparts = fdisk_table_get_nents(cf->table);
+
+ DBG(UI, ul_debug("goto table %d", where));
+
+ if (where < 0)
+ where = 0;
+ else if ((size_t) where > nparts - 1)
+ where = nparts - 1;
+
+ if ((size_t) where == cf->lines_idx)
+ return 0;
+
+ old = cf->lines_idx;
+ cf->lines_idx = where;
+
+ if (!partition_on_page(cf, old) ||!partition_on_page(cf, where))
+ ui_draw_table(cf);
+ else {
+ ui_draw_partition(cf, old); /* cleanup old */
+ ui_draw_partition(cf, where); /* draw new */
+ }
+ ui_clean_info();
+ ui_draw_menu(cf);
+ ui_draw_extra(cf);
+ refresh();
+
+ return 0;
+}
+
+static int ui_refresh(struct cfdisk *cf)
+{
+ struct fdisk_label *lb;
+ char *id = NULL;
+ uint64_t bytes = fdisk_get_nsectors(cf->cxt) * fdisk_get_sector_size(cf->cxt);
+ char *strsz;
+
+ if (!ui_enabled)
+ return -EINVAL;
+
+ strsz = size_to_human_string(SIZE_DECIMAL_2DIGITS
+ | SIZE_SUFFIX_SPACE
+ | SIZE_SUFFIX_3LETTER, bytes);
+
+ lb = fdisk_get_label(cf->cxt, NULL);
+ assert(lb);
+
+ clear();
+
+ /* header */
+ attron(A_BOLD);
+ ui_center(0, _("Disk: %s"), fdisk_get_devname(cf->cxt));
+ attroff(A_BOLD);
+ ui_center(1, _("Size: %s, %"PRIu64" bytes, %ju sectors"),
+ strsz, bytes, (uintmax_t) fdisk_get_nsectors(cf->cxt));
+ if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id)
+ ui_center(2, _("Label: %s, identifier: %s"),
+ fdisk_label_get_name(lb), id);
+ else
+ ui_center(2, _("Label: %s"), fdisk_label_get_name(lb));
+ free(strsz);
+ free(id);
+
+ ui_draw_table(cf);
+ ui_draw_menu(cf);
+ refresh();
+ return 0;
+}
+
+static ssize_t ui_get_string(const char *prompt,
+ const char *hint, char *buf, size_t len)
+{
+ int ln = MENU_START_LINE, cl = 1;
+ ssize_t rc = -1;
+ struct mbs_editor *edit;
+
+ DBG(UI, ul_debug("ui get string"));
+
+ assert(buf);
+ assert(len);
+
+ move(ln, 0);
+ clrtoeol();
+
+ move(ln + 1, 0);
+ clrtoeol();
+
+ if (prompt) {
+ mvaddstr(ln, cl, prompt);
+ cl += mbs_safe_width(prompt);
+ }
+
+ edit = mbs_new_edit(buf, len, ui_cols - cl);
+ if (!edit)
+ goto done;
+
+ mbs_edit_goto(edit, MBS_EDIT_END);
+
+ if (hint)
+ ui_hint(hint);
+ else
+ ui_clean_hint();
+
+ curs_set(1);
+
+ while (!sig_die) {
+ wint_t c; /* we have fallback in widechar.h */
+
+ move(ln, cl);
+ clrtoeol();
+ mvaddstr(ln, cl, edit->buf);
+ move(ln, cl + edit->cursor_cells);
+ refresh();
+
+#if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \
+ defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
+ if (get_wch(&c) == ERR) {
+#else
+ if ((c = getch()) == (wint_t) ERR) {
+#endif
+ if (sig_die)
+ break;
+ if (sig_resize) {
+ resize();
+ continue;
+ }
+ if (!isatty(STDIN_FILENO))
+ exit(2);
+ else
+ goto done;
+ }
+
+ DBG(UI, ul_debug("ui get string: key=%lc", c));
+
+ if (c == '\r' || c == '\n' || c == KEY_ENTER)
+ break;
+
+ rc = 1;
+
+ switch (c) {
+ case KEY_ESC:
+ rc = -CFDISK_ERR_ESC;
+ goto done;
+ case KEY_LEFT:
+ rc = mbs_edit_goto(edit, MBS_EDIT_LEFT);
+ break;
+ case KEY_RIGHT:
+ rc = mbs_edit_goto(edit, MBS_EDIT_RIGHT);
+ break;
+ case KEY_END:
+ rc = mbs_edit_goto(edit, MBS_EDIT_END);
+ break;
+ case KEY_HOME:
+ rc = mbs_edit_goto(edit, MBS_EDIT_HOME);
+ break;
+ case KEY_UP:
+ case KEY_DOWN:
+ break;
+ case KEY_DC:
+ rc = mbs_edit_delete(edit);
+ break;
+ case '\b':
+ case KEY_DELETE:
+ case KEY_BACKSPACE:
+ rc = mbs_edit_backspace(edit);
+ break;
+ default:
+ rc = mbs_edit_insert(edit, c);
+ break;
+ }
+ if (rc == 1)
+ beep();
+ }
+
+ if (sig_die)
+ die_on_signal();
+
+ rc = strlen(edit->buf); /* success */
+done:
+ move(ln, 0);
+ clrtoeol();
+ curs_set(0);
+ refresh();
+ mbs_free_edit(edit);
+
+ return rc;
+}
+
+static int ui_get_size(struct cfdisk *cf, /* context */
+ const char *prompt, /* UI dialog string */
+ uint64_t *res, /* result in bytes */
+ uint64_t low, /* minimal size */
+ uint64_t up, /* maximal size */
+ int *expsize) /* explicitly specified size */
+{
+ char buf[128];
+ uint64_t user = 0;
+ ssize_t rc;
+ char *dflt = size_to_human_string(0, *res);
+
+ DBG(UI, ul_debug("get_size (default=%"PRIu64")", *res));
+
+ ui_clean_info();
+
+ snprintf(buf, sizeof(buf), "%s", dflt);
+
+ do {
+ int pwr = 0, insec = 0;
+
+ rc = ui_get_string(prompt,
+ _("May be followed by M for MiB, G for GiB, "
+ "T for TiB, or S for sectors."),
+ buf, sizeof(buf));
+ ui_clean_warn();
+
+ if (rc == 0) {
+ ui_warnx(_("Please, specify size."));
+ continue; /* nothing specified */
+ } if (rc == -CFDISK_ERR_ESC)
+ break; /* cancel dialog */
+
+ if (strcmp(buf, dflt) == 0)
+ user = *res, rc = 0; /* no change, use default */
+ else {
+ size_t len = strlen(buf);
+ if (buf[len - 1] == 'S' || buf[len - 1] == 's') {
+ insec = 1;
+ buf[len - 1] = '\0';
+ }
+ rc = parse_size(buf, (uintmax_t *)&user, &pwr); /* parse */
+ }
+
+ if (rc == 0) {
+ DBG(UI, ul_debug("get_size user=%"PRIu64", power=%d, in-sectors=%s",
+ user, pwr, insec ? "yes" : "no"));
+ if (insec)
+ user *= fdisk_get_sector_size(cf->cxt);
+ if (user < low) {
+ ui_warnx(_("Minimum size is %"PRIu64" bytes."), low);
+ rc = -ERANGE;
+ }
+ if (user > up && pwr && user < up + (1ULL << pwr * 10))
+ /* ignore when the user specified size overflow
+ * with in range specified by suffix (e.g. MiB) */
+ user = up;
+
+ if (user > up) {
+ ui_warnx(_("Maximum size is %"PRIu64" bytes."), up);
+ rc = -ERANGE;
+ }
+ if (rc == 0 && insec && expsize)
+ *expsize = 1;
+
+ } else
+ ui_warnx(_("Failed to parse size."));
+ } while (rc != 0);
+
+ if (rc == 0)
+ *res = user;
+ free(dflt);
+
+ DBG(UI, ul_debug("get_size (result=%"PRIu64", rc=%zd)", *res, rc));
+ return rc;
+}
+
+static struct fdisk_parttype *ui_get_parttype(struct cfdisk *cf,
+ struct fdisk_parttype *cur)
+{
+ struct cfdisk_menuitem *d, *cm;
+ size_t i = 0, nitems, idx = 0;
+ struct fdisk_parttype *t = NULL;
+ struct fdisk_label *lb;
+ int codetypes = 0;
+
+ DBG(UI, ul_debug("asking for parttype."));
+
+ lb = fdisk_get_label(cf->cxt, NULL);
+
+ /* create cfdisk menu according to label types, note that the
+ * last cm[] item has to be empty -- so nitems + 1 */
+ nitems = fdisk_label_get_nparttypes(lb);
+ if (!nitems)
+ return NULL;
+
+ cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
+ if (!cm)
+ return NULL;
+
+ codetypes = fdisk_label_has_code_parttypes(lb);
+
+ for (i = 0; i < nitems; i++) {
+ const struct fdisk_parttype *x = fdisk_label_get_parttype(lb, i);
+ char *name;
+
+ cm[i].userdata = (void *) x;
+ if (codetypes)
+ xasprintf(&name, "%2x %s",
+ fdisk_parttype_get_code(x),
+ _(fdisk_parttype_get_name(x)));
+ else {
+ name = (char *) _(fdisk_parttype_get_name(x));
+ cm[i].desc = fdisk_parttype_get_string(x);
+ }
+ cm[i].name = name;
+ if (x == cur)
+ idx = i;
+ }
+
+ /* make the new menu active */
+ menu_push(cf, cm);
+ cf->menu->vertical = 1;
+ cf->menu->idx = idx;
+ menu_set_title(cf->menu, _("Select partition type"));
+ ui_draw_menu(cf);
+ refresh();
+
+ while (!sig_die) {
+ int key = getch();
+
+ if (sig_die)
+ break;
+ if (sig_resize)
+ ui_menu_resize(cf);
+ if (ui_menu_move(cf, key) == 0)
+ continue;
+
+ switch (key) {
+ case KEY_ENTER:
+ case '\n':
+ case '\r':
+ d = menu_get_menuitem(cf, cf->menu->idx);
+ if (d)
+ t = (struct fdisk_parttype *) d->userdata;
+ goto done;
+ case KEY_ESC:
+ case 'q':
+ case 'Q':
+ goto done;
+ }
+ }
+
+ if (sig_die)
+ die_on_signal();
+done:
+ menu_pop(cf);
+ if (codetypes) {
+ for (i = 0; i < nitems; i++)
+ free((char *) cm[i].name);
+ }
+ free(cm);
+ DBG(UI, ul_debug("get parrtype done [type=%s] ", t ?
+ fdisk_parttype_get_name(t) : NULL));
+ return t;
+}
+
+static int ui_script_read(struct cfdisk *cf)
+{
+ struct fdisk_script *sc = NULL;
+ char buf[PATH_MAX] = { 0 };
+ int rc;
+
+ erase();
+ rc = ui_get_string( _("Enter script file name: "),
+ _("The script file will be applied to in-memory partition table."),
+ buf, sizeof(buf));
+ if (rc <= 0)
+ return rc;
+
+ rc = -1;
+ errno = 0;
+ sc = fdisk_new_script_from_file(cf->cxt, buf);
+ if (!sc && errno)
+ ui_warn(_("Cannot open %s"), buf);
+ else if (!sc)
+ ui_warnx(_("Failed to parse script file %s"), buf);
+ else if (fdisk_apply_script(cf->cxt, sc) != 0)
+ ui_warnx(_("Failed to apply script %s"), buf);
+ else
+ rc = 0;
+
+ ui_clean_hint();
+ fdisk_unref_script(sc);
+ return rc;
+}
+
+static int ui_script_write(struct cfdisk *cf)
+{
+ struct fdisk_script *sc = NULL;
+ char buf[PATH_MAX] = { 0 };
+ FILE *f = NULL;
+ int rc;
+
+ rc = ui_get_string( _("Enter script file name: "),
+ _("The current in-memory partition table will be dumped to the file."),
+ buf, sizeof(buf));
+ if (rc <= 0)
+ return rc;
+
+ rc = 0;
+ sc = fdisk_new_script(cf->cxt);
+ if (!sc) {
+ ui_warn(_("Failed to allocate script handler"));
+ goto done;
+ }
+
+ rc = fdisk_script_read_context(sc, NULL);
+ if (rc) {
+ ui_warnx(_("Failed to read disk layout into script."));
+ goto done;
+ }
+
+ DBG(UI, ul_debug("writing dump into: '%s'", buf));
+ f = fopen(buf, "w");
+ if (!f) {
+ ui_warn(_("Cannot open %s"), buf);
+ rc = -errno;
+ goto done;
+ }
+
+ rc = fdisk_script_write_file(sc, f);
+ if (!rc)
+ ui_info(_("Disk layout successfully dumped."));
+done:
+ if (rc)
+ ui_warn(_("Failed to write script %s"), buf);
+ if (f)
+ fclose(f);
+ fdisk_unref_script(sc);
+ return rc;
+}
+
+/* prints menu with libfdisk labels and waits for users response */
+static int ui_create_label(struct cfdisk *cf)
+{
+ struct cfdisk_menuitem *d, *cm;
+ int rc = 1, refresh_menu = 1;
+ size_t i = 0, nitems;
+ struct fdisk_label *lb = NULL;
+
+ assert(cf);
+
+ DBG(UI, ul_debug("asking for new disklabe."));
+
+ /* create cfdisk menu according to libfdisk labels, note that the
+ * last cm[] item has to be empty -- so nitems + 1 */
+ nitems = fdisk_get_nlabels(cf->cxt);
+ cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
+
+ while (fdisk_next_label(cf->cxt, &lb) == 0) {
+ if (fdisk_label_is_disabled(lb) ||
+ fdisk_label_get_type(lb) == FDISK_DISKLABEL_BSD)
+ continue;
+ cm[i++].name = fdisk_label_get_name(lb);
+ }
+
+ erase();
+
+ /* make the new menu active */
+ menu_push(cf, cm);
+ cf->menu->vertical = 1;
+ menu_set_title(cf->menu, _("Select label type"));
+
+ if (!cf->zero_start)
+ ui_info(_("Device does not contain a recognized partition table."));
+
+
+ while (!sig_die) {
+ int key;
+
+ if (refresh_menu) {
+ ui_draw_menu(cf);
+ ui_hint(_("Select a type to create a new label or press 'L' to load script file."));
+ refresh();
+ refresh_menu = 0;
+ }
+
+ key = getch();
+
+ if (sig_die)
+ break;
+ if (sig_resize)
+ ui_menu_resize(cf);
+ if (ui_menu_move(cf, key) == 0)
+ continue;
+ switch (key) {
+ case KEY_ENTER:
+ case '\n':
+ case '\r':
+ d = menu_get_menuitem(cf, cf->menu->idx);
+ if (d)
+ rc = fdisk_create_disklabel(cf->cxt, d->name);
+ goto done;
+ case KEY_ESC:
+ case 'q':
+ case 'Q':
+ goto done;
+ case 'l':
+ case 'L':
+ rc = ui_script_read(cf);
+ if (rc == 0)
+ goto done;
+ refresh_menu = 1;
+ break;
+ }
+ }
+
+ if (sig_die)
+ die_on_signal();
+done:
+ menu_pop(cf);
+ free(cm);
+ DBG(UI, ul_debug("create label done [rc=%d] ", rc));
+ return rc;
+}
+
+
+static int ui_help(void)
+{
+ size_t i;
+ static const char *help[] = {
+ N_("This is cfdisk, a curses-based disk partitioning program."),
+ N_("It lets you create, delete, and modify partitions on a block device."),
+ " ",
+ N_("Command Meaning"),
+ N_("------- -------"),
+ N_(" b Toggle bootable flag of the current partition"),
+ N_(" d Delete the current partition"),
+ N_(" h Print this screen"),
+ N_(" n Create new partition from free space"),
+ N_(" q Quit program without writing partition table"),
+ N_(" s Fix partitions order (only when in disarray)"),
+ N_(" t Change the partition type"),
+ N_(" u Dump disk layout to sfdisk compatible script file"),
+ N_(" W Write partition table to disk (you must enter uppercase W);"),
+ N_(" since this might destroy data on the disk, you must either"),
+ N_(" confirm or deny the write by entering 'yes' or 'no'"),
+ N_(" x Display/hide extra information about a partition"),
+ N_("Up Arrow Move cursor to the previous partition"),
+ N_("Down Arrow Move cursor to the next partition"),
+ N_("Left Arrow Move cursor to the previous menu item"),
+ N_("Right Arrow Move cursor to the next menu item"),
+ " ",
+ N_("Note: All of the commands can be entered with either upper or lower"),
+ N_("case letters (except for Write)."),
+ " ",
+ N_("Use lsblk(8) or partx(8) to see more details about the device."),
+ " ",
+ " ",
+ "Copyright (C) 2014-2017 Karel Zak <kzak@redhat.com>"
+ };
+
+ erase();
+ for (i = 0; i < ARRAY_SIZE(help); i++)
+ mvaddstr(i, 1, _(help[i]));
+
+ ui_info(_("Press a key to continue."));
+
+ getch();
+
+ if (sig_die)
+ die_on_signal();
+ return 0;
+}
+
+/* TODO: use @sz, now 128bytes */
+static int main_menu_ignore_keys(struct cfdisk *cf, char *ignore,
+ size_t sz __attribute__((__unused__)))
+{
+ struct fdisk_partition *pa = get_current_partition(cf);
+ size_t i = 0;
+
+ if (!pa)
+ return 0;
+ if (fdisk_partition_is_freespace(pa)) {
+ ignore[i++] = 'd'; /* delete */
+ ignore[i++] = 't'; /* set type */
+ ignore[i++] = 'b'; /* set bootable */
+ ignore[i++] = 'r'; /* resize */
+ cf->menu->prefkey = 'n';
+ } else {
+ cf->menu->prefkey = 'q';
+ ignore[i++] = 'n';
+ if (!fdisk_is_label(cf->cxt, DOS) &&
+ !fdisk_is_label(cf->cxt, SGI))
+ ignore[i++] = 'b';
+ }
+
+ if (!cf->wrong_order)
+ ignore[i++] = 's';
+
+ if (fdisk_is_readonly(cf->cxt))
+ ignore[i++] = 'W';
+
+ return i;
+}
+
+
+/* returns: error: < 0, success: 0, quit: 1 */
+static int main_menu_action(struct cfdisk *cf, int key)
+{
+ size_t n;
+ int ref = 0, rc, org_order = cf->wrong_order;
+ const char *info = NULL, *warn = NULL;
+ struct fdisk_partition *pa;
+
+ assert(cf);
+ assert(cf->cxt);
+ assert(cf->menu);
+
+ if (key == 0) {
+ struct cfdisk_menuitem *d = menu_get_menuitem(cf, cf->menu->idx);
+ if (!d)
+ return 0;
+ key = d->key;
+
+ } else if (key != 'w' && key != 'W')
+ key = tolower(key); /* case insensitive except 'W'rite */
+
+ DBG(MENU, ul_debug("main menu action: key=%c", key));
+
+ if (cf->menu->ignore && strchr(cf->menu->ignore, key)) {
+ DBG(MENU, ul_debug(" ignore '%c'", key));
+ return 0;
+ }
+
+ pa = get_current_partition(cf);
+ if (!pa)
+ return -EINVAL;
+ n = fdisk_partition_get_partno(pa);
+
+ DBG(MENU, ul_debug("menu action on %p", pa));
+ ui_clean_hint();
+ ui_clean_info();
+
+ switch (key) {
+ case 'b': /* Bootable flag */
+ {
+ int fl = fdisk_is_label(cf->cxt, DOS) ? DOS_FLAG_ACTIVE :
+ fdisk_is_label(cf->cxt, SGI) ? SGI_FLAG_BOOT : 0;
+
+ if (fl && fdisk_toggle_partition_flag(cf->cxt, n, fl))
+ warn = _("Could not toggle the flag.");
+ else if (fl)
+ ref = 1;
+ break;
+ }
+#ifdef KEY_DC
+ case KEY_DC:
+#endif
+ case 'd': /* Delete */
+ if (fdisk_delete_partition(cf->cxt, n) != 0)
+ warn = _("Could not delete partition %zu.");
+ else
+ info = _("Partition %zu has been deleted.");
+ ref = 1;
+ break;
+ case 'h': /* Help */
+ case '?':
+ ui_help();
+ ref = 1;
+ break;
+ case 'n': /* New */
+ {
+ uint64_t start, size, dflt_size, secs, max_size;
+ struct fdisk_partition *npa; /* the new partition */
+ int expsize = 0; /* size specified explicitly in sectors */
+
+ if (!fdisk_partition_is_freespace(pa) || !fdisk_partition_has_start(pa))
+ return -EINVAL;
+
+ /* free space range */
+ start = fdisk_partition_get_start(pa);
+ size = max_size = dflt_size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cf->cxt);
+
+ if (ui_get_size(cf, _("Partition size: "), &size,
+ fdisk_get_sector_size(cf->cxt),
+ max_size, &expsize) == -CFDISK_ERR_ESC)
+ break;
+
+ secs = size / fdisk_get_sector_size(cf->cxt);
+
+ npa = fdisk_new_partition();
+ if (!npa)
+ return -ENOMEM;
+
+ if (dflt_size == size) /* default is to fillin all free space */
+ fdisk_partition_end_follow_default(npa, 1);
+ else
+ fdisk_partition_set_size(npa, secs);
+
+ if (expsize)
+ fdisk_partition_size_explicit(pa, 1);
+
+ fdisk_partition_set_start(npa, start);
+ fdisk_partition_partno_follow_default(npa, 1);
+ /* add to disk label -- libfdisk will ask for missing details */
+ rc = fdisk_add_partition(cf->cxt, npa, NULL);
+ fdisk_unref_partition(npa);
+ if (rc == 0)
+ ref = 1;
+ break;
+ }
+ case 'q': /* Quit */
+ return 1;
+ case 't': /* Type */
+ {
+ struct fdisk_parttype *t;
+
+ if (fdisk_partition_is_freespace(pa))
+ return -EINVAL;
+ t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
+ t = ui_get_parttype(cf, t);
+ ref = 1;
+
+ if (t && fdisk_set_partition_type(cf->cxt, n, t) == 0)
+ info = _("Changed type of partition %zu.");
+ else
+ info = _("The type of partition %zu is unchanged.");
+ break;
+ }
+ case 'r': /* resize */
+ {
+ struct fdisk_partition *npa, *next;
+ uint64_t size, max_size, secs;
+
+ if (fdisk_partition_is_freespace(pa) || !fdisk_partition_has_start(pa))
+ return -EINVAL;
+
+ size = fdisk_partition_get_size(pa);
+
+ /* is the next freespace? */
+ next = fdisk_table_get_partition(cf->table, cf->lines_idx + 1);
+ if (next && fdisk_partition_is_freespace(next))
+ size += fdisk_partition_get_size(next);
+
+ size *= fdisk_get_sector_size(cf->cxt);
+ max_size = size;
+
+ if (ui_get_size(cf, _("New size: "), &size,
+ fdisk_get_sector_size(cf->cxt),
+ max_size, NULL) == -CFDISK_ERR_ESC)
+ break;
+ secs = size / fdisk_get_sector_size(cf->cxt);
+ npa = fdisk_new_partition();
+ if (!npa)
+ return -ENOMEM;
+
+ fdisk_partition_set_size(npa, secs);
+
+ rc = fdisk_set_partition(cf->cxt, n, npa);
+ fdisk_unref_partition(npa);
+ if (rc == 0) {
+ ref = 1;
+ info = _("Partition %zu resized.");
+ }
+ break;
+ }
+ case 's': /* Sort */
+ if (cf->wrong_order) {
+ fdisk_reorder_partitions(cf->cxt);
+ ref = 1;
+ }
+ break;
+ case 'u': /* dUmp */
+ ui_script_write(cf);
+ break;
+ case 'W': /* Write */
+ {
+ char buf[64] = { 0 };
+
+ if (fdisk_is_readonly(cf->cxt)) {
+ warn = _("Device is open in read-only mode.");
+ break;
+ }
+
+ rc = ui_get_string(
+ _("Are you sure you want to write the partition "
+ "table to disk? "),
+ _("Type \"yes\" or \"no\", or press ESC to leave this dialog."),
+ buf, sizeof(buf));
+
+ ref = 1;
+ if (rc <= 0 || (strcasecmp(buf, "yes") != 0 &&
+ strcasecmp(buf, _("yes")) != 0)) {
+ info = _("Did not write partition table to disk.");
+ break;
+ }
+ rc = fdisk_write_disklabel(cf->cxt);
+ if (rc)
+ warn = _("Failed to write disklabel.");
+ else {
+ if (cf->device_is_used)
+ fdisk_reread_changes(cf->cxt, cf->original_layout);
+ else
+ fdisk_reread_partition_table(cf->cxt);
+ info = _("The partition table has been altered.");
+ }
+ cf->nwrites++;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (ref) {
+ lines_refresh(cf);
+ ui_refresh(cf);
+ ui_draw_extra(cf);
+ } else
+ ui_draw_menu(cf);
+
+ ui_clean_hint();
+
+ if (warn)
+ ui_warnx(warn, n + 1);
+ else if (info)
+ ui_info(info, n + 1);
+ else if (key == 'n' && cf->wrong_order && org_order == 0)
+ ui_info(_("Note that partition table entries are not in disk order now."));
+
+ return 0;
+}
+
+static void ui_resize_refresh(struct cfdisk *cf)
+{
+ DBG(UI, ul_debug("ui resize/refresh"));
+ resize();
+ menu_refresh_size(cf);
+ lines_refresh(cf);
+ ui_refresh(cf);
+ ui_draw_extra(cf);
+}
+
+static void toggle_show_extra(struct cfdisk *cf)
+{
+ if (cf->show_extra && cf->act_win) {
+ wclear(cf->act_win);
+ touchwin(stdscr);
+ }
+ cf->show_extra = cf->show_extra ? 0 : 1;
+
+ if (cf->show_extra)
+ ui_draw_extra(cf);
+ DBG(MENU, ul_debug("extra: %s", cf->show_extra ? "ENABLED" : "DISABLED" ));
+}
+
+static int ui_run(struct cfdisk *cf)
+{
+ int rc = 0;
+
+ ui_lines = LINES;
+ ui_cols = COLS;
+ DBG(UI, ul_debug("start cols=%zu, lines=%zu", ui_cols, ui_lines));
+
+ if (fdisk_get_collision(cf->cxt)) {
+ ui_warnx(_("Device already contains a %s signature; it will be removed by a write command."),
+ fdisk_get_collision(cf->cxt));
+ fdisk_enable_wipe(cf->cxt, 1);
+ ui_hint(_("Press a key to continue."));
+ getch();
+ }
+
+ if (!fdisk_has_label(cf->cxt) || cf->zero_start) {
+ rc = ui_create_label(cf);
+ if (rc < 0) {
+ errno = -rc;
+ ui_err(EXIT_FAILURE,
+ _("failed to create a new disklabel"));
+ }
+ if (rc)
+ return rc;
+ }
+
+ cols_init(cf);
+ rc = lines_refresh(cf);
+ if (rc)
+ ui_errx(EXIT_FAILURE, _("failed to read partitions"));
+
+ menu_push(cf, main_menuitems);
+ cf->menu->ignore_cb = main_menu_ignore_keys;
+
+ rc = ui_refresh(cf);
+ if (rc)
+ return rc;
+
+ cf->show_extra = 1;
+ ui_draw_extra(cf);
+
+ if (fdisk_is_readonly(cf->cxt))
+ ui_warnx(_("Device is open in read-only mode."));
+ else if (cf->wrong_order)
+ ui_info(_("Note that partition table entries are not in disk order now."));
+
+ while (!sig_die) {
+ int key = getch();
+
+ rc = 0;
+
+ if (sig_die)
+ break;
+ if (sig_resize)
+ /* Note that ncurses getch() returns ERR when interrupted
+ * by signal, but SLang does not interrupt at all. */
+ ui_resize_refresh(cf);
+ if (key == ERR)
+ continue;
+ if (key == '\014') { /* ^L refresh */
+ ui_resize_refresh(cf);
+ continue;
+ }
+ if (ui_menu_move(cf, key) == 0)
+ continue;
+
+ DBG(UI, ul_debug("main action key >%1$c< [\\0%1$o].", key));
+
+ switch (key) {
+ case KEY_DOWN:
+ case '\016': /* ^N */
+ case 'j': /* Vi-like alternative */
+ ui_table_goto(cf, cf->lines_idx + 1);
+ break;
+ case KEY_UP:
+ case '\020': /* ^P */
+ case 'k': /* Vi-like alternative */
+ ui_table_goto(cf, (int) cf->lines_idx - 1);
+ break;
+ case KEY_PPAGE:
+ if (cf->page_sz) {
+ ui_table_goto(cf, (int) cf->lines_idx - cf->page_sz);
+ break;
+ }
+ /* fallthrough */
+ case KEY_HOME:
+ ui_table_goto(cf, 0);
+ break;
+ case KEY_NPAGE:
+ if (cf->page_sz) {
+ ui_table_goto(cf, cf->lines_idx + cf->page_sz);
+ break;
+ }
+ /* fallthrough */
+ case KEY_END:
+ ui_table_goto(cf, (int) cf->nlines - 1);
+ break;
+ case KEY_ENTER:
+ case '\n':
+ case '\r':
+ rc = main_menu_action(cf, 0);
+ break;
+ case 'X':
+ case 'x': /* Extra */
+ toggle_show_extra(cf);
+ break;
+ default:
+ rc = main_menu_action(cf, key);
+ if (rc < 0)
+ beep();
+ break;
+ }
+
+ if (rc == 1)
+ break; /* quit */
+ }
+
+ menu_pop(cf);
+
+ DBG(UI, ul_debug("end"));
+ return 0;
+}
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %1$s [options] <disk>\n"), program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Display or manipulate a disk partition table.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fprintf(out,
+ _(" -L, --color[=<when>] colorize output (%s, %s or %s)\n"), "auto", "always", "never");
+ fprintf(out,
+ " %s\n", USAGE_COLORS_DEFAULT);
+ fputs(_(" -z, --zero start with zeroed partition table\n"), out);
+ fprintf(out,
+ _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
+
+ fputs(USAGE_SEPARATOR, out);
+ printf(USAGE_HELP_OPTIONS(26));
+
+ printf(USAGE_MAN_TAIL("cfdisk(8)"));
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ const char *diskpath = NULL, *lockmode = NULL;
+ int rc, c, colormode = UL_COLORMODE_UNDEF;
+ struct cfdisk _cf = { .lines_idx = 0 },
+ *cf = &_cf;
+ enum {
+ OPT_LOCK = CHAR_MAX + 1
+ };
+ static const struct option longopts[] = {
+ { "color", optional_argument, NULL, 'L' },
+ { "lock", optional_argument, NULL, OPT_LOCK },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "zero", no_argument, NULL, 'z' },
+ { NULL, 0, NULL, 0 },
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ while((c = getopt_long(argc, argv, "L::hVz", longopts, NULL)) != -1) {
+ switch(c) {
+ case 'h':
+ usage();
+ break;
+ case 'L':
+ colormode = UL_COLORMODE_AUTO;
+ if (optarg)
+ colormode = colormode_or_err(optarg,
+ _("unsupported color mode"));
+ break;
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'z':
+ cf->zero_start = 1;
+ break;
+ case OPT_LOCK:
+ lockmode = "1";
+ if (optarg) {
+ if (*optarg == '=')
+ optarg++;
+ lockmode = optarg;
+ }
+ break;
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ colors_init(colormode, "cfdisk");
+
+ fdisk_init_debug(0);
+ scols_init_debug(0);
+ cfdisk_init_debug();
+ cf->cxt = fdisk_new_context();
+ if (!cf->cxt)
+ err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
+
+ fdisk_set_ask(cf->cxt, ask_callback, (void *) cf);
+
+ if (optind == argc) {
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(default_disks); i++) {
+ if (access(default_disks[i], F_OK) == 0) {
+ diskpath = default_disks[i];
+ break;
+ }
+ }
+ if (!diskpath)
+ diskpath = default_disks[0]; /* default, used for "cannot open" */
+ } else
+ diskpath = argv[optind];
+
+ rc = fdisk_assign_device(cf->cxt, diskpath, 0);
+ if (rc == -EACCES)
+ rc = fdisk_assign_device(cf->cxt, diskpath, 1);
+ if (rc != 0)
+ err(EXIT_FAILURE, _("cannot open %s"), diskpath);
+
+ if (!fdisk_is_readonly(cf->cxt)) {
+ if (blkdev_lock(fdisk_get_devfd(cf->cxt), diskpath, lockmode) != 0)
+ return EXIT_FAILURE;
+
+ cf->device_is_used = fdisk_device_is_used(cf->cxt);
+ fdisk_get_partitions(cf->cxt, &cf->original_layout);
+ }
+
+ /* Don't use err(), warn() from this point */
+ ui_init(cf);
+ ui_run(cf);
+ ui_end();
+
+ cfdisk_free_lines(cf);
+ free(cf->linesbuf);
+ free(cf->fields);
+
+ fdisk_unref_table(cf->table);
+#ifdef HAVE_LIBMOUNT
+ mnt_unref_table(cf->fstab);
+ mnt_unref_table(cf->mtab);
+ mnt_unref_cache(cf->mntcache);
+#endif
+ rc = fdisk_deassign_device(cf->cxt, cf->nwrites == 0);
+ fdisk_unref_context(cf->cxt);
+ DBG(MISC, ul_debug("bye! [rc=%d]", rc));
+ return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/disk-utils/cramfs.h b/disk-utils/cramfs.h
new file mode 100644
index 0000000..e0ddbda
--- /dev/null
+++ b/disk-utils/cramfs.h
@@ -0,0 +1,114 @@
+/*
+ * cramfs_common - cramfs common code
+ *
+ * Copyright (c) 2008 Roy Peled, the.roy.peled -at- gmail
+ * Copyright (c) 2004-2006 by Juliane Holzt, kju -at- fqdn.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __CRAMFS_H
+#define __CRAMFS_H
+
+#include <stdint.h>
+
+#define CRAMFS_MAGIC 0x28cd3d45 /* some random number */
+#define CRAMFS_SIGNATURE "Compressed ROMFS"
+
+/*
+ * Width of various bitfields in struct cramfs_inode.
+ * Primarily used to generate warnings in mkcramfs.
+ */
+#define CRAMFS_MODE_WIDTH 16
+#define CRAMFS_UID_WIDTH 16
+#define CRAMFS_SIZE_WIDTH 24
+#define CRAMFS_GID_WIDTH 8
+#define CRAMFS_NAMELEN_WIDTH 6
+#define CRAMFS_OFFSET_WIDTH 26
+
+#ifndef HOST_IS_BIG_ENDIAN
+#ifdef WORDS_BIGENDIAN
+#define HOST_IS_BIG_ENDIAN 1
+#else
+#define HOST_IS_BIG_ENDIAN 0
+#endif
+#endif
+
+/*
+ * Reasonably terse representation of the inode data.
+ */
+struct cramfs_inode {
+ uint32_t mode:16, uid:16;
+ /* SIZE for device files is i_rdev */
+ uint32_t size:24, gid:8;
+ /*
+ * NAMELEN is the length of the file name, divided by 4 and
+ * rounded up. (cramfs doesn't support hard links.)
+ *
+ * OFFSET: For symlinks and non-empty regular files, this
+ * contains the offset (divided by 4) of the file data in
+ * compressed form (starting with an array of block pointers;
+ * see README). For non-empty directories it is the offset
+ * (divided by 4) of the inode of the first file in that
+ * directory. For anything else, offset is zero.
+ */
+ uint32_t namelen:6, offset:26;
+};
+
+struct cramfs_info {
+ uint32_t crc;
+ uint32_t edition;
+ uint32_t blocks;
+ uint32_t files;
+};
+
+/*
+ * Superblock information at the beginning of the FS.
+ */
+struct cramfs_super {
+ uint32_t magic; /* 0x28cd3d45 - random number */
+ uint32_t size; /* Not used. mkcramfs currently
+ writes a constant 1<<16 here. */
+ uint32_t flags; /* 0 */
+ uint32_t future; /* 0 */
+ uint8_t signature[16]; /* "Compressed ROMFS" */
+ struct cramfs_info fsid;/* unique filesystem info */
+ uint8_t name[16]; /* user-defined name */
+ struct cramfs_inode root; /* Root inode data */
+};
+
+#define CRAMFS_FLAG_FSID_VERSION_2 0x00000001 /* fsid version #2 */
+#define CRAMFS_FLAG_SORTED_DIRS 0x00000002 /* sorted dirs */
+#define CRAMFS_FLAG_HOLES 0x00000100 /* support for holes */
+#define CRAMFS_FLAG_WRONG_SIGNATURE 0x00000200 /* reserved */
+#define CRAMFS_FLAG_SHIFTED_ROOT_OFFSET 0x00000400 /* shifted root fs */
+
+/*
+ * Valid values in super.flags. Currently we refuse to mount
+ * if (flags & ~CRAMFS_SUPPORTED_FLAGS). Maybe that should be
+ * changed to test super.future instead.
+ */
+#define CRAMFS_SUPPORTED_FLAGS (0xff)
+
+/* Uncompression interfaces to the underlying zlib */
+int cramfs_uncompress_block(void *dst, int dstlen, void *src, int srclen);
+int cramfs_uncompress_init(void);
+int cramfs_uncompress_exit(void);
+
+uint32_t u32_toggle_endianness(int big_endian, uint32_t what);
+void super_toggle_endianness(int from_big_endian, struct cramfs_super *super);
+void inode_to_host(int from_big_endian, struct cramfs_inode *inode_in,
+ struct cramfs_inode *inode_out);
+void inode_from_host(int to_big_endian, struct cramfs_inode *inode_in,
+ struct cramfs_inode *inode_out);
+
+#endif
diff --git a/disk-utils/cramfs_common.c b/disk-utils/cramfs_common.c
new file mode 100644
index 0000000..9fe3fa1
--- /dev/null
+++ b/disk-utils/cramfs_common.c
@@ -0,0 +1,109 @@
+/*
+ * cramfs_common - cramfs common code
+ *
+ * Copyright (c) 2008 Roy Peled, the.roy.peled -at- gmail.com
+ * Copyright (c) 2004-2006 by Juliane Holzt, kju -at- fqdn.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <string.h>
+#include "cramfs.h"
+#include "../include/bitops.h"
+
+uint32_t u32_toggle_endianness(int big_endian, uint32_t what)
+{
+ return big_endian == HOST_IS_BIG_ENDIAN ? what : swab32(what);
+}
+
+void super_toggle_endianness(int big_endian, struct cramfs_super *super)
+{
+ if (big_endian != HOST_IS_BIG_ENDIAN) {
+ super->magic = swab32(super->magic);
+ super->size = swab32(super->size);
+ super->flags = swab32(super->flags);
+ super->future = swab32(super->future);
+ super->fsid.crc = swab32(super->fsid.crc);
+ super->fsid.edition = swab32(super->fsid.edition);
+ super->fsid.blocks = swab32(super->fsid.blocks);
+ super->fsid.files = swab32(super->fsid.files);
+ }
+}
+
+static void inode_toggle_endianness(int input_big_endian, int output_big_endian,
+ struct cramfs_inode *inode_in,
+ struct cramfs_inode *inode_out)
+{
+ if (input_big_endian == output_big_endian) {
+ memmove(inode_out, inode_in, sizeof(*inode_out));
+ } else {
+ unsigned char inode_out_buf[sizeof(*inode_in)];
+ unsigned char *inode_in_buf = (unsigned char*)inode_in;
+
+ inode_out_buf[0] = inode_in_buf[1]; /* 16 bit: mode */
+ inode_out_buf[1] = inode_in_buf[0];
+
+ inode_out_buf[2] = inode_in_buf[3]; /* 16 bit: uid */
+ inode_out_buf[3] = inode_in_buf[2];
+
+ inode_out_buf[4] = inode_in_buf[6]; /* 24 bit: size */
+ inode_out_buf[5] = inode_in_buf[5];
+ inode_out_buf[6] = inode_in_buf[4];
+
+ inode_out_buf[7] = inode_in_buf[7]; /* 8 bit: gid width */
+
+ /*
+ * Stop the madness! Outlaw C bitfields! They are unportable
+ * and nasty! See for yourself what a mess this is:
+ */
+ if (output_big_endian) {
+ inode_out_buf[ 8] = ( (inode_in_buf[ 8]&0x3F) << 2 ) |
+ ( (inode_in_buf[11]&0xC0) >> 6 );
+
+ inode_out_buf[ 9] = ( (inode_in_buf[11]&0x3F) << 2 ) |
+ ( (inode_in_buf[10]&0xC0) >> 6 );
+
+ inode_out_buf[10] = ( (inode_in_buf[10]&0x3F) << 2 ) |
+ ( (inode_in_buf[ 9]&0xC0) >> 6 );
+
+ inode_out_buf[11] = ( (inode_in_buf[ 9]&0x3F) << 2 ) |
+ ( (inode_in_buf[ 8]&0xC0) >> 6 );
+ } else {
+ inode_out_buf[ 8] = ( (inode_in_buf[ 8]&0xFD) >> 2 ) |
+ ( (inode_in_buf[11]&0x03) << 6 );
+
+ inode_out_buf[ 9] = ( (inode_in_buf[11]&0xFD) >> 2 ) |
+ ( (inode_in_buf[10]&0x03) << 6 );
+
+ inode_out_buf[10] = ( (inode_in_buf[10]&0xFD) >> 2 ) |
+ ( (inode_in_buf[ 9]&0x03) << 6 );
+
+ inode_out_buf[11] = ( (inode_in_buf[ 9]&0xFD) >> 2 ) |
+ ( (inode_in_buf[ 8]&0x03) << 6 );
+ }
+ memmove(inode_out, inode_out_buf, sizeof(*inode_out));
+ }
+}
+
+void inode_to_host(int from_big_endian, struct cramfs_inode *inode_in,
+ struct cramfs_inode *inode_out)
+{
+ inode_toggle_endianness(from_big_endian, HOST_IS_BIG_ENDIAN, inode_in,
+ inode_out);
+}
+
+void inode_from_host(int to_big_endian, struct cramfs_inode *inode_in,
+ struct cramfs_inode *inode_out)
+{
+ inode_toggle_endianness(HOST_IS_BIG_ENDIAN, to_big_endian, inode_in,
+ inode_out);
+}
diff --git a/disk-utils/delpart.8 b/disk-utils/delpart.8
new file mode 100644
index 0000000..7384e25
--- /dev/null
+++ b/disk-utils/delpart.8
@@ -0,0 +1,27 @@
+.\" delpart.8 -- man page for delpart
+.\" Copyright 2007 Karel Zak <kzak@redhat.com>
+.\" Copyright 2007 Red Hat, Inc.
+.\" May be distributed under the GNU General Public License
+.TH DELPART 8 "January 2015" "util-linux" "System Administration"
+.SH NAME
+delpart \- tell the kernel to forget about a partition
+.SH SYNOPSIS
+.B delpart
+.I device partition
+.SH DESCRIPTION
+.B delpart
+asks the Linux kernel to forget about the specified \fIpartition\fR
+(a number) on the specified \fIdevice\fR.
+The command is a simple wrapper around the "del partition" ioctl.
+
+This command doesn't manipulate partitions on a block device.
+
+.SH SEE ALSO
+.BR addpart (8),
+.BR fdisk (8),
+.BR parted (8),
+.BR partprobe (8),
+.BR partx (8)
+.SH AVAILABILITY
+The delpart command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/delpart.c b/disk-utils/delpart.c
new file mode 100644
index 0000000..7ee0c52
--- /dev/null
+++ b/disk-utils/delpart.c
@@ -0,0 +1,65 @@
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "c.h"
+#include "nls.h"
+#include "partx.h"
+#include "strutils.h"
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s <disk device> <partition number>\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Tell the kernel to forget about a specified partition.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ printf(USAGE_HELP_OPTIONS(16));
+ printf(USAGE_MAN_TAIL("delpart(8)"));
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int c, fd;
+
+ static const struct option longopts[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0},
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ while ((c = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
+ switch (c) {
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ if (argc != 3) {
+ warnx(_("not enough arguments"));
+ errtryhelp(EXIT_FAILURE);
+ }
+
+
+ if ((fd = open(argv[1], O_RDONLY)) < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), argv[1]);
+
+ if (partx_del_partition(fd,
+ strtou32_or_err(argv[2], _("invalid partition number argument"))))
+ err(EXIT_FAILURE, _("failed to remove partition"));
+
+ return EXIT_SUCCESS;
+}
diff --git a/disk-utils/fdformat.8 b/disk-utils/fdformat.8
new file mode 100644
index 0000000..8453a5a
--- /dev/null
+++ b/disk-utils/fdformat.8
@@ -0,0 +1,79 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH FDFORMAT 8 "June 2020" "util-linux" "System Administration"
+.SH NAME
+fdformat \- low-level format a floppy disk
+.SH SYNOPSIS
+.B fdformat
+.RI [options] " device"
+.SH DESCRIPTION
+.B fdformat
+does a low-level format on a floppy disk.
+.I device
+is usually one of the following (for floppy devices the major = 2, and the
+minor is shown for informational purposes only):
+.sp
+.nf
+.RS
+/dev/fd0d360 (minor = 4)
+/dev/fd0h1200 (minor = 8)
+/dev/fd0D360 (minor = 12)
+/dev/fd0H360 (minor = 12)
+/dev/fd0D720 (minor = 16)
+/dev/fd0H720 (minor = 16)
+/dev/fd0h360 (minor = 20)
+/dev/fd0h720 (minor = 24)
+/dev/fd0H1440 (minor = 28)
+.PP
+/dev/fd1d360 (minor = 5)
+/dev/fd1h1200 (minor = 9)
+/dev/fd1D360 (minor = 13)
+/dev/fd1H360 (minor = 13)
+/dev/fd1D720 (minor = 17)
+/dev/fd1H720 (minor = 17)
+/dev/fd1h360 (minor = 21)
+/dev/fd1h720 (minor = 25)
+/dev/fd1H1440 (minor = 29)
+.RE
+.fi
+.PP
+The generic floppy devices, /dev/fd0 and /dev/fd1, will fail to work with
+.B fdformat
+when a non-standard format is being used, or if the format has not been
+autodetected earlier. In this case, use
+.BR setfdprm (8)
+to load the disk parameters.
+.SH OPTIONS
+.TP
+\fB\-f\fR, \fB\-\-from\fR \fIN\fR
+Start at the track \fIN\fR (default is 0).
+.TP
+\fB\-t\fR, \fB\-\-to\fR \fIN\fR
+Stop at the track \fIN\fR.
+.TP
+\fB\-r\fR, \fB\-\-repair\fR \fIN\fR
+Try to repair tracks failed during the verification (max \fIN\fR retries).
+.TP
+\fB\-n\fR, \fB\-\-no\-verify\fR
+Skip the verification that is normally performed after the formatting.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help text and exit.
+.SH NOTES
+This utility does not handle USB floppy disk drives. Use
+.BR ufiformat (8)
+instead.
+.SH AUTHORS
+Werner Almesberger (almesber@nessie.cs.id.ethz.ch)
+.SH SEE ALSO
+.BR fd (4),
+.BR emkfs (8),
+.BR mkfs (8),
+.BR setfdprm (8),
+.BR ufiformat (8)
+.SH AVAILABILITY
+The fdformat command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/fdformat.c b/disk-utils/fdformat.c
new file mode 100644
index 0000000..38849eb
--- /dev/null
+++ b/disk-utils/fdformat.c
@@ -0,0 +1,256 @@
+/*
+ * fdformat.c - Low-level formats a floppy disk - Werner Almesberger
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/fd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "blkdev.h"
+#include "strutils.h"
+#include "closestream.h"
+#include "nls.h"
+#include "xalloc.h"
+
+#define SECTOR_SIZE 512
+
+static struct floppy_struct param;
+
+
+static void format_begin(int ctrl)
+{
+ if (ioctl(ctrl, FDFMTBEG, NULL) < 0)
+ err(EXIT_FAILURE, "ioctl: FDFMTBEG");
+}
+
+static void format_end(int ctrl)
+{
+ if (ioctl(ctrl, FDFMTEND, NULL) < 0)
+ err(EXIT_FAILURE, "ioctl: FDFMTEND");
+}
+
+static void format_track_head(int ctrl, struct format_descr *descr)
+{
+ if (ioctl(ctrl, FDFMTTRK, (long) descr) < 0)
+ err(EXIT_FAILURE, "ioctl: FDFMTTRK");
+}
+
+static void seek_track_head(int ctrl, struct format_descr *descr)
+{
+ lseek(ctrl, ((off_t) descr->track * param.head + descr->head)
+ * param.sect * SECTOR_SIZE, SEEK_SET);
+}
+
+static void format_disk(int ctrl, unsigned int track_from, unsigned int track_to)
+{
+ struct format_descr current;
+
+ printf(_("Formatting ... "));
+ fflush(stdout);
+
+ format_begin(ctrl);
+
+ for (current.track = track_from; current.track <= track_to; current.track++) {
+ for (current.head = 0; current.head < param.head; current.head++) {
+ printf("%3u/%u\b\b\b\b\b", current.track, current.head);
+ fflush(stdout);
+ format_track_head(ctrl, &current);
+ }
+ }
+
+ format_end(ctrl);
+
+ printf(" \b\b\b\b\b%s", _("done\n"));
+}
+
+static void verify_disk(int ctrl, unsigned int track_from, unsigned int track_to, unsigned int repair)
+{
+ unsigned char *data;
+ struct format_descr current;
+ int track_size, count;
+ unsigned int retries_left;
+
+ track_size = param.sect * SECTOR_SIZE;
+ data = xmalloc(track_size);
+ printf(_("Verifying ... "));
+ fflush(stdout);
+
+ current.track = track_from;
+ current.head = 0;
+ seek_track_head (ctrl, &current);
+
+ for (current.track = track_from; current.track <= track_to; current.track++) {
+ for (current.head = 0; current.head < param.head; current.head++) {
+ int read_bytes;
+
+ printf("%3u\b\b\b", current.track);
+ fflush(stdout);
+
+ retries_left = repair;
+ do {
+ read_bytes = read(ctrl, data, track_size);
+ if (read_bytes != track_size) {
+ if (retries_left) {
+ format_begin(ctrl);
+ format_track_head(ctrl, &current);
+ format_end(ctrl);
+ seek_track_head (ctrl, &current);
+ retries_left--;
+ if (retries_left)
+ continue;
+ }
+ if (read_bytes < 0)
+ perror(_("Read: "));
+ fprintf(stderr,
+ _("Problem reading track/head %u/%u,"
+ " expected %d, read %d\n"),
+ current.track, current.head, track_size, read_bytes);
+ free(data);
+ exit(EXIT_FAILURE);
+ }
+ for (count = 0; count < track_size; count++)
+ if (data[count] != FD_FILL_BYTE) {
+ if (retries_left) {
+ format_begin(ctrl);
+ format_track_head(ctrl, &current);
+ format_end(ctrl);
+ seek_track_head (ctrl, &current);
+ retries_left--;
+ if (retries_left)
+ continue;
+ }
+ printf(_("bad data in track/head %u/%u\n"
+ "Continuing ... "), current.track, current.head);
+ fflush(stdout);
+ break;
+ }
+ break;
+ } while (retries_left);
+ }
+ }
+
+ free(data);
+ printf(_("done\n"));
+}
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s [options] <device>\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Do a low-level formatting of a floppy disk.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -f, --from <N> start at the track N (default 0)\n"), out);
+ fputs(_(" -t, --to <N> stop at the track N\n"), out);
+ fputs(_(" -r, --repair <N> try to repair tracks failed during\n"
+ " the verification (max N retries)\n"), out);
+ fputs(_(" -n, --no-verify disable the verification after the format\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ printf(USAGE_HELP_OPTIONS(19));
+ printf(USAGE_MAN_TAIL("fdformat(8)"));
+
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int ch;
+ int ctrl;
+ int verify = 1;
+ unsigned int repair = 0;
+ unsigned int track_from = 0;
+ unsigned int track_to = 0;
+ int has_user_defined_track_to = 0;
+ struct stat st;
+
+ static const struct option longopts[] = {
+ {"from", required_argument, NULL, 'f'},
+ {"to", required_argument, NULL, 't'},
+ {"repair", required_argument, NULL, 'r'},
+ {"no-verify", no_argument, NULL, 'n'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ while ((ch = getopt_long(argc, argv, "f:t:r:nVh", longopts, NULL)) != -1)
+ switch (ch) {
+ case 'f':
+ track_from = strtou32_or_err(optarg, _("invalid argument - from"));
+ break;
+ case 't':
+ has_user_defined_track_to = 1;
+ track_to = strtou32_or_err(optarg, _("invalid argument - to"));
+ break;
+ case 'r':
+ repair = strtou32_or_err(optarg, _("invalid argument - repair"));
+ break;
+ case 'n':
+ verify = 0;
+ break;
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ warnx(_("no device specified"));
+ errtryhelp(EXIT_FAILURE);
+ }
+ if (stat(argv[0], &st) < 0)
+ err(EXIT_FAILURE, _("stat of %s failed"), argv[0]);
+ if (!S_ISBLK(st.st_mode))
+ /* do not test major - perhaps this was an USB floppy */
+ errx(EXIT_FAILURE, _("%s: not a block device"), argv[0]);
+ ctrl = open_blkdev_or_file(&st, argv[0], O_RDWR);
+ if (ctrl < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), argv[0]);
+ if (ioctl(ctrl, FDGETPRM, (long) &param) < 0)
+ err(EXIT_FAILURE, _("could not determine current format type"));
+
+ 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);
+
+ if (!has_user_defined_track_to)
+ track_to = param.track - 1;
+
+ if (track_from >= param.track)
+ err(EXIT_FAILURE, _("user defined start track exceeds the medium specific maximum"));
+ if (track_to >= param.track)
+ err(EXIT_FAILURE, _("user defined end track exceeds the medium specific maximum"));
+ if (track_from > track_to)
+ err(EXIT_FAILURE, _("user defined start track exceeds the user defined end track"));
+
+ format_disk(ctrl, track_from, track_to);
+
+ if (verify)
+ verify_disk(ctrl, track_from, track_to, repair);
+
+ if (close_fd(ctrl) != 0)
+ err(EXIT_FAILURE, _("close failed"));
+
+ return EXIT_SUCCESS;
+}
diff --git a/disk-utils/fdisk-list.c b/disk-utils/fdisk-list.c
new file mode 100644
index 0000000..8cc1162
--- /dev/null
+++ b/disk-utils/fdisk-list.c
@@ -0,0 +1,543 @@
+#include <libfdisk.h>
+#include <libsmartcols.h>
+#include <assert.h>
+
+#include "c.h"
+#include "xalloc.h"
+#include "nls.h"
+#include "blkdev.h"
+#include "mbsalign.h"
+#include "pathnames.h"
+#include "canonicalize.h"
+#include "strutils.h"
+#include "sysfs.h"
+#include "colors.h"
+#include "ttyutils.h"
+
+#include "fdisk-list.h"
+
+/* see init_fields() */
+static const char *fields_string;
+static int *fields_ids;
+static size_t fields_nids;
+static const struct fdisk_label *fields_label;
+
+static int is_ide_cdrom_or_tape(char *device)
+{
+ int fd, ret;
+
+ if ((fd = open(device, O_RDONLY)) < 0)
+ return 0;
+ ret = blkdev_is_cdrom(fd);
+
+ close(fd);
+ return ret;
+}
+
+void list_disk_identifier(struct fdisk_context *cxt)
+{
+ struct fdisk_label *lb = fdisk_get_label(cxt, NULL);
+ char *id = NULL;
+
+ if (fdisk_has_label(cxt))
+ fdisk_info(cxt, _("Disklabel type: %s"),
+ fdisk_label_get_name(lb));
+
+ if (!fdisk_is_details(cxt) && fdisk_get_disklabel_id(cxt, &id) == 0 && id) {
+ fdisk_info(cxt, _("Disk identifier: %s"), id);
+ free(id);
+ }
+}
+
+void list_disk_geometry(struct fdisk_context *cxt)
+{
+ struct fdisk_label *lb = fdisk_get_label(cxt, NULL);
+ uint64_t bytes = fdisk_get_nsectors(cxt) * fdisk_get_sector_size(cxt);
+ char *strsz = size_to_human_string(SIZE_DECIMAL_2DIGITS
+ | SIZE_SUFFIX_SPACE
+ | SIZE_SUFFIX_3LETTER, bytes);
+
+ color_scheme_enable("header", UL_COLOR_BOLD);
+ fdisk_info(cxt, _("Disk %s: %s, %ju bytes, %ju sectors"),
+ fdisk_get_devname(cxt), strsz,
+ bytes, (uintmax_t) fdisk_get_nsectors(cxt));
+ color_disable();
+ free(strsz);
+
+ if (fdisk_get_devmodel(cxt))
+ fdisk_info(cxt, _("Disk model: %s"), fdisk_get_devmodel(cxt));
+
+ if (lb && (fdisk_label_require_geometry(lb) || fdisk_use_cylinders(cxt)))
+ fdisk_info(cxt, _("Geometry: %d heads, %llu sectors/track, %llu cylinders"),
+ fdisk_get_geom_heads(cxt),
+ fdisk_get_geom_sectors(cxt),
+ fdisk_get_geom_cylinders(cxt));
+
+ fdisk_info(cxt, _("Units: %s of %d * %ld = %ld bytes"),
+ fdisk_get_unit(cxt, FDISK_PLURAL),
+ fdisk_get_units_per_sector(cxt),
+ fdisk_get_sector_size(cxt),
+ fdisk_get_units_per_sector(cxt) * fdisk_get_sector_size(cxt));
+
+ fdisk_info(cxt, _("Sector size (logical/physical): %lu bytes / %lu bytes"),
+ fdisk_get_sector_size(cxt),
+ fdisk_get_physector_size(cxt));
+ fdisk_info(cxt, _("I/O size (minimum/optimal): %lu bytes / %lu bytes"),
+ fdisk_get_minimal_iosize(cxt),
+ fdisk_get_optimal_iosize(cxt));
+ if (fdisk_get_alignment_offset(cxt))
+ fdisk_info(cxt, _("Alignment offset: %lu bytes"),
+ fdisk_get_alignment_offset(cxt));
+
+ list_disk_identifier(cxt);
+}
+
+void list_disklabel(struct fdisk_context *cxt)
+{
+ struct fdisk_table *tb = NULL;
+ struct fdisk_partition *pa = NULL;
+ struct fdisk_iter *itr = NULL;
+ struct fdisk_label *lb;
+ struct libscols_table *out = NULL;
+ const char *bold = NULL;
+ int *ids = NULL; /* IDs of fdisk_fields */
+ size_t nids = 0, i;
+ int post = 0;
+
+ /* print label specific stuff by libfdisk FDISK_ASK_INFO API */
+ fdisk_list_disklabel(cxt);
+
+ /* get partitions and generate output */
+ if (fdisk_get_partitions(cxt, &tb) || fdisk_table_get_nents(tb) <= 0)
+ goto done;
+
+ ids = init_fields(cxt, NULL, &nids);
+ if (!ids)
+ goto done;
+
+ itr = fdisk_new_iter(FDISK_ITER_FORWARD);
+ if (!itr) {
+ fdisk_warn(cxt, _("failed to allocate iterator"));
+ goto done;
+ }
+
+ out = scols_new_table();
+ if (!out) {
+ fdisk_warn(cxt, _("failed to allocate output table"));
+ goto done;
+ }
+
+ if (colors_wanted()) {
+ scols_table_enable_colors(out, 1);
+ bold = color_scheme_get_sequence("header", UL_COLOR_BOLD);
+ }
+
+ lb = fdisk_get_label(cxt, NULL);
+ assert(lb);
+
+ /* define output table columns */
+ for (i = 0; i < nids; i++) {
+ int fl = 0;
+ struct libscols_column *co;
+ const struct fdisk_field *field =
+ fdisk_label_get_field(lb, ids[i]);
+ if (!field)
+ continue;
+ if (fdisk_field_is_number(field))
+ fl |= SCOLS_FL_RIGHT;
+ if (fdisk_field_get_id(field) == FDISK_FIELD_TYPE)
+ fl |= SCOLS_FL_TRUNC;
+
+ co = scols_table_new_column(out,
+ _(fdisk_field_get_name(field)),
+ fdisk_field_get_width(field), fl);
+ if (!co)
+ goto done;
+
+ /* set column header color */
+ if (bold)
+ scols_cell_set_color(scols_column_get_header(co), bold);
+ }
+
+ /* fill-in output table */
+ while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
+ struct libscols_line *ln = scols_table_new_line(out, NULL);
+
+ if (!ln) {
+ fdisk_warn(cxt, _("failed to allocate output line"));
+ goto done;
+ }
+
+ for (i = 0; i < nids; i++) {
+ char *data = NULL;
+
+ if (fdisk_partition_to_string(pa, cxt, ids[i], &data))
+ continue;
+ if (scols_line_refer_data(ln, i, data)) {
+ fdisk_warn(cxt, _("failed to add output data"));
+ goto done;
+ }
+ }
+ }
+
+ /* print */
+ if (!scols_table_is_empty(out)) {
+ fdisk_info(cxt, ""); /* just line break */
+ scols_print_table(out);
+ }
+
+ /* print warnings */
+ fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
+ while (itr && fdisk_table_next_partition(tb, itr, &pa) == 0) {
+ if (!fdisk_partition_has_start(pa))
+ continue;
+ if (!fdisk_lba_is_phy_aligned(cxt, fdisk_partition_get_start(pa))) {
+ if (!post)
+ fdisk_info(cxt, ""); /* line break */
+ fdisk_warnx(cxt, _("Partition %zu does not start on physical sector boundary."),
+ fdisk_partition_get_partno(pa) + 1);
+ post++;
+ }
+ if (fdisk_partition_has_wipe(cxt, pa)) {
+ if (!post)
+ fdisk_info(cxt, ""); /* line break */
+
+ fdisk_info(cxt, _("Filesystem/RAID signature on partition %zu will be wiped."),
+ fdisk_partition_get_partno(pa) + 1);
+ post++;
+ }
+ }
+
+ if (fdisk_table_wrong_order(tb)) {
+ if (!post)
+ fdisk_info(cxt, ""); /* line break */
+ fdisk_info(cxt, _("Partition table entries are not in disk order."));
+ }
+done:
+ scols_unref_table(out);
+ fdisk_unref_table(tb);
+ fdisk_free_iter(itr);
+}
+
+void list_freespace(struct fdisk_context *cxt)
+{
+ struct fdisk_table *tb = NULL;
+ struct fdisk_partition *pa = NULL;
+ struct fdisk_iter *itr = NULL;
+ struct libscols_table *out = NULL;
+ const char *bold = NULL;
+ size_t i;
+ uintmax_t sumsize = 0, bytes = 0;
+ char *strsz;
+
+ static const char *colnames[] = { N_("Start"), N_("End"), N_("Sectors"), N_("Size") };
+ static const int colids[] = { FDISK_FIELD_START, FDISK_FIELD_END, FDISK_FIELD_SECTORS, FDISK_FIELD_SIZE };
+
+ if (fdisk_get_freespaces(cxt, &tb))
+ goto done;
+
+ itr = fdisk_new_iter(FDISK_ITER_FORWARD);
+ if (!itr) {
+ fdisk_warn(cxt, _("failed to allocate iterator"));
+ goto done;
+ }
+
+ out = scols_new_table();
+ if (!out) {
+ fdisk_warn(cxt, _("failed to allocate output table"));
+ goto done;
+ }
+
+ if (colors_wanted()) {
+ scols_table_enable_colors(out, 1);
+ bold = color_scheme_get_sequence("header", UL_COLOR_BOLD);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(colnames); i++) {
+ struct libscols_column *co = scols_table_new_column(out, _(colnames[i]), 5, SCOLS_FL_RIGHT);
+
+ if (!co)
+ goto done;
+ if (bold)
+ scols_cell_set_color(scols_column_get_header(co), bold);
+ }
+
+ /* fill-in output table */
+ while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
+ struct libscols_line *ln = scols_table_new_line(out, NULL);
+ char *data;
+
+ if (!ln) {
+ fdisk_warn(cxt, _("failed to allocate output line"));
+ goto done;
+ }
+ for (i = 0; i < ARRAY_SIZE(colids); i++) {
+ if (fdisk_partition_to_string(pa, cxt, colids[i], &data))
+ continue;
+ if (scols_line_refer_data(ln, i, data)) {
+ fdisk_warn(cxt, _("failed to add output data"));
+ goto done;
+ }
+ }
+
+ if (fdisk_partition_has_size(pa))
+ sumsize += fdisk_partition_get_size(pa);
+ }
+
+ bytes = sumsize * fdisk_get_sector_size(cxt);
+ strsz = size_to_human_string(SIZE_DECIMAL_2DIGITS
+ | SIZE_SUFFIX_SPACE
+ | SIZE_SUFFIX_3LETTER, bytes);
+
+ color_scheme_enable("header", UL_COLOR_BOLD);
+ fdisk_info(cxt, _("Unpartitioned space %s: %s, %ju bytes, %ju sectors"),
+ fdisk_get_devname(cxt), strsz,
+ bytes, sumsize);
+ color_disable();
+ free(strsz);
+
+ fdisk_info(cxt, _("Units: %s of %d * %ld = %ld bytes"),
+ fdisk_get_unit(cxt, FDISK_PLURAL),
+ fdisk_get_units_per_sector(cxt),
+ fdisk_get_sector_size(cxt),
+ fdisk_get_units_per_sector(cxt) * fdisk_get_sector_size(cxt));
+
+ fdisk_info(cxt, _("Sector size (logical/physical): %lu bytes / %lu bytes"),
+ fdisk_get_sector_size(cxt),
+ fdisk_get_physector_size(cxt));
+
+ /* print */
+ if (!scols_table_is_empty(out)) {
+ fdisk_info(cxt, ""); /* line break */
+ scols_print_table(out);
+ }
+done:
+ scols_unref_table(out);
+ fdisk_unref_table(tb);
+ fdisk_free_iter(itr);
+}
+
+char *next_proc_partition(FILE **f)
+{
+ char line[128 + 1];
+
+ if (!*f) {
+ *f = fopen(_PATH_PROC_PARTITIONS, "r");
+ if (!*f) {
+ warn(_("cannot open %s"), _PATH_PROC_PARTITIONS);
+ return NULL;
+ }
+ }
+
+ while (fgets(line, sizeof(line), *f)) {
+ char buf[PATH_MAX], *cn;
+ dev_t devno;
+
+ if (sscanf(line, " %*d %*d %*d %128[^\n ]", buf) != 1)
+ continue;
+
+ devno = sysfs_devname_to_devno(buf);
+ if (devno <= 0)
+ continue;
+
+ if (sysfs_devno_is_dm_private(devno, NULL) ||
+ sysfs_devno_is_wholedisk(devno) <= 0)
+ continue;
+
+ if (!sysfs_devno_to_devpath(devno, buf, sizeof(buf)))
+ continue;
+
+ cn = canonicalize_path(buf);
+ if (!cn)
+ continue;
+
+ if (!is_ide_cdrom_or_tape(cn))
+ return cn;
+ }
+ fclose(*f);
+ *f = NULL;
+
+ return NULL;
+}
+
+int print_device_pt(struct fdisk_context *cxt, char *device, int warnme,
+ int verify, int seperator)
+{
+ if (fdisk_assign_device(cxt, device, 1) != 0) { /* read-only */
+ if (warnme || errno == EACCES)
+ warn(_("cannot open %s"), device);
+ return -1;
+ }
+
+ if (seperator)
+ fputs("\n\n", stdout);
+
+ list_disk_geometry(cxt);
+
+ if (fdisk_has_label(cxt)) {
+ list_disklabel(cxt);
+ if (verify)
+ fdisk_verify_disklabel(cxt);
+ }
+ fdisk_deassign_device(cxt, 1);
+ return 0;
+}
+
+int print_device_freespace(struct fdisk_context *cxt, char *device, int warnme,
+ int seperator)
+{
+ if (fdisk_assign_device(cxt, device, 1) != 0) { /* read-only */
+ if (warnme || errno == EACCES)
+ warn(_("cannot open %s"), device);
+ return -1;
+ }
+
+ if (seperator)
+ fputs("\n\n", stdout);
+
+ list_freespace(cxt);
+ fdisk_deassign_device(cxt, 1);
+ return 0;
+}
+
+void print_all_devices_pt(struct fdisk_context *cxt, int verify)
+{
+ FILE *f = NULL;
+ int sep = 0;
+ char *dev;
+
+ while ((dev = next_proc_partition(&f))) {
+ print_device_pt(cxt, dev, 0, verify, sep);
+ free(dev);
+ sep = 1;
+ }
+}
+
+void print_all_devices_freespace(struct fdisk_context *cxt)
+{
+ FILE *f = NULL;
+ int sep = 0;
+ char *dev;
+
+ while ((dev = next_proc_partition(&f))) {
+ print_device_freespace(cxt, dev, 0, sep);
+ free(dev);
+ sep = 1;
+ }
+}
+
+/* usable for example in usage() */
+void list_available_columns(FILE *out)
+{
+ size_t i;
+ int termwidth;
+ struct fdisk_label *lb = NULL;
+ struct fdisk_context *cxt = fdisk_new_context();
+
+ if (!cxt)
+ return;
+
+ termwidth = get_terminal_width(80);
+
+ fprintf(out, USAGE_COLUMNS);
+
+ while (fdisk_next_label(cxt, &lb) == 0) {
+ size_t width = 6; /* label name and separators */
+
+ fprintf(out, " %s:", fdisk_label_get_name(lb));
+ for (i = 1; i < FDISK_NFIELDS; i++) {
+ const struct fdisk_field *fl = fdisk_label_get_field(lb, i);
+ const char *name = fl ? fdisk_field_get_name(fl) : NULL;
+ size_t len;
+
+ if (!name)
+ continue;
+ len = strlen(name) + 1;
+ if (width + len > (size_t) termwidth) {
+ fputs("\n ", out);
+ width = 6;
+ }
+ fprintf(out, " %s", name);
+ width += len;
+ }
+ fputc('\n', out);
+ }
+
+ fdisk_unref_context(cxt);
+}
+
+static int fieldname_to_id(const char *name, size_t namesz)
+{
+ const struct fdisk_field *fl;
+ char buf[namesz + 1];
+
+ assert(name);
+ assert(namesz);
+ assert(fields_label);
+
+ memcpy(buf, name, namesz);
+ buf[namesz] = '\0';
+
+ fl = fdisk_label_get_field_by_name(fields_label, buf);
+ if (!fl) {
+ warnx(_("%s unknown column: %s"),
+ fdisk_label_get_name(fields_label), buf);
+ return -1;
+ }
+ return fdisk_field_get_id(fl);
+}
+
+/*
+ * Initialize array with output columns (fields_ids[]) according to
+ * comma delimited list of columns (@str). If the list string is not
+ * defined then use library defaults. This function is "-o <list>"
+ * backend.
+ *
+ * If the columns are already initialized then returns already existing columns.
+ */
+int *init_fields(struct fdisk_context *cxt, const char *str, size_t *n)
+{
+ int *dflt_ids = NULL;
+ struct fdisk_label *lb;
+
+ if (!fields_string)
+ fields_string = str;
+ if (!cxt)
+ goto done;
+
+ lb = fdisk_get_label(cxt, NULL);
+
+ if (!lb || fields_label != lb) { /* label changed: reset */
+ free(fields_ids);
+ fields_ids = NULL;
+ fields_label = lb;
+ fields_nids = 0;
+ }
+
+ if (!fields_label) /* no label */
+ goto done;
+ if (fields_nids)
+ goto done; /* already initialized */
+
+ /* library default */
+ if (fdisk_label_get_fields_ids(NULL, cxt, &dflt_ids, &fields_nids))
+ goto done;
+
+ fields_ids = xcalloc(FDISK_NFIELDS * 2, sizeof(int));
+
+ /* copy defaults to the list with wanted fields */
+ memcpy(fields_ids, dflt_ids, fields_nids * sizeof(int));
+ free(dflt_ids);
+
+ /* extend or replace fields_nids[] according to fields_string */
+ if (fields_string &&
+ string_add_to_idarray(fields_string, fields_ids, FDISK_NFIELDS * 2,
+ &fields_nids, fieldname_to_id) < 0)
+ exit(EXIT_FAILURE);
+done:
+ fields_label = NULL;
+ if (n)
+ *n = fields_nids;
+ return fields_ids;
+}
+
diff --git a/disk-utils/fdisk-list.h b/disk-utils/fdisk-list.h
new file mode 100644
index 0000000..a31ab0a
--- /dev/null
+++ b/disk-utils/fdisk-list.h
@@ -0,0 +1,47 @@
+#ifndef UTIL_LINUX_FDISK_LIST_H
+#define UTIL_LINUX_FDISK_LIST_H
+
+extern void list_disklabel(struct fdisk_context *cxt);
+extern void list_disk_identifier(struct fdisk_context *cxt);
+extern void list_disk_geometry(struct fdisk_context *cxt);
+extern void list_freespace(struct fdisk_context *cxt);
+
+extern char *next_proc_partition(FILE **f);
+extern int print_device_pt(struct fdisk_context *cxt, char *device, int warnme, int verify, int seperator);
+extern int print_device_freespace(struct fdisk_context *cxt, char *device, int warnme, int seperator);
+
+extern void print_all_devices_pt(struct fdisk_context *cxt, int verify);
+extern void print_all_devices_freespace(struct fdisk_context *cxt);
+
+extern void list_available_columns(FILE *out);
+extern int *init_fields(struct fdisk_context *cxt, const char *str, size_t *n);
+
+
+/* used by fdisk and sfdisk */
+enum {
+ WIPEMODE_AUTO = 0,
+ WIPEMODE_NEVER = 1,
+ WIPEMODE_ALWAYS = 2
+};
+
+static inline int wipemode_from_string(const char *str)
+{
+ size_t i;
+ static const char *modes[] = {
+ [WIPEMODE_AUTO] = "auto",
+ [WIPEMODE_NEVER] = "never",
+ [WIPEMODE_ALWAYS] = "always"
+ };
+
+ if (!str || !*str)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(modes); i++) {
+ if (strcasecmp(str, modes[i]) == 0)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+#endif /* UTIL_LINUX_FDISK_LIST_H */
diff --git a/disk-utils/fdisk-menu.c b/disk-utils/fdisk-menu.c
new file mode 100644
index 0000000..9f09eea
--- /dev/null
+++ b/disk-utils/fdisk-menu.c
@@ -0,0 +1,1112 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "c.h"
+#include "rpmatch.h"
+#include "fdisk.h"
+#include "pt-sun.h"
+#include "pt-mbr.h"
+
+struct menu_entry {
+ const char key; /* command key */
+ const char *title; /* help string */
+ unsigned int normal : 1, /* normal mode */
+ expert : 1, /* expert mode */
+ hidden : 1; /* be sensitive for this key,
+ but don't print it in help */
+
+ enum fdisk_labeltype label; /* only for this label */
+ int exclude; /* all labels except these */
+ enum fdisk_labeltype parent; /* for nested PT */
+};
+
+#define IS_MENU_SEP(e) ((e)->key == '-')
+#define IS_MENU_HID(e) ((e)->hidden)
+
+struct menu {
+ enum fdisk_labeltype label; /* only for this label */
+ int exclude; /* all labels except these */
+
+ unsigned int nonested : 1; /* don't make this menu active in nested PT */
+
+ int (*callback)(struct fdisk_context **,
+ const struct menu *,
+ const struct menu_entry *);
+
+ struct menu_entry entries[]; /* NULL terminated array */
+};
+
+struct menu_context {
+ size_t menu_idx; /* the current menu */
+ size_t entry_idx; /* index with in the current menu */
+};
+
+#define MENU_CXT_EMPTY { 0, 0 }
+#define DECLARE_MENU_CB(x) \
+ static int x(struct fdisk_context **, \
+ const struct menu *, \
+ const struct menu_entry *)
+
+DECLARE_MENU_CB(gpt_menu_cb);
+DECLARE_MENU_CB(sun_menu_cb);
+DECLARE_MENU_CB(sgi_menu_cb);
+DECLARE_MENU_CB(geo_menu_cb);
+DECLARE_MENU_CB(dos_menu_cb);
+DECLARE_MENU_CB(bsd_menu_cb);
+DECLARE_MENU_CB(createlabel_menu_cb);
+DECLARE_MENU_CB(generic_menu_cb);
+
+/*
+ * Menu entry macros:
+ * MENU_X* expert mode only
+ * MENU_B* both -- expert + normal mode
+ *
+ * *_E exclude this label
+ * *_H hidden
+ * *_L only for this label
+ */
+
+/* separator */
+#define MENU_SEP(t) { .title = t, .key = '-', .normal = 1 }
+#define MENU_XSEP(t) { .title = t, .key = '-', .expert = 1 }
+#define MENU_BSEP(t) { .title = t, .key = '-', .expert = 1, .normal = 1 }
+
+/* entry */
+#define MENU_ENT(k, t) { .title = t, .key = k, .normal = 1 }
+#define MENU_ENT_E(k, t, l) { .title = t, .key = k, .normal = 1, .exclude = l }
+#define MENU_ENT_L(k, t, l) { .title = t, .key = k, .normal = 1, .label = l }
+
+#define MENU_XENT(k, t) { .title = t, .key = k, .expert = 1 }
+#define MENU_XENT_H(k, t) { .title = t, .key = k, .expert = 1, .hidden = 1 }
+
+#define MENU_BENT(k, t) { .title = t, .key = k, .expert = 1, .normal = 1 }
+#define MENU_BENT_E(k, t, l) { .title = t, .key = k, .expert = 1, .normal = 1, .exclude = l }
+
+#define MENU_ENT_NEST(k, t, l, p) { .title = t, .key = k, .normal = 1, .label = l, .parent = p }
+#define MENU_BENT_NEST_H(k, t, l, p) { .title = t, .key = k, .expert = 1, .normal = 1, .label = l, .parent = p, .hidden = 1 }
+
+/* Generic menu */
+static const struct menu menu_generic = {
+ .callback = generic_menu_cb,
+ .entries = {
+ MENU_BSEP(N_("Generic")),
+ MENU_ENT ('d', N_("delete a partition")),
+ MENU_ENT ('F', N_("list free unpartitioned space")),
+ MENU_ENT ('l', N_("list known partition types")),
+ MENU_ENT ('n', N_("add a new partition")),
+ MENU_BENT ('p', N_("print the partition table")),
+ MENU_ENT ('t', N_("change a partition type")),
+ MENU_BENT_E('v', N_("verify the partition table"), FDISK_DISKLABEL_BSD),
+ MENU_ENT ('i', N_("print information about a partition")),
+
+ MENU_XENT('d', N_("print the raw data of the first sector from the device")),
+ MENU_XENT('D', N_("print the raw data of the disklabel from the device")),
+ MENU_XENT('f', N_("fix partitions order")),
+
+ MENU_SEP(N_("Misc")),
+ MENU_BENT ('m', N_("print this menu")),
+ MENU_ENT_E('u', N_("change display/entry units"), FDISK_DISKLABEL_GPT),
+ MENU_ENT_E('x', N_("extra functionality (experts only)"), FDISK_DISKLABEL_BSD),
+
+ MENU_SEP(N_("Script")),
+ MENU_ENT ('I', N_("load disk layout from sfdisk script file")),
+ MENU_ENT ('O', N_("dump disk layout to sfdisk script file")),
+
+ MENU_BSEP(N_("Save & Exit")),
+ MENU_ENT_E('w', N_("write table to disk and exit"), FDISK_DISKLABEL_BSD),
+ MENU_ENT_L('w', N_("write table to disk"), FDISK_DISKLABEL_BSD),
+ MENU_BENT ('q', N_("quit without saving changes")),
+ MENU_XENT ('r', N_("return to main menu")),
+
+ MENU_ENT_NEST('r', N_("return from BSD to DOS"), FDISK_DISKLABEL_BSD, FDISK_DISKLABEL_DOS),
+
+ MENU_ENT_NEST('r', N_("return from protective/hybrid MBR to GPT"), FDISK_DISKLABEL_DOS, FDISK_DISKLABEL_GPT),
+
+ { 0, NULL }
+ }
+};
+
+static const struct menu menu_createlabel = {
+ .callback = createlabel_menu_cb,
+ .exclude = FDISK_DISKLABEL_BSD,
+ .nonested = 1,
+ .entries = {
+ MENU_SEP(N_("Create a new label")),
+ MENU_ENT('g', N_("create a new empty GPT partition table")),
+ MENU_ENT('G', N_("create a new empty SGI (IRIX) partition table")),
+ MENU_ENT('o', N_("create a new empty DOS partition table")),
+ MENU_ENT('s', N_("create a new empty Sun partition table")),
+
+ /* backward compatibility -- be sensitive to 'g', but don't
+ * print it in the expert menu */
+ MENU_XENT_H('g', N_("create an IRIX (SGI) partition table")),
+ { 0, NULL }
+ }
+};
+
+static const struct menu menu_geo = {
+ .callback = geo_menu_cb,
+ .exclude = FDISK_DISKLABEL_GPT | FDISK_DISKLABEL_BSD,
+ .entries = {
+ MENU_XSEP(N_("Geometry (for the current label)")),
+ MENU_XENT('c', N_("change number of cylinders")),
+ MENU_XENT('h', N_("change number of heads")),
+ MENU_XENT('s', N_("change number of sectors/track")),
+ { 0, NULL }
+ }
+};
+
+static const struct menu menu_gpt = {
+ .callback = gpt_menu_cb,
+ .label = FDISK_DISKLABEL_GPT,
+ .entries = {
+ MENU_BSEP(N_("GPT")),
+ MENU_XENT('i', N_("change disk GUID")),
+ MENU_XENT('n', N_("change partition name")),
+ MENU_XENT('u', N_("change partition UUID")),
+ MENU_XENT('l', N_("change table length")),
+ MENU_BENT('M', N_("enter protective/hybrid MBR")),
+
+ MENU_XSEP(""),
+ MENU_XENT('A', N_("toggle the legacy BIOS bootable flag")),
+ MENU_XENT('B', N_("toggle the no block IO protocol flag")),
+ MENU_XENT('R', N_("toggle the required partition flag")),
+ MENU_XENT('S', N_("toggle the GUID specific bits")),
+
+ { 0, NULL }
+ }
+};
+
+static const struct menu menu_sun = {
+ .callback = sun_menu_cb,
+ .label = FDISK_DISKLABEL_SUN,
+ .entries = {
+ MENU_BSEP(N_("Sun")),
+ MENU_ENT('a', N_("toggle the read-only flag")),
+ MENU_ENT('c', N_("toggle the mountable flag")),
+
+ MENU_XENT('a', N_("change number of alternate cylinders")),
+ MENU_XENT('e', N_("change number of extra sectors per cylinder")),
+ MENU_XENT('i', N_("change interleave factor")),
+ MENU_XENT('o', N_("change rotation speed (rpm)")),
+ MENU_XENT('y', N_("change number of physical cylinders")),
+ { 0, NULL }
+ }
+};
+
+static const struct menu menu_sgi = {
+ .callback = sgi_menu_cb,
+ .label = FDISK_DISKLABEL_SGI,
+ .entries = {
+ MENU_SEP(N_("SGI")),
+ MENU_ENT('a', N_("select bootable partition")),
+ MENU_ENT('b', N_("edit bootfile entry")),
+ MENU_ENT('c', N_("select sgi swap partition")),
+ MENU_ENT('i', N_("create SGI info")),
+ { 0, NULL }
+ }
+};
+
+static const struct menu menu_dos = {
+ .callback = dos_menu_cb,
+ .label = FDISK_DISKLABEL_DOS,
+ .entries = {
+ MENU_BSEP(N_("DOS (MBR)")),
+ MENU_ENT('a', N_("toggle a bootable flag")),
+ MENU_ENT('b', N_("edit nested BSD disklabel")),
+ MENU_ENT('c', N_("toggle the dos compatibility flag")),
+
+ MENU_XENT('b', N_("move beginning of data in a partition")),
+ MENU_XENT('i', N_("change the disk identifier")),
+
+ MENU_BENT_NEST_H('M', N_("return from protective/hybrid MBR to GPT"), FDISK_DISKLABEL_DOS, FDISK_DISKLABEL_GPT),
+
+ { 0, NULL }
+ }
+};
+
+static const struct menu menu_bsd = {
+ .callback = bsd_menu_cb,
+ .label = FDISK_DISKLABEL_BSD,
+ .entries = {
+ MENU_SEP(N_("BSD")),
+ MENU_ENT('e', N_("edit drive data")),
+ MENU_ENT('i', N_("install bootstrap")),
+ MENU_ENT('s', N_("show complete disklabel")),
+ MENU_ENT('x', N_("link BSD partition to non-BSD partition")),
+ { 0, NULL }
+ }
+};
+
+static const struct menu *menus[] = {
+ &menu_gpt,
+ &menu_sun,
+ &menu_sgi,
+ &menu_dos,
+ &menu_bsd,
+ &menu_geo,
+ &menu_generic,
+ &menu_createlabel,
+};
+
+static const struct menu_entry *next_menu_entry(
+ struct fdisk_context *cxt,
+ struct menu_context *mc)
+{
+ struct fdisk_label *lb = fdisk_get_label(cxt, NULL);
+ struct fdisk_context *parent = fdisk_get_parent(cxt);
+ unsigned int type = 0, pr_type = 0;
+
+ assert(cxt);
+
+ if (lb)
+ type = fdisk_label_get_type(lb);
+ if (parent)
+ pr_type = fdisk_label_get_type(fdisk_get_label(parent, NULL));
+
+ while (mc->menu_idx < ARRAY_SIZE(menus)) {
+ const struct menu *m = menus[mc->menu_idx];
+ const struct menu_entry *e = &(m->entries[mc->entry_idx]);
+
+ /*
+ * whole-menu filter
+ */
+
+ /* no more entries */
+ if (e->title == NULL ||
+ /* menu wanted for specified labels only */
+ (m->label && (!lb || !(m->label & type))) ||
+ /* unwanted for nested PT */
+ (m->nonested && parent) ||
+ /* menu excluded for specified labels */
+ (m->exclude && lb && (m->exclude & type))) {
+ mc->menu_idx++;
+ mc->entry_idx = 0;
+ continue;
+ }
+
+ /*
+ * per entry filter
+ */
+
+ /* excluded for the current label */
+ if ((e->exclude && lb && e->exclude & type) ||
+ /* entry wanted for specified labels only */
+ (e->label && (!lb || !(e->label & type))) ||
+ /* exclude non-expert entries in expect mode */
+ (e->expert == 0 && fdisk_is_details(cxt)) ||
+ /* nested only */
+ (e->parent && (!parent || pr_type != e->parent)) ||
+ /* exclude non-normal entries in normal mode */
+ (e->normal == 0 && !fdisk_is_details(cxt))) {
+ mc->entry_idx++;
+ continue;
+ }
+ mc->entry_idx++;
+ return e;
+
+ }
+ return NULL;
+}
+
+/* returns @menu and menu entry for then @key */
+static const struct menu_entry *get_fdisk_menu_entry(
+ struct fdisk_context *cxt,
+ int key,
+ const struct menu **menu)
+{
+ struct menu_context mc = MENU_CXT_EMPTY;
+ const struct menu_entry *e;
+
+ while ((e = next_menu_entry(cxt, &mc))) {
+ if (IS_MENU_SEP(e) || e->key != key)
+ continue;
+
+ if (menu)
+ *menu = menus[mc.menu_idx];
+ return e;
+ }
+
+ return NULL;
+}
+
+static int menu_detect_collisions(struct fdisk_context *cxt)
+{
+ struct menu_context mc = MENU_CXT_EMPTY;
+ const struct menu_entry *e, *r;
+
+ while ((e = next_menu_entry(cxt, &mc))) {
+ if (IS_MENU_SEP(e))
+ continue;
+
+ r = get_fdisk_menu_entry(cxt, e->key, NULL);
+ if (!r) {
+ DBG(MENU, ul_debug("warning: not found "
+ "entry for %c", e->key));
+ return -1;
+ }
+ if (r != e) {
+ DBG(MENU, ul_debug("warning: duplicate key '%c'",
+ e->key));
+ DBG(MENU, ul_debug(" : %s", e->title));
+ DBG(MENU, ul_debug(" : %s", r->title));
+ abort();
+ }
+ }
+
+ return 0;
+}
+
+static int print_fdisk_menu(struct fdisk_context *cxt)
+{
+ struct menu_context mc = MENU_CXT_EMPTY;
+ const struct menu_entry *e;
+
+ ON_DBG(MENU, menu_detect_collisions(cxt));
+
+ if (fdisk_is_details(cxt))
+ printf(_("\nHelp (expert commands):\n"));
+ else
+ printf(_("\nHelp:\n"));
+
+ while ((e = next_menu_entry(cxt, &mc))) {
+ if (IS_MENU_HID(e))
+ continue; /* hidden entry */
+ if (IS_MENU_SEP(e) && (!e->title || !*e->title))
+ printf("\n");
+ else if (IS_MENU_SEP(e)) {
+ color_scheme_enable("help-title", UL_COLOR_BOLD);
+ printf("\n %s\n", _(e->title));
+ color_disable();
+ } else
+ printf(" %c %s\n", e->key, _(e->title));
+ }
+ fputc('\n', stdout);
+
+ if (fdisk_get_parent(cxt)) {
+ struct fdisk_label *l = fdisk_get_label(cxt, NULL),
+ *p = fdisk_get_label(fdisk_get_parent(cxt), NULL);
+
+ fdisk_info(cxt, _("You're editing nested '%s' partition table, "
+ "primary partition table is '%s'."),
+ fdisk_label_get_name(l),
+ fdisk_label_get_name(p));
+ }
+
+ return 0;
+}
+
+/* Asks for command, verify the key and perform the command or
+ * returns the command key if no callback for the command is
+ * implemented.
+ *
+ * Note that this function might exchange the context pointer to
+ * switch to another (nested) context.
+ *
+ * Returns: <0 on error
+ * 0 on success (the command performed)
+ * >0 if no callback (then returns the key)
+ */
+int process_fdisk_menu(struct fdisk_context **cxt0)
+{
+ struct fdisk_context *cxt = *cxt0;
+ const struct menu_entry *ent;
+ const struct menu *menu;
+ int key, rc;
+ const char *prompt;
+ char buf[BUFSIZ] = { '\0' };
+
+ if (fdisk_is_details(cxt))
+ prompt = _("Expert command (m for help): ");
+ else
+ prompt = _("Command (m for help): ");
+
+ fputc('\n',stdout);
+ rc = get_user_reply(prompt, buf, sizeof(buf));
+
+ if (rc == -ECANCELED) {
+ /* Map ^C and ^D in main menu to 'q' */
+ if (is_interactive
+ && fdisk_label_is_changed(fdisk_get_label(cxt, NULL))) {
+ rc = get_user_reply(
+ _("\nAll unwritten changes will be lost, do you really want to quit? "),
+ buf, sizeof(buf));
+ if (rc || !rpmatch(buf))
+ return 0;
+ }
+ key = 'q';
+ } else if (rc) {
+ return rc;
+ } else
+ key = buf[0];
+
+ ent = get_fdisk_menu_entry(cxt, key, &menu);
+ if (!ent) {
+ fdisk_warnx(cxt, _("%c: unknown command"), key);
+ return -EINVAL;
+ }
+
+ DBG(MENU, ul_debug("selected: key=%c, entry='%s'",
+ key, ent->title));
+
+ /* menu has implemented callback, use it */
+ if (menu->callback)
+ rc = menu->callback(cxt0, menu, ent);
+ else {
+ DBG(MENU, ul_debug("no callback for key '%c'", key));
+ rc = -EINVAL;
+ }
+
+ DBG(MENU, ul_debug("process menu done [rc=%d]", rc));
+ return rc;
+}
+
+static int script_read(struct fdisk_context *cxt)
+{
+ struct fdisk_script *sc = NULL;
+ char *filename = NULL;
+ int rc;
+
+ rc = fdisk_ask_string(cxt, _("Enter script file name"), &filename);
+ if (rc)
+ return rc;
+
+ errno = 0;
+ sc = fdisk_new_script_from_file(cxt, filename);
+ if (!sc && errno)
+ fdisk_warn(cxt, _("Cannot open %s"), filename);
+ else if (!sc)
+ fdisk_warnx(cxt, _("Failed to parse script file %s"), filename);
+ else if (fdisk_apply_script(cxt, sc) != 0) {
+ fdisk_warnx(cxt, _("Failed to apply script %s"), filename);
+ fdisk_warnx(cxt, _("Resetting fdisk!"));
+ rc = fdisk_reassign_device(cxt);
+ if (rc == 0 && !fdisk_has_label(cxt)) {
+ fdisk_info(cxt, _("Device does not contain a recognized partition table."));
+ rc = fdisk_create_disklabel(cxt, NULL);
+ }
+ } else
+ fdisk_info(cxt, _("Script successfully applied."));
+
+ fdisk_unref_script(sc);
+ free(filename);
+ return rc;
+}
+
+static int script_write(struct fdisk_context *cxt)
+{
+ struct fdisk_script *sc = NULL;
+ char *filename = NULL;
+ FILE *f = NULL;
+ int rc;
+
+ rc = fdisk_ask_string(cxt, _("Enter script file name"), &filename);
+ if (rc)
+ return rc;
+
+ sc = fdisk_new_script(cxt);
+ if (!sc) {
+ fdisk_warn(cxt, _("Failed to allocate script handler"));
+ goto done;
+ }
+
+ rc = fdisk_script_read_context(sc, NULL);
+ if (rc) {
+ fdisk_warnx(cxt, _("Failed to transform disk layout into script"));
+ goto done;
+ }
+
+ f = fopen(filename, "w");
+ if (!f) {
+ fdisk_warn(cxt, _("Cannot open %s"), filename);
+ goto done;
+ }
+
+ rc = fdisk_script_write_file(sc, f);
+ if (rc)
+ fdisk_warn(cxt, _("Failed to write script %s"), filename);
+ else
+ fdisk_info(cxt, _("Script successfully saved."));
+done:
+ if (f)
+ fclose(f);
+ fdisk_unref_script(sc);
+ free(filename);
+ return rc;
+}
+
+static int ask_for_wipe(struct fdisk_context *cxt, size_t partno)
+{
+ struct fdisk_partition *tmp = NULL;
+ char *fstype = NULL;
+ int rc, yes = 0;
+
+ rc = fdisk_get_partition(cxt, partno, &tmp);
+ if (rc)
+ goto done;
+
+ rc = fdisk_partition_to_string(tmp, cxt, FDISK_FIELD_FSTYPE, &fstype);
+ if (rc || fstype == NULL)
+ goto done;
+
+ fdisk_warnx(cxt, _("Partition #%zu contains a %s signature."), partno + 1, fstype);
+
+ if (pwipemode == WIPEMODE_AUTO && isatty(STDIN_FILENO))
+ fdisk_ask_yesno(cxt, _("Do you want to remove the signature?"), &yes);
+ else if (pwipemode == WIPEMODE_ALWAYS)
+ yes = 1;
+
+ if (yes) {
+ fdisk_info(cxt, _("The signature will be removed by a write command."));
+ rc = fdisk_wipe_partition(cxt, partno, TRUE);
+ }
+done:
+ fdisk_unref_partition(tmp);
+ free(fstype);
+ return rc;
+}
+
+/*
+ * Basic fdisk actions
+ */
+static int generic_menu_cb(struct fdisk_context **cxt0,
+ const struct menu *menu __attribute__((__unused__)),
+ const struct menu_entry *ent)
+{
+ struct fdisk_context *cxt = *cxt0;
+ int rc = 0;
+ size_t n;
+
+ /* actions shared between expert and normal mode */
+ switch (ent->key) {
+ case 'p':
+ list_disk_geometry(cxt);
+ list_disklabel(cxt);
+ break;
+ case 'w':
+ if (fdisk_is_readonly(cxt)) {
+ fdisk_warnx(cxt, _("Device is open in read-only mode."));
+ break;
+ }
+ rc = fdisk_write_disklabel(cxt);
+ if (rc)
+ err(EXIT_FAILURE, _("failed to write disklabel"));
+
+ fdisk_info(cxt, _("The partition table has been altered."));
+ if (fdisk_get_parent(cxt))
+ break; /* nested PT, don't leave */
+
+ if (device_is_used)
+ rc = fdisk_reread_changes(cxt, original_layout);
+ else
+ rc = fdisk_reread_partition_table(cxt);
+ if (!rc)
+ rc = fdisk_deassign_device(cxt, 0);
+ /* fallthrough */
+ case 'q':
+ fdisk_unref_context(cxt);
+ fputc('\n', stdout);
+ exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+ case 'm':
+ rc = print_fdisk_menu(cxt);
+ break;
+ case 'v':
+ rc = fdisk_verify_disklabel(cxt);
+ break;
+ case 'i':
+ rc = print_partition_info(cxt);
+ break;
+ case 'F':
+ list_freespace(cxt);
+ break;
+ }
+
+ /* expert mode */
+ if (ent->expert) {
+ switch (ent->key) {
+ case 'd':
+ dump_firstsector(cxt);
+ break;
+ case 'D':
+ dump_disklabel(cxt);
+ break;
+ case 'f':
+ rc = fdisk_reorder_partitions(cxt);
+ if (rc)
+ fdisk_warnx(cxt, _("Failed to fix partitions order."));
+ else
+ fdisk_info(cxt, _("Partitions order fixed."));
+ break;
+ case 'r':
+ rc = fdisk_enable_details(cxt, 0);
+ break;
+ }
+ return rc;
+ }
+
+ /* normal mode */
+ switch (ent->key) {
+ case 'd':
+ rc = fdisk_ask_partnum(cxt, &n, FALSE);
+ if (rc)
+ break; /* no partitions yet (or ENOMEM, ...) */
+
+ rc = fdisk_delete_partition(cxt, n);
+ if (rc)
+ fdisk_warnx(cxt, _("Could not delete partition %zu"), n + 1);
+ else
+ fdisk_info(cxt, _("Partition %zu has been deleted."), n + 1);
+ break;
+ case 'I':
+ script_read(cxt);
+ break;
+ case 'O':
+ script_write(cxt);
+ break;
+ case 'l':
+ list_partition_types(cxt);
+ break;
+ case 'n':
+ {
+ size_t partno;
+ rc = fdisk_add_partition(cxt, NULL, &partno);
+ if (!rc)
+ rc = ask_for_wipe(cxt, partno);
+ break;
+ }
+ case 't':
+ change_partition_type(cxt);
+ break;
+ case 'u':
+ fdisk_set_unit(cxt,
+ fdisk_use_cylinders(cxt) ? "sectors" :
+ "cylinders");
+ if (fdisk_use_cylinders(cxt))
+ fdisk_info(cxt, _("Changing display/entry units to cylinders (DEPRECATED!)."));
+ else
+ fdisk_info(cxt, _("Changing display/entry units to sectors."));
+ break;
+ case 'x':
+ fdisk_enable_details(cxt, 1);
+ break;
+ case 'r':
+ /* return from nested BSD to DOS or MBR to GPT */
+ if (fdisk_get_parent(cxt)) {
+ *cxt0 = fdisk_get_parent(cxt);
+
+ fdisk_info(cxt, _("Leaving nested disklabel."));
+ fdisk_unref_context(cxt);
+ }
+ break;
+ }
+
+ return rc;
+}
+
+
+/*
+ * This is fdisk frontend for GPT specific libfdisk functions that
+ * are not exported by generic libfdisk API.
+ */
+static int gpt_menu_cb(struct fdisk_context **cxt0,
+ const struct menu *menu __attribute__((__unused__)),
+ const struct menu_entry *ent)
+{
+ struct fdisk_context *cxt = *cxt0;
+ struct fdisk_context *mbr;
+ struct fdisk_partition *pa = NULL;
+ size_t n;
+ int rc = 0;
+ uintmax_t length = 0;
+
+ assert(cxt);
+ assert(ent);
+ assert(fdisk_is_label(cxt, GPT));
+
+ DBG(MENU, ul_debug("enter GPT menu"));
+
+ if (ent->expert) {
+ switch (ent->key) {
+ case 'i':
+ return fdisk_set_disklabel_id(cxt);
+ case 'l':
+ rc = fdisk_ask_number(cxt, 1, fdisk_get_npartitions(cxt),
+ ~(uint32_t)0, _("New maximum entries"), &length);
+ if (rc)
+ return rc;
+ return fdisk_gpt_set_npartitions(cxt, (uint32_t) length);
+ case 'M':
+ mbr = fdisk_new_nested_context(cxt, "dos");
+ if (!mbr)
+ return -ENOMEM;
+ *cxt0 = cxt = mbr;
+ if (fdisk_is_details(cxt))
+ fdisk_enable_details(cxt, 1); /* keep us in expert mode */
+ fdisk_info(cxt, _("Entering protective/hybrid MBR disklabel."));
+ return 0;
+ }
+
+ /* actions where is necessary partnum */
+ rc = fdisk_ask_partnum(cxt, &n, FALSE);
+ if (rc)
+ return rc;
+
+ switch(ent->key) {
+ case 'u':
+ pa = fdisk_new_partition(); /* new template */
+ if (!pa)
+ rc = -ENOMEM;
+ else {
+ char *str = NULL;
+ rc = fdisk_ask_string(cxt, _("New UUID (in 8-4-4-4-12 format)"), &str);
+ if (!rc)
+ rc = fdisk_partition_set_uuid(pa, str);
+ if (!rc)
+ rc = fdisk_set_partition(cxt, n, pa);
+ free(str);
+ fdisk_unref_partition(pa);
+ }
+ break;
+ case 'n':
+ pa = fdisk_new_partition(); /* new template */
+ if (!pa)
+ rc = -ENOMEM;
+ else {
+ char *str = NULL;
+ rc = fdisk_ask_string(cxt, _("New name"), &str);
+ if (!rc)
+ rc = fdisk_partition_set_name(pa, str);
+ if (!rc)
+ rc = fdisk_set_partition(cxt, n, pa);
+ free(str);
+ fdisk_unref_partition(pa);
+ }
+ break;
+ case 'A':
+ rc = fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_LEGACYBOOT);
+ break;
+ case 'B':
+ rc = fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_NOBLOCK);
+ break;
+ case 'R':
+ rc = fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_REQUIRED);
+ break;
+ case 'S':
+ rc = fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_GUIDSPECIFIC);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+ * This is fdisk frontend for MBR specific libfdisk functions that
+ * are not exported by generic libfdisk API.
+ */
+static int dos_menu_cb(struct fdisk_context **cxt0,
+ const struct menu *menu __attribute__((__unused__)),
+ const struct menu_entry *ent)
+{
+ struct fdisk_context *cxt = *cxt0;
+ int rc = 0;
+
+ DBG(MENU, ul_debug("enter DOS menu"));
+
+ if (!ent->expert) {
+ switch (ent->key) {
+ case 'a':
+ {
+ size_t n;
+ rc = fdisk_ask_partnum(cxt, &n, FALSE);
+ if (!rc)
+ rc = fdisk_toggle_partition_flag(cxt, n, DOS_FLAG_ACTIVE);
+ break;
+ }
+ case 'b':
+ {
+ struct fdisk_context *bsd
+ = fdisk_new_nested_context(cxt, "bsd");
+ if (!bsd)
+ return -ENOMEM;
+ if (!fdisk_has_label(bsd))
+ rc = fdisk_create_disklabel(bsd, "bsd");
+ if (rc)
+ fdisk_unref_context(bsd);
+ else {
+ *cxt0 = cxt = bsd;
+ fdisk_info(cxt, _("Entering nested BSD disklabel."));
+ }
+ break;
+ }
+ case 'c':
+ toggle_dos_compatibility_flag(cxt);
+ break;
+ }
+ return rc;
+ }
+
+ /* expert mode */
+ switch (ent->key) {
+ case 'b':
+ {
+ size_t n;
+ rc = fdisk_ask_partnum(cxt, &n, FALSE);
+ if (!rc)
+ rc = fdisk_dos_move_begin(cxt, n);
+ break;
+ }
+ case 'i':
+ rc = fdisk_set_disklabel_id(cxt);
+ break;
+ case 'M':
+ /* return from nested MBR to GPT (backward compatibility only) */
+ if (fdisk_get_parent(cxt)) {
+ *cxt0 = fdisk_get_parent(cxt);
+
+ fdisk_info(cxt, _("Leaving nested disklabel."));
+ fdisk_unref_context(cxt);
+ }
+ break;
+ }
+ return rc;
+}
+
+static int sun_menu_cb(struct fdisk_context **cxt0,
+ const struct menu *menu __attribute__((__unused__)),
+ const struct menu_entry *ent)
+{
+ struct fdisk_context *cxt = *cxt0;
+ int rc = 0;
+
+ DBG(MENU, ul_debug("enter SUN menu"));
+
+ assert(cxt);
+ assert(ent);
+ assert(fdisk_is_label(cxt, SUN));
+
+ DBG(MENU, ul_debug("enter SUN menu"));
+
+ /* normal mode */
+ if (!ent->expert) {
+ size_t n;
+
+ rc = fdisk_ask_partnum(cxt, &n, FALSE);
+ if (rc)
+ return rc;
+ switch (ent->key) {
+ case 'a':
+ rc = fdisk_toggle_partition_flag(cxt, n, SUN_FLAG_RONLY);
+ break;
+ case 'c':
+ rc = fdisk_toggle_partition_flag(cxt, n, SUN_FLAG_UNMNT);
+ break;
+ }
+ return rc;
+ }
+
+ /* expert mode */
+ switch (ent->key) {
+ case 'a':
+ rc = fdisk_sun_set_alt_cyl(cxt);
+ break;
+ case 'e':
+ rc = fdisk_sun_set_xcyl(cxt);
+ break;
+ case 'i':
+ rc = fdisk_sun_set_ilfact(cxt);
+ break;
+ case 'o':
+ rc = fdisk_sun_set_rspeed(cxt);
+ break;
+ case 'y':
+ rc = fdisk_sun_set_pcylcount(cxt);
+ break;
+ }
+ return rc;
+}
+
+static int sgi_menu_cb(struct fdisk_context **cxt0,
+ const struct menu *menu __attribute__((__unused__)),
+ const struct menu_entry *ent)
+{
+ struct fdisk_context *cxt = *cxt0;
+ int rc = -EINVAL;
+ size_t n = 0;
+
+ DBG(MENU, ul_debug("enter SGI menu"));
+
+ assert(cxt);
+ assert(ent);
+ assert(fdisk_is_label(cxt, SGI));
+
+ if (ent->expert)
+ return rc;
+
+ switch (ent->key) {
+ case 'a':
+ rc = fdisk_ask_partnum(cxt, &n, FALSE);
+ if (!rc)
+ rc = fdisk_toggle_partition_flag(cxt, n, SGI_FLAG_BOOT);
+ break;
+ case 'b':
+ fdisk_sgi_set_bootfile(cxt);
+ break;
+ case 'c':
+ rc = fdisk_ask_partnum(cxt, &n, FALSE);
+ if (!rc)
+ rc = fdisk_toggle_partition_flag(cxt, n, SGI_FLAG_SWAP);
+ break;
+ case 'i':
+ rc = fdisk_sgi_create_info(cxt);
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * This is fdisk frontend for BSD specific libfdisk functions that
+ * are not exported by generic libfdisk API.
+ */
+static int bsd_menu_cb(struct fdisk_context **cxt0,
+ const struct menu *menu __attribute__((__unused__)),
+ const struct menu_entry *ent)
+{
+ struct fdisk_context *cxt = *cxt0;
+ int rc = 0, org;
+
+ assert(cxt);
+ assert(ent);
+ assert(fdisk_is_label(cxt, BSD));
+
+ DBG(MENU, ul_debug("enter BSD menu"));
+
+ switch(ent->key) {
+ case 'e':
+ rc = fdisk_bsd_edit_disklabel(cxt);
+ break;
+ case 'i':
+ rc = fdisk_bsd_write_bootstrap(cxt);
+ break;
+ case 's':
+ org = fdisk_is_details(cxt);
+
+ fdisk_enable_details(cxt, 1);
+ list_disklabel(cxt);
+ fdisk_enable_details(cxt, org);
+ break;
+ case 'x':
+ rc = fdisk_bsd_link_partition(cxt);
+ break;
+ }
+ return rc;
+}
+
+/* C/H/S commands
+ *
+ * The geometry setting from this dialog is not persistent and maybe reset by
+ * fdisk_reset_device_properties() (for example when you create a new disk
+ * label). Note that on command line specified -C/-H/-S setting is persistent
+ * as it's based on fdisk_save_user_geometry().
+ */
+static int geo_menu_cb(struct fdisk_context **cxt0,
+ const struct menu *menu __attribute__((__unused__)),
+ const struct menu_entry *ent)
+{
+ struct fdisk_context *cxt = *cxt0;
+ struct fdisk_label *lb = fdisk_get_label(cxt, NULL);
+ int rc = -EINVAL;
+ uintmax_t c = 0, h = 0, s = 0;
+ fdisk_sector_t mi, ma;
+
+ DBG(MENU, ul_debug("enter GEO menu"));
+
+ assert(cxt);
+ assert(ent);
+
+ /* default */
+ if (!lb)
+ lb = fdisk_get_label(cxt, "dos");
+
+ switch (ent->key) {
+ case 'c':
+ fdisk_label_get_geomrange_cylinders(lb, &mi, &ma);
+ rc = fdisk_ask_number(cxt, mi, fdisk_get_geom_cylinders(cxt),
+ ma, _("Number of cylinders"), &c);
+ break;
+ case 'h':
+ {
+ unsigned int i, a;
+ fdisk_label_get_geomrange_heads(lb, &i, &a);
+ rc = fdisk_ask_number(cxt, i, fdisk_get_geom_heads(cxt),
+ a, _("Number of heads"), &h);
+ break;
+ }
+ case 's':
+ fdisk_label_get_geomrange_sectors(lb, &mi, &ma);
+ rc = fdisk_ask_number(cxt, mi, fdisk_get_geom_sectors(cxt),
+ ma, _("Number of sectors"), &s);
+ break;
+ }
+
+ if (!rc)
+ fdisk_override_geometry(cxt, c, h, s);
+ return rc;
+}
+
+static int createlabel_menu_cb(struct fdisk_context **cxt0,
+ const struct menu *menu __attribute__((__unused__)),
+ const struct menu_entry *ent)
+{
+ struct fdisk_context *cxt = *cxt0;
+ const char *wanted = NULL;
+ int rc = -EINVAL;
+
+ DBG(MENU, ul_debug("enter Create label menu"));
+
+ assert(cxt);
+ assert(ent);
+
+ if (ent->expert) {
+ switch (ent->key) {
+ case 'g':
+ /* Deprecated, use 'G' in main menu, just for backward
+ * compatibility only. */
+ wanted = "sgi";
+ break;
+ }
+ } else {
+ switch (ent->key) {
+ case 'g':
+ wanted = "gpt";
+ break;
+ case 'G':
+ wanted = "sgi";
+ break;
+ case 'o':
+ wanted = "dos";
+ break;
+ case 's':
+ wanted = "sun";
+ break;
+ }
+ }
+
+ if (wanted) {
+ rc = fdisk_create_disklabel(cxt, wanted);
+ if (rc) {
+ errno = -rc;
+ fdisk_warn(cxt, _("Failed to create '%s' disk label"), wanted);
+ }
+ }
+ if (rc == 0 && fdisk_get_collision(cxt))
+ follow_wipe_mode(cxt);
+
+ return rc;
+}
diff --git a/disk-utils/fdisk.8 b/disk-utils/fdisk.8
new file mode 100644
index 0000000..b45e29a
--- /dev/null
+++ b/disk-utils/fdisk.8
@@ -0,0 +1,392 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" Copyright 1998 Andries E. Brouwer (aeb@cwi.nl)
+.\" Copyright 2012 Davidlohr Bueso <dave@gnu.org>
+.\" Copyright (C) 2013 Karel Zak <kzak@redhat.com>
+.\" May be distributed under the GNU General Public License
+.TH FDISK 8 "February 2016" "util-linux" "System Administration"
+
+.SH NAME
+fdisk \- manipulate disk partition table
+
+.SH SYNOPSIS
+.B fdisk
+[options]
+.I device
+.sp
+.B fdisk \-l
+.RI [ device ...]
+
+.SH DESCRIPTION
+.B fdisk
+is a dialog-driven program for creation and manipulation of partition tables.
+It understands GPT, MBR, Sun, SGI and BSD partition tables.
+
+Block devices can be divided into one or more logical disks called
+.IR partitions .
+This division is recorded in the
+.IR "partition table" ,
+usually found in sector 0 of the disk.
+(In the BSD world one talks about `disk slices' and a `disklabel'.)
+
+All partitioning is driven by device I/O limits (the topology) by default.
+.B fdisk
+is able to optimize the disk layout for a 4K-sector size and use an alignment offset on
+modern devices for MBR and GPT. It is always a good idea to follow \fBfdisk\fR's defaults
+as the default values (e.g., first and last partition sectors) and partition
+sizes specified by the +/-<size>{M,G,...} notation are always aligned according
+to the device properties.
+
+CHS (Cylinder-Head-Sector) addressing is deprecated and not used by default.
+Please, do not follow old articles and recommendations with "fdisk \-S <n> \-H
+<n>" advices for SSD or 4K-sector devices.
+
+Note that
+.BR partx (8)
+provides a rich interface for scripts to print disk layouts,
+.B fdisk
+is mostly designed for humans. Backward compatibility in the output of
+.B fdisk
+is not guaranteed. The input (the commands) should always be backward compatible.
+
+.SH OPTIONS
+.TP
+\fB\-b\fR, \fB\-\-sector\-size\fR \fIsectorsize\fP
+Specify the sector size of the disk. Valid values are 512, 1024, 2048, and 4096.
+(Recent kernels know the sector size. Use this option only on old kernels or
+to override the kernel's ideas.) Since util-linux-2.17, \fBfdisk\fR differentiates
+between logical and physical sector size. This option changes both sector sizes to
+.IB sectorsize .
+.TP
+\fB\-B\fR, \fB\-\-protect\-boot\fP
+Don't erase the beginning of the first disk sector when creating a new disk label. This
+feature is supported for GPT and MBR.
+.TP
+\fB\-c\fR, \fB\-\-compatibility\fR[=\fImode\fR]
+Specify the compatibility mode, 'dos' or 'nondos'. The default is non-DOS
+mode. For backward compatibility, it is possible to use the option without
+the \fImode\fR argument -- then the default is used. Note that the optional
+\fImode\fR argument cannot be separated from the \fB\-c\fR option by a space,
+the correct form is for example '\-c=dos'.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display a help text and exit.
+.TP
+\fB\-L\fR, \fB\-\-color\fR[=\fIwhen\fR]
+Colorize the output. The optional argument \fIwhen\fP
+can be \fBauto\fR, \fBnever\fR or \fBalways\fR. If the \fIwhen\fR argument is omitted,
+it defaults to \fBauto\fR. The colors can be disabled; for the current built-in default
+see the \fB\-\-help\fR output. See also the \fBCOLORS\fR section.
+.TP
+\fB\-l\fR, \fB\-\-list\fR
+List the partition tables for the specified devices and then exit.
+If no devices are given, those mentioned in
+.I /proc/partitions
+(if that file exists) are used.
+.TP
+\fB\-x\fR, \fB\-\-list\-details\fR
+Like \fB\-\-list\fR, but provides more details.
+.TP
+\fB\-\-lock\fR[=\fImode\fR]
+Use exclusive BSD lock for device or file it operates. The optional argument
+\fImode\fP can be \fByes\fR, \fBno\fR (or 1 and 0) or \fBnonblock\fR. If the \fImode\fR
+argument is omitted, it defaults to \fB"yes"\fR. This option overwrites
+environment variable \fB$LOCK_BLOCK_DEVICE\fR. The default is not to use any
+lock at all, but it's recommended to avoid collisions with udevd or other
+tools.
+.TP
+\fB\-n\fR, \fB\-\-noauto\-pt\fR
+Don't automatically create a default partition table on empty device. The partition table
+has to be explicitly created by user (by command like 'o', 'g', etc.).
+.TP
+.BR \-o , " \-\-output " \fIlist\fP
+Specify which output columns to print. Use
+.B \-\-help
+to get a list of all supported columns.
+
+The default list of columns may be extended if \fIlist\fP is
+specified in the format \fI+list\fP (e.g., \fB\-o +UUID\fP).
+.TP
+\fB\-s\fR, \fB\-\-getsz\fR
+Print the size in 512-byte sectors of each given block device. This option is DEPRECATED
+in favour of
+.BR blockdev (8).
+.TP
+\fB\-t\fR, \fB\-\-type\fR \fItype\fR
+Enable support only for disklabels of the specified \fItype\fP, and disable
+support for all other types.
+.TP
+\fB\-u\fR, \fB\-\-units\fR[=\fIunit\fR]
+When listing partition tables, show sizes in 'sectors' or in 'cylinders'. The
+default is to show sizes in sectors. For backward compatibility, it is possible
+to use the option without the \fIunit\fR argument -- then the default is used.
+Note that the optional \fIunit\fR argument cannot be separated from the \fB\-u\fR
+option by a space, the correct form is for example '\-u=cylinders'.
+
+.TP
+\fB\-C\fR, \fB\-\-cylinders\fR \fInumber\fR
+Specify the number of cylinders of the disk.
+I have no idea why anybody would want to do so.
+.TP
+\fB\-H\fR, \fB\-\-heads\fR \fInumber\fR
+Specify the number of heads of the disk. (Not the physical number,
+of course, but the number used for partition tables.)
+Reasonable values are 255 and 16.
+.TP
+\fB\-S\fR, \fB\-\-sectors\fR \fInumber\fR
+Specify the number of sectors per track of the disk.
+(Not the physical number, of course, but the number used for
+partition tables.) A reasonable value is 63.
+
+.TP
+\fB\-w\fR, \fB\-\-wipe\fR \fIwhen\fR
+Wipe filesystem, RAID and partition-table signatures from the device, in order
+to avoid possible collisions. The argument \fIwhen\fR can be \fBauto\fR,
+\fBnever\fR or \fBalways\fR. When this option is not given, the default is
+\fBauto\fR, in which case signatures are wiped only when in interactive mode.
+In all cases detected signatures are reported by warning messages
+before a new partition table is created. See also
+.BR wipefs (8)
+command.
+
+.TP
+\fB\-W\fR, \fB\-\-wipe-partitions\fR \fIwhen\fR
+Wipe filesystem, RAID and partition-table signatures from a newly created
+partitions, in order to avoid possible collisions. The argument \fIwhen\fR can
+be \fBauto\fR, \fBnever\fR or \fBalways\fR. When this option is not given, the
+default is \fBauto\fR, in which case signatures are wiped only when in
+interactive mode and after confirmation by user. In all cases detected
+signatures are reported by warning messages before a new partition is
+created. See also
+.BR wipefs (8)
+command.
+
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display version information and exit.
+
+.SH DEVICES
+The
+.I device
+is usually /dev/sda, /dev/sdb or so. A device name refers to the entire disk.
+Old systems without libata (a library used inside the Linux kernel to support
+ATA host controllers and devices) make a difference between IDE and SCSI disks.
+In such cases the device name will be /dev/hd* (IDE) or /dev/sd* (SCSI).
+
+The
+.I partition
+is a device name followed by a partition number. For example, /dev/sda1 is the
+first partition on the first hard disk in the system. See also Linux kernel
+documentation (the Documentation/admin-guide/devices.txt file).
+
+.SH SIZES
+The "last sector" dialog accepts partition size specified by number of sectors
+or by +/-<size>{K,B,M,G,...} notation.
+
+If the size is prefixed by '+' then it is interpreted as relative to the
+partition first sector. If the size is prefixed by '\-' then it is interpreted
+as relative to the high limit (last available sector for the partition).
+
+In the case the size is specified in bytes than the number may be followed by
+the multiplicative suffixes KiB=1024, MiB=1024*1024, and so on for GiB, TiB,
+PiB, EiB, ZiB and YiB. The "iB" is optional, e.g., "K" has the same meaning as
+"KiB".
+
+The relative sizes are always aligned according to device I/O limits. The
++/-<size>{K,B,M,G,...} notation is recommended.
+
+For backward compatibility fdisk also accepts the suffixes KB=1000,
+MB=1000*1000, and so on for GB, TB, PB, EB, ZB and YB. These 10^N suffixes
+are deprecated.
+
+.SH SCRIPT FILES
+.B fdisk
+allows reading (by 'I' command) sfdisk compatible script files. The script is
+applied to in-memory partition table, and then it is possible to modify the
+partition table before you write it to the device.
+.PP
+And vice-versa it is possible to write the current in-memory disk layout
+to the script file by command 'O'.
+.PP
+The script files are compatible between cfdisk, sfdisk, fdisk and other
+libfdisk applications. For more details see
+.BR sfdisk (8).
+
+.SH DISK LABELS
+.B GPT (GUID Partition Table)
+.RS
+GPT is modern standard for the layout of the partition table. GPT uses 64-bit
+logical block addresses, checksums, UUIDs and names for partitions and an
+unlimited number of partitions (although the number of partitions is
+usually restricted to 128 in many partitioning tools).
+
+Note that the first sector is still reserved for a
+.B protective MBR
+in the GPT specification. It prevents MBR-only partitioning tools
+from mis-recognizing and overwriting GPT disks.
+
+GPT is always a better choice than MBR, especially on modern hardware with a UEFI
+boot loader.
+.RE
+
+.B DOS-type (MBR)
+.RS
+A DOS-type partition table can describe an unlimited number of partitions. In sector 0
+there is room for the description of 4 partitions (called `primary'). One of
+these may be an extended partition; this is a box holding logical partitions,
+with descriptors found in a linked list of sectors, each preceding the
+corresponding logical partitions. The four primary partitions, present or not,
+get numbers 1-4. Logical partitions are numbered starting from 5.
+
+In a DOS-type partition table the starting offset and the size of each
+partition is stored in two ways: as an absolute number of sectors (given in 32
+bits), and as a
+.B Cylinders/Heads/Sectors
+triple (given in 10+8+6 bits). The former is OK -- with 512-byte sectors this
+will work up to 2 TB. The latter has two problems. First, these C/H/S fields
+can be filled only when the number of heads and the number of sectors per track
+are known. And second, even if we know what these numbers should be, the 24
+bits that are available do not suffice. DOS uses C/H/S only, Windows uses
+both, Linux never uses C/H/S. The
+.B C/H/S addressing is deprecated
+and may be unsupported in some later fdisk version.
+
+.B Please, read the DOS-mode section if you want DOS-compatible partitions.
+.B fdisk
+does not care about cylinder boundaries by default.
+.RE
+
+.B BSD/Sun-type
+.RS
+A BSD/Sun disklabel can describe 8 partitions, the third of which should be a `whole
+disk' partition. Do not start a partition that actually uses its first sector
+(like a swap partition) at cylinder 0, since that will destroy the disklabel.
+Note that a
+.B BSD label
+is usually nested within a DOS partition.
+.RE
+
+.B IRIX/SGI-type
+.RS
+An IRIX/SGI disklabel can describe 16 partitions, the eleventh of which should be an entire
+`volume' partition, while the ninth should be labeled `volume header'. The
+volume header will also cover the partition table, i.e., it starts at block
+zero and extends by default over five cylinders. The remaining space in the
+volume header may be used by header directory entries. No partitions may
+overlap with the volume header. Also do not change its type or make some
+filesystem on it, since you will lose the partition table. Use this type of
+label only when working with Linux on IRIX/SGI machines or IRIX/SGI disks under
+Linux.
+.RE
+
+A sync() and an ioctl(BLKRRPART) (rereading the partition table from disk)
+are performed before exiting when the partition table has been updated.
+
+.SH DOS mode and DOS 6.x WARNING
+.B Note that all this is deprecated. You don't have to care about things like
+.B geometry and cylinders on modern operating systems. If you really want
+.B DOS-compatible partitioning then you have to enable DOS mode and cylinder
+.B units by using the '\-c=dos \-u=cylinders' fdisk command-line options.
+
+The DOS 6.x FORMAT command looks for some information in the first sector of
+the data area of the partition, and treats this information as more reliable
+than the information in the partition table. DOS FORMAT expects DOS FDISK to
+clear the first 512 bytes of the data area of a partition whenever a size
+change occurs. DOS FORMAT will look at this extra information even if the /U
+flag is given -- we consider this a bug in DOS FORMAT and DOS FDISK.
+
+The bottom line is that if you use \fBfdisk\fR or \fBcfdisk\fR to change the
+size of a DOS partition table entry, then you must also use
+.BR dd "(1) to " "zero the first 512 bytes"
+of that partition before using DOS FORMAT to format the partition. For
+example, if you were using \fBfdisk\fR to make a DOS partition table entry for
+/dev/sda1, then (after exiting \fBfdisk\fR and rebooting Linux so that the
+partition table information is valid) you would use the command "dd
+if=/dev/zero of=/dev/sda1 bs=512 count=1" to zero the first 512 bytes of the
+partition.
+
+.B fdisk
+usually obtains the disk geometry automatically. This is not necessarily the
+physical disk geometry (indeed, modern disks do not really have anything like a
+physical geometry, certainly not something that can be described in the simplistic
+Cylinders/Heads/Sectors form), but it is the disk geometry that MS-DOS uses for
+the partition table.
+
+Usually all goes well by default, and there are no problems if Linux is the
+only system on the disk. However, if the disk has to be shared with other
+operating systems, it is often a good idea to let an fdisk from another
+operating system make at least one partition. When Linux boots it looks at the
+partition table, and tries to deduce what (fake) geometry is required for good
+cooperation with other systems.
+
+Whenever a partition table is printed out in DOS mode, a consistency check is
+performed on the partition table entries. This check verifies that the
+physical and logical start and end points are identical, and that each
+partition starts and ends on a cylinder boundary (except for the first
+partition).
+
+Some versions of MS-DOS create a first partition which does not begin
+on a cylinder boundary, but on sector 2 of the first cylinder.
+Partitions beginning in cylinder 1 cannot begin on a cylinder boundary, but
+this is unlikely to cause difficulty unless you have OS/2 on your machine.
+
+For best results, you should always use an OS-specific partition table
+program. For example, you should make DOS partitions with the DOS FDISK
+program and Linux partitions with the Linux fdisk or Linux cfdisk programs.
+.SH COLORS
+Implicit coloring can be disabled by an empty file \fI/etc/terminal-colors.d/fdisk.disable\fR.
+
+See
+.BR terminal-colors.d (5)
+for more details about colorization configuration. The logical color names
+supported by
+.B fdisk
+are:
+.TP
+.B header
+The header of the output tables.
+.TP
+.B help-title
+The help section titles.
+.TP
+.B warn
+The warning messages.
+.TP
+.B welcome
+The welcome message.
+
+.SH ENVIRONMENT
+.IP FDISK_DEBUG=all
+enables fdisk debug output.
+.IP LIBFDISK_DEBUG=all
+enables libfdisk debug output.
+.IP LIBBLKID_DEBUG=all
+enables libblkid debug output.
+.IP LIBSMARTCOLS_DEBUG=all
+enables libsmartcols debug output.
+.IP LIBSMARTCOLS_DEBUG_PADDING=on
+use visible padding characters. Requires enabled LIBSMARTCOLS_DEBUG.
+.IP LOCK_BLOCK_DEVICE=<mode>
+use exclusive BSD lock. The mode is "1" or "0". See \fB\-\-lock\fR for more details.
+
+.SH AUTHORS
+.MT kzak@redhat.com
+Karel Zak
+.ME
+.br
+.MT dave@gnu.org
+Davidlohr Bueso
+.ME
+.br
+.PP
+The original version was written by
+Andries E. Brouwer, A. V. Le Blanc and others.
+
+.SH SEE ALSO
+.BR cfdisk (8),
+.BR mkfs (8),
+.BR partx (8),
+.BR sfdisk (8)
+
+.SH AVAILABILITY
+The fdisk command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/fdisk.c b/disk-utils/fdisk.c
new file mode 100644
index 0000000..f802d44
--- /dev/null
+++ b/disk-utils/fdisk.c
@@ -0,0 +1,1180 @@
+/*
+ * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk)
+ * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
+ *
+ * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software. You can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation: either version 1 or
+ * (at your option) any later version.
+ */
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <limits.h>
+#include <signal.h>
+#include <poll.h>
+#include <libsmartcols.h>
+#ifdef HAVE_LIBREADLINE
+# define _FUNCTION_DEF
+# include <readline/readline.h>
+#endif
+
+#include "c.h"
+#include "xalloc.h"
+#include "all-io.h"
+#include "nls.h"
+#include "rpmatch.h"
+#include "blkdev.h"
+#include "mbsalign.h"
+#include "pathnames.h"
+#include "canonicalize.h"
+#include "strutils.h"
+#include "closestream.h"
+#include "pager.h"
+
+#include "fdisk.h"
+
+#include "pt-sun.h" /* to toggle flags */
+
+#ifdef HAVE_LINUX_COMPILER_H
+# include <linux/compiler.h>
+#endif
+#ifdef HAVE_LINUX_BLKPG_H
+# include <linux/blkpg.h>
+#endif
+
+int pwipemode = WIPEMODE_AUTO;
+int device_is_used;
+int is_interactive;
+struct fdisk_table *original_layout;
+
+static int wipemode = WIPEMODE_AUTO;
+
+/*
+ * fdisk debug stuff (see fdisk.h and include/debug.h)
+ */
+UL_DEBUG_DEFINE_MASK(fdisk);
+UL_DEBUG_DEFINE_MASKNAMES(fdisk) = UL_DEBUG_EMPTY_MASKNAMES;
+
+static void fdiskprog_init_debug(void)
+{
+ __UL_INIT_DEBUG_FROM_ENV(fdisk, FDISKPROG_DEBUG_, 0, FDISK_DEBUG);
+}
+
+static void reply_sighandler(int sig __attribute__((unused)))
+{
+ DBG(ASK, ul_debug("got signal"));
+}
+
+static int reply_running;
+
+#ifdef HAVE_LIBREADLINE
+static char *reply_line;
+
+static void reply_linehandler(char *line)
+{
+ reply_line = line;
+ reply_running = 0;
+ rl_callback_handler_remove(); /* avoid duplicate prompt */
+}
+#endif
+
+int get_user_reply(const char *prompt, char *buf, size_t bufsz)
+{
+ struct sigaction oldact, act = {
+ .sa_handler = reply_sighandler
+ };
+ struct pollfd fds[] = {
+ { .fd = fileno(stdin), .events = POLLIN }
+ };
+ size_t sz;
+ int ret = 0;
+
+ DBG(ASK, ul_debug("asking for user reply %s", is_interactive ? "[interactive]" : ""));
+
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGINT, &act, &oldact);
+
+#ifdef HAVE_LIBREADLINE
+ if (is_interactive)
+ rl_callback_handler_install(prompt, reply_linehandler);
+#endif
+ errno = 0;
+ reply_running = 1;
+ do {
+ int rc;
+
+ *buf = '\0';
+#ifdef HAVE_LIBREADLINE
+ if (!is_interactive)
+#endif
+ {
+ fputs(prompt, stdout);
+ fflush(stdout);
+ }
+
+ rc = poll(fds, 1, -1);
+ if (rc == -1 && errno == EINTR) { /* interrupted by signal */
+ DBG(ASK, ul_debug("cancel by CTRL+C"));
+ ret = -ECANCELED;
+ goto done;
+ }
+ if (rc == -1 && errno != EAGAIN) { /* error */
+ ret = -errno;
+ goto done;
+ }
+#ifdef HAVE_LIBREADLINE
+ if (is_interactive) {
+ /* read input and copy to buf[] */
+ rl_callback_read_char();
+ if (!reply_running && reply_line) {
+ sz = strlen(reply_line);
+ if (sz == 0)
+ buf[sz++] = '\n';
+ else
+ memcpy(buf, reply_line, min(sz, bufsz));
+ buf[min(sz, bufsz - 1)] = '\0';
+ free(reply_line);
+ reply_line = NULL;
+ }
+ } else
+#endif
+ {
+ if (!fgets(buf, bufsz, stdin))
+ *buf = '\0';
+ break;
+ }
+ } while (reply_running);
+
+ if (!*buf) {
+ DBG(ASK, ul_debug("cancel by CTRL+D"));
+ ret = -ECANCELED;
+ clearerr(stdin);
+ goto done;
+ }
+
+ /*
+ * cleanup the reply
+ */
+ sz = ltrim_whitespace((unsigned char *) buf);
+ if (sz && *(buf + sz - 1) == '\n')
+ *(buf + sz - 1) = '\0';
+
+done:
+#ifdef HAVE_LIBREADLINE
+ if (is_interactive)
+ rl_callback_handler_remove();
+#endif
+ sigaction(SIGINT, &oldact, NULL);
+ DBG(ASK, ul_debug("user's reply: >>>%s<<< [rc=%d]", buf, ret));
+ return ret;
+}
+
+static int ask_menu(struct fdisk_context *cxt, struct fdisk_ask *ask,
+ char *buf, size_t bufsz)
+
+{
+ const char *q = fdisk_ask_get_query(ask);
+ int dft = fdisk_ask_menu_get_default(ask);
+
+ if (q) {
+ fputs(q, stdout); /* print header */
+ fputc('\n', stdout);
+ }
+
+ do {
+ char prompt[128];
+ int key, c, rc;
+ const char *name, *desc;
+ size_t i = 0;
+
+ /* print menu items */
+ while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0)
+ fprintf(stdout, " %c %s (%s)\n", key, name, desc);
+
+ /* ask for key */
+ snprintf(prompt, sizeof(prompt), _("Select (default %c): "), dft);
+ rc = get_user_reply(prompt, buf, bufsz);
+ if (rc)
+ return rc;
+ if (!*buf) {
+ fdisk_info(cxt, _("Using default response %c."), dft);
+ c = dft;
+ } else
+ c = tolower(buf[0]);
+
+ /* check result */
+ i = 0;
+ while (fdisk_ask_menu_get_item(ask, i++, &key, NULL, NULL) == 0) {
+ if (c == key) {
+ fdisk_ask_menu_set_result(ask, c);
+ return 0; /* success */
+ }
+ }
+ fdisk_warnx(cxt, _("Value out of range."));
+ } while (1);
+
+ return -EINVAL;
+}
+
+
+#define tochar(num) ((int) ('a' + num - 1))
+static int ask_number(struct fdisk_context *cxt,
+ struct fdisk_ask *ask,
+ char *buf, size_t bufsz)
+{
+ char prompt[128] = { '\0' };
+ const char *q = fdisk_ask_get_query(ask);
+ const char *range = fdisk_ask_number_get_range(ask);
+
+ uint64_t dflt = fdisk_ask_number_get_default(ask),
+ low = fdisk_ask_number_get_low(ask),
+ high = fdisk_ask_number_get_high(ask);
+ int inchar = fdisk_ask_number_inchars(ask);
+
+ assert(q);
+
+ DBG(ASK, ul_debug("asking for number "
+ "['%s', <%"PRIu64",%"PRIu64">, default=%"PRIu64", range: %s]",
+ q, low, high, dflt, range));
+
+ if (range && dflt >= low && dflt <= high) {
+ if (inchar)
+ snprintf(prompt, sizeof(prompt), _("%s (%s, default %c): "),
+ q, range, tochar(dflt));
+ else
+ snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "),
+ q, range, dflt);
+
+ } else if (dflt >= low && dflt <= high) {
+ if (inchar)
+ snprintf(prompt, sizeof(prompt), _("%s (%c-%c, default %c): "),
+ q, tochar(low), tochar(high), tochar(dflt));
+ else
+ snprintf(prompt, sizeof(prompt),
+ _("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "),
+ q, low, high, dflt);
+ } else if (inchar)
+ snprintf(prompt, sizeof(prompt), _("%s (%c-%c): "),
+ q, tochar(low), tochar(high));
+ else
+ snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "),
+ q, low, high);
+
+ do {
+ int rc = get_user_reply(prompt, buf, bufsz);
+ uint64_t num = 0;
+
+ if (rc)
+ return rc;
+ if (!*buf && dflt >= low && dflt <= high)
+ return fdisk_ask_number_set_result(ask, dflt);
+
+ if (isdigit_string(buf)) {
+ char *end;
+
+ errno = 0;
+ num = strtoumax(buf, &end, 10);
+ if (errno || buf == end || (end && *end))
+ continue;
+ } else if (inchar && isalpha(*buf)) {
+ num = tolower(*buf) - 'a' + 1;
+ } else
+ rc = -EINVAL;
+
+ if (rc == 0 && num >= low && num <= high)
+ return fdisk_ask_number_set_result(ask, num);
+
+ fdisk_warnx(cxt, _("Value out of range."));
+ } while (1);
+
+ return -1;
+}
+
+static int ask_offset(struct fdisk_context *cxt,
+ struct fdisk_ask *ask,
+ char *buf, size_t bufsz)
+{
+ char prompt[128] = { '\0' };
+ const char *q = fdisk_ask_get_query(ask);
+ const char *range = fdisk_ask_number_get_range(ask);
+
+ uint64_t dflt = fdisk_ask_number_get_default(ask),
+ low = fdisk_ask_number_get_low(ask),
+ high = fdisk_ask_number_get_high(ask),
+ base = fdisk_ask_number_get_base(ask);
+
+ assert(q);
+
+ DBG(ASK, ul_debug("asking for offset ['%s', <%"PRIu64",%"PRIu64">, base=%"PRIu64", default=%"PRIu64", range: %s]",
+ q, low, high, base, dflt, range));
+
+ if (range && dflt >= low && dflt <= high)
+ snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "),
+ q, range, dflt);
+ else if (dflt >= low && dflt <= high)
+ snprintf(prompt, sizeof(prompt),
+ _("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "),
+ q, low, high, dflt);
+ else
+ snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "),
+ q, low, high);
+
+ do {
+ uintmax_t num = 0;
+ char sig = 0, *p;
+ int pwr = 0;
+
+ int rc = get_user_reply(prompt, buf, bufsz);
+ if (rc)
+ return rc;
+ if (!*buf && dflt >= low && dflt <= high)
+ return fdisk_ask_number_set_result(ask, dflt);
+
+ p = buf;
+ if (*p == '+' || *p == '-') {
+ sig = *buf;
+ p++;
+ }
+
+ rc = parse_size(p, &num, &pwr);
+ if (rc)
+ continue;
+ DBG(ASK, ul_debug("parsed size: %ju", num));
+ if (sig && pwr) {
+ /* +{size}{K,M,...} specified, the "num" is in bytes */
+ uint64_t unit = fdisk_ask_number_get_unit(ask);
+ num += unit/2; /* round */
+ num /= unit;
+ }
+ if (sig == '+')
+ num += base;
+ else if (sig == '-' && fdisk_ask_number_is_wrap_negative(ask))
+ num = high - num;
+ else if (sig == '-')
+ num = base - num;
+
+ DBG(ASK, ul_debug("final offset: %ju [sig: %c, power: %d, %s]",
+ num, sig, pwr,
+ sig ? "relative" : "absolute"));
+ if (num >= low && num <= high) {
+ if (sig && pwr)
+ fdisk_ask_number_set_relative(ask, 1);
+ return fdisk_ask_number_set_result(ask, (uint64_t)num);
+ }
+ fdisk_warnx(cxt, _("Value out of range."));
+ } while (1);
+
+ return -1;
+}
+
+static unsigned int info_count;
+
+static void fputs_info(struct fdisk_ask *ask, FILE *out)
+{
+ const char *msg;
+ assert(ask);
+
+ msg = fdisk_ask_print_get_mesg(ask);
+ if (!msg)
+ return;
+ if (info_count == 1)
+ fputc('\n', out);
+
+ fputs(msg, out);
+ fputc('\n', out);
+}
+
+int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
+ void *data __attribute__((__unused__)))
+{
+ int rc = 0;
+ char buf[BUFSIZ] = { '\0' };
+
+ assert(cxt);
+ assert(ask);
+
+ if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_INFO)
+ info_count = 0;
+
+ switch(fdisk_ask_get_type(ask)) {
+ case FDISK_ASKTYPE_MENU:
+ return ask_menu(cxt, ask, buf, sizeof(buf));
+ case FDISK_ASKTYPE_NUMBER:
+ return ask_number(cxt, ask, buf, sizeof(buf));
+ case FDISK_ASKTYPE_OFFSET:
+ return ask_offset(cxt, ask, buf, sizeof(buf));
+ case FDISK_ASKTYPE_INFO:
+ if (!fdisk_is_listonly(cxt))
+ info_count++;
+ fputs_info(ask, stdout);
+ break;
+ case FDISK_ASKTYPE_WARNX:
+ fflush(stdout);
+ color_scheme_fenable("warn", UL_COLOR_RED, stderr);
+ fputs(fdisk_ask_print_get_mesg(ask), stderr);
+ color_fdisable(stderr);
+ fputc('\n', stderr);
+ break;
+ case FDISK_ASKTYPE_WARN:
+ fflush(stdout);
+ color_scheme_fenable("warn", UL_COLOR_RED, stderr);
+ fputs(fdisk_ask_print_get_mesg(ask), stderr);
+ errno = fdisk_ask_print_get_errno(ask);
+ fprintf(stderr, ": %m\n");
+ color_fdisable(stderr);
+ break;
+ case FDISK_ASKTYPE_YESNO:
+ fputc('\n', stdout);
+ do {
+ int x;
+ fputs(fdisk_ask_get_query(ask), stdout);
+ rc = get_user_reply(_(" [Y]es/[N]o: "), buf, sizeof(buf));
+ if (rc)
+ break;
+ x = rpmatch(buf);
+ if (x == RPMATCH_YES || x == RPMATCH_NO) {
+ fdisk_ask_yesno_set_result(ask, x);
+ break;
+ }
+ } while(1);
+ DBG(ASK, ul_debug("yes-no ask: reply '%s' [rc=%d]", buf, rc));
+ break;
+ case FDISK_ASKTYPE_STRING:
+ {
+ char prmt[BUFSIZ];
+ snprintf(prmt, sizeof(prmt), "%s: ", fdisk_ask_get_query(ask));
+ fputc('\n', stdout);
+ rc = get_user_reply(prmt, buf, sizeof(buf));
+ if (rc == 0)
+ fdisk_ask_string_set_result(ask, xstrdup(buf));
+ DBG(ASK, ul_debug("string ask: reply '%s' [rc=%d]", buf, rc));
+ break;
+ }
+ default:
+ warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask));
+ return -EINVAL;
+ }
+ return rc;
+}
+
+static struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt, int *canceled)
+{
+ const char *q;
+ struct fdisk_label *lb;
+
+ assert(cxt);
+ lb = fdisk_get_label(cxt, NULL);
+
+ if (!lb)
+ return NULL;
+
+ *canceled = 0;
+
+ if (fdisk_label_has_parttypes_shortcuts(lb))
+ q = fdisk_label_has_code_parttypes(lb) ?
+ _("Hex code or alias (type L to list all): ") :
+ _("Partition type or alias (type L to list all): ");
+ else
+ q = fdisk_label_has_code_parttypes(lb) ?
+ _("Hex code (type L to list all codes): ") :
+ _("Partition type (type L to list all types): ");
+ do {
+ char buf[256] = { '\0' };
+ int rc = get_user_reply(q, buf, sizeof(buf));
+
+ if (rc) {
+ if (rc == -ECANCELED)
+ *canceled = 1;
+ break;
+ }
+
+ if (buf[1] == '\0' && toupper(*buf) == 'L')
+ list_partition_types(cxt);
+ else if (*buf) {
+ struct fdisk_parttype *t = fdisk_label_advparse_parttype(lb, buf,
+ FDISK_PARTTYPE_PARSE_DATA
+ | FDISK_PARTTYPE_PARSE_ALIAS
+ | FDISK_PARTTYPE_PARSE_SEQNUM);
+ if (!t)
+ fdisk_info(cxt, _("Failed to parse '%s' partition type."), buf);
+ return t;
+ }
+ } while (1);
+
+ return NULL;
+}
+
+
+void list_partition_types(struct fdisk_context *cxt)
+{
+ size_t ntypes = 0, next = 0;
+ struct fdisk_label *lb;
+ int pager = 0;
+
+ assert(cxt);
+ lb = fdisk_get_label(cxt, NULL);
+ if (!lb)
+ return;
+ ntypes = fdisk_label_get_nparttypes(lb);
+ if (!ntypes)
+ return;
+
+ if (fdisk_label_has_code_parttypes(lb)) {
+ /*
+ * Prints in 4 columns in format <hex> <name>
+ */
+ size_t last[4], done = 0, size;
+ int i;
+
+ size = ntypes;
+
+ for (i = 3; i >= 0; i--)
+ last[3 - i] = done += (size + i - done) / (i + 1);
+ i = done = 0;
+
+ do {
+ #define NAME_WIDTH 15
+ char name[NAME_WIDTH * MB_LEN_MAX];
+ size_t width = NAME_WIDTH;
+ const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, next);
+ size_t ret;
+
+ if (fdisk_parttype_get_name(t)) {
+ printf("%s%02x ", i ? " " : "\n",
+ fdisk_parttype_get_code(t));
+ ret = mbsalign(_(fdisk_parttype_get_name(t)),
+ name, sizeof(name),
+ &width, MBS_ALIGN_LEFT, 0);
+
+ if (ret == (size_t)-1 || ret >= sizeof(name))
+ printf("%-15.15s",
+ _(fdisk_parttype_get_name(t)));
+ else
+ fputs(name, stdout);
+ }
+
+ next = last[i++] + done;
+ if (i > 3 || next >= last[i]) {
+ i = 0;
+ next = ++done;
+ }
+ } while (done < last[0]);
+
+ putchar('\n');
+ } else {
+ /*
+ * Prints 1 column in format <idx> <name> <typestr>
+ */
+ size_t i;
+
+ pager_open();
+ pager = 1;
+
+ for (i = 0; i < ntypes; i++) {
+ const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, i);
+ printf("%3zu %-30s %s\n", i + 1,
+ fdisk_parttype_get_name(t),
+ fdisk_parttype_get_string(t));
+ }
+
+ }
+
+
+ /*
+ * Aliases
+ */
+ if (fdisk_label_has_parttypes_shortcuts(lb)) {
+ const char *alias = NULL, *typestr = NULL;
+ int rc = 0;
+
+ fputs(_("\nAliases:\n"), stdout);
+
+ for (next = 0; rc == 0 || rc == 2; next++) {
+ /* rc: <0 error, 0 success, 1 end, 2 deprecated */
+ rc = fdisk_label_get_parttype_shortcut(lb,
+ next, &typestr, NULL, &alias);
+ if (rc == 0)
+ printf(" %-14s - %s\n", alias, typestr);
+ }
+ }
+
+ if (pager)
+ pager_close();
+
+}
+
+void toggle_dos_compatibility_flag(struct fdisk_context *cxt)
+{
+ struct fdisk_label *lb = fdisk_get_label(cxt, "dos");
+ int flag;
+
+ if (!lb)
+ return;
+
+ flag = !fdisk_dos_is_compatible(lb);
+ fdisk_info(cxt, flag ?
+ _("DOS Compatibility flag is set (DEPRECATED!)") :
+ _("DOS Compatibility flag is not set"));
+
+ fdisk_dos_enable_compatible(lb, flag);
+
+ if (fdisk_is_label(cxt, DOS))
+ fdisk_reset_alignment(cxt); /* reset the current label */
+}
+
+void change_partition_type(struct fdisk_context *cxt)
+{
+ size_t i;
+ struct fdisk_parttype *t = NULL;
+ struct fdisk_partition *pa = NULL;
+ const char *old = NULL;
+ int canceled = 0;
+
+ assert(cxt);
+
+ if (fdisk_ask_partnum(cxt, &i, FALSE))
+ return;
+
+ if (fdisk_get_partition(cxt, i, &pa)) {
+ fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1);
+ return;
+ }
+
+ t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
+ old = t ? fdisk_parttype_get_name(t) : _("Unknown");
+
+ do {
+ t = ask_partition_type(cxt, &canceled);
+ if (canceled)
+ break;
+ } while (!t);
+
+ if (canceled == 0 && t && fdisk_set_partition_type(cxt, i, t) == 0)
+ fdisk_info(cxt,
+ _("Changed type of partition '%s' to '%s'."),
+ old, t ? fdisk_parttype_get_name(t) : _("Unknown"));
+ else
+ fdisk_info(cxt,
+ _("Type of partition %zu is unchanged: %s."),
+ i + 1, old);
+
+ fdisk_unref_partition(pa);
+ fdisk_unref_parttype(t);
+}
+
+int print_partition_info(struct fdisk_context *cxt)
+{
+ struct fdisk_partition *pa = NULL;
+ int rc = 0;
+ size_t i, nfields;
+ int *fields = NULL;
+ struct fdisk_label *lb = fdisk_get_label(cxt, NULL);
+
+ if ((rc = fdisk_ask_partnum(cxt, &i, FALSE)))
+ return rc;
+
+ if ((rc = fdisk_get_partition(cxt, i, &pa))) {
+ fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1);
+ return rc;
+ }
+
+ if ((rc = fdisk_label_get_fields_ids_all(lb, cxt, &fields, &nfields)))
+ goto clean_data;
+
+ for (i = 0; i < nfields; ++i) {
+ int id = fields[i];
+ char *data = NULL;
+ const struct fdisk_field *fd = fdisk_label_get_field(lb, id);
+
+ if (!fd)
+ continue;
+
+ rc = fdisk_partition_to_string(pa, cxt, id, &data);
+ if (rc < 0)
+ goto clean_data;
+ if (!data || !*data)
+ continue;
+ fdisk_info(cxt, "%15s: %s", fdisk_field_get_name(fd), data);
+ free(data);
+ }
+
+clean_data:
+ fdisk_unref_partition(pa);
+ free(fields);
+ return rc;
+}
+
+static size_t skip_empty(const unsigned char *buf, size_t i, size_t sz)
+{
+ size_t next;
+ const unsigned char *p0 = buf + i;
+
+ for (next = i + 16; next < sz; next += 16) {
+ if (memcmp(p0, buf + next, 16) != 0)
+ break;
+ }
+
+ return next == i + 16 ? i : next;
+}
+
+static void dump_buffer(off_t base, unsigned char *buf, size_t sz, int all)
+{
+ size_t i, l, next = 0;
+
+ if (!buf)
+ return;
+ for (i = 0, l = 0; i < sz; i++, l++) {
+ if (l == 0) {
+ if (all == 0 && !next)
+ next = skip_empty(buf, i, sz);
+ printf("%08jx ", (intmax_t)base + i);
+ }
+ printf(" %02x", buf[i]);
+ if (l == 7) /* words separator */
+ fputs(" ", stdout);
+ else if (l == 15) {
+ fputc('\n', stdout); /* next line */
+ l = -1;
+ if (next > i) {
+ printf("*\n");
+ i = next - 1;
+ }
+ next = 0;
+ }
+ }
+ if (l > 0)
+ printf("\n");
+}
+
+static void dump_blkdev(struct fdisk_context *cxt, const char *name,
+ uint64_t offset, size_t size, int all)
+{
+ int fd = fdisk_get_devfd(cxt);
+
+ fdisk_info(cxt, _("\n%s: offset = %"PRIu64", size = %zu bytes."),
+ name, offset, size);
+
+ assert(fd >= 0);
+
+ if (lseek(fd, (off_t) offset, SEEK_SET) == (off_t) -1)
+ fdisk_warn(cxt, _("cannot seek"));
+ else {
+ unsigned char *buf = xmalloc(size);
+
+ if (read_all(fd, (char *) buf, size) != (ssize_t) size)
+ fdisk_warn(cxt, _("cannot read"));
+ else
+ dump_buffer(offset, buf, size, all);
+ free(buf);
+ }
+}
+
+void dump_firstsector(struct fdisk_context *cxt)
+{
+ int all = !isatty(STDOUT_FILENO);
+
+ assert(cxt);
+
+ dump_blkdev(cxt, _("First sector"), 0, fdisk_get_sector_size(cxt), all);
+}
+
+void dump_disklabel(struct fdisk_context *cxt)
+{
+ int all = !isatty(STDOUT_FILENO);
+ int i = 0;
+ const char *name = NULL;
+ uint64_t offset = 0;
+ size_t size = 0;
+
+ assert(cxt);
+
+ while (fdisk_locate_disklabel(cxt, i++, &name, &offset, &size) == 0 && size)
+ dump_blkdev(cxt, name, offset, size, all);
+}
+
+static fdisk_sector_t get_dev_blocks(char *dev)
+{
+ int fd, ret;
+ fdisk_sector_t size;
+
+ if ((fd = open(dev, O_RDONLY)) < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), dev);
+ ret = blkdev_get_sectors(fd, (unsigned long long *) &size);
+ close(fd);
+ if (ret < 0)
+ err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), dev);
+ return size/2;
+}
+
+
+void follow_wipe_mode(struct fdisk_context *cxt)
+{
+ int dowipe = wipemode == WIPEMODE_ALWAYS ? 1 : 0;
+
+ if (isatty(STDIN_FILENO) && wipemode == WIPEMODE_AUTO)
+ dowipe = 1; /* do it in interactive mode */
+
+ if (fdisk_is_ptcollision(cxt) && wipemode != WIPEMODE_NEVER)
+ dowipe = 1; /* always remove old PT */
+
+ fdisk_enable_wipe(cxt, dowipe);
+ if (dowipe)
+ fdisk_warnx(cxt, _(
+ "The device contains '%s' signature and it will be removed by a write command. "
+ "See fdisk(8) man page and --wipe option for more details."),
+ fdisk_get_collision(cxt));
+ else
+ fdisk_warnx(cxt, _(
+ "The device contains '%s' signature and it may remain on the device. "
+ "It is recommended to wipe the device with wipefs(8) or "
+ "fdisk --wipe, in order to avoid possible collisions."),
+ fdisk_get_collision(cxt));
+}
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+
+ fputs(USAGE_HEADER, out);
+
+ fprintf(out,
+ _(" %1$s [options] <disk> change partition table\n"
+ " %1$s [options] -l [<disk>...] list partition table(s)\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Display or manipulate a disk partition table.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -b, --sector-size <size> physical and logical sector size\n"), out);
+ fputs(_(" -B, --protect-boot don't erase bootbits when creating a new label\n"), out);
+ fputs(_(" -c, --compatibility[=<mode>] mode is 'dos' or 'nondos' (default)\n"), out);
+ fprintf(out,
+ _(" -L, --color[=<when>] colorize output (%s, %s or %s)\n"), "auto", "always", "never");
+ fprintf(out,
+ " %s\n", USAGE_COLORS_DEFAULT);
+ fputs(_(" -l, --list display partitions and exit\n"), out);
+ fputs(_(" -x, --list-details like --list but with more details\n"), out);
+
+ fputs(_(" -n, --noauto-pt don't create default partition table on empty devices\n"), out);
+ fputs(_(" -o, --output <list> output columns\n"), out);
+ fputs(_(" -t, --type <type> recognize specified partition table type only\n"), out);
+ fputs(_(" -u, --units[=<unit>] display units: 'cylinders' or 'sectors' (default)\n"), out);
+ fputs(_(" -s, --getsz display device size in 512-byte sectors [DEPRECATED]\n"), out);
+ fputs(_(" --bytes print SIZE in bytes rather than in human readable format\n"), out);
+ fprintf(out,
+ _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
+ fprintf(out,
+ _(" -w, --wipe <mode> wipe signatures (%s, %s or %s)\n"), "auto", "always", "never");
+ fprintf(out,
+ _(" -W, --wipe-partitions <mode> wipe signatures from new partitions (%s, %s or %s)\n"), "auto", "always", "never");
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_(" -C, --cylinders <number> specify the number of cylinders\n"), out);
+ fputs(_(" -H, --heads <number> specify the number of heads\n"), out);
+ fputs(_(" -S, --sectors <number> specify the number of sectors per track\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ printf(USAGE_HELP_OPTIONS(31));
+
+ list_available_columns(out);
+
+ printf(USAGE_MAN_TAIL("fdisk(8)"));
+ exit(EXIT_SUCCESS);
+}
+
+
+enum {
+ ACT_FDISK = 0, /* default */
+ ACT_LIST,
+ ACT_LIST_DETAILS,
+ ACT_SHOWSIZE
+};
+
+int main(int argc, char **argv)
+{
+ int rc, i, c, act = ACT_FDISK, noauto_pt = 0;
+ int colormode = UL_COLORMODE_UNDEF;
+ struct fdisk_context *cxt;
+ char *outarg = NULL;
+ const char *devname, *lockmode = NULL;
+ enum {
+ OPT_BYTES = CHAR_MAX + 1,
+ OPT_LOCK
+ };
+ static const struct option longopts[] = {
+ { "bytes", no_argument, NULL, OPT_BYTES },
+ { "color", optional_argument, NULL, 'L' },
+ { "compatibility", optional_argument, NULL, 'c' },
+ { "cylinders", required_argument, NULL, 'C' },
+ { "heads", required_argument, NULL, 'H' },
+ { "sectors", required_argument, NULL, 'S' },
+ { "getsz", no_argument, NULL, 's' },
+ { "help", no_argument, NULL, 'h' },
+ { "list", no_argument, NULL, 'l' },
+ { "list-details", no_argument, NULL, 'x' },
+ { "lock", optional_argument, NULL, OPT_LOCK },
+ { "noauto-pt", no_argument, NULL, 'n' },
+ { "sector-size", required_argument, NULL, 'b' },
+ { "type", required_argument, NULL, 't' },
+ { "units", optional_argument, NULL, 'u' },
+ { "version", no_argument, NULL, 'V' },
+ { "output", no_argument, NULL, 'o' },
+ { "protect-boot", no_argument, NULL, 'B' },
+ { "wipe", required_argument, NULL, 'w' },
+ { "wipe-partitions",required_argument, NULL, 'W' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ fdisk_init_debug(0);
+ scols_init_debug(0);
+ fdiskprog_init_debug();
+
+ cxt = fdisk_new_context();
+ if (!cxt)
+ err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
+
+ fdisk_set_ask(cxt, ask_callback, NULL);
+
+ while ((c = getopt_long(argc, argv, "b:Bc::C:hH:lL::no:sS:t:u::vVw:W:x",
+ longopts, NULL)) != -1) {
+ switch (c) {
+ case 'b':
+ {
+ size_t sz = strtou32_or_err(optarg,
+ _("invalid sector size argument"));
+ if (sz != 512 && sz != 1024 && sz != 2048 && sz != 4096)
+ errx(EXIT_FAILURE, _("invalid sector size argument"));
+ fdisk_save_user_sector_size(cxt, sz, sz);
+ break;
+ }
+ case 'B':
+ fdisk_enable_bootbits_protection(cxt, 1);
+ break;
+ case 'C':
+ fdisk_save_user_geometry(cxt,
+ strtou32_or_err(optarg,
+ _("invalid cylinders argument")),
+ 0, 0);
+ break;
+ case 'c':
+ if (optarg) {
+ /* this setting is independent on the current
+ * actively used label
+ */
+ char *p = *optarg == '=' ? optarg + 1 : optarg;
+ struct fdisk_label *lb = fdisk_get_label(cxt, "dos");
+
+ if (!lb)
+ err(EXIT_FAILURE, _("not found DOS label driver"));
+ if (strcmp(p, "dos") == 0)
+ fdisk_dos_enable_compatible(lb, TRUE);
+ else if (strcmp(p, "nondos") == 0)
+ fdisk_dos_enable_compatible(lb, FALSE);
+ else
+ errx(EXIT_FAILURE, _("unknown compatibility mode '%s'"), p);
+ }
+ /* use default if no optarg specified */
+ break;
+ case 'H':
+ fdisk_save_user_geometry(cxt, 0,
+ strtou32_or_err(optarg,
+ _("invalid heads argument")),
+ 0);
+ break;
+ case 'S':
+ fdisk_save_user_geometry(cxt, 0, 0,
+ strtou32_or_err(optarg,
+ _("invalid sectors argument")));
+ break;
+ case 'l':
+ act = ACT_LIST;
+ break;
+ case 'x':
+ act = ACT_LIST_DETAILS;
+ break;
+ case 'L':
+ colormode = UL_COLORMODE_AUTO;
+ if (optarg)
+ colormode = colormode_or_err(optarg,
+ _("unsupported color mode"));
+ break;
+ case 'n':
+ noauto_pt = 1;
+ break;
+ case 'o':
+ outarg = optarg;
+ break;
+ case 's':
+ act = ACT_SHOWSIZE;
+ break;
+ case 't':
+ {
+ struct fdisk_label *lb = NULL;
+
+ while (fdisk_next_label(cxt, &lb) == 0)
+ fdisk_label_set_disabled(lb, 1);
+
+ lb = fdisk_get_label(cxt, optarg);
+ if (!lb)
+ errx(EXIT_FAILURE, _("unsupported disklabel: %s"), optarg);
+ fdisk_label_set_disabled(lb, 0);
+ break;
+ }
+ case 'u':
+ if (optarg && *optarg == '=')
+ optarg++;
+ if (fdisk_set_unit(cxt, optarg) != 0)
+ errx(EXIT_FAILURE, _("unsupported unit"));
+ break;
+ case 'V': /* preferred for util-linux */
+ case 'v': /* for backward compatibility only */
+ print_version(EXIT_SUCCESS);
+ case 'w':
+ wipemode = wipemode_from_string(optarg);
+ if (wipemode < 0)
+ errx(EXIT_FAILURE, _("unsupported wipe mode"));
+ break;
+ case 'W':
+ pwipemode = wipemode_from_string(optarg);
+ if (pwipemode < 0)
+ errx(EXIT_FAILURE, _("unsupported wipe mode"));
+ break;
+ case 'h':
+ usage();
+ case OPT_BYTES:
+ fdisk_set_size_unit(cxt, FDISK_SIZEUNIT_BYTES);
+ break;
+ case OPT_LOCK:
+ lockmode = "1";
+ if (optarg) {
+ if (*optarg == '=')
+ optarg++;
+ lockmode = optarg;
+ }
+ break;
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ if (argc-optind != 1 && fdisk_has_user_device_properties(cxt))
+ warnx(_("The device properties (sector size and geometry) should"
+ " be used with one specified device only."));
+
+ colors_init(colormode, "fdisk");
+ is_interactive = isatty(STDIN_FILENO);
+
+ switch (act) {
+ case ACT_LIST:
+ case ACT_LIST_DETAILS:
+ fdisk_enable_listonly(cxt, 1);
+
+ if (act == ACT_LIST_DETAILS)
+ fdisk_enable_details(cxt, 1);
+
+ init_fields(cxt, outarg, NULL);
+
+ if (argc > optind) {
+ int k;
+
+ for (rc = 0, k = optind; k < argc; k++)
+ rc += print_device_pt(cxt, argv[k], 1, 0, k != optind);
+
+ if (rc)
+ return EXIT_FAILURE;
+ } else
+ print_all_devices_pt(cxt, 0);
+ break;
+
+ case ACT_SHOWSIZE:
+ /* deprecated */
+ if (argc - optind <= 0) {
+ warnx(_("bad usage"));
+ errtryhelp(EXIT_FAILURE);
+ }
+ for (i = optind; i < argc; i++) {
+ uintmax_t blks = get_dev_blocks(argv[i]);
+
+ if (argc - optind == 1)
+ printf("%ju\n", blks);
+ else
+ printf("%s: %ju\n", argv[i], blks);
+ }
+ break;
+
+ case ACT_FDISK:
+ if (argc-optind != 1) {
+ warnx(_("bad usage"));
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ /* Here starts interactive mode, use fdisk_{warn,info,..} functions */
+ color_scheme_enable("welcome", UL_COLOR_GREEN);
+ fdisk_info(cxt, _("Welcome to fdisk (%s)."), PACKAGE_STRING);
+ color_disable();
+ fdisk_info(cxt, _("Changes will remain in memory only, until you decide to write them.\n"
+ "Be careful before using the write command.\n"));
+
+ devname = argv[optind];
+ rc = fdisk_assign_device(cxt, devname, 0);
+ if (rc == -EACCES) {
+ rc = fdisk_assign_device(cxt, devname, 1);
+ if (rc == 0)
+ fdisk_warnx(cxt, _("Device is open in read-only mode."));
+ }
+ if (rc)
+ err(EXIT_FAILURE, _("cannot open %s"), devname);
+
+ fflush(stdout);
+
+ if (!fdisk_is_readonly(cxt)
+ && blkdev_lock(fdisk_get_devfd(cxt), devname, lockmode) != 0) {
+ fdisk_deassign_device(cxt, 1);
+ fdisk_unref_context(cxt);
+ return EXIT_FAILURE;
+ }
+
+ if (fdisk_get_collision(cxt))
+ follow_wipe_mode(cxt);
+
+ if (!fdisk_has_label(cxt)) {
+ fdisk_info(cxt, _("Device does not contain a recognized partition table."));
+ if (!noauto_pt)
+ fdisk_create_disklabel(cxt, NULL);
+
+ } else if (fdisk_is_label(cxt, GPT) && fdisk_gpt_is_hybrid(cxt))
+ fdisk_warnx(cxt, _(
+ "A hybrid GPT was detected. You have to sync "
+ "the hybrid MBR manually (expert command 'M')."));
+
+ init_fields(cxt, outarg, NULL); /* -o <columns> */
+
+ if (!fdisk_is_readonly(cxt)) {
+ fdisk_get_partitions(cxt, &original_layout);
+ device_is_used = fdisk_device_is_used(cxt);
+ }
+
+ while (1)
+ process_fdisk_menu(&cxt);
+ }
+
+ fdisk_unref_context(cxt);
+ return EXIT_SUCCESS;
+}
diff --git a/disk-utils/fdisk.h b/disk-utils/fdisk.h
new file mode 100644
index 0000000..e1147e2
--- /dev/null
+++ b/disk-utils/fdisk.h
@@ -0,0 +1,56 @@
+#ifndef UTIL_LINUX_FDISK_H
+#define UTIL_LINUX_FDISK_H
+/*
+ * fdisk.h
+ */
+
+#include "c.h"
+#include <assert.h>
+#include <libfdisk.h>
+
+/* Let's temporary include private libfdisk header file. The final libfdisk.h
+ * maybe included when fdisk.c and libfdisk code will be completely spit.
+ */
+#include "blkdev.h"
+#include "colors.h"
+#include "debug.h"
+#include "nls.h"
+
+#include "fdisk-list.h"
+
+#define FDISKPROG_DEBUG_INIT (1 << 1)
+#define FDISKPROG_DEBUG_MENU (1 << 3)
+#define FDISKPROG_DEBUG_MISC (1 << 4)
+#define FDISKPROG_DEBUG_ASK (1 << 5)
+#define FDISKPROG_DEBUG_ALL 0xFFFF
+
+extern int pwipemode;
+extern struct fdisk_table *original_layout;
+extern int device_is_used;
+extern int is_interactive;
+
+UL_DEBUG_DECLARE_MASK(fdisk);
+#define DBG(m, x) __UL_DBG(fdisk, FDISKPROG_DEBUG_, m, x)
+#define ON_DBG(m, x) __UL_DBG_CALL(fdisk, FDISKPROG_DEBUG_, m, x)
+
+extern int get_user_reply(const char *prompt,
+ char *buf, size_t bufsz);
+extern int process_fdisk_menu(struct fdisk_context **cxt);
+
+extern int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
+ void *data __attribute__((__unused__)));
+
+extern int print_partition_info(struct fdisk_context *cxt);
+
+/* prototypes for fdisk.c */
+extern void dump_firstsector(struct fdisk_context *cxt);
+extern void dump_disklabel(struct fdisk_context *cxt);
+
+extern void list_partition_types(struct fdisk_context *cxt);
+extern void change_partition_type(struct fdisk_context *cxt);
+
+extern void toggle_dos_compatibility_flag(struct fdisk_context *cxt);
+
+extern void follow_wipe_mode(struct fdisk_context *cxt);
+
+#endif /* UTIL_LINUX_FDISK_H */
diff --git a/disk-utils/fsck.8 b/disk-utils/fsck.8
new file mode 100644
index 0000000..97eb8d8
--- /dev/null
+++ b/disk-utils/fsck.8
@@ -0,0 +1,418 @@
+.\" Copyright 1993, 1994, 1995 by Theodore Ts'o. All Rights Reserved.
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.TH FSCK 8 "February 2009" "util-linux" "System Administration"
+.SH NAME
+fsck \- check and repair a Linux filesystem
+.SH SYNOPSIS
+.B fsck
+.RB [ \-lsAVRTMNP ]
+.RB [ \-r
+.RI [ fd ]]
+.RB [ \-C
+.RI [ fd ]]
+.RB [ \-t
+.IR fstype ]
+.RI [ filesystem \&...\&]
+.RB [ \-\- ]
+.RI [ fs-specific-options ]
+.SH DESCRIPTION
+.B fsck
+is used to check and optionally repair one or more Linux filesystems.
+.I filesys
+can be a device name (e.g.,
+.IR /dev/hdc1 ", " /dev/sdb2 ),
+a mount point (e.g.,
+.IR / ", " /usr ", " /home ),
+or an filesystem label or UUID specifier (e.g.,
+UUID=8868abf6-88c5-4a83-98b8-bfc24057f7bd or LABEL=root).
+Normally, the
+.B fsck
+program will try to handle filesystems on different physical disk drives
+in parallel to reduce the total amount of time needed to check all of them.
+.PP
+If no filesystems are specified on the command line, and the
+.B \-A
+option is not specified,
+.B fsck
+will default to checking filesystems in
+.I /etc/fstab
+serially. This is equivalent to the
+.B \-As
+options.
+.PP
+The exit status returned by
+.B fsck
+is the sum of the following conditions:
+.PP
+.RS
+.PD 0
+.TP
+.B 0
+No errors
+.TP
+.B 1
+Filesystem errors corrected
+.TP
+.B 2
+System should be rebooted
+.TP
+.B 4
+Filesystem errors left uncorrected
+.TP
+.B 8
+Operational error
+.TP
+.B 16
+Usage or syntax error
+.TP
+.B 32
+Checking canceled by user request
+.TP
+.B 128
+Shared-library error
+.PD
+.RE
+.PP
+The exit status returned when multiple filesystems are checked
+is the bit-wise OR of the exit statuses for each
+filesystem that is checked.
+.PP
+In actuality,
+.B fsck
+is simply a front-end for the various filesystem checkers
+(\fBfsck\fR.\fIfstype\fR) available under Linux. The
+filesystem-specific checker is searched for in the
+PATH environment variable. If the PATH is undefined then
+fallback to "/sbin".
+.PP
+Please see the filesystem-specific checker manual pages for
+further details.
+.SH OPTIONS
+.TP
+.B \-l
+Create an exclusive
+.BR flock (2)
+lock file (/run/fsck/<diskname>.lock) for whole-disk device.
+This option can be used with one device only (this means that \fB\-A\fR and
+\fB\-l\fR are mutually exclusive). This option is recommended when more
+.BR fsck (8)
+instances are executed in the same time. The option is ignored when used for
+multiple devices or for non-rotating disks. \fBfsck\fR does not lock underlying
+devices when executed to check stacked devices (e.g.\& MD or DM) \(en this feature is
+not implemented yet.
+.TP
+.BR \-r \ [ \fIfd\fR ]
+Report certain statistics for each fsck when it completes. These statistics
+include the exit status, the maximum run set size (in kilobytes), the elapsed
+all-clock time and the user and system CPU time used by the fsck run. For
+example:
+
+/dev/sda1: status 0, rss 92828, real 4.002804, user 2.677592, sys 0.86186
+
+GUI front-ends may specify a file descriptor
+.IR fd ,
+in which case the progress bar information will be sent to that file descriptor
+in a machine parsable format. For example:
+
+/dev/sda1 0 92828 4.002804 2.677592 0.86186
+.TP
+.B \-s
+Serialize
+.B fsck
+operations. This is a good idea if you are checking multiple
+filesystems and the checkers are in an interactive mode. (Note:
+.BR e2fsck (8)
+runs in an interactive mode by default. To make
+.BR e2fsck (8)
+run in a non-interactive mode, you must either specify the
+.B \-p
+or
+.B \-a
+option, if you wish for errors to be corrected automatically, or the
+.B \-n
+option if you do not.)
+.TP
+.BI \-t " fslist"
+Specifies the type(s) of filesystem to be checked. When the
+.B \-A
+flag is specified, only filesystems that match
+.I fslist
+are checked. The
+.I fslist
+parameter is a comma-separated list of filesystems and options
+specifiers. All of the filesystems in this comma-separated list may be
+prefixed by a negation operator
+.RB ' no '
+or
+.RB ' ! ',
+which requests that only those filesystems not listed in
+.I fslist
+will be checked. If none of the filesystems in
+.I fslist
+is prefixed by a negation operator, then only those listed filesystems
+will be checked.
+.sp
+Options specifiers may be included in the comma-separated
+.IR fslist .
+They must have the format
+.BI opts= fs-option\fR.
+If an options specifier is present, then only filesystems which contain
+.I fs-option
+in their mount options field of
+.I /etc/fstab
+will be checked. If the options specifier is prefixed by a negation
+operator, then only
+those filesystems that do not have
+.I fs-option
+in their mount options field of
+.I /etc/fstab
+will be checked.
+.sp
+For example, if
+.B opts=ro
+appears in
+.IR fslist ,
+then only filesystems listed in
+.I /etc/fstab
+with the
+.B ro
+option will be checked.
+.sp
+For compatibility with Mandrake distributions whose boot scripts
+depend upon an unauthorized UI change to the
+.B fsck
+program, if a filesystem type of
+.B loop
+is found in
+.IR fslist ,
+it is treated as if
+.B opts=loop
+were specified as an argument to the
+.B \-t
+option.
+.sp
+Normally, the filesystem type is deduced by searching for
+.I filesys
+in the
+.I /etc/fstab
+file and using the corresponding entry.
+If the type cannot be deduced, and there is only a single filesystem
+given as an argument to the
+.B \-t
+option,
+.B fsck
+will use the specified filesystem type. If this type is not
+available, then the default filesystem type (currently ext2) is used.
+.TP
+.B \-A
+Walk through the
+.I /etc/fstab
+file and try to check all filesystems in one run. This option is
+typically used from the
+.I /etc/rc
+system initialization file, instead of multiple commands for checking
+a single filesystem.
+.sp
+The root filesystem will be checked first unless the
+.B \-P
+option is specified (see below). After that,
+filesystems will be checked in the order specified by the
+.I fs_passno
+(the sixth) field in the
+.I /etc/fstab
+file.
+Filesystems with a
+.I fs_passno
+value of 0 are skipped and are not checked at all. Filesystems with a
+.I fs_passno
+value of greater than zero will be checked in order,
+with filesystems with the lowest
+.I fs_passno
+number being checked first.
+If there are multiple filesystems with the same pass number,
+.B fsck
+will attempt to check them in parallel, although it will avoid running
+multiple filesystem checks on the same physical disk.
+.sp
+.B fsck
+does not check stacked devices (RAIDs, dm-crypt, \&...\&) in parallel with any other
+device. See below for FSCK_FORCE_ALL_PARALLEL setting. The /sys filesystem is
+used to determine dependencies between devices.
+.sp
+Hence, a very common configuration in
+.I /etc/fstab
+files is to set the root filesystem to have a
+.I fs_passno
+value of 1
+and to set all other filesystems to have a
+.I fs_passno
+value of 2. This will allow
+.B fsck
+to automatically run filesystem checkers in parallel if it is advantageous
+to do so. System administrators might choose
+not to use this configuration if they need to avoid multiple filesystem
+checks running in parallel for some reason \(en for example, if the
+machine in question is short on memory so that
+excessive paging is a concern.
+.sp
+.B fsck
+normally does not check whether the device actually exists before
+calling a filesystem specific checker. Therefore non-existing
+devices may cause the system to enter filesystem repair mode during
+boot if the filesystem specific checker returns a fatal error. The
+.I /etc/fstab
+mount option
+.B nofail
+may be used to have
+.B fsck
+skip non-existing devices.
+.B fsck
+also skips non-existing devices that have the special filesystem type
+.BR auto .
+.TP
+.BR \-C \ [ \fIfd\fR ]
+Display completion/progress bars for those filesystem checkers (currently
+only for ext[234]) which support them. \fBfsck\fR will manage the
+filesystem checkers so that only one of them will display
+a progress bar at a time. GUI front-ends may specify a file descriptor
+.IR fd ,
+in which case the progress bar information will be sent to that file descriptor.
+.TP
+.B \-M
+Do not check mounted filesystems and return an exit status of 0
+for mounted filesystems.
+.TP
+.B \-N
+Don't execute, just show what would be done.
+.TP
+.B \-P
+When the
+.B \-A
+flag is set, check the root filesystem in parallel with the other filesystems.
+This is not the safest thing in the world to do,
+since if the root filesystem is in doubt things like the
+.BR e2fsck (8)
+executable might be corrupted! This option is mainly provided
+for those sysadmins who don't want to repartition the root
+filesystem to be small and compact (which is really the right solution).
+.TP
+.B \-R
+When checking all filesystems with the
+.B \-A
+flag, skip the root filesystem. (This is useful in case the root
+filesystem has already been mounted read-write.)
+.TP
+.B \-T
+Don't show the title on startup.
+.TP
+.B \-V
+Produce verbose output, including all filesystem-specific commands
+that are executed.
+.TP
+\fB\-?\fR, \fB\-\-help\fR
+Display help text and exit.
+.TP
+\fB\-\-version\fR
+Display version information and exit.
+.SH FILESYSTEM SPECIFIC OPTIONS
+.B Options which are not understood by fsck are passed to the filesystem-specific checker!
+.PP
+These options
+.B must
+not take arguments, as there is no
+way for
+.B fsck
+to be able to properly guess which options take arguments and which
+don't.
+.PP
+Options and arguments which follow the
+.B \-\-
+are treated as filesystem-specific options to be passed to the
+filesystem-specific checker.
+.PP
+Please note that \fBfsck\fR is not
+designed to pass arbitrarily complicated options to filesystem-specific
+checkers. If you're doing something complicated, please just
+execute the filesystem-specific checker directly. If you pass
+.B fsck
+some horribly complicated options and arguments, and it doesn't do
+what you expect,
+.B don't bother reporting it as a bug.
+You're almost certainly doing something that you shouldn't be doing
+with
+.BR fsck .
+Options to different filesystem-specific fsck's are not standardized.
+.SH ENVIRONMENT
+The
+.B fsck
+program's behavior is affected by the following environment variables:
+.TP
+.B FSCK_FORCE_ALL_PARALLEL
+If this environment variable is set,
+.B fsck
+will attempt to check all of the specified filesystems in parallel, regardless of
+whether the filesystems appear to be on the same device. (This is useful for
+RAID systems or high-end storage systems such as those sold by companies such
+as IBM or EMC.) Note that the fs_passno value is still used.
+.TP
+.B FSCK_MAX_INST
+This environment variable will limit the maximum number of filesystem
+checkers that can be running at one time. This allows configurations
+which have a large number of disks to avoid
+.B fsck
+starting too many filesystem checkers at once, which might overload
+CPU and memory resources available on the system. If this value is
+zero, then an unlimited number of processes can be spawned. This is
+currently the default, but future versions of
+.B fsck
+may attempt to automatically determine how many filesystem checks can
+be run based on gathering accounting data from the operating system.
+.TP
+.B PATH
+The
+.B PATH
+environment variable is used to find filesystem checkers.
+.TP
+.B FSTAB_FILE
+This environment variable allows the system administrator
+to override the standard location of the
+.I /etc/fstab
+file. It is also useful for developers who are testing
+.BR fsck .
+.TP
+.B LIBBLKID_DEBUG=all
+enables libblkid debug output.
+.TP
+.B LIBMOUNT_DEBUG=all
+enables libmount debug output.
+.SH FILES
+.I /etc/fstab
+.SH AUTHORS
+.nf
+Theodore Ts'o <tytso@mit.edu>
+Karel Zak <kzak@redhat.com>
+.fi
+.SH SEE ALSO
+.na
+.BR fstab (5),
+.BR mkfs (8),
+.BR fsck.ext2 (8)
+or
+.BR fsck.ext3 (8)
+or
+.BR e2fsck (8),
+.BR fsck.cramfs (8),
+.BR fsck.jfs (8),
+.BR fsck.nfs (8),
+.BR fsck.minix (8),
+.BR fsck.msdos (8),
+.BR fsck.vfat (8),
+.BR fsck.xfs (8),
+.BR reiserfsck (8)
+.ad
+.SH AVAILABILITY
+The fsck command is part of the util-linux package and is available from
+.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/disk-utils/fsck.c b/disk-utils/fsck.c
new file mode 100644
index 0000000..fda80a6
--- /dev/null
+++ b/disk-utils/fsck.c
@@ -0,0 +1,1688 @@
+/*
+ * fsck --- A generic, parallelizing front-end for the fsck program.
+ * It will automatically try to run fsck programs in parallel if the
+ * devices are on separate spindles. It is based on the same ideas as
+ * the generic front end for fsck by David Engel and Fred van Kempen,
+ * but it has been completely rewritten from scratch to support
+ * parallel execution.
+ *
+ * Written by Theodore Ts'o, <tytso@mit.edu>
+ *
+ * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994:
+ * o Changed -t fstype to behave like with mount when -A (all file
+ * systems) or -M (like mount) is specified.
+ * o fsck looks if it can find the fsck.type program to decide
+ * if it should ignore the fs type. This way more fsck programs
+ * can be added without changing this front-end.
+ * o -R flag skip root file system.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+ * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o.
+ *
+ * Copyright (C) 2009-2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ */
+
+#define _XOPEN_SOURCE 600 /* for inclusion of sa_handler in Solaris */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <paths.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <dirent.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <blkid.h>
+#include <libmount.h>
+
+#include "nls.h"
+#include "pathnames.h"
+#include "exitcodes.h"
+#include "c.h"
+#include "fileutils.h"
+#include "monotonic.h"
+#include "strutils.h"
+
+#define XALLOC_EXIT_CODE FSCK_EX_ERROR
+#include "xalloc.h"
+
+#define CLOSE_EXIT_CODE FSCK_EX_ERROR
+#include "closestream.h"
+
+#ifndef DEFAULT_FSTYPE
+# define DEFAULT_FSTYPE "ext2"
+#endif
+
+#define MAX_DEVICES 32
+#define MAX_ARGS 32
+
+#define FSCK_RUNTIME_DIRNAME "/run/fsck"
+
+static const char *ignored_types[] = {
+ "ignore",
+ "iso9660",
+ "sw",
+ NULL
+};
+
+static const char *really_wanted[] = {
+ "minix",
+ "ext2",
+ "ext3",
+ "ext4",
+ "ext4dev",
+ "jfs",
+ "reiserfs"
+};
+
+/*
+ * Internal structure for mount table entries.
+ */
+struct fsck_fs_data
+{
+ const char *device;
+ dev_t disk;
+ unsigned int stacked:1,
+ done:1,
+ eval_device:1;
+};
+
+/*
+ * Structure to allow exit codes to be stored
+ */
+struct fsck_instance {
+ int pid;
+ int flags; /* FLAG_{DONE|PROGRESS} */
+
+ int lock; /* flock()ed lockpath file descriptor or -1 */
+ char *lockpath; /* /run/fsck/<diskname>.lock or NULL */
+
+ int exit_status;
+ struct timeval start_time;
+ struct timeval end_time;
+ char * prog;
+ char * type;
+
+ struct rusage rusage;
+ struct libmnt_fs *fs;
+ struct fsck_instance *next;
+};
+
+#define FLAG_DONE 1
+#define FLAG_PROGRESS 2
+
+/*
+ * Global variables for options
+ */
+static char *devices[MAX_DEVICES];
+static char *args[MAX_ARGS];
+static int num_devices, num_args;
+
+static int lockdisk;
+static int verbose;
+static int doall;
+static int noexecute;
+static int serialize;
+static int skip_root;
+static int ignore_mounted;
+static int notitle;
+static int parallel_root;
+static int progress;
+static int progress_fd;
+static int force_all_parallel;
+static int report_stats;
+static FILE *report_stats_file;
+
+static int num_running;
+static int max_running;
+
+static volatile int cancel_requested;
+static int kill_sent;
+static char *fstype;
+static struct fsck_instance *instance_list;
+
+#define FSCK_DEFAULT_PATH "/sbin"
+static char *fsck_path;
+
+
+/* parsed fstab and mtab */
+static struct libmnt_table *fstab, *mtab;
+static struct libmnt_cache *mntcache;
+
+static int count_slaves(dev_t disk);
+
+static int string_to_int(const char *s)
+{
+ long l;
+ char *p;
+
+ l = strtol(s, &p, 0);
+ if (*p || l == LONG_MIN || l == LONG_MAX || l < 0 || l > INT_MAX)
+ return -1;
+
+ return (int) l;
+}
+
+/* Do we really really want to check this fs? */
+static int fs_check_required(const char *type)
+{
+ size_t i;
+
+ for(i = 0; i < ARRAY_SIZE(really_wanted); i++) {
+ if (strcmp(type, really_wanted[i]) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int is_mounted(struct libmnt_fs *fs)
+{
+ int rc;
+ const char *src;
+
+ src = mnt_fs_get_source(fs);
+ if (!src)
+ return 0;
+ if (!mntcache)
+ mntcache = mnt_new_cache();
+ if (!mtab) {
+ mtab = mnt_new_table();
+ if (!mtab)
+ err(FSCK_EX_ERROR, ("failed to initialize libmount table"));
+ mnt_table_set_cache(mtab, mntcache);
+ mnt_table_parse_mtab(mtab, NULL);
+ }
+
+ rc = mnt_table_find_source(mtab, src, MNT_ITER_BACKWARD) ? 1 : 0;
+ if (verbose) {
+ if (rc)
+ printf(_("%s is mounted\n"), src);
+ else
+ printf(_("%s is not mounted\n"), src);
+ }
+ return rc;
+}
+
+static int ignore(struct libmnt_fs *);
+
+static struct fsck_fs_data *fs_create_data(struct libmnt_fs *fs)
+{
+ struct fsck_fs_data *data = mnt_fs_get_userdata(fs);
+
+ if (!data) {
+ data = xcalloc(1, sizeof(*data));
+ mnt_fs_set_userdata(fs, data);
+ }
+ return data;
+}
+
+/*
+ * fs from fstab might contains real device name as well as symlink,
+ * LABEL or UUID, this function returns canonicalized result.
+ */
+static const char *fs_get_device(struct libmnt_fs *fs)
+{
+ struct fsck_fs_data *data = mnt_fs_get_userdata(fs);
+
+ if (!data || !data->eval_device) {
+ const char *spec = mnt_fs_get_source(fs);
+
+ if (!data)
+ data = fs_create_data(fs);
+
+ data->eval_device = 1;
+ data->device = mnt_resolve_spec(spec, mnt_table_get_cache(fstab));
+ if (!data->device)
+ data->device = xstrdup(spec);
+ }
+
+ return data->device;
+}
+
+static dev_t fs_get_disk(struct libmnt_fs *fs, int check)
+{
+ struct fsck_fs_data *data;
+ const char *device;
+ struct stat st;
+
+ data = mnt_fs_get_userdata(fs);
+ if (data && data->disk)
+ return data->disk;
+
+ if (!check)
+ return 0;
+
+ if (mnt_fs_is_netfs(fs) || mnt_fs_is_pseudofs(fs))
+ return 0;
+
+ device = fs_get_device(fs);
+ if (!device)
+ return 0;
+
+ data = fs_create_data(fs);
+
+ if (!stat(device, &st) &&
+ !blkid_devno_to_wholedisk(st.st_rdev, NULL, 0, &data->disk)) {
+
+ if (data->disk)
+ data->stacked = count_slaves(data->disk) > 0 ? 1 : 0;
+ return data->disk;
+ }
+ return 0;
+}
+
+static int fs_is_stacked(struct libmnt_fs *fs)
+{
+ struct fsck_fs_data *data = mnt_fs_get_userdata(fs);
+ return data ? data->stacked : 0;
+}
+
+static int fs_is_done(struct libmnt_fs *fs)
+{
+ struct fsck_fs_data *data = mnt_fs_get_userdata(fs);
+ return data ? data->done : 0;
+}
+
+static void fs_set_done(struct libmnt_fs *fs)
+{
+ struct fsck_fs_data *data = fs_create_data(fs);
+
+ if (data)
+ data->done = 1;
+}
+
+static int is_irrotational_disk(dev_t disk)
+{
+ char path[PATH_MAX];
+ FILE *f;
+ int rc, x;
+
+
+ rc = snprintf(path, sizeof(path),
+ "/sys/dev/block/%d:%d/queue/rotational",
+ major(disk), minor(disk));
+
+ if (rc < 0 || (unsigned int) rc >= sizeof(path))
+ return 0;
+
+ f = fopen(path, "r");
+ if (!f)
+ return 0;
+
+ rc = fscanf(f, "%d", &x);
+ if (rc != 1) {
+ if (ferror(f))
+ warn(_("cannot read %s"), path);
+ else
+ warnx(_("parse error: %s"), path);
+ }
+ fclose(f);
+
+ return rc == 1 ? !x : 0;
+}
+
+static void lock_disk(struct fsck_instance *inst)
+{
+ dev_t disk = fs_get_disk(inst->fs, 1);
+ char *diskpath = NULL, *diskname;
+
+ inst->lock = -1;
+
+ if (!disk || is_irrotational_disk(disk))
+ goto done;
+
+ diskpath = blkid_devno_to_devname(disk);
+ if (!diskpath)
+ goto done;
+
+ if (access(FSCK_RUNTIME_DIRNAME, F_OK) != 0) {
+ int rc = mkdir(FSCK_RUNTIME_DIRNAME,
+ S_IWUSR|
+ S_IRUSR|S_IRGRP|S_IROTH|
+ S_IXUSR|S_IXGRP|S_IXOTH);
+ if (rc && errno != EEXIST) {
+ warn(_("cannot create directory %s"),
+ FSCK_RUNTIME_DIRNAME);
+ goto done;
+ }
+ }
+
+ diskname = stripoff_last_component(diskpath);
+ if (!diskname)
+ diskname = diskpath;
+
+ xasprintf(&inst->lockpath, FSCK_RUNTIME_DIRNAME "/%s.lock", diskname);
+
+ if (verbose)
+ printf(_("Locking disk by %s ... "), inst->lockpath);
+
+ inst->lock = open(inst->lockpath, O_RDONLY|O_CREAT|O_CLOEXEC,
+ S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
+ if (inst->lock >= 0) {
+ int rc = -1;
+
+ /* inform users that we're waiting on the lock */
+ if (verbose &&
+ (rc = flock(inst->lock, LOCK_EX | LOCK_NB)) != 0 &&
+ errno == EWOULDBLOCK)
+ printf(_("(waiting) "));
+
+ if (rc != 0 && flock(inst->lock, LOCK_EX) != 0) {
+ close(inst->lock); /* failed */
+ inst->lock = -1;
+ }
+ }
+
+ if (verbose)
+ /* TRANSLATORS: These are followups to "Locking disk...". */
+ printf("%s.\n", inst->lock >= 0 ? _("succeeded") : _("failed"));
+
+
+done:
+ if (inst->lock < 0) {
+ free(inst->lockpath);
+ inst->lockpath = NULL;
+ }
+ free(diskpath);
+}
+
+static void unlock_disk(struct fsck_instance *inst)
+{
+ if (inst->lock < 0)
+ return;
+
+ if (verbose)
+ printf(_("Unlocking %s.\n"), inst->lockpath);
+
+ close(inst->lock); /* unlock */
+
+ free(inst->lockpath);
+
+ inst->lock = -1;
+ inst->lockpath = NULL;
+}
+
+static void free_instance(struct fsck_instance *i)
+{
+ if (lockdisk)
+ unlock_disk(i);
+ free(i->prog);
+ free(i->lockpath);
+ mnt_unref_fs(i->fs);
+ free(i);
+}
+
+static struct libmnt_fs *add_dummy_fs(const char *device)
+{
+ struct libmnt_fs *fs = mnt_new_fs();
+
+ if (fs && mnt_fs_set_source(fs, device) == 0 &&
+ mnt_table_add_fs(fstab, fs) == 0) {
+ mnt_unref_fs(fs);
+ return fs;
+ }
+
+ mnt_unref_fs(fs);
+ err(FSCK_EX_ERROR, _("failed to setup description for %s"), device);
+}
+
+static void fs_interpret_type(struct libmnt_fs *fs)
+{
+ const char *device;
+ const char *type = mnt_fs_get_fstype(fs);
+
+ if (type && strcmp(type, "auto") != 0)
+ return;
+
+ mnt_fs_set_fstype(fs, NULL);
+
+ device = fs_get_device(fs);
+ if (device) {
+ int ambi = 0;
+ char *tp;
+ struct libmnt_cache *cache = mnt_table_get_cache(fstab);
+
+ tp = mnt_get_fstype(device, &ambi, cache);
+ if (!ambi)
+ mnt_fs_set_fstype(fs, tp);
+ if (!cache)
+ free(tp);
+ }
+}
+
+static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)),
+ const char *filename, int line)
+{
+ warnx(_("%s: parse error at line %d -- ignored"), filename, line);
+ return 1;
+}
+
+/*
+ * Load the filesystem database from /etc/fstab
+ */
+static void load_fs_info(void)
+{
+ const char *path;
+
+ fstab = mnt_new_table();
+ if (!fstab)
+ err(FSCK_EX_ERROR, ("failed to initialize libmount table"));
+
+ mnt_table_set_parser_errcb(fstab, parser_errcb);
+ mnt_table_set_cache(fstab, mntcache);
+
+ errno = 0;
+
+ /*
+ * Let's follow libmount defaults if $FSTAB_FILE is not specified
+ */
+ path = getenv("FSTAB_FILE");
+
+ if (mnt_table_parse_fstab(fstab, path)) {
+ if (!path)
+ path = mnt_get_fstab_path();
+
+ /* don't print error when there is no fstab at all */
+ if (access(path, F_OK) == 0) {
+ if (errno)
+ warn(_("%s: failed to parse fstab"), path);
+ else
+ warnx(_("%s: failed to parse fstab"), path);
+ }
+ }
+}
+
+/*
+ * Lookup filesys in /etc/fstab and return the corresponding entry.
+ * The @path has to be real path (no TAG) by mnt_resolve_spec().
+ */
+static struct libmnt_fs *lookup(char *path)
+{
+ struct libmnt_fs *fs;
+
+ if (!path)
+ return NULL;
+
+ fs = mnt_table_find_srcpath(fstab, path, MNT_ITER_FORWARD);
+ if (!fs) {
+ /*
+ * Maybe mountpoint has been specified on fsck command line.
+ * Yeah, crazy feature...
+ *
+ * Note that mnt_table_find_target() may canonicalize paths in
+ * the fstab to support symlinks. This is really unwanted,
+ * because readlink() on mountpoints may trigger automounts.
+ *
+ * So, disable the cache and compare the paths as strings
+ * without care about symlinks...
+ */
+ mnt_table_set_cache(fstab, NULL);
+ fs = mnt_table_find_target(fstab, path, MNT_ITER_FORWARD);
+ mnt_table_set_cache(fstab, mntcache);
+ }
+ return fs;
+}
+
+/* Find fsck program for a given fs type. */
+static int find_fsck(const char *type, char **progpath)
+{
+ char *s;
+ const char *tpl;
+ char *prog = NULL;
+ char *p = xstrdup(fsck_path);
+ int rc;
+
+ /* Are we looking for a program or just a type? */
+ tpl = (strncmp(type, "fsck.", 5) ? "%s/fsck.%s" : "%s/%s");
+
+ for(s = strtok(p, ":"); s; s = strtok(NULL, ":")) {
+ xasprintf(&prog, tpl, s, type);
+ if (access(prog, X_OK) == 0)
+ break;
+ free(prog);
+ prog = NULL;
+ }
+
+ free(p);
+ rc = prog ? 1 : 0;
+
+ if (progpath)
+ *progpath = prog;
+ else
+ free(prog);
+
+ return rc;
+}
+
+static int progress_active(void)
+{
+ struct fsck_instance *inst;
+
+ for (inst = instance_list; inst; inst = inst->next) {
+ if (inst->flags & FLAG_DONE)
+ continue;
+ if (inst->flags & FLAG_PROGRESS)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Process run statistics for finished fsck instances.
+ *
+ * If report_stats is 0, do nothing, otherwise print a selection of
+ * interesting rusage statistics as well as elapsed wallclock time.
+ */
+static void print_stats(struct fsck_instance *inst)
+{
+ struct timeval delta;
+
+ if (!inst || !report_stats || noexecute)
+ return;
+
+ timersub(&inst->end_time, &inst->start_time, &delta);
+
+ if (report_stats_file)
+ fprintf(report_stats_file, "%s %d %ld "
+ "%ld.%06ld %ld.%06ld %ld.%06ld\n",
+ fs_get_device(inst->fs),
+ inst->exit_status,
+ inst->rusage.ru_maxrss,
+ (long)delta.tv_sec, (long)delta.tv_usec,
+ (long)inst->rusage.ru_utime.tv_sec,
+ (long)inst->rusage.ru_utime.tv_usec,
+ (long)inst->rusage.ru_stime.tv_sec,
+ (long)inst->rusage.ru_stime.tv_usec);
+ else
+ fprintf(stdout, "%s: status %d, rss %ld, "
+ "real %ld.%06ld, user %ld.%06ld, sys %ld.%06ld\n",
+ fs_get_device(inst->fs),
+ inst->exit_status,
+ inst->rusage.ru_maxrss,
+ (long)delta.tv_sec, (long)delta.tv_usec,
+ (long)inst->rusage.ru_utime.tv_sec,
+ (long)inst->rusage.ru_utime.tv_usec,
+ (long)inst->rusage.ru_stime.tv_sec,
+ (long)inst->rusage.ru_stime.tv_usec);
+}
+
+/*
+ * Execute a particular fsck program, and link it into the list of
+ * child processes we are waiting for.
+ */
+static int execute(const char *progname, const char *progpath,
+ const char *type, struct libmnt_fs *fs, int interactive)
+{
+ char *argv[80];
+ int argc, i;
+ struct fsck_instance *inst, *p;
+ pid_t pid;
+
+ inst = xcalloc(1, sizeof(*inst));
+
+ argv[0] = xstrdup(progname);
+ argc = 1;
+
+ for (i=0; i <num_args; i++)
+ argv[argc++] = xstrdup(args[i]);
+
+ if (progress &&
+ ((strcmp(type, "ext2") == 0) ||
+ (strcmp(type, "ext3") == 0) ||
+ (strcmp(type, "ext4") == 0) ||
+ (strcmp(type, "ext4dev") == 0))) {
+
+ char tmp[80];
+ tmp[0] = 0;
+ if (!progress_active()) {
+ snprintf(tmp, 80, "-C%d", progress_fd);
+ inst->flags |= FLAG_PROGRESS;
+ } else if (progress_fd)
+ snprintf(tmp, 80, "-C%d", progress_fd * -1);
+ if (tmp[0])
+ argv[argc++] = xstrdup(tmp);
+ }
+
+ argv[argc++] = xstrdup(fs_get_device(fs));
+ argv[argc] = NULL;
+
+ if (verbose || noexecute) {
+ const char *tgt = mnt_fs_get_target(fs);
+
+ if (!tgt)
+ tgt = fs_get_device(fs);
+ printf("[%s (%d) -- %s] ", progpath, num_running, tgt);
+ for (i=0; i < argc; i++)
+ printf("%s ", argv[i]);
+ printf("\n");
+ }
+
+ mnt_ref_fs(fs);
+ inst->fs = fs;
+ inst->lock = -1;
+
+ if (lockdisk)
+ lock_disk(inst);
+
+ /* Fork and execute the correct program. */
+ if (noexecute)
+ pid = -1;
+ else if ((pid = fork()) < 0) {
+ warn(_("fork failed"));
+ free_instance(inst);
+ return errno;
+ } else if (pid == 0) {
+ if (!interactive)
+ close(0);
+ execv(progpath, argv);
+ err(FSCK_EX_ERROR, _("%s: execute failed"), progpath);
+ }
+
+ for (i=0; i < argc; i++)
+ free(argv[i]);
+
+ inst->pid = pid;
+ inst->prog = xstrdup(progname);
+ inst->type = xstrdup(type);
+ gettime_monotonic(&inst->start_time);
+ inst->next = NULL;
+
+ /*
+ * Find the end of the list, so we add the instance on at the end.
+ */
+ for (p = instance_list; p && p->next; p = p->next);
+
+ if (p)
+ p->next = inst;
+ else
+ instance_list = inst;
+
+ return 0;
+}
+
+/*
+ * Send a signal to all outstanding fsck child processes
+ */
+static int kill_all(int signum)
+{
+ struct fsck_instance *inst;
+ int n = 0;
+
+ for (inst = instance_list; inst; inst = inst->next) {
+ if (inst->flags & FLAG_DONE)
+ continue;
+ kill(inst->pid, signum);
+ n++;
+ }
+ return n;
+}
+
+/*
+ * Wait for one child process to exit; when it does, unlink it from
+ * the list of executing child processes, and return it.
+ */
+static struct fsck_instance *wait_one(int flags)
+{
+ int status = 0;
+ int sig;
+ struct fsck_instance *inst, *inst2, *prev;
+ pid_t pid;
+ struct rusage rusage;
+
+ if (!instance_list)
+ return NULL;
+
+ if (noexecute) {
+ inst = instance_list;
+ prev = NULL;
+#ifdef RANDOM_DEBUG
+ while (inst->next && (random() & 1)) {
+ prev = inst;
+ inst = inst->next;
+ }
+#endif
+ inst->exit_status = 0;
+ goto ret_inst;
+ }
+
+ /*
+ * gcc -Wall fails saving throw against stupidity
+ * (inst and prev are thought to be uninitialized variables)
+ */
+ inst = prev = NULL;
+
+ do {
+ pid = wait4(-1, &status, flags, &rusage);
+ if (cancel_requested && !kill_sent) {
+ kill_all(SIGTERM);
+ kill_sent++;
+ }
+ if ((pid == 0) && (flags & WNOHANG))
+ return NULL;
+ if (pid < 0) {
+ if ((errno == EINTR) || (errno == EAGAIN))
+ continue;
+ if (errno == ECHILD) {
+ warnx(_("wait: no more child process?!?"));
+ return NULL;
+ }
+ warn(_("waitpid failed"));
+ continue;
+ }
+ for (prev = NULL, inst = instance_list;
+ inst;
+ prev = inst, inst = inst->next) {
+ if (inst->pid == pid)
+ break;
+ }
+ } while (!inst);
+
+ if (WIFEXITED(status))
+ status = WEXITSTATUS(status);
+ else if (WIFSIGNALED(status)) {
+ sig = WTERMSIG(status);
+ if (sig == SIGINT) {
+ status = FSCK_EX_UNCORRECTED;
+ } else {
+ warnx(_("Warning... %s for device %s exited "
+ "with signal %d."),
+ inst->prog, fs_get_device(inst->fs), sig);
+ status = FSCK_EX_ERROR;
+ }
+ } else {
+ warnx(_("%s %s: status is %x, should never happen."),
+ inst->prog, fs_get_device(inst->fs), status);
+ status = FSCK_EX_ERROR;
+ }
+
+ inst->exit_status = status;
+ inst->flags |= FLAG_DONE;
+ gettime_monotonic(&inst->end_time);
+ memcpy(&inst->rusage, &rusage, sizeof(struct rusage));
+
+ if (progress && (inst->flags & FLAG_PROGRESS) &&
+ !progress_active()) {
+ for (inst2 = instance_list; inst2; inst2 = inst2->next) {
+ if (inst2->flags & FLAG_DONE)
+ continue;
+ if (strcmp(inst2->type, "ext2") != 0 &&
+ strcmp(inst2->type, "ext3") &&
+ strcmp(inst2->type, "ext4") != 0 &&
+ strcmp(inst2->type, "ext4dev") != 0)
+ continue;
+ /*
+ * If we've just started the fsck, wait a tiny
+ * bit before sending the kill, to give it
+ * time to set up the signal handler
+ */
+ if (inst2->start_time.tv_sec < time(NULL) + 2) {
+ if (fork() == 0) {
+ sleep(1);
+ kill(inst2->pid, SIGUSR1);
+ exit(FSCK_EX_OK);
+ }
+ } else
+ kill(inst2->pid, SIGUSR1);
+ inst2->flags |= FLAG_PROGRESS;
+ break;
+ }
+ }
+ret_inst:
+ if (prev)
+ prev->next = inst->next;
+ else
+ instance_list = inst->next;
+
+ print_stats(inst);
+
+ if (verbose > 1)
+ printf(_("Finished with %s (exit status %d)\n"),
+ fs_get_device(inst->fs), inst->exit_status);
+ num_running--;
+ return inst;
+}
+
+#define FLAG_WAIT_ALL 0
+#define FLAG_WAIT_ATLEAST_ONE 1
+/*
+ * Wait until all executing child processes have exited; return the
+ * logical OR of all of their exit code values.
+ */
+static int wait_many(int flags)
+{
+ struct fsck_instance *inst;
+ int global_status = 0;
+ int wait_flags = 0;
+
+ while ((inst = wait_one(wait_flags))) {
+ global_status |= inst->exit_status;
+ free_instance(inst);
+#ifdef RANDOM_DEBUG
+ if (noexecute && (flags & WNOHANG) && !(random() % 3))
+ break;
+#endif
+ if (flags & FLAG_WAIT_ATLEAST_ONE)
+ wait_flags = WNOHANG;
+ }
+ return global_status;
+}
+
+/*
+ * Run the fsck program on a particular device
+ *
+ * If the type is specified using -t, and it isn't prefixed with "no"
+ * (as in "noext2") and only one filesystem type is specified, then
+ * use that type regardless of what is specified in /etc/fstab.
+ *
+ * If the type isn't specified by the user, then use either the type
+ * specified in /etc/fstab, or DEFAULT_FSTYPE.
+ */
+static int fsck_device(struct libmnt_fs *fs, int interactive)
+{
+ char *progname, *progpath;
+ const char *type;
+ int retval;
+
+ fs_interpret_type(fs);
+
+ type = mnt_fs_get_fstype(fs);
+
+ if (type && strcmp(type, "auto") != 0)
+ ;
+ else if (fstype && strncmp(fstype, "no", 2) != 0 &&
+ strncmp(fstype, "opts=", 5) != 0 && strncmp(fstype, "loop", 4) != 0 &&
+ !strchr(fstype, ','))
+ type = fstype;
+ else
+ type = DEFAULT_FSTYPE;
+
+ xasprintf(&progname, "fsck.%s", type);
+
+ if (!find_fsck(progname, &progpath)) {
+ free(progname);
+ if (fs_check_required(type)) {
+ retval = ENOENT;
+ goto err;
+ }
+ return 0;
+ }
+
+ num_running++;
+ retval = execute(progname, progpath, type, fs, interactive);
+ free(progname);
+ free(progpath);
+ if (retval) {
+ num_running--;
+ goto err;
+ }
+ return 0;
+err:
+ warnx(_("error %d (%m) while executing fsck.%s for %s"),
+ retval, type, fs_get_device(fs));
+ return FSCK_EX_ERROR;
+}
+
+
+/*
+ * Deal with the fsck -t argument.
+ */
+static struct fs_type_compile {
+ char **list;
+ int *type;
+ int negate;
+} fs_type_compiled;
+
+#define FS_TYPE_NORMAL 0
+#define FS_TYPE_OPT 1
+#define FS_TYPE_NEGOPT 2
+
+static void compile_fs_type(char *fs_type, struct fs_type_compile *cmp)
+{
+ char *cp, *list, *s;
+ int num = 2;
+ int negate, first_negate = 1;
+
+ if (fs_type) {
+ for (cp=fs_type; *cp; cp++) {
+ if (*cp == ',')
+ num++;
+ }
+ }
+
+ cmp->list = xcalloc(num, sizeof(char *));
+ cmp->type = xcalloc(num, sizeof(int));
+ cmp->negate = 0;
+
+ if (!fs_type)
+ return;
+
+ list = xstrdup(fs_type);
+ num = 0;
+ s = strtok(list, ",");
+ while(s) {
+ negate = 0;
+ if (strncmp(s, "no", 2) == 0) {
+ s += 2;
+ negate = 1;
+ } else if (*s == '!') {
+ s++;
+ negate = 1;
+ }
+ if (strcmp(s, "loop") == 0)
+ /* loop is really short-hand for opts=loop */
+ goto loop_special_case;
+ else if (strncmp(s, "opts=", 5) == 0) {
+ s += 5;
+ loop_special_case:
+ cmp->type[num] = negate ? FS_TYPE_NEGOPT : FS_TYPE_OPT;
+ } else {
+ if (first_negate) {
+ cmp->negate = negate;
+ first_negate = 0;
+ }
+ if ((negate && !cmp->negate) ||
+ (!negate && cmp->negate)) {
+ errx(FSCK_EX_USAGE,
+ _("Either all or none of the filesystem types passed to -t must be prefixed\n"
+ "with 'no' or '!'."));
+ }
+ }
+
+ cmp->list[num++] = xstrdup(s);
+ s = strtok(NULL, ",");
+ }
+ free(list);
+}
+
+/*
+ * This function returns true if a particular option appears in a
+ * comma-delimited options list
+ */
+static int opt_in_list(const char *opt, const char *optlist)
+{
+ char *list, *s;
+
+ if (!optlist)
+ return 0;
+ list = xstrdup(optlist);
+
+ s = strtok(list, ",");
+ while(s) {
+ if (strcmp(s, opt) == 0) {
+ free(list);
+ return 1;
+ }
+ s = strtok(NULL, ",");
+ }
+ free(list);
+ return 0;
+}
+
+/* See if the filesystem matches the criteria given by the -t option */
+static int fs_match(struct libmnt_fs *fs, struct fs_type_compile *cmp)
+{
+ int n, ret = 0, checked_type = 0;
+ char *cp;
+
+ if (cmp->list == NULL || cmp->list[0] == NULL)
+ return 1;
+
+ for (n=0; (cp = cmp->list[n]); n++) {
+ switch (cmp->type[n]) {
+ case FS_TYPE_NORMAL:
+ {
+ const char *type = mnt_fs_get_fstype(fs);
+
+ checked_type++;
+ if (type && strcmp(cp, type) == 0)
+ ret = 1;
+ break;
+ }
+ case FS_TYPE_NEGOPT:
+ if (opt_in_list(cp, mnt_fs_get_options(fs)))
+ return 0;
+ break;
+ case FS_TYPE_OPT:
+ if (!opt_in_list(cp, mnt_fs_get_options(fs)))
+ return 0;
+ break;
+ }
+ }
+ if (checked_type == 0)
+ return 1;
+ return (cmp->negate ? !ret : ret);
+}
+
+/*
+ * Check if a device exists
+ */
+static int device_exists(const char *device)
+{
+ struct stat st;
+
+ if (stat(device, &st) == -1)
+ return 0;
+ if (!S_ISBLK(st.st_mode))
+ return 0;
+ return 1;
+}
+
+static int fs_ignored_type(struct libmnt_fs *fs)
+{
+ const char **ip, *type;
+
+ if (mnt_fs_is_netfs(fs) || mnt_fs_is_pseudofs(fs) || mnt_fs_is_swaparea(fs))
+ return 1;
+
+ type = mnt_fs_get_fstype(fs);
+
+ for(ip = ignored_types; type && *ip; ip++) {
+ if (strcmp(type, *ip) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Check if we should ignore this filesystem. */
+static int ignore(struct libmnt_fs *fs)
+{
+ const char *type;
+
+ /*
+ * If the pass number is 0, ignore it.
+ */
+ if (mnt_fs_get_passno(fs) == 0)
+ return 1;
+
+ /*
+ * If this is a bind mount, ignore it.
+ */
+ if (opt_in_list("bind", mnt_fs_get_options(fs))) {
+ warnx(_("%s: skipping bad line in /etc/fstab: "
+ "bind mount with nonzero fsck pass number"),
+ mnt_fs_get_target(fs));
+ return 1;
+ }
+
+ /*
+ * ignore devices that don't exist and have the "nofail" mount option
+ */
+ if (!device_exists(fs_get_device(fs))) {
+ if (opt_in_list("nofail", mnt_fs_get_options(fs))) {
+ if (verbose)
+ printf(_("%s: skipping nonexistent device\n"),
+ fs_get_device(fs));
+ return 1;
+ }
+ if (verbose)
+ printf(_("%s: nonexistent device (\"nofail\" fstab "
+ "option may be used to skip this device)\n"),
+ fs_get_device(fs));
+ }
+
+ fs_interpret_type(fs);
+
+ /*
+ * If a specific fstype is specified, and it doesn't match,
+ * ignore it.
+ */
+ if (!fs_match(fs, &fs_type_compiled))
+ return 1;
+
+ type = mnt_fs_get_fstype(fs);
+ if (!type) {
+ if (verbose)
+ printf(_("%s: skipping unknown filesystem type\n"),
+ fs_get_device(fs));
+ return 1;
+ }
+
+ /* Are we ignoring this type? */
+ if (fs_ignored_type(fs))
+ return 1;
+
+
+
+ /* See if the <fsck.fs> program is available. */
+ if (!find_fsck(type, NULL)) {
+ if (fs_check_required(type))
+ warnx(_("cannot check %s: fsck.%s not found"),
+ fs_get_device(fs), type);
+ return 1;
+ }
+
+ /* We can and want to check this file system type. */
+ return 0;
+}
+
+static int count_slaves(dev_t disk)
+{
+ DIR *dir;
+ struct dirent *dp;
+ char dirname[PATH_MAX];
+ int count = 0;
+
+ snprintf(dirname, sizeof(dirname),
+ "/sys/dev/block/%u:%u/slaves/",
+ major(disk), minor(disk));
+
+ if (!(dir = opendir(dirname)))
+ return -1;
+
+ while ((dp = readdir(dir)) != NULL) {
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_LNK)
+ continue;
+#endif
+ if (dp->d_name[0] == '.' &&
+ ((dp->d_name[1] == 0) ||
+ ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
+ continue;
+
+ count++;
+ }
+
+ closedir(dir);
+ return count;
+}
+
+/*
+ * Returns TRUE if a partition on the same disk is already being
+ * checked.
+ */
+static int disk_already_active(struct libmnt_fs *fs)
+{
+ struct fsck_instance *inst;
+ dev_t disk;
+
+ if (force_all_parallel)
+ return 0;
+
+ if (instance_list && fs_is_stacked(instance_list->fs))
+ /* any instance for a stacked device is already running */
+ return 1;
+
+ disk = fs_get_disk(fs, 1);
+
+ /*
+ * If we don't know the base device, assume that the device is
+ * already active if there are any fsck instances running.
+ *
+ * Don't check a stacked device with any other disk too.
+ */
+ if (!disk || fs_is_stacked(fs))
+ return (instance_list != NULL);
+
+ for (inst = instance_list; inst; inst = inst->next) {
+ dev_t idisk = fs_get_disk(inst->fs, 0);
+
+ if (!idisk || disk == idisk)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Check all file systems, using the /etc/fstab table. */
+static int check_all(void)
+{
+ int not_done_yet = 1;
+ int passno = 1;
+ int pass_done;
+ int status = FSCK_EX_OK;
+
+ struct libmnt_fs *fs;
+ struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_FORWARD);
+
+ if (!itr)
+ err(FSCK_EX_ERROR, _("failed to allocate iterator"));
+
+ /*
+ * Do an initial scan over the filesystem; mark filesystems
+ * which should be ignored as done, and resolve any "auto"
+ * filesystem types (done as a side-effect of calling ignore()).
+ */
+ while (mnt_table_next_fs(fstab, itr, &fs) == 0) {
+ if (ignore(fs)) {
+ fs_set_done(fs);
+ continue;
+ }
+ }
+
+ if (verbose)
+ fputs(_("Checking all file systems.\n"), stdout);
+
+ /*
+ * Find and check the root filesystem.
+ */
+ if (!parallel_root) {
+ fs = mnt_table_find_target(fstab, "/", MNT_ITER_FORWARD);
+ if (fs) {
+ if (!skip_root &&
+ !fs_is_done(fs) &&
+ !(ignore_mounted && is_mounted(fs))) {
+ status |= fsck_device(fs, 1);
+ status |= wait_many(FLAG_WAIT_ALL);
+ if (status > FSCK_EX_NONDESTRUCT) {
+ mnt_free_iter(itr);
+ return status;
+ }
+ }
+ fs_set_done(fs);
+ }
+ }
+
+ /*
+ * This is for the bone-headed user who enters the root
+ * filesystem twice. Skip root will skip all root entries.
+ */
+ if (skip_root) {
+ mnt_reset_iter(itr, MNT_ITER_FORWARD);
+
+ while(mnt_table_next_fs(fstab, itr, &fs) == 0) {
+ const char *tgt = mnt_fs_get_target(fs);
+
+ if (tgt && strcmp(tgt, "/") == 0)
+ fs_set_done(fs);
+ }
+ }
+
+ while (not_done_yet) {
+ not_done_yet = 0;
+ pass_done = 1;
+
+ mnt_reset_iter(itr, MNT_ITER_FORWARD);
+
+ while(mnt_table_next_fs(fstab, itr, &fs) == 0) {
+
+ if (cancel_requested)
+ break;
+ if (fs_is_done(fs))
+ continue;
+ /*
+ * If the filesystem's pass number is higher
+ * than the current pass number, then we don't
+ * do it yet.
+ */
+ if (mnt_fs_get_passno(fs) > passno) {
+ not_done_yet++;
+ continue;
+ }
+ if (ignore_mounted && is_mounted(fs)) {
+ fs_set_done(fs);
+ continue;
+ }
+ /*
+ * If a filesystem on a particular device has
+ * already been spawned, then we need to defer
+ * this to another pass.
+ */
+ if (disk_already_active(fs)) {
+ pass_done = 0;
+ continue;
+ }
+ /*
+ * Spawn off the fsck process
+ */
+ status |= fsck_device(fs, serialize);
+ fs_set_done(fs);
+
+ /*
+ * Only do one filesystem at a time, or if we
+ * have a limit on the number of fsck's extant
+ * at one time, apply that limit.
+ */
+ if (serialize ||
+ (max_running && (num_running >= max_running))) {
+ pass_done = 0;
+ break;
+ }
+ }
+ if (cancel_requested)
+ break;
+ if (verbose > 1)
+ printf(_("--waiting-- (pass %d)\n"), passno);
+
+ status |= wait_many(pass_done ? FLAG_WAIT_ALL :
+ FLAG_WAIT_ATLEAST_ONE);
+ if (pass_done) {
+ if (verbose > 1)
+ printf("----------------------------------\n");
+ passno++;
+ } else
+ not_done_yet++;
+ }
+
+ if (cancel_requested && !kill_sent) {
+ kill_all(SIGTERM);
+ kill_sent++;
+ }
+
+ status |= wait_many(FLAG_WAIT_ATLEAST_ONE);
+ mnt_free_iter(itr);
+ return status;
+}
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s [options] -- [fs-options] [<filesystem> ...]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Check and repair a Linux filesystem.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -A check all filesystems\n"), out);
+ fputs(_(" -C [<fd>] display progress bar; file descriptor is for GUIs\n"), out);
+ fputs(_(" -l lock the device to guarantee exclusive access\n"), out);
+ fputs(_(" -M do not check mounted filesystems\n"), out);
+ fputs(_(" -N do not execute, just show what would be done\n"), out);
+ fputs(_(" -P check filesystems in parallel, including root\n"), out);
+ fputs(_(" -R skip root filesystem; useful only with '-A'\n"), out);
+ fputs(_(" -r [<fd>] report statistics for each device checked;\n"
+ " file descriptor is for GUIs\n"), out);
+ fputs(_(" -s serialize the checking operations\n"), out);
+ fputs(_(" -T do not show the title on startup\n"), out);
+ fputs(_(" -t <type> specify filesystem types to be checked;\n"
+ " <type> is allowed to be a comma-separated list\n"), out);
+ fputs(_(" -V explain what is being done\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ printf( " -?, --help %s\n", USAGE_OPTSTR_HELP);
+ printf( " --version %s\n", USAGE_OPTSTR_VERSION);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("See the specific fsck.* commands for available fs-options."), out);
+ printf(USAGE_MAN_TAIL("fsck(8)"));
+ exit(FSCK_EX_OK);
+}
+
+static void signal_cancel(int sig __attribute__((__unused__)))
+{
+ cancel_requested++;
+}
+
+static void parse_argv(int argc, char *argv[])
+{
+ int i, j;
+ char *arg, *dev, *tmp = NULL;
+ char options[128];
+ int opt = 0;
+ int opts_for_fsck = 0;
+ struct sigaction sa;
+ int report_stats_fd = -1;
+
+ /*
+ * Set up signal action
+ */
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = signal_cancel;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ num_devices = 0;
+ num_args = 0;
+ instance_list = NULL;
+
+ for (i=1; i < argc; i++) {
+ arg = argv[i];
+ if (!arg)
+ continue;
+
+ /* the only two longopts to satisfy UL standards */
+ if (!opts_for_fsck && !strcmp(arg, "--help"))
+ usage();
+ if (!opts_for_fsck && !strcmp(arg, "--version"))
+ print_version(FSCK_EX_OK);
+
+ if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) {
+ if (num_devices >= MAX_DEVICES)
+ errx(FSCK_EX_ERROR, _("too many devices"));
+
+ dev = mnt_resolve_spec(arg, mntcache);
+
+ if (!dev && strchr(arg, '=')) {
+ /*
+ * Check to see if we failed because
+ * /proc/partitions isn't found.
+ */
+ if (access(_PATH_PROC_PARTITIONS, R_OK) < 0) {
+ warn(_("cannot open %s"),
+ _PATH_PROC_PARTITIONS);
+ errx(FSCK_EX_ERROR, _("Is /proc mounted?"));
+ }
+ /*
+ * Check to see if this is because
+ * we're not running as root
+ */
+ if (geteuid())
+ errx(FSCK_EX_ERROR,
+ _("must be root to scan for matching filesystems: %s"),
+ arg);
+ else
+ errx(FSCK_EX_ERROR,
+ _("couldn't find matching filesystem: %s"),
+ arg);
+ }
+ devices[num_devices++] = dev ? dev : xstrdup(arg);
+ continue;
+ }
+ if (arg[0] != '-' || opts_for_fsck) {
+ if (num_args >= MAX_ARGS)
+ errx(FSCK_EX_ERROR, _("too many arguments"));
+ args[num_args++] = xstrdup(arg);
+ continue;
+ }
+ for (j=1; arg[j]; j++) {
+ if (opts_for_fsck) {
+ options[++opt] = arg[j];
+ continue;
+ }
+ switch (arg[j]) {
+ case 'A':
+ doall = 1;
+ break;
+ case 'C':
+ progress = 1;
+ if (arg[j+1]) { /* -C<fd> */
+ progress_fd = string_to_int(arg+j+1);
+ if (progress_fd < 0)
+ progress_fd = 0;
+ else
+ goto next_arg;
+ } else if (i+1 < argc && *argv[i+1] != '-') { /* -C <fd> */
+ progress_fd = string_to_int(argv[i+1]);
+ if (progress_fd < 0)
+ progress_fd = 0;
+ else {
+ ++i;
+ goto next_arg;
+ }
+ }
+ break;
+ case 'l':
+ lockdisk = 1;
+ break;
+ case 'V':
+ verbose++;
+ break;
+ case 'N':
+ noexecute = 1;
+ break;
+ case 'R':
+ skip_root = 1;
+ break;
+ case 'T':
+ notitle = 1;
+ break;
+ case 'M':
+ ignore_mounted = 1;
+ break;
+ case 'P':
+ parallel_root = 1;
+ break;
+ case 'r':
+ report_stats = 1;
+ if (arg[j+1]) { /* -r<fd> */
+ report_stats_fd = strtou32_or_err(arg+j+1, _("invalid argument of -r"));
+ goto next_arg;
+ } else if (i+1 < argc && *argv[i+1] >= '0' && *argv[i+1] <= '9') { /* -r <fd> */
+ report_stats_fd = strtou32_or_err(argv[i+1], _("invalid argument of -r"));
+ ++i;
+ goto next_arg;
+ }
+ break;
+ case 's':
+ serialize = 1;
+ break;
+ case 't':
+ tmp = NULL;
+ if (fstype)
+ errx(FSCK_EX_USAGE,
+ _("option '%s' may be specified only once"), "-t");
+ if (arg[j+1])
+ tmp = arg+j+1;
+ else if ((i+1) < argc)
+ tmp = argv[++i];
+ else
+ errx(FSCK_EX_USAGE,
+ _("option '%s' requires an argument"), "-t");
+ fstype = xstrdup(tmp);
+ compile_fs_type(fstype, &fs_type_compiled);
+ goto next_arg;
+ case '-':
+ opts_for_fsck++;
+ break;
+ case '?':
+ usage();
+ break;
+ default:
+ options[++opt] = arg[j];
+ break;
+ }
+ }
+ next_arg:
+ if (opt) {
+ options[0] = '-';
+ options[++opt] = '\0';
+ if (num_args >= MAX_ARGS)
+ errx(FSCK_EX_ERROR, _("too many arguments"));
+ args[num_args++] = xstrdup(options);
+ opt = 0;
+ }
+ }
+
+ /* Validate the report stats file descriptor to avoid disasters */
+ if (report_stats_fd >= 0) {
+ report_stats_file = fdopen(report_stats_fd, "w");
+ if (!report_stats_file)
+ err(FSCK_EX_ERROR,
+ _("invalid argument of -r: %d"),
+ report_stats_fd);
+ }
+
+ if (getenv("FSCK_FORCE_ALL_PARALLEL"))
+ force_all_parallel++;
+ if ((tmp = getenv("FSCK_MAX_INST")))
+ max_running = atoi(tmp);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, status = 0;
+ int interactive = 0;
+ struct libmnt_fs *fs;
+ const char *path = getenv("PATH");
+
+ setvbuf(stdout, NULL, _IONBF, BUFSIZ);
+ setvbuf(stderr, NULL, _IONBF, BUFSIZ);
+
+ setlocale(LC_MESSAGES, "");
+ setlocale(LC_CTYPE, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ strutils_set_exitcode(FSCK_EX_USAGE);
+ mnt_init_debug(0); /* init libmount debug mask */
+ mntcache = mnt_new_cache(); /* no fatal error if failed */
+
+ parse_argv(argc, argv);
+
+ if (!notitle)
+ printf(UTIL_LINUX_VERSION);
+
+ load_fs_info();
+
+ fsck_path = xstrdup(path && *path ? path : FSCK_DEFAULT_PATH);
+
+ if ((num_devices == 1) || (serialize))
+ interactive = 1;
+
+ if (lockdisk && (doall || num_devices > 1)) {
+ warnx(_("the -l option can be used with one "
+ "device only -- ignore"));
+ lockdisk = 0;
+ }
+
+ /* If -A was specified ("check all"), do that! */
+ if (doall)
+ return check_all();
+
+ if (num_devices == 0) {
+ serialize++;
+ interactive++;
+ return check_all();
+ }
+ for (i = 0 ; i < num_devices; i++) {
+ if (cancel_requested) {
+ if (!kill_sent) {
+ kill_all(SIGTERM);
+ kill_sent++;
+ }
+ break;
+ }
+ fs = lookup(devices[i]);
+ if (!fs)
+ fs = add_dummy_fs(devices[i]);
+ else if (fs_ignored_type(fs))
+ continue;
+ if (ignore_mounted && is_mounted(fs))
+ continue;
+ status |= fsck_device(fs, interactive);
+ if (serialize ||
+ (max_running && (num_running >= max_running))) {
+ struct fsck_instance *inst;
+
+ inst = wait_one(0);
+ if (inst) {
+ status |= inst->exit_status;
+ free_instance(inst);
+ }
+ if (verbose > 1)
+ printf("----------------------------------\n");
+ }
+ }
+ status |= wait_many(FLAG_WAIT_ALL);
+ free(fsck_path);
+ mnt_unref_cache(mntcache);
+ mnt_unref_table(fstab);
+ mnt_unref_table(mtab);
+ return status;
+}
diff --git a/disk-utils/fsck.cramfs.8 b/disk-utils/fsck.cramfs.8
new file mode 100644
index 0000000..90d7942
--- /dev/null
+++ b/disk-utils/fsck.cramfs.8
@@ -0,0 +1,61 @@
+.TH FSCK.CRAMFS 8 "April 2013" "util-linux" "System Administration"
+.SH NAME
+fsck.cramfs \- fsck compressed ROM file system
+.SH SYNOPSIS
+.B fsck.cramfs
+[options]
+.I file
+.SH DESCRIPTION
+.I fsck.cramfs
+is used to check the cramfs file system.
+.SH OPTIONS
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Enable verbose messaging.
+.TP
+\fB\-b\fR, \fB\-\-blocksize\fR \fIblocksize\fR
+Use this blocksize, defaults to page size. Must be equal to what was set at
+creation time. Only used for \-\-extract.
+.TP
+\fB\-\-extract\fR[=\fIdirectory\fR]
+Test to uncompress the whole file system. Optionally extract contents of the
+.I file
+to
+.IR directory .
+.TP
+\fB\-a\fR
+This option is silently ignored.
+.TP
+\fB\-y\fR
+This option is silently ignored.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help text and exit.
+.SH EXIT STATUS
+.RS
+.PD 0
+.TP
+.B 0
+success
+.TP
+.B 4
+file system was left uncorrected
+.TP
+.B 8
+operation error, such as unable to allocate memory
+.TP
+.B 16
+usage information was printed
+.PD
+.RE
+.SH SEE ALSO
+.BR mount (8),
+.BR mkfs.cramfs (8)
+.SH AVAILABILITY
+The fsck.cramfs command is part of the util-linux package and is available from
+.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/disk-utils/fsck.cramfs.c b/disk-utils/fsck.cramfs.c
new file mode 100644
index 0000000..5145bcf
--- /dev/null
+++ b/disk-utils/fsck.cramfs.c
@@ -0,0 +1,715 @@
+/*
+ * cramfsck - check a cramfs file system
+ *
+ * Copyright (C) 2000-2002 Transmeta Corporation
+ * 2005 Adrian Bunk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * 1999/12/03: Linus Torvalds (cramfs tester and unarchive program)
+ * 2000/06/03: Daniel Quinlan (CRC and length checking program)
+ * 2000/06/04: Daniel Quinlan (merged programs, added options, support
+ * for special files, preserve permissions and
+ * ownership, cramfs superblock v2, bogus mode
+ * test, pathname length test, etc.)
+ * 2000/06/06: Daniel Quinlan (support for holes, pretty-printing,
+ * symlink size test)
+ * 2000/07/11: Daniel Quinlan (file length tests, start at offset 0 or 512,
+ * fsck-compatible exit codes)
+ * 2000/07/15: Daniel Quinlan (initial support for block devices)
+ * 2002/01/10: Daniel Quinlan (additional checks, test more return codes,
+ * use read if mmap fails, standardize messages)
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+#include <fcntl.h>
+
+/* We don't use our include/crc32.h, but crc32 from zlib!
+ *
+ * The zlib implementation performs pre/post-conditioning. The util-linux
+ * imlemenation requires post-conditioning (xor) in the applications.
+ */
+#include <zlib.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "c.h"
+#include "cramfs.h"
+#include "nls.h"
+#include "blkdev.h"
+#include "exitcodes.h"
+#include "strutils.h"
+#include "closestream.h"
+
+#define XALLOC_EXIT_CODE FSCK_EX_ERROR
+#include "xalloc.h"
+
+static int fd; /* ROM image file descriptor */
+static char *filename; /* ROM image filename */
+static struct cramfs_super super; /* just find the cramfs superblock once */
+static int cramfs_is_big_endian = 0; /* source is big endian */
+static int opt_verbose = 0; /* 1 = verbose (-v), 2+ = very verbose (-vv) */
+static int opt_extract = 0; /* extract cramfs (-x) */
+static char *extract_dir = ""; /* optional extraction directory (-x) */
+
+#define PAD_SIZE 512
+
+static uid_t euid; /* effective UID */
+
+/* (cramfs_super + start) <= start_dir < end_dir <= start_data <= end_data */
+static unsigned long start_dir = ~0UL; /* start of first non-root inode */
+static unsigned long end_dir = 0; /* end of the directory structure */
+static unsigned long start_data = ~0UL; /* start of the data (256 MB = max) */
+static unsigned long end_data = 0; /* end of the data */
+
+
+/* Guarantee access to at least 8kB at a time */
+#define ROMBUFFER_BITS 13
+#define ROMBUFFERSIZE (1 << ROMBUFFER_BITS)
+#define ROMBUFFERMASK (ROMBUFFERSIZE - 1)
+static char read_buffer[ROMBUFFERSIZE * 2];
+static unsigned long read_buffer_block = ~0UL;
+
+static z_stream stream;
+
+/* Prototypes */
+static void expand_fs(char *, struct cramfs_inode *);
+
+static char *outbuffer;
+
+static size_t blksize = 0;
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %s [options] <file>\n"), program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Check and repair a compressed ROM filesystem.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -a for compatibility only, ignored\n"), out);
+ fputs(_(" -v, --verbose be more verbose\n"), out);
+ fputs(_(" -y for compatibility only, ignored\n"), out);
+ fputs(_(" -b, --blocksize <size> use this blocksize, defaults to page size\n"), out);
+ fputs(_(" --extract[=<dir>] test uncompression, optionally extract into <dir>\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ printf(USAGE_HELP_OPTIONS(26));
+
+ printf(USAGE_MAN_TAIL("fsck.cramfs(8)"));
+ exit(FSCK_EX_OK);
+}
+
+static int get_superblock_endianness(uint32_t magic)
+{
+ if (magic == CRAMFS_MAGIC) {
+ cramfs_is_big_endian = HOST_IS_BIG_ENDIAN;
+ return 0;
+ }
+
+ if (magic ==
+ u32_toggle_endianness(!HOST_IS_BIG_ENDIAN, CRAMFS_MAGIC)) {
+ cramfs_is_big_endian = !HOST_IS_BIG_ENDIAN;
+ return 0;
+ }
+
+ return -1;
+}
+
+static void test_super(int *start, size_t * length)
+{
+ struct stat st;
+
+ /* find the physical size of the file or block device */
+ if (stat(filename, &st) < 0)
+ err(FSCK_EX_ERROR, _("stat of %s failed"), filename);
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ err(FSCK_EX_ERROR, _("cannot open %s"), filename);
+
+ if (S_ISBLK(st.st_mode)) {
+ unsigned long long bytes;
+ if (blkdev_get_size(fd, &bytes))
+ err(FSCK_EX_ERROR,
+ _("ioctl failed: unable to determine device size: %s"),
+ filename);
+ *length = bytes;
+ } else if (S_ISREG(st.st_mode))
+ *length = st.st_size;
+ else
+ errx(FSCK_EX_ERROR, _("not a block device or file: %s"), filename);
+
+ if (*length < sizeof(struct cramfs_super))
+ errx(FSCK_EX_UNCORRECTED, _("file length too short"));
+
+ /* find superblock */
+ if (read(fd, &super, sizeof(super)) != sizeof(super))
+ err(FSCK_EX_ERROR, _("cannot read %s"), filename);
+ if (get_superblock_endianness(super.magic) != -1)
+ *start = 0;
+ else if (*length >= (PAD_SIZE + sizeof(super))) {
+ if (lseek(fd, PAD_SIZE, SEEK_SET) == (off_t) -1)
+ err(FSCK_EX_ERROR, _("seek on %s failed"), filename);
+ if (read(fd, &super, sizeof(super)) != sizeof(super))
+ err(FSCK_EX_ERROR, _("cannot read %s"), filename);
+ if (get_superblock_endianness(super.magic) != -1)
+ *start = PAD_SIZE;
+ else
+ errx(FSCK_EX_UNCORRECTED, _("superblock magic not found"));
+ } else
+ errx(FSCK_EX_UNCORRECTED, _("superblock magic not found"));
+
+ if (opt_verbose)
+ printf(_("cramfs endianness is %s\n"),
+ cramfs_is_big_endian ? _("big") : _("little"));
+
+ super_toggle_endianness(cramfs_is_big_endian, &super);
+ if (super.flags & ~CRAMFS_SUPPORTED_FLAGS)
+ errx(FSCK_EX_ERROR, _("unsupported filesystem features"));
+
+ /* What are valid superblock sizes? */
+ if (super.size < *start + sizeof(struct cramfs_super))
+ errx(FSCK_EX_UNCORRECTED, _("superblock size (%d) too small"),
+ super.size);
+
+ if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {
+ if (super.fsid.files == 0)
+ errx(FSCK_EX_UNCORRECTED, _("zero file count"));
+ if (*length < super.size)
+ errx(FSCK_EX_UNCORRECTED, _("file length too short"));
+ else if (*length > super.size)
+ warnx(_("file extends past end of filesystem"));
+ } else
+ warnx(_("old cramfs format"));
+}
+
+static void test_crc(int start)
+{
+ void *buf;
+ uint32_t crc;
+
+ if (!(super.flags & CRAMFS_FLAG_FSID_VERSION_2)) {
+ warnx(_("unable to test CRC: old cramfs format"));
+ return;
+ }
+
+ crc = crc32(0L, NULL, 0);
+
+ buf =
+ mmap(NULL, super.size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (buf == MAP_FAILED) {
+ buf =
+ mmap(NULL, super.size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (buf != MAP_FAILED) {
+ ssize_t tmp;
+ if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+ err(FSCK_EX_ERROR, _("seek on %s failed"), filename);
+ tmp = read(fd, buf, super.size);
+ if (tmp < 0)
+ err(FSCK_EX_ERROR, _("cannot read %s"), filename);
+ if (tmp != (ssize_t) super.size)
+ errx(FSCK_EX_ERROR, _("failed to read %"PRIu32" bytes from file %s"),
+ super.size, filename);
+ }
+ }
+ if (buf != MAP_FAILED) {
+ ((struct cramfs_super *)((unsigned char *) buf + start))->fsid.crc =
+ crc32(0L, NULL, 0);
+ crc = crc32(crc, (unsigned char *) buf + start, super.size - start);
+ munmap(buf, super.size);
+ } else {
+ int retval;
+ size_t length = 0;
+
+ buf = xmalloc(4096);
+ if (lseek(fd, start, SEEK_SET) == (off_t) -1)
+ err(FSCK_EX_ERROR, _("seek on %s failed"), filename);
+ for (;;) {
+ retval = read(fd, buf, 4096);
+ if (retval < 0)
+ err(FSCK_EX_ERROR, _("cannot read %s"), filename);
+ else if (retval == 0)
+ break;
+ if (length == 0)
+ ((struct cramfs_super *)buf)->fsid.crc =
+ crc32(0L, NULL, 0);
+ length += retval;
+ if (length > (super.size - start)) {
+ crc = crc32(crc, buf,
+ retval - (length -
+ (super.size - start)));
+ break;
+ }
+ crc = crc32(crc, buf, retval);
+ }
+ free(buf);
+ }
+
+ if (crc != super.fsid.crc)
+ errx(FSCK_EX_UNCORRECTED, _("crc error"));
+}
+
+static void print_node(char type, struct cramfs_inode *i, char *name)
+{
+ char info[10];
+
+ if (S_ISCHR(i->mode) || (S_ISBLK(i->mode)))
+ /* major/minor numbers can be as high as 2^12 or 4096 */
+ snprintf(info, 10, "%4d,%4d", major(i->size), minor(i->size));
+ else
+ /* size be as high as 2^24 or 16777216 */
+ snprintf(info, 10, "%9d", i->size);
+
+ printf("%c %04o %s %5d:%-3d %s\n",
+ type, i->mode & ~S_IFMT, info, i->uid, i->gid,
+ !*name && type == 'd' ? "/" : name);
+}
+
+/*
+ * Create a fake "blocked" access
+ */
+static void *romfs_read(unsigned long offset)
+{
+ unsigned int block = offset >> ROMBUFFER_BITS;
+ if (block != read_buffer_block) {
+ ssize_t x;
+
+ read_buffer_block = block;
+ if (lseek(fd, block << ROMBUFFER_BITS, SEEK_SET) == (off_t) -1)
+ warn(_("seek failed"));
+
+ x = read(fd, read_buffer, ROMBUFFERSIZE * 2);
+ if (x < 0)
+ warn(_("read romfs failed"));
+ }
+ return read_buffer + (offset & ROMBUFFERMASK);
+}
+
+static struct cramfs_inode *cramfs_iget(struct cramfs_inode *i)
+{
+ struct cramfs_inode *inode = xmalloc(sizeof(struct cramfs_inode));
+
+ inode_to_host(cramfs_is_big_endian, i, inode);
+ return inode;
+}
+
+static struct cramfs_inode *iget(unsigned int ino)
+{
+ return cramfs_iget(romfs_read(ino));
+}
+
+static void iput(struct cramfs_inode *inode)
+{
+ free(inode);
+}
+
+/*
+ * Return the offset of the root directory
+ */
+static struct cramfs_inode *read_super(void)
+{
+ struct cramfs_inode *root = cramfs_iget(&super.root);
+ unsigned long offset = root->offset << 2;
+
+ if (!S_ISDIR(root->mode))
+ errx(FSCK_EX_UNCORRECTED, _("root inode is not directory"));
+ if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
+ ((offset != sizeof(struct cramfs_super)) &&
+ (offset != PAD_SIZE + sizeof(struct cramfs_super)))) {
+ errx(FSCK_EX_UNCORRECTED, _("bad root offset (%lu)"), offset);
+ }
+ return root;
+}
+
+static int uncompress_block(void *src, size_t len)
+{
+ int err;
+
+ stream.next_in = src;
+ stream.avail_in = len;
+
+ stream.next_out = (unsigned char *)outbuffer;
+ stream.avail_out = blksize * 2;
+
+ inflateReset(&stream);
+
+ if (len > blksize * 2)
+ errx(FSCK_EX_UNCORRECTED, _("data block too large"));
+
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END)
+ errx(FSCK_EX_UNCORRECTED, _("decompression error: %s"),
+ zError(err));
+ return stream.total_out;
+}
+
+#ifndef HAVE_LCHOWN
+#define lchown chown
+#endif
+
+static void do_uncompress(char *path, int outfd, unsigned long offset,
+ unsigned long size)
+{
+ unsigned long curr = offset + 4 * ((size + blksize - 1) / blksize);
+
+ do {
+ unsigned long out = blksize;
+ unsigned long next = u32_toggle_endianness(cramfs_is_big_endian,
+ *(uint32_t *)
+ romfs_read(offset));
+
+ if (next > end_data)
+ end_data = next;
+
+ offset += 4;
+ if (curr == next) {
+ if (opt_verbose > 1)
+ printf(_(" hole at %lu (%zu)\n"), curr,
+ blksize);
+ if (size < blksize)
+ out = size;
+ memset(outbuffer, 0x00, out);
+ } else {
+ if (opt_verbose > 1)
+ printf(_(" uncompressing block at %lu to %lu (%lu)\n"),
+ curr, next, next - curr);
+ out = uncompress_block(romfs_read(curr), next - curr);
+ }
+ if (size >= blksize) {
+ if (out != blksize)
+ errx(FSCK_EX_UNCORRECTED,
+ _("non-block (%ld) bytes"), out);
+ } else {
+ if (out != size)
+ errx(FSCK_EX_UNCORRECTED,
+ _("non-size (%ld vs %ld) bytes"), out,
+ size);
+ }
+ size -= out;
+ if (*extract_dir != '\0' && write(outfd, outbuffer, out) < 0)
+ err(FSCK_EX_ERROR, _("write failed: %s"), path);
+ curr = next;
+ } while (size);
+}
+static void change_file_status(char *path, struct cramfs_inode *i)
+{
+ const struct timeval epoch[] = { {0,0}, {0,0} };
+
+ if (euid == 0) {
+ if (lchown(path, i->uid, i->gid) < 0)
+ err(FSCK_EX_ERROR, _("lchown failed: %s"), path);
+ if (S_ISLNK(i->mode))
+ return;
+ if (((S_ISUID | S_ISGID) & i->mode) && chmod(path, i->mode) < 0)
+ err(FSCK_EX_ERROR, _("chown failed: %s"), path);
+ }
+ if (S_ISLNK(i->mode))
+ return;
+ if (utimes(path, epoch) < 0)
+ err(FSCK_EX_ERROR, _("utimes failed: %s"), path);
+}
+
+static void do_directory(char *path, struct cramfs_inode *i)
+{
+ int pathlen = strlen(path);
+ int count = i->size;
+ unsigned long offset = i->offset << 2;
+ char *newpath = xmalloc(pathlen + 256);
+
+ if (offset == 0 && count != 0)
+ errx(FSCK_EX_UNCORRECTED,
+ _("directory inode has zero offset and non-zero size: %s"),
+ path);
+
+ if (offset != 0 && offset < start_dir)
+ start_dir = offset;
+
+ /* TODO: Do we need to check end_dir for empty case? */
+ memcpy(newpath, path, pathlen);
+ newpath[pathlen] = '/';
+ pathlen++;
+ if (opt_verbose)
+ print_node('d', i, path);
+
+ if (*extract_dir != '\0') {
+ if (mkdir(path, i->mode) < 0)
+ err(FSCK_EX_ERROR, _("mkdir failed: %s"), path);
+ change_file_status(path, i);
+ }
+ while (count > 0) {
+ struct cramfs_inode *child = iget(offset);
+ int size;
+ int newlen = child->namelen << 2;
+
+ size = sizeof(struct cramfs_inode) + newlen;
+ count -= size;
+
+ offset += sizeof(struct cramfs_inode);
+
+ memcpy(newpath + pathlen, romfs_read(offset), newlen);
+ newpath[pathlen + newlen] = 0;
+ if (newlen == 0)
+ errx(FSCK_EX_UNCORRECTED, _("filename length is zero"));
+ if ((pathlen + newlen) - strlen(newpath) > 3)
+ errx(FSCK_EX_UNCORRECTED, _("bad filename length"));
+ expand_fs(newpath, child);
+
+ offset += newlen;
+
+ if (offset <= start_dir)
+ errx(FSCK_EX_UNCORRECTED, _("bad inode offset"));
+ if (offset > end_dir)
+ end_dir = offset;
+ iput(child); /* free(child) */
+ }
+ free(newpath);
+}
+
+static void do_file(char *path, struct cramfs_inode *i)
+{
+ unsigned long offset = i->offset << 2;
+ int outfd = 0;
+
+ if (offset == 0 && i->size != 0)
+ errx(FSCK_EX_UNCORRECTED,
+ _("file inode has zero offset and non-zero size"));
+ if (i->size == 0 && offset != 0)
+ errx(FSCK_EX_UNCORRECTED,
+ _("file inode has zero size and non-zero offset"));
+ if (offset != 0 && offset < start_data)
+ start_data = offset;
+ if (opt_verbose)
+ print_node('f', i, path);
+ if (*extract_dir != '\0') {
+ outfd = open(path, O_WRONLY | O_CREAT | O_TRUNC, i->mode);
+ if (outfd < 0)
+ err(FSCK_EX_ERROR, _("cannot open %s"), path);
+ }
+ if (i->size)
+ do_uncompress(path, outfd, offset, i->size);
+ if ( *extract_dir != '\0') {
+ if (close_fd(outfd) != 0)
+ err(FSCK_EX_ERROR, _("write failed: %s"), path);
+ change_file_status(path, i);
+ }
+}
+
+static void do_symlink(char *path, struct cramfs_inode *i)
+{
+ unsigned long offset = i->offset << 2;
+ unsigned long curr = offset + 4;
+ unsigned long next =
+ u32_toggle_endianness(cramfs_is_big_endian,
+ *(uint32_t *) romfs_read(offset));
+ unsigned long size;
+
+ if (offset == 0)
+ errx(FSCK_EX_UNCORRECTED, _("symbolic link has zero offset"));
+ if (i->size == 0)
+ errx(FSCK_EX_UNCORRECTED, _("symbolic link has zero size"));
+
+ if (offset < start_data)
+ start_data = offset;
+ if (next > end_data)
+ end_data = next;
+
+ size = uncompress_block(romfs_read(curr), next - curr);
+ if (size != i->size)
+ errx(FSCK_EX_UNCORRECTED, _("size error in symlink: %s"), path);
+ outbuffer[size] = 0;
+ if (opt_verbose) {
+ char *str;
+
+ xasprintf(&str, "%s -> %s", path, outbuffer);
+ print_node('l', i, str);
+ if (opt_verbose > 1)
+ printf(_(" uncompressing block at %lu to %lu (%lu)\n"),
+ curr, next, next - curr);
+ free(str);
+ }
+ if (*extract_dir != '\0') {
+ if (symlink(outbuffer, path) < 0)
+ err(FSCK_EX_ERROR, _("symlink failed: %s"), path);
+ change_file_status(path, i);
+ }
+}
+
+static void do_special_inode(char *path, struct cramfs_inode *i)
+{
+ dev_t devtype = 0;
+ char type;
+
+ if (i->offset)
+ /* no need to shift offset */
+ errx(FSCK_EX_UNCORRECTED,
+ _("special file has non-zero offset: %s"), path);
+
+ if (S_ISCHR(i->mode)) {
+ devtype = i->size;
+ type = 'c';
+ } else if (S_ISBLK(i->mode)) {
+ devtype = i->size;
+ type = 'b';
+ } else if (S_ISFIFO(i->mode)) {
+ if (i->size != 0)
+ errx(FSCK_EX_UNCORRECTED, _("fifo has non-zero size: %s"),
+ path);
+ type = 'p';
+ } else if (S_ISSOCK(i->mode)) {
+ if (i->size != 0)
+ errx(FSCK_EX_UNCORRECTED,
+ _("socket has non-zero size: %s"), path);
+ type = 's';
+ } else {
+ errx(FSCK_EX_UNCORRECTED, _("bogus mode: %s (%o)"), path, i->mode);
+ return; /* not reached */
+ }
+
+ if (opt_verbose)
+ print_node(type, i, path);
+
+ if (*extract_dir != '\0') {
+ if (mknod(path, i->mode, devtype) < 0)
+ err(FSCK_EX_ERROR, _("mknod failed: %s"), path);
+ change_file_status(path, i);
+ }
+}
+
+static void expand_fs(char *path, struct cramfs_inode *inode)
+{
+ if (S_ISDIR(inode->mode))
+ do_directory(path, inode);
+ else if (S_ISREG(inode->mode))
+ do_file(path, inode);
+ else if (S_ISLNK(inode->mode))
+ do_symlink(path, inode);
+ else
+ do_special_inode(path, inode);
+}
+
+static void test_fs(int start)
+{
+ struct cramfs_inode *root;
+
+ root = read_super();
+ umask(0);
+ euid = geteuid();
+ stream.next_in = NULL;
+ stream.avail_in = 0;
+ inflateInit(&stream);
+ expand_fs(extract_dir, root);
+ inflateEnd(&stream);
+ if (start_data != ~0UL) {
+ if (start_data < (sizeof(struct cramfs_super) + start))
+ errx(FSCK_EX_UNCORRECTED,
+ _("directory data start (%lu) < sizeof(struct cramfs_super) + start (%zu)"),
+ start_data, sizeof(struct cramfs_super) + start);
+ if (end_dir != start_data)
+ errx(FSCK_EX_UNCORRECTED,
+ _("directory data end (%lu) != file data start (%lu)"),
+ end_dir, start_data);
+ }
+ if (super.flags & CRAMFS_FLAG_FSID_VERSION_2 && end_data > super.size)
+ errx(FSCK_EX_UNCORRECTED, _("invalid file data offset"));
+
+ iput(root); /* free(root) */
+}
+
+int main(int argc, char **argv)
+{
+ int c; /* for getopt */
+ int start = 0;
+ size_t length = 0;
+
+ static const struct option longopts[] = {
+ {"verbose", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {"blocksize", required_argument, NULL, 'b'},
+ {"extract", optional_argument, NULL, 'x'},
+ {NULL, 0, NULL, 0},
+ };
+
+ setlocale(LC_MESSAGES, "");
+ setlocale(LC_CTYPE, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ strutils_set_exitcode(FSCK_EX_USAGE);
+
+ /* command line options */
+ while ((c = getopt_long(argc, argv, "ayvVhb:", longopts, NULL)) != EOF)
+ switch (c) {
+ case 'a': /* ignore */
+ case 'y':
+ break;
+ case 'h':
+ usage();
+ break;
+ case 'V':
+ print_version(FSCK_EX_OK);
+ case 'x':
+ opt_extract = 1;
+ if(optarg)
+ extract_dir = optarg;
+ break;
+ case 'v':
+ opt_verbose++;
+ break;
+ case 'b':
+ blksize = strtou32_or_err(optarg, _("invalid blocksize argument"));
+ break;
+ default:
+ errtryhelp(FSCK_EX_USAGE);
+ }
+
+ if ((argc - optind) != 1){
+ warnx(_("bad usage"));
+ errtryhelp(FSCK_EX_USAGE);
+ }
+ filename = argv[optind];
+
+ test_super(&start, &length);
+ test_crc(start);
+
+ if(opt_extract) {
+ if (blksize == 0)
+ blksize = getpagesize();
+ outbuffer = xmalloc(blksize * 2);
+ test_fs(start);
+ }
+
+ if (opt_verbose)
+ printf(_("%s: OK\n"), filename);
+
+ exit(FSCK_EX_OK);
+}
diff --git a/disk-utils/fsck.minix.8 b/disk-utils/fsck.minix.8
new file mode 100644
index 0000000..ca05089
--- /dev/null
+++ b/disk-utils/fsck.minix.8
@@ -0,0 +1,167 @@
+.\" Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be freely distributed.
+.TH FSCK.MINIX 8 "June 2015" "util-linux" "System Administration"
+.SH NAME
+fsck.minix \- check consistency of Minix filesystem
+.SH SYNOPSIS
+.B fsck.minix
+[options]
+.I device
+.SH DESCRIPTION
+.B fsck.minix
+performs a consistency check for the Linux MINIX filesystem.
+.PP
+The program assumes the filesystem is quiescent.
+.B fsck.minix
+should not be used on a mounted device unless you can be sure nobody is
+writing to it. Remember that the kernel can write to device when it
+searches for files.
+.PP
+The \fIdevice\fR name will usually have the following form:
+.RS
+.TS
+tab(:);
+l l.
+/dev/hda[1\(en63]:IDE disk 1
+/dev/hdb[1\(en63]:IDE disk 2
+/dev/sda[1\(en15]:SCSI disk 1
+/dev/sdb[1\(en15]:SCSI disk 2
+.TE
+.RE
+.PP
+If the filesystem was changed, i.e., repaired, then
+.B fsck.minix
+will print "FILE SYSTEM HAS CHANGED" and will
+.BR sync (2)
+three times before exiting. There is
+.I no
+need to reboot after check.
+.SH WARNING
+.B fsck.minix
+should
+.B not
+be used on a mounted filesystem. Using
+.B fsck.minix
+on a mounted filesystem is very dangerous, due to the possibility that
+deleted files are still in use, and can seriously damage a perfectly good
+filesystem! If you absolutely have to run
+.B fsck.minix
+on a mounted filesystem, such as the root filesystem, make sure nothing
+is writing to the disk, and that no files are "zombies" waiting for
+deletion.
+.SH OPTIONS
+.TP
+\fB\-l\fR, \fB\-\-list\fR
+List all filenames.
+.TP
+\fB\-r\fR, \fB\-\-repair\fR
+Perform interactive repairs.
+.TP
+\fB\-a\fR, \fB\-\-auto\fR
+Perform automatic repairs. This option implies
+.B \-\-repair
+and serves to answer all of the questions asked with the default. Note
+that this can be extremely dangerous in the case of extensive filesystem
+damage.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Be verbose.
+.TP
+\fB\-s\fR, \fB\-\-super\fR
+Output super-block information.
+.TP
+\fB\-m\fR, \fB\-\-uncleared\fR
+Activate MINIX-like "mode not cleared" warnings.
+.TP
+\fB\-f\fR, \fB\-\-force\fR
+Force a filesystem check even if the filesystem was marked as valid.
+Marking is done by the kernel when the filesystem is unmounted.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help text and exit.
+.SH DIAGNOSTICS
+There are numerous diagnostic messages. The ones mentioned here are the
+most commonly seen in normal usage.
+.PP
+If the device does not exist,
+.B fsck.minix
+will print "unable to read super block". If the device exists, but is not
+a MINIX filesystem,
+.B fsck.minix
+will print "bad magic number in super-block".
+.SH EXIT STATUS
+The exit status returned by
+.B fsck.minix
+is the sum of the following:
+.PP
+.RS
+.PD 0
+.TP
+.B 0
+No errors
+.TP
+.B 3
+Filesystem errors corrected, system should be rebooted if filesystem was
+mounted
+.TP
+.B 4
+Filesystem errors left uncorrected
+.TP
+.B 7
+Combination of exit statuses 3 and 4
+.TP
+.B 8
+Operational error
+.TP
+.B 16
+Usage or syntax error
+.PD
+.RE
+.SH AUTHORS
+.MT torvalds@\:cs.\:helsinki.\:fi
+Linus Torvalds
+.ME
+.br
+Exit status values by
+.MT faith@\:cs.\:unc.\:edu
+Rik Faith
+.ME
+.br
+Added support for filesystem valid flag:
+.MT greg%\:wind.\:uucp@\:plains.\:nodak.\:edu
+Dr.\& Wettstein
+.ME .
+.br
+Check to prevent fsck of mounted filesystem added by
+.MT quinlan@\:yggdrasil.\:com
+Daniel Quinlan
+.ME .
+.br
+Minix v2 fs support by
+.MT schwab@\:issan.\:informatik.\:uni-dortmund.\:de
+Andreas Schwab
+.ME ,
+updated by
+.MT janl@\:math.\:uio.\:no
+Nicolai Langfeldt
+.ME .
+.br
+Portability patch by
+.MT rmk@\:ecs.\:soton.\:ac.\:uk
+Russell King
+.ME .
+.SH SEE ALSO
+.BR fsck (8),
+.BR fsck.ext2 (8),
+.BR mkfs (8),
+.BR mkfs.ext2 (8),
+.BR mkfs.minix (8),
+.BR reboot (8)
+.SH AVAILABILITY
+The fsck.minix command is part of the util-linux package and is available from
+.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/disk-utils/fsck.minix.c b/disk-utils/fsck.minix.c
new file mode 100644
index 0000000..bd44f5b
--- /dev/null
+++ b/disk-utils/fsck.minix.c
@@ -0,0 +1,1435 @@
+/*
+ * fsck.minix.c - a file system consistency checker for Linux.
+ *
+ * (C) 1991, 1992 Linus Torvalds. This file may be redistributed
+ * as per the GNU copyleft.
+ */
+
+/*
+ * 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 systemindependent.
+ * (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@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * 2008-04-06 James Youngman <jay@gnu.org>
+ * - Issue better error message if we fail to open the device.
+ * - Restore terminal state if we get a fatal signal.
+ *
+ *
+ * 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).
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <getopt.h>
+
+#include "c.h"
+#include "exitcodes.h"
+#include "minix_programs.h"
+#include "nls.h"
+#include "pathnames.h"
+#include "bitops.h"
+#include "ismounted.h"
+#include "all-io.h"
+#include "closestream.h"
+#include "rpmatch.h"
+#include "strutils.h"
+
+#define ROOT_INO 1
+#define YESNO_LENGTH 64
+
+/* Global variables used in minix_programs.h inline functions */
+int fs_version = 1;
+char *super_block_buffer;
+
+static char *inode_buffer;
+
+#define Inode (((struct minix_inode *) inode_buffer) - 1)
+#define Inode2 (((struct minix2_inode *) inode_buffer) - 1)
+
+static char *device_name;
+static int device_fd;
+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 size_t dirsize = 16;
+static size_t namelen = 14;
+static struct termios termios;
+static volatile sig_atomic_t termios_set;
+
+/* File-name data */
+#define MAX_DEPTH 50
+static int name_depth;
+static char name_list[MAX_DEPTH][MINIX_NAME_MAX + 1];
+
+/* Copy of the previous, just for error reporting - see get_current_name. This
+ * is a waste of 12kB or so. */
+static char current_name[MAX_DEPTH * (MINIX_NAME_MAX + 1) + 1];
+
+static unsigned char *inode_count = NULL;
+static unsigned char *zone_count = NULL;
+
+static void recursive_check(unsigned int ino);
+static void recursive_check2(unsigned int ino);
+
+static char *inode_map;
+static char *zone_map;
+
+#define inode_in_use(x) (isset(inode_map,(x)) != 0)
+#define zone_in_use(x) (isset(zone_map,(x)-get_first_zone()+1) != 0)
+
+#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)-get_first_zone()+1),changed=1)
+#define unmark_zone(x) (clrbit(zone_map,(x)-get_first_zone()+1),changed=1)
+
+static void
+reset(void) {
+ if (termios_set)
+ tcsetattr(STDIN_FILENO, TCSANOW, &termios);
+}
+
+static void
+fatalsig(int sig) {
+ /* We received a fatal signal. Reset the terminal. Also reset the
+ * signal handler and re-send the signal, so that the parent process
+ * knows which signal actually caused our death. */
+ signal(sig, SIG_DFL);
+ reset();
+ raise(sig);
+}
+
+static void __attribute__((__noreturn__))
+leave(int status) {
+ reset();
+ exit(status);
+}
+
+static void __attribute__((__noreturn__))
+usage(void) {
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s [options] <device>\n"), program_invocation_short_name);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Check the consistency of a Minix filesystem.\n"), out);
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -l, --list list all filenames\n"), out);
+ fputs(_(" -a, --auto automatic repair\n"), out);
+ fputs(_(" -r, --repair interactive repair\n"), out);
+ fputs(_(" -v, --verbose be verbose\n"), out);
+ fputs(_(" -s, --super output super-block information\n"), out);
+ fputs(_(" -m, --uncleared activate mode not cleared warnings\n"), out);
+ fputs(_(" -f, --force force check\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ printf(USAGE_HELP_OPTIONS(18));
+ printf(USAGE_MAN_TAIL("fsck.minix(8)"));
+ exit(FSCK_EX_OK);
+}
+
+static void die(const char *fmt, ...)
+ __attribute__ ((__format__(__printf__, 1, 2)));
+
+static void
+die(const char *fmt, ...) {
+ va_list ap;
+
+ fprintf(stderr, UTIL_LINUX_VERSION);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+ leave(FSCK_EX_ERROR);
+}
+
+/* This simply goes through the file-name data and prints out the current file. */
+static void
+get_current_name(void) {
+ int i = 0, ct;
+ char *p, *q;
+
+ q = current_name;
+ while (i < name_depth) {
+ p = name_list[i++];
+ ct = namelen;
+ *q++ = '/';
+ while (ct-- && *p)
+ *q++ = *p++;
+ }
+ if (i == 0)
+ *q++ = '/';
+ *q = 0;
+}
+
+static int
+ask(const char *string, int def) {
+ int resp;
+ char input[YESNO_LENGTH];
+
+ if (!repair) {
+ printf("\n");
+ errors_uncorrected = 1;
+ return 0;
+ }
+ if (automatic) {
+ printf("\n");
+ if (!def)
+ errors_uncorrected = 1;
+ return def;
+ }
+ /* TRANSLATORS: these yes no questions uses rpmatch(), and should be
+ * translated. */
+ printf(def ? _("%s (y/n)? ") : _("%s (n/y)? "), string);
+ fflush(stdout);
+ ignore_result( fgets(input, YESNO_LENGTH, stdin) );
+ resp = rpmatch(input);
+ switch (resp) {
+ case RPMATCH_INVALID:
+ /* def = def */
+ break;
+ case RPMATCH_NO:
+ case RPMATCH_YES:
+ def = resp;
+ break;
+ default:
+ /* rpmatch bug? */
+ abort();
+ }
+ 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) {
+ int cont;
+
+ if (!is_mounted(device_name))
+ return;
+
+ printf(_("%s is mounted. "), device_name);
+ if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
+ cont = ask(_("Do you really want to continue"), 0);
+ else
+ cont = 0;
+ if (!cont) {
+ printf(_("check aborted.\n"));
+ exit(FSCK_EX_OK);
+ }
+}
+
+
+static int is_valid_zone_nr(unsigned short nr)
+{
+ if (nr < get_first_zone())
+ return 0;
+ if (nr >= get_nzones())
+ return 0;
+ return 1;
+}
+
+/* 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_nr(unsigned short *nr, int *corrected) {
+ if (!*nr)
+ return 0;
+
+ if (*nr < get_first_zone()) {
+ get_current_name();
+ printf(_("Zone nr < FIRSTZONE in file `%s'."), current_name);
+ } else if (*nr >= get_nzones()) {
+ get_current_name();
+ printf(_("Zone nr >= ZONES in file `%s'."), current_name);
+ } else
+ return *nr;
+
+ if (ask(_("Remove block"), 1)) {
+ *nr = 0;
+ *corrected = 1;
+ }
+ return 0;
+}
+
+static int
+check_zone_nr2(unsigned int *nr, int *corrected) {
+ if (!*nr)
+ return 0;
+
+ if (*nr < get_first_zone()) {
+ get_current_name();
+ printf(_("Zone nr < FIRSTZONE in file `%s'."), current_name);
+ } else if (*nr >= get_nzones()) {
+ get_current_name();
+ printf(_("Zone nr >= ZONES in file `%s'."), current_name);
+ } else
+ return *nr;
+
+ if (ask(_("Remove block"), 1)) {
+ *nr = 0;
+ *corrected = 1;
+ }
+ return 0;
+}
+
+/* read-block reads block nr into the buffer at addr. */
+static void
+read_block(unsigned int nr, char *addr) {
+ if (!nr) {
+ memset(addr, 0, MINIX_BLOCK_SIZE);
+ return;
+ }
+ if (MINIX_BLOCK_SIZE * nr != lseek(device_fd, MINIX_BLOCK_SIZE * nr, SEEK_SET)) {
+ get_current_name();
+ printf(_("Read error: unable to seek to block in file '%s'\n"),
+ current_name);
+ memset(addr, 0, MINIX_BLOCK_SIZE);
+ errors_uncorrected = 1;
+ } else if (MINIX_BLOCK_SIZE != read(device_fd, addr, MINIX_BLOCK_SIZE)) {
+ get_current_name();
+ printf(_("Read error: bad block in file '%s'\n"), current_name);
+ memset(addr, 0, MINIX_BLOCK_SIZE);
+ errors_uncorrected = 1;
+ }
+}
+
+/* write_block writes block nr to disk. */
+static void
+write_block(unsigned int nr, char *addr) {
+ if (!nr)
+ return;
+ if (nr < get_first_zone() || nr >= get_nzones()) {
+ printf(_("Internal error: trying to write bad block\n"
+ "Write request ignored\n"));
+ errors_uncorrected = 1;
+ return;
+ }
+ if (MINIX_BLOCK_SIZE * nr != lseek(device_fd, MINIX_BLOCK_SIZE * nr, SEEK_SET))
+ die(_("seek failed in write_block"));
+ if (MINIX_BLOCK_SIZE != write(device_fd, addr, MINIX_BLOCK_SIZE)) {
+ get_current_name();
+ printf(_("Write error: bad block in file '%s'\n"),
+ 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) {
+ unsigned short ind[MINIX_BLOCK_SIZE >> 1];
+ unsigned short dind[MINIX_BLOCK_SIZE >> 1];
+ int blk_chg, block, result;
+ size_t range;
+
+ 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;
+ range = blknr / 512;
+ if (ARRAY_SIZE(dind) <= range) {
+ printf(_("Warning: block out of range\n"));
+ return 1;
+ }
+ result = check_zone_nr(dind + range, &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;
+}
+
+static int
+map_block2(struct minix2_inode *inode, unsigned int blknr) {
+ unsigned int ind[MINIX_BLOCK_SIZE >> 2];
+ unsigned int dind[MINIX_BLOCK_SIZE >> 2];
+ unsigned int tind[MINIX_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;
+}
+
+static void
+write_super_block(void) {
+ /* v3 super block does not track state */
+ if (fs_version == 3)
+ return;
+ /* 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 (MINIX_BLOCK_SIZE != lseek(device_fd, MINIX_BLOCK_SIZE, SEEK_SET))
+ die(_("seek failed in write_super_block"));
+ if (MINIX_BLOCK_SIZE != write(device_fd, super_block_buffer, MINIX_BLOCK_SIZE))
+ die(_("unable to write super-block"));
+}
+
+static void
+write_tables(void) {
+ unsigned long buffsz = get_inode_buffer_size();
+ unsigned long imaps = get_nimaps();
+ unsigned long zmaps = get_nzmaps();
+
+ write_super_block();
+
+ if (write_all(device_fd, inode_map, imaps * MINIX_BLOCK_SIZE))
+ die(_("Unable to write inode map"));
+
+ if (write_all(device_fd, zone_map, zmaps * MINIX_BLOCK_SIZE))
+ die(_("Unable to write zone map"));
+
+ if (write_all(device_fd, inode_buffer, buffsz))
+ die(_("Unable to write inodes"));
+}
+
+static void
+get_dirsize(void) {
+ int block;
+ char blk[MINIX_BLOCK_SIZE];
+ size_t size;
+
+ if (fs_version == 2 || fs_version == 3)
+ block = Inode2[ROOT_INO].i_zone[0];
+ else
+ block = Inode[ROOT_INO].i_zone[0];
+ read_block(block, blk);
+
+ for (size = 16; size < MINIX_BLOCK_SIZE; size <<= 1) {
+ if (strcmp(blk + size + 2, "..") == 0) {
+ dirsize = size;
+ namelen = size - 2;
+ return;
+ }
+ }
+ /* use defaults */
+}
+
+static void
+read_superblock(void) {
+ if (MINIX_BLOCK_SIZE != lseek(device_fd, MINIX_BLOCK_SIZE, SEEK_SET))
+ die(_("seek failed"));
+
+ super_block_buffer = calloc(1, MINIX_BLOCK_SIZE);
+ if (!super_block_buffer)
+ die(_("unable to alloc buffer for superblock"));
+
+ if (MINIX_BLOCK_SIZE != read(device_fd, super_block_buffer, MINIX_BLOCK_SIZE))
+ die(_("unable to read super block"));
+ if (Super.s_magic == MINIX_SUPER_MAGIC) {
+ namelen = 14;
+ dirsize = 16;
+ fs_version = 1;
+ } else if (Super.s_magic == MINIX_SUPER_MAGIC2) {
+ namelen = 30;
+ dirsize = 32;
+ fs_version = 1;
+ } else if (Super.s_magic == MINIX2_SUPER_MAGIC) {
+ namelen = 14;
+ dirsize = 16;
+ fs_version = 2;
+ } else if (Super.s_magic == MINIX2_SUPER_MAGIC2) {
+ namelen = 30;
+ dirsize = 32;
+ fs_version = 2;
+ } else if (Super3.s_magic == MINIX3_SUPER_MAGIC) {
+ namelen = 60;
+ dirsize = 64;
+ fs_version = 3;
+ } else
+ die(_("bad magic number in super-block"));
+ if (get_zone_size() != 0 || MINIX_BLOCK_SIZE != 1024)
+ die(_("Only 1k blocks/zones supported"));
+ if (get_ninodes() == 0 || get_ninodes() == UINT32_MAX)
+ die(_("bad s_ninodes field in super-block"));
+ if (get_nimaps() * MINIX_BLOCK_SIZE * 8 < get_ninodes() + 1)
+ die(_("bad s_imap_blocks field in super-block"));
+ if (get_first_zone() > (off_t) get_nzones())
+ die(_("bad s_firstdatazone field in super-block"));
+ if (get_nzmaps() * MINIX_BLOCK_SIZE * 8 <
+ get_nzones() - get_first_zone() + 1)
+ die(_("bad s_zmap_blocks field in super-block"));
+}
+
+static void
+read_tables(void) {
+ unsigned long inodes = get_ninodes();
+ size_t buffsz = get_inode_buffer_size();
+ off_t norm_first_zone = first_zone_data();
+ off_t first_zone = get_first_zone();
+ unsigned long zones = get_nzones();
+ unsigned long imaps = get_nimaps();
+ unsigned long zmaps = get_nzmaps();
+ ssize_t rc;
+
+ inode_map = malloc(imaps * MINIX_BLOCK_SIZE);
+ if (!inode_map)
+ die(_("Unable to allocate buffer for inode map"));
+ zone_map = malloc(zmaps * MINIX_BLOCK_SIZE);
+ if (!zone_map)
+ die(_("Unable to allocate buffer for zone map"));
+ inode_buffer = malloc(buffsz);
+ if (!inode_buffer)
+ die(_("Unable to allocate buffer for inodes"));
+ inode_count = calloc(1, inodes + 1);
+ if (!inode_count)
+ die(_("Unable to allocate buffer for inode count"));
+ zone_count = calloc(1, zones);
+ if (!zone_count)
+ die(_("Unable to allocate buffer for zone count"));
+
+ rc = read(device_fd, inode_map, imaps * MINIX_BLOCK_SIZE);
+ if (rc < 0 || imaps * MINIX_BLOCK_SIZE != (size_t) rc)
+ die(_("Unable to read inode map"));
+
+ rc = read(device_fd, zone_map, zmaps * MINIX_BLOCK_SIZE);
+ if (rc < 0 || zmaps * MINIX_BLOCK_SIZE != (size_t) rc)
+ die(_("Unable to read zone map"));
+
+ rc = read(device_fd, inode_buffer, buffsz);
+ if (rc < 0 || buffsz != (size_t) rc)
+ die(_("Unable to read inodes"));
+ if (norm_first_zone != first_zone) {
+ printf(_("Warning: Firstzone != Norm_firstzone\n"));
+ errors_uncorrected = 1;
+ }
+ get_dirsize();
+ if (show) {
+ printf(_("%ld inodes\n"), inodes);
+ printf(_("%ld blocks\n"), zones);
+ printf(_("Firstdatazone=%jd (%jd)\n"),
+ (intmax_t)first_zone, (intmax_t)norm_first_zone);
+ printf(_("Zonesize=%d\n"), MINIX_BLOCK_SIZE << get_zone_size());
+ printf(_("Maxsize=%zu\n"), get_max_size());
+ if (fs_version < 3)
+ printf(_("Filesystem state=%d\n"), Super.s_state);
+ printf(_("namelen=%zd\n\n"), namelen);
+ }
+}
+
+static struct minix_inode *
+get_inode(unsigned int nr) {
+ struct minix_inode *inode;
+
+ if (!nr || nr > get_ninodes())
+ return NULL;
+ total++;
+ inode = Inode + nr;
+ if (!inode_count[nr]) {
+ if (!inode_in_use(nr)) {
+ get_current_name();
+ printf(_("Inode %d marked unused, "
+ "but used for file '%s'\n"), nr, current_name);
+ if (repair) {
+ if (ask(_("Mark 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 {
+ get_current_name();
+ printf(_("The file `%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;
+}
+
+static struct minix2_inode *
+get_inode2(unsigned int nr) {
+ struct minix2_inode *inode;
+
+ if (!nr || nr > get_ninodes())
+ return NULL;
+ total++;
+ inode = Inode2 + nr;
+ if (!inode_count[nr]) {
+ if (!inode_in_use(nr)) {
+ get_current_name();
+ printf(_("Inode %d marked unused, "
+ "but used for file '%s'\n"), nr, current_name);
+ if (repair) {
+ if (ask(_("Mark 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 {
+ get_current_name();
+ printf(_("The file `%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;
+}
+
+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"));
+}
+
+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"));
+}
+
+static int
+add_zone(unsigned short *znr, int *corrected) {
+ int block;
+
+ block = check_zone_nr(znr, corrected);
+ if (!block)
+ return 0;
+ if (zone_count[block]) {
+ get_current_name();
+ printf(_("Block has been used before. Now in file `%s'."),
+ current_name);
+ if (ask(_("Clear"), 1)) {
+ *znr = 0;
+ block = 0;
+ *corrected = 1;
+ }
+ }
+ if (!block)
+ return 0;
+ if (!zone_in_use(block)) {
+ get_current_name();
+ printf(_("Block %d in file `%s' is marked not in use."),
+ block, current_name);
+ if (ask(_("Correct"), 1))
+ mark_zone(block);
+ }
+ if (!++zone_count[block])
+ zone_count[block]--;
+ return block;
+}
+
+static int
+add_zone2(unsigned int *znr, int *corrected) {
+ int block;
+
+ block = check_zone_nr2(znr, corrected);
+ if (!block)
+ return 0;
+ if (zone_count[block]) {
+ get_current_name();
+ printf(_("Block has been used before. Now in file `%s'."),
+ current_name);
+ if (ask(_("Clear"), 1)) {
+ *znr = 0;
+ block = 0;
+ *corrected = 1;
+ }
+ }
+ if (!block)
+ return 0;
+ if (!zone_in_use(block)) {
+ get_current_name();
+ printf(_("Block %d in file `%s' is marked not in use."),
+ block, current_name);
+ if (ask(_("Correct"), 1))
+ mark_zone(block);
+ }
+ if (!++zone_count[block])
+ zone_count[block]--;
+ return block;
+}
+
+static void
+add_zone_ind(unsigned short *znr, int *corrected) {
+ static char blk[MINIX_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 < (MINIX_BLOCK_SIZE >> 1); i++)
+ add_zone(i + (unsigned short *)blk, &chg_blk);
+ if (chg_blk)
+ write_block(block, blk);
+}
+
+static void
+add_zone_ind2(unsigned int *znr, int *corrected) {
+ static char blk[MINIX_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 < MINIX_BLOCK_SIZE >> 2; i++)
+ add_zone2(i + (unsigned int *)blk, &chg_blk);
+ if (chg_blk)
+ write_block(block, blk);
+}
+
+static void
+add_zone_dind(unsigned short *znr, int *corrected) {
+ static char blk[MINIX_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 < (MINIX_BLOCK_SIZE >> 1); i++)
+ add_zone_ind(i + (unsigned short *)blk, &blk_chg);
+ if (blk_chg)
+ write_block(block, blk);
+}
+
+static void
+add_zone_dind2(unsigned int *znr, int *corrected) {
+ static char blk[MINIX_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 < MINIX_BLOCK_SIZE >> 2; i++)
+ add_zone_ind2(i + (unsigned int *)blk, &blk_chg);
+ if (blk_chg)
+ write_block(block, blk);
+}
+
+static void
+add_zone_tind2(unsigned int *znr, int *corrected) {
+ static char blk[MINIX_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 < MINIX_BLOCK_SIZE >> 2; i++)
+ add_zone_dind2(i + (unsigned int *)blk, &blk_chg);
+ if (blk_chg)
+ write_block(block, blk);
+}
+
+static void
+check_zones(unsigned int i) {
+ struct minix_inode *inode;
+
+ if (!i || i > get_ninodes())
+ 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);
+}
+
+static void
+check_zones2(unsigned int i) {
+ struct minix2_inode *inode;
+
+ if (!i || i > get_ninodes())
+ 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);
+}
+
+static void
+check_file(struct minix_inode *dir, unsigned int offset) {
+ static char blk[MINIX_BLOCK_SIZE + 2];
+ struct minix_inode *inode;
+ unsigned int ino;
+ char *name;
+ int block;
+
+ block = map_block(dir, offset / MINIX_BLOCK_SIZE);
+ read_block(block, blk);
+ name = blk + (offset % MINIX_BLOCK_SIZE) + 2;
+ ino = *(unsigned short *)(name - 2);
+ if (ino > get_ninodes()) {
+ get_current_name();
+ printf(_("The directory '%s' contains a bad inode number "
+ "for file '%.*s'."), current_name, (int)namelen, name);
+ if (ask(_(" Remove"), 1)) {
+ *(unsigned short *)(name - 2) = 0;
+ write_block(block, blk);
+ }
+ ino = 0;
+ }
+ if (name_depth < MAX_DEPTH)
+ xstrncpy(name_list[name_depth], name, namelen);
+ else
+ return;
+ name_depth++;
+ inode = get_inode(ino);
+ name_depth--;
+ if (!offset) {
+ if (!inode || strcmp(".", name) != 0) {
+ get_current_name();
+ printf(_("%s: bad directory: '.' isn't first\n"),
+ current_name);
+ errors_uncorrected = 1;
+ } else
+ return;
+ }
+ if (offset == dirsize) {
+ if (!inode || strcmp("..", name) != 0) {
+ get_current_name();
+ printf(_("%s: bad directory: '..' isn't second\n"),
+ current_name);
+ errors_uncorrected = 1;
+ } else
+ return;
+ }
+ if (!inode)
+ return;
+ if (name_depth < MAX_DEPTH)
+ xstrncpy(name_list[name_depth], name, namelen);
+ else
+ return;
+ name_depth++;
+ if (list) {
+ if (verbose)
+ printf("%6d %07o %3d ", ino,
+ inode->i_mode, inode->i_nlinks);
+ get_current_name();
+ printf("%s", current_name);
+ if (S_ISDIR(inode->i_mode))
+ printf(":\n");
+ else
+ printf("\n");
+ }
+ check_zones(ino);
+ if (inode && S_ISDIR(inode->i_mode))
+ recursive_check(ino);
+ name_depth--;
+}
+
+static void
+check_file2(struct minix2_inode *dir, unsigned int offset) {
+ static char blk[MINIX_BLOCK_SIZE + 4];
+ struct minix2_inode *inode;
+ ino_t ino;
+ char *name;
+ int block;
+ const int version_offset = fs_version == 3 ? 4 : 2;
+
+ block = map_block2(dir, offset / MINIX_BLOCK_SIZE);
+ read_block(block, blk);
+ name = blk + (offset % MINIX_BLOCK_SIZE) + version_offset;
+ ino = version_offset == 4 ? *(uint32_t *)(name - version_offset)
+ : *(uint16_t *)(name - version_offset);
+ if (ino > get_ninodes()) {
+ get_current_name();
+ printf(_("The directory '%s' contains a bad inode number "
+ "for file '%.*s'."), current_name, (int)namelen, name);
+ if (ask(_(" Remove"), 1)) {
+ memset(name - version_offset, 0, version_offset);
+ write_block(block, blk);
+ }
+ ino = 0;
+ }
+ if (name_depth < MAX_DEPTH)
+ xstrncpy(name_list[name_depth], name, namelen);
+ else
+ return;
+ name_depth++;
+ inode = get_inode2(ino);
+ name_depth--;
+ if (!offset) {
+ if (!inode || strcmp(".", name) != 0) {
+ get_current_name();
+ printf(_("%s: bad directory: '.' isn't first\n"),
+ current_name);
+ errors_uncorrected = 1;
+ } else
+ return;
+ }
+ if (offset == dirsize) {
+ if (!inode || strcmp("..", name) != 0) {
+ get_current_name();
+ printf(_("%s: bad directory: '..' isn't second\n"),
+ current_name);
+ errors_uncorrected = 1;
+ } else
+ return;
+ }
+ if (!inode)
+ return;
+ name_depth++;
+ if (list) {
+ if (verbose)
+ printf("%6ju %07o %3d ", (uintmax_t)ino, inode->i_mode,
+ inode->i_nlinks);
+ get_current_name();
+ printf("%s", current_name);
+ if (S_ISDIR(inode->i_mode))
+ printf(":\n");
+ else
+ printf("\n");
+ }
+ check_zones2(ino);
+ if (inode && S_ISDIR(inode->i_mode))
+ recursive_check2(ino);
+ name_depth--;
+}
+
+static void
+recursive_check(unsigned int ino) {
+ struct minix_inode *dir;
+ off_t offset;
+
+ dir = Inode + ino;
+ if (!S_ISDIR(dir->i_mode))
+ die(_("internal error"));
+ if (dir->i_size < 2 * dirsize) {
+ get_current_name();
+ printf(_("%s: bad directory: size < 32"), current_name);
+ errors_uncorrected = 1;
+ }
+
+ if ((!repair || automatic) && !is_valid_zone_nr(*dir->i_zone)) {
+ get_current_name();
+ printf(_("%s: bad directory: invalid i_zone, use --repair to fix\n"), current_name);
+ return;
+ }
+ for (offset = 0; offset < dir->i_size; offset += dirsize)
+ check_file(dir, offset);
+}
+
+static void
+recursive_check2(unsigned int ino) {
+ struct minix2_inode *dir;
+ off_t offset;
+
+ dir = Inode2 + ino;
+ if (!S_ISDIR(dir->i_mode))
+ die(_("internal error"));
+ if (dir->i_size < 2 * dirsize) {
+ get_current_name();
+ printf(_("%s: bad directory: size < 32"), current_name);
+ errors_uncorrected = 1;
+ }
+ for (offset = 0; offset < dir->i_size; offset += dirsize)
+ check_file2(dir, offset);
+}
+
+static int
+bad_zone(int i) {
+ char buffer[1024];
+
+ if (MINIX_BLOCK_SIZE * i != lseek(device_fd, MINIX_BLOCK_SIZE * i, SEEK_SET))
+ die(_("seek failed in bad_zone"));
+ return (MINIX_BLOCK_SIZE != read(device_fd, buffer, MINIX_BLOCK_SIZE));
+}
+
+static void
+check_counts(void) {
+ unsigned long i;
+
+ for (i = 1; i <= get_ninodes(); i++) {
+ if (!inode_in_use(i) && Inode[i].i_mode && warn_mode) {
+ printf(_("Inode %lu mode not cleared."), i);
+ if (ask(_("Clear"), 1)) {
+ Inode[i].i_mode = 0;
+ changed = 1;
+ }
+ }
+ if (!inode_count[i]) {
+ if (!inode_in_use(i))
+ continue;
+ printf(_("Inode %lu not used, marked used in the bitmap."), i);
+ if (ask(_("Clear"), 1))
+ unmark_inode(i);
+ continue;
+ }
+ if (!inode_in_use(i)) {
+ printf(_("Inode %lu used, marked unused in the bitmap."), i);
+ if (ask(_("Set"), 1))
+ mark_inode(i);
+ }
+ if (Inode[i].i_nlinks != inode_count[i]) {
+ printf(_("Inode %lu (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 = get_first_zone(); i < get_nzones(); i++) {
+ if (zone_in_use(i) == zone_count[i])
+ continue;
+ if (!zone_count[i]) {
+ if (bad_zone(i))
+ continue;
+ printf(_("Zone %lu: marked in use, no file uses it."),
+ i);
+ if (ask(_("Unmark"), 1))
+ unmark_zone(i);
+ continue;
+ }
+ if (zone_in_use(i))
+ printf(_("Zone %lu: in use, counted=%d\n"),
+ i, zone_count[i]);
+ else
+ printf(_("Zone %lu: not in use, counted=%d\n"),
+ i, zone_count[i]);
+ }
+}
+
+static void
+check_counts2(void) {
+ unsigned long i;
+
+ for (i = 1; i <= get_ninodes(); i++) {
+ if (!inode_in_use(i) && Inode2[i].i_mode && warn_mode) {
+ printf(_("Inode %lu mode not cleared."), i);
+ if (ask(_("Clear"), 1)) {
+ Inode2[i].i_mode = 0;
+ changed = 1;
+ }
+ }
+ if (!inode_count[i]) {
+ if (!inode_in_use(i))
+ continue;
+ printf(_("Inode %lu not used, marked used in the bitmap."), i);
+ if (ask(_("Clear"), 1))
+ unmark_inode(i);
+ continue;
+ }
+ if (!inode_in_use(i)) {
+ printf(_("Inode %lu used, marked unused in the bitmap."), i);
+ if (ask(_("Set"), 1))
+ mark_inode(i);
+ }
+ if (Inode2[i].i_nlinks != inode_count[i]) {
+ printf(_("Inode %lu (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 = get_first_zone(); i < get_nzones(); i++) {
+ if (zone_in_use(i) == zone_count[i])
+ continue;
+ if (!zone_count[i]) {
+ if (bad_zone(i))
+ continue;
+ printf(_("Zone %lu: marked in use, no file uses it."),
+ i);
+ if (ask(_("Unmark"), 1))
+ unmark_zone(i);
+ continue;
+ }
+ if (zone_in_use(i))
+ printf(_("Zone %lu: in use, counted=%d\n"),
+ i, zone_count[i]);
+ else
+ printf(_("Zone %lu: not in use, counted=%d\n"),
+ i, zone_count[i]);
+ }
+}
+
+static void
+check(void) {
+ memset(inode_count, 0, (get_ninodes() + 1) * sizeof(*inode_count));
+ memset(zone_count, 0, get_nzones() * sizeof(*zone_count));
+ check_zones(ROOT_INO);
+ recursive_check(ROOT_INO);
+ check_counts();
+}
+
+static void
+check2(void) {
+ memset(inode_count, 0, (get_ninodes() + 1) * sizeof(*inode_count));
+ memset(zone_count, 0, get_nzones() * sizeof(*zone_count));
+ check_zones2(ROOT_INO);
+ recursive_check2(ROOT_INO);
+ check_counts2();
+}
+
+int
+main(int argc, char **argv) {
+ struct termios tmp;
+ int count;
+ int retcode = FSCK_EX_OK;
+ int i;
+ static const struct option longopts[] = {
+ {"list", no_argument, NULL, 'l'},
+ {"auto", no_argument, NULL, 'a'},
+ {"repair", no_argument, NULL, 'r'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"super", no_argument, NULL, 's'},
+ {"uncleared", no_argument, NULL, 'm'},
+ {"force", no_argument, NULL, 'f'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ strutils_set_exitcode(FSCK_EX_USAGE);
+
+ if (INODE_SIZE * MINIX_INODES_PER_BLOCK != MINIX_BLOCK_SIZE)
+ die(_("bad inode size"));
+ if (INODE2_SIZE * MINIX2_INODES_PER_BLOCK != MINIX_BLOCK_SIZE)
+ die(_("bad v2 inode size"));
+
+ while ((i = getopt_long(argc, argv, "larvsmfVh", longopts, NULL)) != -1)
+ switch (i) {
+ 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;
+ case 'V':
+ print_version(FSCK_EX_OK);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(FSCK_EX_USAGE);
+ }
+ argc -= optind;
+ argv += optind;
+ if (0 < argc) {
+ device_name = argv[0];
+ } else {
+ warnx(_("no device specified"));
+ errtryhelp(FSCK_EX_USAGE);
+ }
+ check_mount(); /* trying to check a mounted filesystem? */
+ if (repair && !automatic && (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)))
+ die(_("need terminal for interactive repairs"));
+
+ device_fd = open(device_name, repair ? O_RDWR : O_RDONLY);
+ if (device_fd < 0)
+ die(_("cannot open %s: %s"), device_name, strerror(errno));
+ for (count = 0; count < 3; count++)
+ sync();
+ 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. */
+ if (fs_version < 3 && !(Super.s_state & MINIX_ERROR_FS) &&
+ (Super.s_state & MINIX_VALID_FS) && !force) {
+ if (repair)
+ printf(_("%s is clean, no check.\n"), device_name);
+ return retcode;
+ }
+
+ 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();
+
+ /* Restore the terminal state on fatal signals. We don't do this for
+ * SIGALRM, SIGUSR1 or SIGUSR2. */
+ signal(SIGINT, fatalsig);
+ signal(SIGQUIT, fatalsig);
+ signal(SIGTERM, fatalsig);
+
+ if (repair && !automatic) {
+ tcgetattr(STDIN_FILENO, &termios);
+ tmp = termios;
+ tmp.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(STDIN_FILENO, TCSANOW, &tmp);
+ termios_set = 1;
+ }
+
+ if (fs_version == 2 || fs_version == 3) {
+ check_root2();
+ check2();
+ } else {
+ check_root();
+ check();
+ }
+ if (verbose) {
+ unsigned long inode, free;
+
+ for (inode = 1, free = 0; inode <= get_ninodes(); inode++)
+ if (!inode_in_use(inode))
+ free++;
+ printf(_("\n%6ld inodes used (%ld%%)\n"),
+ (get_ninodes() - free),
+ 100 * (get_ninodes() - free) / get_ninodes());
+ for (inode = get_first_zone(), free = 0; inode < get_nzones(); inode++)
+ if (!zone_in_use(inode))
+ free++;
+ printf(_("%6ld zones used (%ld%%)\n"), (get_nzones() - free),
+ 100 * (get_nzones() - free) / get_nzones());
+ printf(_("\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"),
+ regular, directory, chardev, blockdev,
+ links - 2 * directory + 1, symlinks,
+ total - 2 * directory + 1);
+ }
+ if (changed) {
+ write_tables();
+ printf(_("----------------------------\n"
+ "FILE SYSTEM HAS BEEN CHANGED\n"
+ "----------------------------\n"));
+ for (count = 0; count < 3; count++)
+ sync();
+ } else if (repair)
+ write_super_block();
+
+ if (repair && !automatic)
+ tcsetattr(STDIN_FILENO, TCSANOW, &termios);
+
+ if (close_fd(device_fd) != 0)
+ err(FSCK_EX_ERROR, _("write failed"));
+ if (changed)
+ retcode += 3;
+ if (errors_uncorrected)
+ retcode += 4;
+ return retcode;
+}
diff --git a/disk-utils/isosize.8 b/disk-utils/isosize.8
new file mode 100644
index 0000000..8d58993
--- /dev/null
+++ b/disk-utils/isosize.8
@@ -0,0 +1,55 @@
+.TH ISOSIZE 8 "June 2011" "util-linux" "System Administration"
+.SH NAME
+isosize \- output the length of an iso9660 filesystem
+.SH SYNOPSIS
+.B isosize
+.RI [options] " iso9660_image_file"
+.SH DESCRIPTION
+This command outputs the length of an iso9660 filesystem that
+is contained in the specified file. This file may be a normal file or
+a block device (e.g.\& /dev/hdd or /dev/sr0). In the absence of
+any options (and errors), it will output the size of the iso9660
+filesystem in bytes. This can now be a large number (>> 4\ GB).
+.SH OPTIONS
+.TP
+.BR \-x , " \-\-sectors"
+Show the block count and block size in human-readable form.
+The output uses the term "sectors" for "blocks".
+.TP
+.BR \-d , " \-\-divisor " \fInumber\fR
+Only has an effect when
+.B \-x
+is not given. The value shown (if no errors)
+is the iso9660 file size in bytes divided by
+.IR number .
+So if
+.I number
+is the block size then the shown value will be the block count.
+.PP
+The size of the file (or block device) holding an iso9660
+filesystem can be marginally larger than the actual size of the
+iso9660 filesystem. One reason for this is that cd writers
+are allowed to add "run out" sectors at the end of an iso9660
+image.
+.SH EXIT STATUS
+.RS
+.PD 0
+.TP
+.B 0
+success
+.TP
+.B 1
+generic failure, such as invalid usage
+.TP
+.B 32
+all failed
+.TP
+.B 64
+some failed
+.PD
+.RE
+.SH AVAILABILITY
+The isosize command is part of the util-linux package and is available from
+.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/disk-utils/isosize.c b/disk-utils/isosize.c
new file mode 100644
index 0000000..9299663
--- /dev/null
+++ b/disk-utils/isosize.c
@@ -0,0 +1,167 @@
+/*
+ * isosize.c - Andries Brouwer, 000608
+ *
+ * use header info to find size of iso9660 file system
+ * output a number - useful in scripts
+ *
+ * Synopsis:
+ * isosize [-x] [-d <num>] <filename>
+ * where "-x" gives length in sectors and sector size while
+ * without this argument the size is given in bytes
+ * without "-x" gives length in bytes unless "-d <num>" is
+ * given. In the latter case the length in bytes divided
+ * by <num> is given
+ *
+ * Version 2.03 2000/12/21
+ * - add "-d <num>" option and use long long to fix things > 2 GB
+ * Version 2.02 2000/10/11
+ * - error messages on IO failures [D. Gilbert]
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "nls.h"
+#include "c.h"
+#include "strutils.h"
+#include "closestream.h"
+#include "iso9660.h"
+
+#define ISOSIZE_EXIT_ALLFAILED 32
+#define ISOSIZE_EXIT_SOMEOK 64
+
+static int is_iso(int fd)
+{
+ char label[8];
+
+ if (pread(fd, &label, 8, 0x8000) == -1)
+ return 1;
+ return memcmp(&label, &"\1CD001\1", 8);
+}
+
+static int isosize(int argc, char *filenamep, int xflag, long divisor)
+{
+ int fd, nsecs, ssize, rc = -1;
+ unsigned char volume_space_size[8];
+ unsigned char logical_block_size[4];
+
+ if ((fd = open(filenamep, O_RDONLY)) < 0) {
+ warn(_("cannot open %s"), filenamep);
+ goto done;
+ }
+ if (is_iso(fd))
+ warnx(_("%s: might not be an ISO filesystem"), filenamep);
+
+ if (pread(fd, volume_space_size, sizeof(volume_space_size), 0x8050) != sizeof(volume_space_size) ||
+ pread(fd, logical_block_size, sizeof(logical_block_size), 0x8080) != sizeof(logical_block_size)) {
+ if (errno)
+ warn(_("read error on %s"), filenamep);
+ else
+ warnx(_("read error on %s"), filenamep);
+ goto done;
+ }
+
+ nsecs = isonum_733(volume_space_size, xflag);
+ /* isonum_723 returns nowadays always 2048 */
+ ssize = isonum_723(logical_block_size, xflag);
+
+ if (1 < argc)
+ printf("%s: ", filenamep);
+ if (xflag)
+ printf(_("sector count: %d, sector size: %d\n"), nsecs, ssize);
+ else {
+ long long product = nsecs;
+
+ if (divisor == 0)
+ printf("%lld\n", product * ssize);
+ else if (divisor == ssize)
+ printf("%d\n", nsecs);
+ else
+ printf("%lld\n", (product * ssize) / divisor);
+ }
+
+ rc = 0;
+done:
+ if (fd >= 0)
+ close(fd);
+ return rc;
+}
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+
+ fputs(USAGE_HEADER, stdout);
+ fprintf(stdout,
+ _(" %s [options] <iso9660_image_file> ...\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, stdout);
+ fputs(_("Show the length of an ISO-9660 filesystem.\n"), stdout);
+
+ fputs(USAGE_OPTIONS, stdout);
+ fputs(_(" -d, --divisor=<number> divide the amount of bytes by <number>\n"), stdout);
+ fputs(_(" -x, --sectors show sector count and size\n"), stdout);
+
+ printf(USAGE_HELP_OPTIONS(25));
+ printf(USAGE_MAN_TAIL("isosize(8)"));
+
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int j, ct_err = 0, ct, opt, xflag = 0;
+ long divisor = 0;
+
+ static const struct option longopts[] = {
+ {"divisor", required_argument, NULL, 'd'},
+ {"sectors", no_argument, NULL, 'x'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ while ((opt = getopt_long(argc, argv, "d:xVh", longopts, NULL)) != -1) {
+ switch (opt) {
+ case 'd':
+ divisor =
+ strtol_or_err(optarg,
+ _("invalid divisor argument"));
+ break;
+ case 'x':
+ xflag = 1;
+ break;
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ ct = argc - optind;
+
+ if (ct <= 0) {
+ warnx(_("no device specified"));
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ for (j = optind; j < argc; j++) {
+ if (isosize(ct, argv[j], xflag, divisor) != 0)
+ ct_err++;
+ }
+
+ return ct == ct_err ? ISOSIZE_EXIT_ALLFAILED : /* all failed */
+ ct_err ? ISOSIZE_EXIT_SOMEOK : /* some ok */
+ EXIT_SUCCESS; /* all success */
+}
diff --git a/disk-utils/minix_programs.h b/disk-utils/minix_programs.h
new file mode 100644
index 0000000..7ad308d
--- /dev/null
+++ b/disk-utils/minix_programs.h
@@ -0,0 +1,118 @@
+#ifndef UTIL_LINUX_MINIX_PROGRAMS_H
+#define UTIL_LINUX_MINIX_PROGRAMS_H
+
+#include "minix.h"
+
+/*
+ * Global variables.
+ */
+extern int fs_version;
+extern char *super_block_buffer;
+
+#define Super (*(struct minix_super_block *) super_block_buffer)
+#define Super3 (*(struct minix3_super_block *) super_block_buffer)
+
+#define INODE_SIZE (sizeof(struct minix_inode))
+#define INODE2_SIZE (sizeof(struct minix2_inode))
+
+#define BITS_PER_BLOCK (MINIX_BLOCK_SIZE << 3)
+
+#define UPPER(size,n) ((size+((n)-1))/(n))
+
+/*
+ * Inline functions.
+ */
+static inline unsigned long get_ninodes(void)
+{
+ switch (fs_version) {
+ case 3:
+ return Super3.s_ninodes;
+ default:
+ return Super.s_ninodes;
+ }
+}
+
+static inline unsigned long get_nzones(void)
+{
+ switch (fs_version) {
+ case 3:
+ return Super3.s_zones;
+ case 2:
+ return Super.s_zones;
+ default:
+ return Super.s_nzones;
+ }
+}
+
+static inline unsigned long get_nimaps(void)
+{
+ switch (fs_version) {
+ case 3:
+ return Super3.s_imap_blocks;
+ default:
+ return Super.s_imap_blocks;
+ }
+}
+
+static inline unsigned long get_nzmaps(void)
+{
+ switch (fs_version) {
+ case 3:
+ return Super3.s_zmap_blocks;
+ default:
+ return Super.s_zmap_blocks;
+ }
+}
+
+static inline off_t get_first_zone(void)
+{
+ switch (fs_version) {
+ case 3:
+ return Super3.s_firstdatazone;
+ default:
+ return Super.s_firstdatazone;
+ }
+}
+
+static inline size_t get_zone_size(void)
+{
+ switch (fs_version) {
+ case 3:
+ return Super3.s_log_zone_size;
+ default:
+ return Super.s_log_zone_size;
+ }
+}
+
+static inline size_t get_max_size(void)
+{
+ switch (fs_version) {
+ case 3:
+ return Super3.s_max_size;
+ default:
+ return Super.s_max_size;
+ }
+}
+
+static inline unsigned long inode_blocks(void)
+{
+ switch (fs_version) {
+ case 3:
+ case 2:
+ return UPPER(get_ninodes(), MINIX2_INODES_PER_BLOCK);
+ default:
+ return UPPER(get_ninodes(), MINIX_INODES_PER_BLOCK);
+ }
+}
+
+static inline off_t first_zone_data(void)
+{
+ return 2 + get_nimaps() + get_nzmaps() + inode_blocks();
+}
+
+static inline size_t get_inode_buffer_size(void)
+{
+ return inode_blocks() * MINIX_BLOCK_SIZE;
+}
+
+#endif /* UTIL_LINUX_MINIX_PROGRAMS_H */
diff --git a/disk-utils/mkfs.8 b/disk-utils/mkfs.8
new file mode 100644
index 0000000..deb00b2
--- /dev/null
+++ b/disk-utils/mkfs.8
@@ -0,0 +1,94 @@
+.TH MKFS 8 "June 2011" "util-linux" "System Administration"
+.SH NAME
+mkfs \- build a Linux filesystem
+.SH SYNOPSIS
+.B mkfs
+[options]
+.RB [ \-t
+.IR type "] [" fs-options ] " device " [ size ]
+.SH DESCRIPTION
+.B This mkfs frontend is deprecated in favour of filesystem specific mkfs.<type> utils.
+.PP
+.B mkfs
+is used to build a Linux filesystem on a device, usually
+a hard disk partition. The
+.I device
+argument is either the device name (e.g.,
+.IR /dev/hda1 ,
+.IR /dev/sdb2 ),
+or a regular file that shall contain the filesystem. The
+.I size
+argument is the number of blocks to be used for the filesystem.
+.PP
+The exit status returned by
+.B mkfs
+is 0 on success and 1 on failure.
+.PP
+In actuality,
+.B mkfs
+is simply a front-end for the various filesystem builders
+(\fBmkfs.\fIfstype\fR)
+available under Linux.
+The filesystem-specific builder is searched for via your PATH
+environment setting only.
+Please see the filesystem-specific builder manual pages for
+further details.
+.SH OPTIONS
+.TP
+.BR \-t , " \-\-type " \fItype\fR
+Specify the \fItype\fR of filesystem to be built.
+If not specified, the default filesystem type
+(currently ext2) is used.
+.TP
+.I fs-options
+Filesystem-specific options to be passed to the real filesystem builder.
+.TP
+.BR \-V , " \-\-verbose"
+Produce verbose output, including all filesystem-specific commands
+that are executed.
+Specifying this option more than once inhibits execution of any
+filesystem-specific commands.
+This is really only useful for testing.
+.TP
+.BR \-V , " \-\-version"
+Display version information and exit. (Option \fB\-V\fR will display
+version information only when it is the only parameter, otherwise it
+will work as \fB\-\-verbose\fR.)
+.TP
+.BR \-h , " \-\-help"
+Display help text and exit.
+.SH BUGS
+All generic options must precede and not be combined with
+filesystem-specific options.
+Some filesystem-specific programs do not automatically
+detect the device size and require the
+.I size
+parameter to be specified.
+.SH AUTHORS
+David Engel (david@ods.com)
+.br
+Fred N.\& van Kempen (waltje@uwalt.nl.mugnet.org)
+.br
+Ron Sommeling (sommel@sci.kun.nl)
+.br
+The manual page was shamelessly adapted from Remy Card's version
+for the ext2 filesystem.
+.SH SEE ALSO
+.na
+.BR fs (5),
+.BR badblocks (8),
+.BR fsck (8),
+.BR mkdosfs (8),
+.BR mke2fs (8),
+.BR mkfs.bfs (8),
+.BR mkfs.ext2 (8),
+.BR mkfs.ext3 (8),
+.BR mkfs.ext4 (8),
+.BR mkfs.minix (8),
+.BR mkfs.msdos (8),
+.BR mkfs.vfat (8),
+.BR mkfs.xfs (8)
+.ad
+.SH AVAILABILITY
+The mkfs command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/mkfs.bfs.8 b/disk-utils/mkfs.bfs.8
new file mode 100644
index 0000000..722b832
--- /dev/null
+++ b/disk-utils/mkfs.bfs.8
@@ -0,0 +1,58 @@
+.\" Copyright 1999 Andries E. Brouwer (aeb@cwi.nl)
+.\" May be freely distributed.
+.TH MKFS.BFS 8 "July 2011" "util-linux" "System Administration"
+.SH NAME
+mkfs.bfs \- make an SCO bfs filesystem
+.SH SYNOPSIS
+.B mkfs.bfs
+.RI [options] " device " [ block-count ]
+.SH DESCRIPTION
+.B mkfs.bfs
+creates an SCO bfs filesystem on a block device
+(usually a disk partition or a file accessed via the loop device).
+.PP
+The
+.I block-count
+parameter is the desired size of the filesystem, in blocks.
+If nothing is specified, the entire partition will be used.
+.SH OPTIONS
+.TP
+.BR \-N , " \-\-inodes " \fInumber\fR
+Specify the desired \fInumber\fR of inodes (at most 512).
+If nothing is specified, some default number in the range 48\(en512 is picked
+depending on the size of the partition.
+.TP
+.BR \-V , " \-\-vname " \fIlabel\fR
+Specify the volume \fIlabel\fR. I have no idea if/where this is used.
+.TP
+.BR \-F , " \-\-fname " \fIname\fR
+Specify the filesystem \fIname\fR. I have no idea if/where this is used.
+.TP
+.BR \-v , " \-\-verbose"
+Explain what is being done.
+.TP
+.B \-c
+This option is silently ignored.
+.TP
+.B \-l
+This option is silently ignored.
+.TP
+.BR \-h , " \-\-help"
+Display help text and exit.
+.TP
+.BR \-V , " \-\-version"
+Display version information and exit.
+Option
+.B \-V
+only works as
+.B \-\-version
+when it is the only option.
+.SH EXIT STATUS
+The exit status returned by
+.B mkfs.bfs
+is 0 when all went well, and 1 when something went wrong.
+.SH SEE ALSO
+.BR mkfs (8)
+.SH AVAILABILITY
+The mkfs.bfs command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/mkfs.bfs.c b/disk-utils/mkfs.bfs.c
new file mode 100644
index 0000000..54d261b
--- /dev/null
+++ b/disk-utils/mkfs.bfs.c
@@ -0,0 +1,301 @@
+/*
+ * mkfs.bfs - Create SCO BFS filesystem - aeb, 1999-09-07
+ *
+ * Usage: mkfs.bfs [-N nr-of-inodes] [-V volume-name] [-F fsname] device
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "blkdev.h"
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "strutils.h"
+#include "xalloc.h"
+#include "bitops.h"
+
+#define BFS_ROOT_INO 2
+#define BFS_NAMELEN 14
+#define BFS_BLOCKSIZE 512
+#define BFS_SUPER_MAGIC 0x1badface
+
+/* superblock - 512 bytes */
+struct bfssb {
+ uint32_t s_magic;
+ uint32_t s_start; /* byte offset of start of data */
+ uint32_t s_end; /* sizeof(slice)-1 */
+
+ /* for recovery during compaction */
+ uint32_t s_from, s_to; /* src and dest block of current transfer */
+ int32_t s_backup_from, s_backup_to;
+
+ /* labels - may well contain garbage */
+ char s_fsname[6];
+ char s_volume[6];
+ char s_pad[472];
+};
+
+/* inode - 64 bytes */
+struct bfsi {
+ uint16_t i_ino;
+ unsigned char i_pad1[2];
+ uint32_t i_first_block;
+ uint32_t i_last_block;
+ uint32_t i_bytes_to_end;
+ uint32_t i_type; /* 1: file, 2: the unique dir */
+ uint32_t i_mode;
+ uint32_t i_uid, i_gid;
+ uint32_t i_nlinks;
+ uint32_t i_atime, i_mtime, i_ctime;
+ unsigned char i_pad2[16];
+};
+
+#define BFS_DIR_TYPE 2
+
+/* directory entry - 16 bytes */
+struct bfsde {
+ uint16_t d_ino;
+ char d_name[BFS_NAMELEN];
+};
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fprintf(out,
+ _("Usage: %s [options] device [block-count]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Make an SCO bfs filesystem.\n"), out);
+
+ fprintf(out, _("\nOptions:\n"
+ " -N, --inodes=NUM specify desired number of inodes\n"
+ " -V, --vname=NAME specify volume name\n"
+ " -F, --fname=NAME specify file system name\n"
+ " -v, --verbose explain what is being done\n"
+ " -c this option is silently ignored\n"
+ " -l this option is silently ignored\n"
+ ));
+ printf(USAGE_HELP_OPTIONS(21));
+
+ printf(USAGE_MAN_TAIL("mkfs.bfs(8)"));
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ char *device, *volume, *fsname;
+ long inodes;
+ unsigned long long total_blocks, ino_bytes, ino_blocks, data_blocks;
+ unsigned long long user_specified_total_blocks = 0;
+ int verbose = 0;
+ int fd;
+ uint32_t first_block;
+ struct bfssb sb;
+ struct bfsi ri;
+ struct bfsde de;
+ struct stat statbuf;
+ time_t now;
+ int c, i, len;
+
+ enum { VERSION_OPTION = CHAR_MAX + 1 };
+ static const struct option longopts[] = {
+ {"inodes", required_argument, NULL, 'N'},
+ {"vname", required_argument, NULL, 'V'},
+ {"fname", required_argument, NULL, 'F'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, VERSION_OPTION},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ if (argc < 2) {
+ warnx(_("not enough arguments"));
+ errtryhelp(EXIT_FAILURE);
+ }
+ if (argc == 2 && !strcmp(argv[1], "-V"))
+ print_version(EXIT_SUCCESS);
+
+ volume = fsname = " "; /* is there a default? */
+ inodes = 0;
+
+ while ((c = getopt_long(argc, argv, "N:V:F:vhcl", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'N':
+ inodes = strtol_or_err(optarg, _("invalid number of inodes"));
+ break;
+
+ case 'V':
+ len = strlen(optarg);
+ if (len <= 0 || len > 6)
+ errx(EXIT_FAILURE, _("volume name too long"));
+ volume = xstrdup(optarg);
+ break;
+
+ case 'F':
+ len = strlen(optarg);
+ if (len <= 0 || len > 6)
+ errx(EXIT_FAILURE, _("fsname name too long"));
+ fsname = xstrdup(optarg);
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'c':
+ case 'l':
+ /* when called via mkfs we may get options c,l,v */
+ break;
+
+ case VERSION_OPTION:
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ if (optind == argc) {
+ warnx(_("no device specified"));
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ device = argv[optind++];
+
+ if (stat(device, &statbuf) < 0)
+ err(EXIT_FAILURE, _("stat of %s failed"), device);
+
+ fd = open_blkdev_or_file(&statbuf, device, O_RDWR);
+ if (fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), device);
+
+ if (optind == argc - 1)
+ user_specified_total_blocks =
+ strtou64_or_err(argv[optind], _("invalid block-count"));
+ else if (optind != argc) {
+ warnx(_("bad usage"));
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ if (blkdev_get_sectors(fd, &total_blocks) == -1) {
+ if (!user_specified_total_blocks)
+ err(EXIT_FAILURE, _("cannot get size of %s"), device);
+ total_blocks = user_specified_total_blocks;
+ } else if (user_specified_total_blocks) {
+ if (user_specified_total_blocks > total_blocks)
+ errx(EXIT_FAILURE,
+ _("blocks argument too large, max is %llu"),
+ total_blocks);
+ total_blocks = user_specified_total_blocks;
+ }
+
+ if (!inodes) {
+ /* pick some reasonable default */
+ inodes = 8 * (total_blocks / 800);
+ if (inodes < 48)
+ inodes = 48;
+ if (512 < inodes)
+ inodes = 512;
+ } else {
+ /* believe the user */
+ if (512 < inodes)
+ errx(EXIT_FAILURE, _("too many inodes - max is 512"));
+ }
+
+ ino_bytes = inodes * sizeof(struct bfsi);
+ ino_blocks = (ino_bytes + BFS_BLOCKSIZE - 1) / BFS_BLOCKSIZE;
+ data_blocks = total_blocks - ino_blocks - 1;
+
+ /* mimic the behavior of SCO's mkfs - maybe this limit is needed */
+ if (data_blocks < 32)
+ errx(EXIT_FAILURE,
+ _("not enough space, need at least %llu blocks"),
+ ino_blocks + 33);
+
+ memset(&sb, 0, sizeof(sb));
+ sb.s_magic = cpu_to_le32(BFS_SUPER_MAGIC);
+ sb.s_start = cpu_to_le32(ino_bytes + sizeof(struct bfssb));
+ sb.s_end = cpu_to_le32(total_blocks * BFS_BLOCKSIZE - 1);
+ sb.s_from = sb.s_to = sb.s_backup_from = sb.s_backup_to = -1;
+ memcpy(sb.s_fsname, fsname, 6);
+ memcpy(sb.s_volume, volume, 6);
+
+ if (verbose) {
+ fprintf(stderr, _("Device: %s\n"), device);
+ fprintf(stderr, _("Volume: <%-6s>\n"), volume);
+ fprintf(stderr, _("FSname: <%-6s>\n"), fsname);
+ fprintf(stderr, _("BlockSize: %d\n"), BFS_BLOCKSIZE);
+ if (ino_blocks == 1)
+ fprintf(stderr, _("Inodes: %ld (in 1 block)\n"),
+ inodes);
+ else
+ fprintf(stderr, _("Inodes: %ld (in %llu blocks)\n"),
+ inodes, ino_blocks);
+ fprintf(stderr, _("Blocks: %llu\n"), total_blocks);
+ fprintf(stderr, _("Inode end: %d, Data end: %d\n"),
+ le32_to_cpu(sb.s_start) - 1, le32_to_cpu(sb.s_end));
+ }
+
+ if (write(fd, &sb, sizeof(sb)) != sizeof(sb))
+ err(EXIT_FAILURE, _("error writing superblock"));
+
+ memset(&ri, 0, sizeof(ri));
+ ri.i_ino = cpu_to_le16(BFS_ROOT_INO);
+ first_block = 1 + ino_blocks;
+ ri.i_first_block = cpu_to_le32(first_block);
+ ri.i_last_block = cpu_to_le32(first_block +
+ (inodes * sizeof(de) - 1) / BFS_BLOCKSIZE);
+ ri.i_bytes_to_end = cpu_to_le32(first_block * BFS_BLOCKSIZE
+ + 2 * sizeof(struct bfsde) - 1);
+ ri.i_type = cpu_to_le32(BFS_DIR_TYPE);
+ ri.i_mode = cpu_to_le32(S_IFDIR | 0755); /* or just 0755 */
+ ri.i_uid = cpu_to_le32(0);
+ ri.i_gid = cpu_to_le32(1); /* random */
+ ri.i_nlinks = 2;
+ time(&now);
+ ri.i_atime = cpu_to_le32(now);
+ ri.i_mtime = cpu_to_le32(now);
+ ri.i_ctime = cpu_to_le32(now);
+
+ if (write(fd, &ri, sizeof(ri)) != sizeof(ri))
+ err(EXIT_FAILURE, _("error writing root inode"));
+
+ memset(&ri, 0, sizeof(ri));
+ for (i = 1; i < inodes; i++)
+ if (write(fd, &ri, sizeof(ri)) != sizeof(ri))
+ err(EXIT_FAILURE, _("error writing inode"));
+
+ if (lseek(fd, (1 + ino_blocks) * BFS_BLOCKSIZE, SEEK_SET) == -1)
+ err(EXIT_FAILURE, _("seek error"));
+
+ memset(&de, 0, sizeof(de));
+ de.d_ino = cpu_to_le16(BFS_ROOT_INO);
+ memcpy(de.d_name, ".", 1);
+ if (write(fd, &de, sizeof(de)) != sizeof(de))
+ err(EXIT_FAILURE, _("error writing . entry"));
+
+ memcpy(de.d_name, "..", 2);
+ if (write(fd, &de, sizeof(de)) != sizeof(de))
+ err(EXIT_FAILURE, _("error writing .. entry"));
+
+ if (close_fd(fd) != 0)
+ err(EXIT_FAILURE, _("error closing %s"), device);
+
+ return EXIT_SUCCESS;
+}
diff --git a/disk-utils/mkfs.c b/disk-utils/mkfs.c
new file mode 100644
index 0000000..3c041fa
--- /dev/null
+++ b/disk-utils/mkfs.c
@@ -0,0 +1,134 @@
+/*
+ * mkfs A simple generic frontend for the for the mkfs program
+ * under Linux. See the manual page for details.
+ *
+ * Authors: David Engel, <david@ods.com>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Ron Sommeling, <sommel@sci.kun.nl>
+ *
+ * Mon Jul 1 18:52:58 1996: janl@math.uio.no (Nicolai Langfeldt):
+ * Incorporated fix by Jonathan Kamens <jik@annex-1-slip-jik.cam.ov.com>
+ * 1999-02-22 Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ */
+
+/*
+ * This command is deprecated. The utility is in maintenance mode,
+ * meaning we keep them in source tree for backward compatibility
+ * only. Do not waste time making this command better, unless the
+ * fix is about security or other very critical issue.
+ *
+ * See Documentation/deprecated.txt for more information.
+ */
+
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "xalloc.h"
+
+#ifndef DEFAULT_FSTYPE
+#define DEFAULT_FSTYPE "ext2"
+#endif
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s [options] [-t <type>] [fs-options] <device> [<size>]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Make a Linux filesystem.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fprintf(out, _(" -t, --type=<type> filesystem type; when unspecified, ext2 is used\n"));
+ fprintf(out, _(" fs-options parameters for the real filesystem builder\n"));
+ fprintf(out, _(" <device> path to the device to be used\n"));
+ fprintf(out, _(" <size> number of blocks to be used on the device\n"));
+ fprintf(out, _(" -V, --verbose explain what is being done;\n"
+ " specifying -V more than once will cause a dry-run\n"));
+ printf(USAGE_HELP_OPTIONS(20));
+
+ printf(USAGE_MAN_TAIL("mkfs(8)"));
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ char *progname; /* name of executable to be called */
+ char *fstype = NULL;
+ int i, more = 0, verbose = 0;
+
+ enum { VERSION_OPTION = CHAR_MAX + 1 };
+
+ static const struct option longopts[] = {
+ {"type", required_argument, NULL, 't'},
+ {"version", no_argument, NULL, VERSION_OPTION},
+ {"verbose", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ if (argc == 2 && !strcmp(argv[1], "-V"))
+ print_version(EXIT_SUCCESS);
+
+ /* Check commandline options. */
+ opterr = 0;
+ while ((more == 0)
+ && ((i = getopt_long(argc, argv, "Vt:h", longopts, NULL))
+ != -1))
+ switch (i) {
+ case 'V':
+ verbose++;
+ break;
+ case 't':
+ fstype = optarg;
+ break;
+ case 'h':
+ usage();
+ case VERSION_OPTION:
+ print_version(EXIT_SUCCESS);
+ default:
+ optind--;
+ more = 1;
+ break; /* start of specific arguments */
+ }
+ if (optind == argc) {
+ warnx(_("no device specified"));
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ /* If -t wasn't specified, use the default */
+ if (fstype == NULL)
+ fstype = DEFAULT_FSTYPE;
+
+ xasprintf(&progname, "mkfs.%s", fstype);
+ argv[--optind] = progname;
+
+ if (verbose) {
+ printf(UTIL_LINUX_VERSION);
+ i = optind;
+ while (argv[i])
+ printf("%s ", argv[i++]);
+ printf("\n");
+ if (verbose > 1)
+ return EXIT_SUCCESS;
+ }
+
+ /* Execute the program */
+ execvp(progname, argv + optind);
+ err(EXIT_FAILURE, _("failed to execute %s"), progname);
+}
diff --git a/disk-utils/mkfs.cramfs.8 b/disk-utils/mkfs.cramfs.8
new file mode 100644
index 0000000..f01229f
--- /dev/null
+++ b/disk-utils/mkfs.cramfs.8
@@ -0,0 +1,90 @@
+.TH MKFS.CRAMFS 8 "April 2013" "util-linux" "System Administration"
+.SH NAME
+mkfs.cramfs \- make compressed ROM file system
+.SH SYNOPSIS
+.B mkfs.cramfs
+[options]
+.I directory file
+.SH DESCRIPTION
+Files on cramfs file systems are zlib-compressed one page at a time to
+allow random read access. The metadata is not compressed, but is
+expressed in a terse representation that is more space-efficient than
+conventional file systems.
+.PP
+The file system is intentionally read-only to simplify its design; random
+write access for compressed files is difficult to implement. cramfs
+ships with a utility (mkcramfs) to pack files into new cramfs images.
+.PP
+File sizes are limited to less than 16\ MB.
+.PP
+Maximum file system size is a little under 272\ MB. (The last file on the
+file system must begin before the 256\ MB block, but can extend past it.)
+.SH ARGUMENTS
+The
+.I directory
+is simply the root of the directory tree that we want to generate a
+compressed filesystem out of.
+.PP
+The
+.I file
+will contain the cram file system, which later can be mounted.
+.SH OPTIONS
+.TP
+\fB\-v\fR
+Enable verbose messaging.
+.TP
+\fB\-E\fR
+Treat all warnings as errors, which are reflected as command exit status.
+.TP
+\fB\-b\fR \fIblocksize\fR
+Use defined block size, which has to be divisible by page size.
+.TP
+\fB\-e\fR \fIedition\fR
+Use defined file system edition number in superblock.
+.TP
+\fB\-N\fR \fIbig, little, host\fR
+Use defined endianness. Value defaults to
+.IR host .
+.TP
+\fB\-i\fR \fIfile\fR
+Insert a
+.I file
+to cramfs file system.
+.TP
+\fB\-n\fR \fIname\fR
+Set name of the cramfs file system.
+.TP
+\fB\-p\fR
+Pad by 512 bytes for boot code.
+.TP
+\fB\-s\fR
+This option is ignored. Originally the \-s turned on directory entry
+sorting.
+.TP
+\fB\-z\fR
+Make explicit holes.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help text and exit.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display version information and exit.
+.SH EXIT STATUS
+.RS
+.PD 0
+.TP
+.B 0
+success
+.TP
+.B 8
+operation error, such as unable to allocate memory
+.PD
+.RE
+.SH SEE ALSO
+.BR fsck.cramfs (8),
+.BR mount (8)
+.SH AVAILABILITY
+The mkfs.cramfs command is part of the util-linux package and is available from
+.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/disk-utils/mkfs.cramfs.c b/disk-utils/mkfs.cramfs.c
new file mode 100644
index 0000000..1d0d17b
--- /dev/null
+++ b/disk-utils/mkfs.cramfs.c
@@ -0,0 +1,925 @@
+/*
+ * mkcramfs - make a cramfs file system
+ *
+ * Copyright (C) 1999-2002 Transmeta Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Old version would die on largish filesystems. Change to mmap the
+ * files one by one instead of all simultaneously. - aeb, 2002-11-01
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+#include <zconf.h>
+
+/* We don't use our include/crc32.h, but crc32 from zlib!
+ *
+ * The zlib implementation performs pre/post-conditioning. The util-linux
+ * imlemenation requires post-conditioning (xor) in the applications.
+ */
+#include <zlib.h>
+
+#include "c.h"
+#include "cramfs.h"
+#include "md5.h"
+#include "nls.h"
+#include "exitcodes.h"
+#include "strutils.h"
+
+#define CLOSE_EXIT_CODE MKFS_EX_ERROR
+#include "closestream.h"
+
+#define XALLOC_EXIT_CODE MKFS_EX_ERROR
+#include "xalloc.h"
+
+/* The kernel only supports PAD_SIZE of 0 and 512. */
+#define PAD_SIZE 512
+
+static int verbose = 0;
+
+static unsigned int blksize = 0; /* settable via -b option, default page size */
+static long total_blocks = 0, total_nodes = 1; /* pre-count the root node */
+static int image_length = 0;
+static int cramfs_is_big_endian = 0; /* target is big endian */
+
+/*
+ * If opt_holes is set, then mkcramfs can create explicit holes in the
+ * data, which saves 26 bytes per hole (which is a lot smaller a
+ * saving than for most filesystems).
+ *
+ * Note that kernels up to at least 2.3.39 don't support cramfs holes,
+ * which is why this is turned off by default.
+ */
+static unsigned int opt_edition = 0;
+static int opt_errors = 0;
+static int opt_holes = 0;
+static int opt_pad = 0;
+static char *opt_image = NULL;
+static char *opt_name = NULL;
+
+static int warn_dev = 0;
+static int warn_gid = 0;
+static int warn_namelen = 0;
+static int warn_skip = 0;
+static int warn_size = 0;
+static int warn_uid = 0;
+
+/* entry.flags */
+#define CRAMFS_EFLAG_MD5 1
+#define CRAMFS_EFLAG_INVALID 2
+
+/* In-core version of inode / directory entry. */
+struct entry {
+ /* stats */
+ unsigned char *name;
+ unsigned int mode, size, uid, gid;
+ unsigned char md5sum[UL_MD5LENGTH];
+ unsigned char flags; /* CRAMFS_EFLAG_* */
+
+ /* FS data */
+ char *path;
+ int fd; /* temporarily open files while mmapped */
+ struct entry *same; /* points to other identical file */
+ unsigned int offset; /* pointer to compressed data in archive */
+ unsigned int dir_offset; /* offset of directory entry in archive */
+
+ /* organization */
+ struct entry *child; /* NULL for non-directory and empty dir */
+ struct entry *next;
+};
+
+/*
+ * Width of various bitfields in struct cramfs_inode.
+ * Used only to generate warnings.
+ */
+#define CRAMFS_SIZE_WIDTH 24
+#define CRAMFS_UID_WIDTH 16
+#define CRAMFS_GID_WIDTH 8
+#define CRAMFS_OFFSET_WIDTH 26
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ fputs(USAGE_HEADER, stdout);
+ printf(_(" %s [-h] [-v] [-b blksize] [-e edition] [-N endian] [-i file] [-n name] dirname outfile\n"),
+ program_invocation_short_name);
+ fputs(USAGE_SEPARATOR, stdout);
+ puts(_("Make compressed ROM file system."));
+ fputs(USAGE_OPTIONS, stdout);
+ puts(_( " -v be verbose"));
+ puts(_( " -E make all warnings errors (non-zero exit status)"));
+ puts(_( " -b blksize use this blocksize, must equal page size"));
+ puts(_( " -e edition set edition number (part of fsid)"));
+ printf(_(" -N endian set cramfs endianness (%s|%s|%s), default %s\n"), "big", "little", "host", "host");
+ puts(_( " -i file insert a file image into the filesystem"));
+ puts(_( " -n name set name of cramfs filesystem"));
+ printf(_(" -p pad by %d bytes for boot code\n"), PAD_SIZE);
+ puts(_( " -s sort directory entries (old option, ignored)"));
+ puts(_( " -z make explicit holes"));
+ puts(_( " dirname root of the filesystem to be compressed"));
+ puts(_( " outfile output file"));
+ fputs(USAGE_SEPARATOR, stdout);
+ printf(USAGE_HELP_OPTIONS(16));
+ printf(USAGE_MAN_TAIL("mkfs.cramfs(8)"));
+ exit(MKFS_EX_OK);
+}
+
+static char *
+do_mmap(char *path, unsigned int size, unsigned int mode){
+ int fd;
+ char *start = NULL;
+
+ if (!size)
+ return NULL;
+
+ if (S_ISLNK(mode)) {
+ start = xmalloc(size);
+ if (readlink(path, start, size) < 0) {
+ warn(_("readlink failed: %s"), path);
+ warn_skip = 1;
+ goto err;
+ }
+ return start;
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ warn(_("cannot open %s"), path);
+ warn_skip = 1;
+ goto err;
+ }
+
+ start = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
+ if (start == MAP_FAILED)
+ err(MKFS_EX_ERROR, "mmap");
+ return start;
+err:
+ free(start);
+ return NULL;
+}
+
+static void
+do_munmap(char *start, unsigned int size, unsigned int mode){
+ if (S_ISLNK(mode))
+ free(start);
+ else
+ munmap(start, size);
+}
+
+/* compute md5sums, so that we do not have to compare every pair of files */
+static void
+mdfile(struct entry *e) {
+ char *start;
+
+ start = do_mmap(e->path, e->size, e->mode);
+ if (start == NULL) {
+ e->flags |= CRAMFS_EFLAG_INVALID;
+ } else {
+ UL_MD5_CTX ctx;
+
+ ul_MD5Init(&ctx);
+ ul_MD5Update(&ctx, (unsigned char *) start, e->size);
+ ul_MD5Final(e->md5sum, &ctx);
+
+ do_munmap(start, e->size, e->mode);
+
+ e->flags |= CRAMFS_EFLAG_MD5;
+ }
+}
+
+/* md5 digests are equal; files are almost certainly the same,
+ but just to be sure, do the comparison */
+static int
+identical_file(struct entry *e1, struct entry *e2){
+ char *start1, *start2;
+ int equal;
+
+ start1 = do_mmap(e1->path, e1->size, e1->mode);
+ if (!start1)
+ return 0;
+ start2 = do_mmap(e2->path, e2->size, e2->mode);
+ if (!start2) {
+ do_munmap(start1, e1->size, e1->mode);
+ return 0;
+ }
+ equal = !memcmp(start1, start2, e1->size);
+ do_munmap(start1, e1->size, e1->mode);
+ do_munmap(start2, e2->size, e2->mode);
+ return equal;
+}
+
+/*
+ * The longest file name component to allow for in the input directory tree.
+ * Ext2fs (and many others) allow up to 255 bytes. A couple of filesystems
+ * allow longer (e.g. smbfs 1024), but there isn't much use in supporting
+ * >255-byte names in the input directory tree given that such names get
+ * truncated to 255 bytes when written to cramfs.
+ */
+#define MAX_INPUT_NAMELEN 255
+
+static int find_identical_file(struct entry *orig, struct entry *new, loff_t *fslen_ub)
+{
+ if (orig == new)
+ return 1;
+ if (!orig)
+ return 0;
+ if (orig->size == new->size && orig->path) {
+ if (!orig->flags)
+ mdfile(orig);
+ if (!new->flags)
+ mdfile(new);
+
+ if ((orig->flags & CRAMFS_EFLAG_MD5) &&
+ (new->flags & CRAMFS_EFLAG_MD5) &&
+ !memcmp(orig->md5sum, new->md5sum, UL_MD5LENGTH) &&
+ identical_file(orig, new)) {
+ new->same = orig;
+ *fslen_ub -= new->size;
+ return 1;
+ }
+ }
+ return find_identical_file(orig->child, new, fslen_ub) ||
+ find_identical_file(orig->next, new, fslen_ub);
+}
+
+static void eliminate_doubles(struct entry *root, struct entry *orig, loff_t *fslen_ub) {
+ if (orig) {
+ if (orig->size && orig->path)
+ find_identical_file(root,orig, fslen_ub);
+ eliminate_doubles(root,orig->child, fslen_ub);
+ eliminate_doubles(root,orig->next, fslen_ub);
+ }
+}
+
+/*
+ * We define our own sorting function instead of using alphasort which
+ * uses strcoll and changes ordering based on locale information.
+ */
+static int cramsort (const struct dirent **a, const struct dirent **b)
+{
+ return strcmp((*a)->d_name, (*b)->d_name);
+}
+
+static unsigned int parse_directory(struct entry *root_entry, const char *name, struct entry **prev, loff_t *fslen_ub)
+{
+ struct dirent **dirlist;
+ int totalsize = 0, dircount, dirindex;
+ char *path, *endpath;
+ size_t len = strlen(name);
+
+ /* Set up the path. */
+ /* TODO: Reuse the parent's buffer to save memcpy'ing and duplication. */
+ path = xmalloc(len + 1 + MAX_INPUT_NAMELEN + 1);
+ memcpy(path, name, len);
+ endpath = path + len;
+ *endpath = '/';
+ endpath++;
+
+ /* read in the directory and sort */
+ dircount = scandir(name, &dirlist, NULL, cramsort);
+
+ if (dircount < 0)
+ err(MKFS_EX_ERROR, _("could not read directory %s"), name);
+
+ /* process directory */
+ for (dirindex = 0; dirindex < dircount; dirindex++) {
+ struct dirent *dirent;
+ struct entry *entry;
+ struct stat st;
+ int size;
+ size_t namelen;
+
+ dirent = dirlist[dirindex];
+
+ /* Ignore "." and ".." - we won't be adding them
+ to the archive */
+ if (dirent->d_name[0] == '.') {
+ if (dirent->d_name[1] == '\0')
+ continue;
+ if (dirent->d_name[1] == '.' &&
+ dirent->d_name[2] == '\0')
+ continue;
+ }
+ namelen = strlen(dirent->d_name);
+ if (namelen > MAX_INPUT_NAMELEN) {
+ namelen = MAX_INPUT_NAMELEN;
+ warn_namelen = 1;
+ }
+
+ memcpy(endpath, dirent->d_name, namelen + 1);
+
+ if (lstat(path, &st) < 0) {
+ warn(_("stat of %s failed"), endpath);
+ warn_skip = 1;
+ continue;
+ }
+ entry = xcalloc(1, sizeof(struct entry));
+ entry->name = (unsigned char *)xstrndup(dirent->d_name, namelen);
+ entry->mode = st.st_mode;
+ entry->size = st.st_size;
+ entry->uid = st.st_uid;
+ if (entry->uid >= 1 << CRAMFS_UID_WIDTH)
+ warn_uid = 1;
+ entry->gid = st.st_gid;
+ if (entry->gid >= 1 << CRAMFS_GID_WIDTH)
+ /* TODO: We ought to replace with a default
+ gid instead of truncating; otherwise there
+ are security problems. Maybe mode should
+ be &= ~070. Same goes for uid once Linux
+ supports >16-bit uids. */
+ warn_gid = 1;
+ size = sizeof(struct cramfs_inode) + ((namelen + 3) & ~3);
+ *fslen_ub += size;
+ if (S_ISDIR(st.st_mode)) {
+ entry->size = parse_directory(root_entry, path, &entry->child, fslen_ub);
+ } else if (S_ISREG(st.st_mode)) {
+ entry->path = xstrdup(path);
+ if (entry->size >= (1 << CRAMFS_SIZE_WIDTH)) {
+ warn_size = 1;
+ entry->size = (1 << CRAMFS_SIZE_WIDTH) - 1;
+ }
+ } else if (S_ISLNK(st.st_mode)) {
+ entry->path = xstrdup(path);
+ } else if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
+ /* maybe we should skip sockets */
+ entry->size = 0;
+ } else {
+ entry->size = st.st_rdev;
+ if (entry->size & -(1<<CRAMFS_SIZE_WIDTH))
+ warn_dev = 1;
+ }
+
+ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
+ int blocks = ((entry->size - 1) / blksize + 1);
+
+ /* block pointers & data expansion allowance + data */
+ if (entry->size)
+ *fslen_ub += (4+26)*blocks + entry->size + 3;
+ }
+
+ /* Link it into the list */
+ *prev = entry;
+ prev = &entry->next;
+ totalsize += size;
+ }
+ free(path);
+ free(dirlist); /* allocated by scandir() with malloc() */
+ return totalsize;
+}
+
+/* Returns sizeof(struct cramfs_super), which includes the root inode. */
+static unsigned int write_superblock(struct entry *root, char *base, int size)
+{
+ struct cramfs_super *super = (struct cramfs_super *) base;
+ unsigned int offset = sizeof(struct cramfs_super) + image_length;
+
+ if (opt_pad) {
+ offset += opt_pad;
+ }
+
+ super->magic = CRAMFS_MAGIC;
+ super->flags = CRAMFS_FLAG_FSID_VERSION_2 | CRAMFS_FLAG_SORTED_DIRS;
+ if (opt_holes)
+ super->flags |= CRAMFS_FLAG_HOLES;
+ if (image_length > 0)
+ super->flags |= CRAMFS_FLAG_SHIFTED_ROOT_OFFSET;
+ super->size = size;
+ memcpy(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature));
+
+ super->fsid.crc = crc32(0L, NULL, 0);
+ super->fsid.edition = opt_edition;
+ super->fsid.blocks = total_blocks;
+ super->fsid.files = total_nodes;
+
+ memset(super->name, 0x00, sizeof(super->name));
+ if (opt_name)
+ str2memcpy((char *)super->name, opt_name, sizeof(super->name));
+ else
+ str2memcpy((char *)super->name, "Compressed", sizeof(super->name));
+
+ super->root.mode = root->mode;
+ super->root.uid = root->uid;
+ super->root.gid = root->gid;
+ super->root.size = root->size;
+ super->root.offset = offset >> 2;
+
+ super_toggle_endianness(cramfs_is_big_endian, super);
+ inode_from_host(cramfs_is_big_endian, &super->root, &super->root);
+
+ return offset;
+}
+
+static void set_data_offset(struct entry *entry, char *base, unsigned long offset)
+{
+ struct cramfs_inode *inode = (struct cramfs_inode *) (base + entry->dir_offset);
+ inode_to_host(cramfs_is_big_endian, inode, inode);
+ if (offset >= (1 << (2 + CRAMFS_OFFSET_WIDTH)))
+ errx(MKFS_EX_ERROR, _("filesystem too big. Exiting."));
+ inode->offset = (offset >> 2);
+ inode_from_host(cramfs_is_big_endian, inode, inode);
+}
+
+
+/*
+ * We do a width-first printout of the directory
+ * entries, using a stack to remember the directories
+ * we've seen.
+ */
+static unsigned int write_directory_structure(struct entry *entry, char *base, unsigned int offset)
+{
+ int stack_entries = 0;
+ int stack_size = 64;
+ struct entry **entry_stack;
+
+ entry_stack = xmalloc(stack_size * sizeof(struct entry *));
+
+ for (;;) {
+ int dir_start = stack_entries;
+ while (entry) {
+ struct cramfs_inode *inode =
+ (struct cramfs_inode *) (base + offset);
+ size_t len = strlen((const char *)entry->name);
+
+ entry->dir_offset = offset;
+
+ inode->mode = entry->mode;
+ inode->uid = entry->uid;
+ inode->gid = entry->gid;
+ inode->size = entry->size;
+ inode->offset = 0;
+ /* Non-empty directories, regfiles and symlinks will
+ write over inode->offset later. */
+
+ offset += sizeof(struct cramfs_inode);
+ total_nodes++; /* another node */
+ memcpy(base + offset, entry->name, len);
+ /* Pad up the name to a 4-byte boundary */
+ while (len & 3) {
+ *(base + offset + len) = '\0';
+ len++;
+ }
+ inode->namelen = len >> 2;
+ offset += len;
+
+ if (verbose)
+ printf(" %s\n", entry->name);
+ if (entry->child) {
+ if (stack_entries >= stack_size) {
+ stack_size *= 2;
+ entry_stack = xrealloc(entry_stack, stack_size * sizeof(struct entry *));
+ }
+ entry_stack[stack_entries] = entry;
+ stack_entries++;
+ }
+ inode_from_host(cramfs_is_big_endian, inode, inode);
+ entry = entry->next;
+ }
+
+ /*
+ * Reverse the order the stack entries pushed during
+ * this directory, for a small optimization of disk
+ * access in the created fs. This change makes things
+ * `ls -UR' order.
+ */
+ {
+ struct entry **lo = entry_stack + dir_start;
+ struct entry **hi = entry_stack + stack_entries;
+ struct entry *tmp;
+
+ while (lo < --hi) {
+ tmp = *lo;
+ *lo++ = *hi;
+ *hi = tmp;
+ }
+ }
+
+ /* Pop a subdirectory entry from the stack, and recurse. */
+ if (!stack_entries)
+ break;
+ stack_entries--;
+ entry = entry_stack[stack_entries];
+
+ set_data_offset(entry, base, offset);
+ if (verbose)
+ printf("'%s':\n", entry->name);
+ entry = entry->child;
+ }
+ free(entry_stack);
+ return offset;
+}
+
+static int is_zero(unsigned char const *begin, unsigned len)
+{
+ if (opt_holes)
+ /* Returns non-zero iff the first LEN bytes from BEGIN are
+ all NULs. */
+ return (len-- == 0 ||
+ (begin[0] == '\0' &&
+ (len-- == 0 ||
+ (begin[1] == '\0' &&
+ (len-- == 0 ||
+ (begin[2] == '\0' &&
+ (len-- == 0 ||
+ (begin[3] == '\0' &&
+ memcmp(begin, begin + 4, len) == 0))))))));
+
+ /* Never create holes. */
+ return 0;
+}
+
+/*
+ * One 4-byte pointer per block and then the actual blocked
+ * output. The first block does not need an offset pointer,
+ * as it will start immediately after the pointer block;
+ * so the i'th pointer points to the end of the i'th block
+ * (i.e. the start of the (i+1)'th block or past EOF).
+ *
+ * Note that size > 0, as a zero-sized file wouldn't ever
+ * have gotten here in the first place.
+ */
+static unsigned int
+do_compress(char *base, unsigned int offset, unsigned char const *name,
+ char *path, unsigned int size, unsigned int mode)
+{
+ unsigned long original_size, original_offset, new_size, blocks, curr;
+ long change;
+ char *start;
+ Bytef *p;
+
+ /* get uncompressed data */
+ start = do_mmap(path, size, mode);
+ if (start == NULL)
+ return offset;
+ p = (Bytef *) start;
+
+ original_size = size;
+ original_offset = offset;
+ blocks = (size - 1) / blksize + 1;
+ curr = offset + 4 * blocks;
+
+ total_blocks += blocks;
+
+ do {
+ uLongf len = 2 * blksize;
+ uLongf input = size;
+ if (input > blksize)
+ input = blksize;
+ size -= input;
+ if (!is_zero (p, input)) {
+ compress((Bytef *)(base + curr), &len, p, input);
+ curr += len;
+ }
+ p += input;
+
+ if (len > blksize*2) {
+ /* (I don't think this can happen with zlib.) */
+ printf(_("AIEEE: block \"compressed\" to > "
+ "2*blocklength (%ld)\n"),
+ len);
+ exit(MKFS_EX_ERROR);
+ }
+
+ *(uint32_t *) (base + offset) = u32_toggle_endianness(cramfs_is_big_endian, curr);
+ offset += 4;
+ } while (size);
+
+ do_munmap(start, original_size, mode);
+
+ curr = (curr + 3) & ~3;
+ new_size = curr - original_offset;
+ /* TODO: Arguably, original_size in these 2 lines should be
+ st_blocks * 512. But if you say that, then perhaps
+ administrative data should also be included in both. */
+ change = new_size - original_size;
+ if (verbose)
+ printf(_("%6.2f%% (%+ld bytes)\t%s\n"),
+ (change * 100) / (double) original_size, change, name);
+
+ return curr;
+}
+
+
+/*
+ * Traverse the entry tree, writing data for every item that has
+ * non-null entry->path (i.e. every symlink and non-empty
+ * regfile).
+ */
+static unsigned int
+write_data(struct entry *entry, char *base, unsigned int offset) {
+ struct entry *e;
+
+ for (e = entry; e; e = e->next) {
+ if (e->path) {
+ if (e->same) {
+ set_data_offset(e, base, e->same->offset);
+ e->offset = e->same->offset;
+ } else if (e->size) {
+ set_data_offset(e, base, offset);
+ e->offset = offset;
+ offset = do_compress(base, offset, e->name,
+ e->path, e->size,e->mode);
+ }
+ } else if (e->child)
+ offset = write_data(e->child, base, offset);
+ }
+ return offset;
+}
+
+static unsigned int write_file(char *file, char *base, unsigned int offset)
+{
+ int fd;
+ char *buf;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ err(MKFS_EX_ERROR, _("cannot open %s"), file);
+ buf = mmap(NULL, image_length, PROT_READ, MAP_PRIVATE, fd, 0);
+ memcpy(base + offset, buf, image_length);
+ munmap(buf, image_length);
+ if (close (fd) < 0)
+ err(MKFS_EX_ERROR, _("cannot close file %s"), file);
+ /* Pad up the image_length to a 4-byte boundary */
+ while (image_length & 3) {
+ *(base + offset + image_length) = '\0';
+ image_length++;
+ }
+ return (offset + image_length);
+}
+
+/*
+ * Maximum size fs you can create is roughly 256MB. (The last file's
+ * data must begin within 256MB boundary but can extend beyond that.)
+ *
+ * Note that if you want it to fit in a ROM then you're limited to what the
+ * hardware and kernel can support (64MB?).
+ */
+static unsigned int
+maxfslen(void) {
+ return (((1 << CRAMFS_OFFSET_WIDTH) - 1) << 2) /* offset */
+ + (1 << CRAMFS_SIZE_WIDTH) - 1 /* filesize */
+ + (1 << CRAMFS_SIZE_WIDTH) * 4 / blksize; /* block pointers */
+}
+
+/*
+ * Usage:
+ *
+ * mkcramfs directory-name outfile
+ *
+ * where "directory-name" is simply the root of the directory
+ * tree that we want to generate a compressed filesystem out
+ * of.
+ */
+int main(int argc, char **argv)
+{
+ struct stat st; /* used twice... */
+ struct entry *root_entry;
+ char *rom_image;
+ ssize_t offset, written;
+ int fd;
+ /* initial guess (upper-bound) of required filesystem size */
+ loff_t fslen_ub = sizeof(struct cramfs_super);
+ unsigned int fslen_max;
+ char const *dirname, *outfile;
+ uint32_t crc = crc32(0L, NULL, 0);
+ int c;
+ cramfs_is_big_endian = HOST_IS_BIG_ENDIAN; /* default is to use host order */
+
+ total_blocks = 0;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ if (argc > 1) {
+ /* first arg may be one of our standard longopts */
+ if (!strcmp(argv[1], "--help"))
+ usage();
+ if (!strcmp(argv[1], "--version")) {
+ print_version(EXIT_SUCCESS);
+ exit(MKFS_EX_OK);
+ }
+ }
+ strutils_set_exitcode(MKFS_EX_USAGE);
+
+ /* command line options */
+ while ((c = getopt(argc, argv, "hb:Ee:i:n:N:psVvz")) != EOF) {
+ switch (c) {
+ case 'h':
+ usage();
+ case 'b':
+ blksize = strtou32_or_err(optarg, _("invalid blocksize argument"));
+ break;
+ case 'E':
+ opt_errors = 1;
+ break;
+ case 'e':
+ opt_edition = strtou32_or_err(optarg, _("invalid edition number argument"));
+ break;
+ case 'N':
+ if (strcmp(optarg, "big") == 0)
+ cramfs_is_big_endian = 1;
+ else if (strcmp(optarg, "little") == 0)
+ cramfs_is_big_endian = 0;
+ else if (strcmp(optarg, "host") == 0)
+ /* default */ ;
+ else
+ errx(MKFS_EX_USAGE, _("invalid endianness given;"
+ " must be 'big', 'little', or 'host'"));
+ break;
+ case 'i':
+ opt_image = optarg;
+ if (lstat(opt_image, &st) < 0)
+ err(MKFS_EX_USAGE, _("stat of %s failed"), opt_image);
+ image_length = st.st_size; /* may be padded later */
+ fslen_ub += (image_length + 3); /* 3 is for padding */
+ break;
+ case 'n':
+ opt_name = optarg;
+ break;
+ case 'p':
+ opt_pad = PAD_SIZE;
+ fslen_ub += PAD_SIZE;
+ break;
+ case 's':
+ /* old option, ignored */
+ break;
+ case 'V':
+ print_version(MKFS_EX_OK);
+ case 'v':
+ verbose = 1;
+ break;
+ case 'z':
+ opt_holes = 1;
+ break;
+ default:
+ errtryhelp(MKFS_EX_USAGE);
+ }
+ }
+
+ if ((argc - optind) != 2) {
+ warnx(_("bad usage"));
+ errtryhelp(MKFS_EX_USAGE);
+ }
+ dirname = argv[optind];
+ outfile = argv[optind + 1];
+
+ if (blksize == 0)
+ blksize = getpagesize();
+
+ if (stat(dirname, &st) < 0)
+ err(MKFS_EX_USAGE, _("stat of %s failed"), dirname);
+ fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0)
+ err(MKFS_EX_USAGE, _("cannot open %s"), outfile);
+
+ root_entry = xcalloc(1, sizeof(struct entry));
+ root_entry->mode = st.st_mode;
+ root_entry->uid = st.st_uid;
+ root_entry->gid = st.st_gid;
+
+ root_entry->size = parse_directory(root_entry, dirname, &root_entry->child, &fslen_ub);
+
+ /* find duplicate files */
+ eliminate_doubles(root_entry,root_entry, &fslen_ub);
+
+ /* always allocate a multiple of blksize bytes because that's
+ what we're going to write later on */
+ fslen_ub = ((fslen_ub - 1) | (blksize - 1)) + 1;
+ fslen_max = maxfslen();
+
+ if (fslen_ub > fslen_max) {
+ warnx( _("warning: guestimate of required size (upper bound) "
+ "is %lldMB, but maximum image size is %uMB. "
+ "We might die prematurely."),
+ (long long)fslen_ub >> 20,
+ fslen_max >> 20);
+ fslen_ub = fslen_max;
+ }
+
+ /* TODO: Why do we use a private/anonymous mapping here
+ followed by a write below, instead of just a shared mapping
+ and a couple of ftruncate calls? Is it just to save us
+ having to deal with removing the file afterwards? If we
+ really need this huge anonymous mapping, we ought to mmap
+ in smaller chunks, so that the user doesn't need nn MB of
+ RAM free. If the reason is to be able to write to
+ un-mmappable block devices, then we could try shared mmap
+ and revert to anonymous mmap if the shared mmap fails. */
+ rom_image = mmap(NULL,
+ fslen_ub?fslen_ub:1,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+
+ if (-1 == (int) (long) rom_image)
+ err(MKFS_EX_ERROR, _("ROM image map"));
+
+ /* Skip the first opt_pad bytes for boot loader code */
+ offset = opt_pad;
+ memset(rom_image, 0x00, opt_pad);
+
+ /* Skip the superblock and come back to write it later. */
+ offset += sizeof(struct cramfs_super);
+
+ /* Insert a file image. */
+ if (opt_image) {
+ if (verbose)
+ printf(_("Including: %s\n"), opt_image);
+ offset = write_file(opt_image, rom_image, offset);
+ }
+
+ offset = write_directory_structure(root_entry->child, rom_image, offset);
+ if (verbose)
+ printf(_("Directory data: %zd bytes\n"), offset);
+
+ offset = write_data(root_entry, rom_image, offset);
+
+ /* We always write a multiple of blksize bytes, so that
+ losetup works. */
+ offset = ((offset - 1) | (blksize - 1)) + 1;
+ if (verbose)
+ printf(_("Everything: %zd kilobytes\n"), offset >> 10);
+
+ /* Write the superblock now that we can fill in all of the fields. */
+ write_superblock(root_entry, rom_image+opt_pad, offset);
+ if (verbose)
+ printf(_("Super block: %zd bytes\n"),
+ sizeof(struct cramfs_super));
+
+ /* Put the checksum in. */
+ crc = crc32(crc, (unsigned char *) (rom_image+opt_pad), (offset-opt_pad));
+ ((struct cramfs_super *) (rom_image+opt_pad))->fsid.crc = u32_toggle_endianness(cramfs_is_big_endian, crc);
+ if (verbose)
+ printf(_("CRC: %x\n"), crc);
+
+ /* Check to make sure we allocated enough space. */
+ if (fslen_ub < offset)
+ errx(MKFS_EX_ERROR,
+ _("not enough space allocated for ROM image "
+ "(%lld allocated, %zu used)"),
+ (long long) fslen_ub, offset);
+
+ written = write(fd, rom_image, offset);
+ if (offset != written)
+ errx(MKFS_EX_ERROR, _("ROM image write failed (%zd %zd)"),
+ written, offset);
+ if (close_fd(fd) != 0)
+ err(MKFS_EX_ERROR, _("ROM image"));
+
+ /*
+ * (These warnings used to come at the start, but they scroll off
+ * the screen too quickly.)
+ */
+ if (warn_namelen)
+ /* Can't happen when reading from ext2fs. */
+ /* Bytes, not chars: think UTF8. */
+ warnx(_("warning: filenames truncated to %u bytes."), MAX_INPUT_NAMELEN);
+ if (warn_skip)
+ warnx(_("warning: files were skipped due to errors."));
+ if (warn_size)
+ warnx(_("warning: file sizes truncated to %luMB "
+ "(minus 1 byte)."), 1L << (CRAMFS_SIZE_WIDTH - 20));
+ if (warn_uid)
+ /* (not possible with current Linux versions) */
+ warnx(_("warning: uids truncated to %u bits. "
+ "(This may be a security concern.)"), CRAMFS_UID_WIDTH);
+ if (warn_gid)
+ warnx(_("warning: gids truncated to %u bits. "
+ "(This may be a security concern.)"), CRAMFS_GID_WIDTH);
+ if (warn_dev)
+ warnx(_("WARNING: device numbers truncated to %u bits. "
+ "This almost certainly means\n"
+ "that some device files will be wrong."),
+ CRAMFS_OFFSET_WIDTH);
+ if (opt_errors &&
+ (warn_namelen|warn_skip|warn_size|warn_uid|warn_gid|warn_dev))
+ exit(MKFS_EX_ERROR);
+
+ return MKFS_EX_OK;
+}
diff --git a/disk-utils/mkfs.minix.8 b/disk-utils/mkfs.minix.8
new file mode 100644
index 0000000..a33d844
--- /dev/null
+++ b/disk-utils/mkfs.minix.8
@@ -0,0 +1,89 @@
+.\" Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be freely distributed.
+.TH MKFS.MINIX 8 "June 2015" "util-linux" "System Administration"
+.SH NAME
+mkfs.minix \- make a Minix filesystem
+.SH SYNOPSIS
+.B mkfs.minix
+[options]
+.I device
+.RI [ size-in-blocks ]
+.SH DESCRIPTION
+.B mkfs.minix
+creates a Linux MINIX filesystem on a device (usually a disk partition).
+
+The
+.I device
+is usually of the following form:
+
+.nf
+.RS
+/dev/hda[1\(en8] (IDE disk 1)
+/dev/hdb[1\(en8] (IDE disk 2)
+/dev/sda[1\(en8] (SCSI disk 1)
+/dev/sdb[1\(en8] (SCSI disk 2)
+.RE
+.fi
+
+The device may be a block device or an image file of one, but this is not
+enforced. Expect not much fun on a character device :-).
+.PP
+The
+.I size-in-blocks
+parameter is the desired size of the file system, in blocks.
+It is present only for backwards compatibility.
+If omitted the size will be determined automatically.
+Only block counts strictly greater than 10 and strictly less than
+65536 are allowed.
+.SH OPTIONS
+.TP
+\fB\-c\fR, \fB\-\-check\fR
+Check the device for bad blocks before creating the filesystem. If any
+are found, the count is printed.
+.TP
+\fB\-n\fR, \fB\-\-namelength\fR \fIlength\fR
+Specify the maximum length of filenames. Currently, the only allowable
+values are 14 and 30 for file system versions 1 and 2. Version 3 allows
+only value 60. The default is 30.
+.TP
+\fB\-i\fR, \fB\-\-inodes\fR \fInumber\fR
+Specify the number of inodes for the filesystem.
+.TP
+\fB\-l\fR, \fB\-\-badblocks\fR \fIfilename\fR
+Read the list of bad blocks from
+.IR filename .
+The file has one bad-block number per line. The count of bad blocks read
+is printed.
+.TP
+.B \-1
+Make a Minix version 1 filesystem. This is the default.
+.TP
+.BR \-2 , " \-v"
+Make a Minix version 2 filesystem.
+.TP
+.B \-3
+Make a Minix version 3 filesystem.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display version information and exit. The long option cannot be combined
+with other options.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help text and exit.
+.SH EXIT STATUS
+The exit status returned by
+.B mkfs.minix
+is one of the following:
+.IP 0
+No errors
+.IP 8
+Operational error
+.IP 16
+Usage or syntax error
+.SH SEE ALSO
+.BR fsck (8),
+.BR mkfs (8),
+.BR reboot (8)
+.SH AVAILABILITY
+The mkfs.minix command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/mkfs.minix.c b/disk-utils/mkfs.minix.c
new file mode 100644
index 0000000..d6f5756
--- /dev/null
+++ b/disk-utils/mkfs.minix.c
@@ -0,0 +1,840 @@
+/*
+ * mkfs.minix.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)
+ *
+ * 06.29.11 - Overall cleanups for util-linux and v3 support
+ * Davidlohr Bueso <dave@gnu.org>
+ *
+ * 06.20.15 - Do not infinite loop or crash on large devices
+ * Joshua Hudson <joshudson@gmail.com>
+ *
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <err.h>
+
+#include "blkdev.h"
+#include "minix_programs.h"
+#include "nls.h"
+#include "pathnames.h"
+#include "bitops.h"
+#include "exitcodes.h"
+#include "strutils.h"
+#include "all-io.h"
+#include "closestream.h"
+#include "ismounted.h"
+
+#define XALLOC_EXIT_CODE MKFS_EX_ERROR
+#include "xalloc.h"
+
+#define MINIX_ROOT_INO 1
+#define MINIX_BAD_INO 2
+
+#define TEST_BUFFER_BLOCKS 16
+#define MAX_GOOD_BLOCKS 512
+
+#define MINIX_MAX_INODES 65535
+
+#define DEFAULT_FS_VERSION 1
+
+/*
+ * Global variables used in minix_programs.h inline functions
+ */
+int fs_version = DEFAULT_FS_VERSION;
+char *super_block_buffer;
+
+static char *inode_buffer = NULL;
+
+#define Inode (((struct minix_inode *) inode_buffer) - 1)
+#define Inode2 (((struct minix2_inode *) inode_buffer) - 1)
+
+struct fs_control {
+ char *device_name; /* device on a Minix file system is created */
+ int device_fd; /* open file descriptor of the device */
+ unsigned long long fs_blocks; /* device block count for the file system */
+ int fs_used_blocks; /* used blocks on a device */
+ int fs_bad_blocks; /* number of bad blocks found from device */
+ uint16_t fs_namelen; /* maximum length of filenames */
+ size_t fs_dirsize; /* maximum size of directory */
+ unsigned long fs_inodes; /* number of inodes */
+ int fs_magic; /* file system magic number */
+ unsigned int
+ check_blocks:1; /* check for bad blocks */
+};
+
+static char root_block[MINIX_BLOCK_SIZE];
+static char boot_block_buffer[512];
+static unsigned short good_blocks_table[MAX_GOOD_BLOCKS];
+
+static char *inode_map;
+static char *zone_map;
+
+#define zone_in_use(x) (isset(zone_map,(x)-get_first_zone()+1) != 0)
+
+#define mark_inode(x) (setbit(inode_map,(x)))
+#define unmark_inode(x) (clrbit(inode_map,(x)))
+
+#define mark_zone(x) (setbit(zone_map,(x)-get_first_zone()+1))
+#define unmark_zone(x) (clrbit(zone_map,(x)-get_first_zone()+1))
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s [options] /dev/name [blocks]\n"), program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -1 use Minix version 1\n"), out);
+ fputs(_(" -2, -v use Minix version 2\n"), out);
+ fputs(_(" -3 use Minix version 3\n"), out);
+ fputs(_(" -n, --namelength <num> maximum length of filenames\n"), out);
+ fputs(_(" -i, --inodes <num> number of inodes for the filesystem\n"), out);
+ fputs(_(" -c, --check check the device for bad blocks\n"), out);
+ fputs(_(" -l, --badblocks <file> list of bad blocks from file\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ printf(USAGE_HELP_OPTIONS(25));
+ printf(USAGE_MAN_TAIL("mkfs.minix(8)"));
+ exit(MKFS_EX_OK);
+}
+
+#ifdef TEST_SCRIPT
+static inline time_t mkfs_minix_time(time_t *t)
+{
+ const char *str = getenv("MKFS_MINIX_TEST_SECOND_SINCE_EPOCH");
+ time_t sec;
+
+ if (str && sscanf(str, "%ld", &sec) == 1)
+ return sec;
+ return time(t);
+}
+#else /* !TEST_SCRIPT */
+# define mkfs_minix_time(x) time(x)
+#endif
+
+static void super_set_state(void)
+{
+ switch (fs_version) {
+ case 1:
+ case 2:
+ Super.s_state |= MINIX_VALID_FS;
+ Super.s_state &= ~MINIX_ERROR_FS;
+ break;
+ default: /* v3 */
+ break;
+ }
+}
+
+static void write_tables(const struct fs_control *ctl) {
+ unsigned long imaps = get_nimaps();
+ unsigned long zmaps = get_nzmaps();
+ size_t buffsz = get_inode_buffer_size();
+
+ /* Mark the super block valid. */
+ super_set_state();
+
+ if (lseek(ctl->device_fd, 0, SEEK_SET))
+ err(MKFS_EX_ERROR, _("%s: seek to boot block failed "
+ " in write_tables"), ctl->device_name);
+ if (write_all(ctl->device_fd, boot_block_buffer, 512))
+ err(MKFS_EX_ERROR, _("%s: unable to clear boot sector"), ctl->device_name);
+ if (MINIX_BLOCK_SIZE != lseek(ctl->device_fd, MINIX_BLOCK_SIZE, SEEK_SET))
+ err(MKFS_EX_ERROR, _("%s: seek failed in write_tables"), ctl->device_name);
+
+ if (write_all(ctl->device_fd, super_block_buffer, MINIX_BLOCK_SIZE))
+ err(MKFS_EX_ERROR, _("%s: unable to write super-block"), ctl->device_name);
+
+ if (write_all(ctl->device_fd, inode_map, imaps * MINIX_BLOCK_SIZE))
+ err(MKFS_EX_ERROR, _("%s: unable to write inode map"), ctl->device_name);
+
+ if (write_all(ctl->device_fd, zone_map, zmaps * MINIX_BLOCK_SIZE))
+ err(MKFS_EX_ERROR, _("%s: unable to write zone map"), ctl->device_name);
+
+ if (write_all(ctl->device_fd, inode_buffer, buffsz))
+ err(MKFS_EX_ERROR, _("%s: unable to write inodes"), ctl->device_name);
+}
+
+static void write_block(const struct fs_control *ctl, int blk, char * buffer) {
+ if (blk * MINIX_BLOCK_SIZE != lseek(ctl->device_fd, blk * MINIX_BLOCK_SIZE, SEEK_SET))
+ errx(MKFS_EX_ERROR, _("%s: seek failed in write_block"), ctl->device_name);
+
+ if (write_all(ctl->device_fd, buffer, MINIX_BLOCK_SIZE))
+ errx(MKFS_EX_ERROR, _("%s: write failed in write_block"), ctl->device_name);
+}
+
+static int get_free_block(struct fs_control *ctl) {
+ unsigned int blk;
+ unsigned int zones = get_nzones();
+ unsigned int first_zone = get_first_zone();
+
+ if (ctl->fs_used_blocks + 1 >= MAX_GOOD_BLOCKS)
+ errx(MKFS_EX_ERROR, _("%s: too many bad blocks"), ctl->device_name);
+ if (ctl->fs_used_blocks)
+ blk = good_blocks_table[ctl->fs_used_blocks - 1] + 1;
+ else
+ blk = first_zone;
+ while (blk < zones && zone_in_use(blk))
+ blk++;
+ if (blk >= zones)
+ errx(MKFS_EX_ERROR, _("%s: not enough good blocks"), ctl->device_name);
+ good_blocks_table[ctl->fs_used_blocks] = blk;
+ ctl->fs_used_blocks++;
+ return blk;
+}
+
+static void mark_good_blocks(const struct fs_control *ctl) {
+ int blk;
+
+ for (blk=0 ; blk < ctl->fs_used_blocks ; blk++)
+ mark_zone(good_blocks_table[blk]);
+}
+
+static inline int next(unsigned long zone) {
+ unsigned long zones = get_nzones();
+ unsigned long first_zone = get_first_zone();
+
+ if (!zone)
+ zone = first_zone-1;
+ while (++zone < zones)
+ if (zone_in_use(zone))
+ return zone;
+ return 0;
+}
+
+static void make_bad_inode_v1(struct fs_control *ctl)
+{
+ struct minix_inode * inode = &Inode[MINIX_BAD_INO];
+ int i,j,zone;
+ int ind=0,dind=0;
+ unsigned short ind_block[MINIX_BLOCK_SIZE>>1];
+ unsigned short dind_block[MINIX_BLOCK_SIZE>>1];
+
+#define NEXT_BAD (zone = next(zone))
+
+ if (!ctl->fs_bad_blocks)
+ return;
+ mark_inode(MINIX_BAD_INO);
+ inode->i_nlinks = 1;
+ inode->i_time = mkfs_minix_time(NULL);
+ inode->i_mode = S_IFREG + 0000;
+ inode->i_size = ctl->fs_bad_blocks * MINIX_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(ctl);
+ memset(ind_block,0,MINIX_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(ctl);
+ memset(dind_block,0,MINIX_BLOCK_SIZE);
+ for (i=0 ; i<512 ; i++) {
+ write_block(ctl, ind,(char *) ind_block);
+ dind_block[i] = ind = get_free_block(ctl);
+ memset(ind_block,0,MINIX_BLOCK_SIZE);
+ for (j=0 ; j<512 ; j++) {
+ ind_block[j] = zone;
+ if (!NEXT_BAD)
+ goto end_bad;
+ }
+ }
+ errx(MKFS_EX_ERROR, _("%s: too many bad blocks"), ctl->device_name);
+end_bad:
+ if (ind)
+ write_block(ctl, ind, (char *) ind_block);
+ if (dind)
+ write_block(ctl, dind, (char *) dind_block);
+}
+
+static void make_bad_inode_v2_v3 (struct fs_control *ctl)
+{
+ struct minix2_inode *inode = &Inode2[MINIX_BAD_INO];
+ int i, j, zone;
+ int ind = 0, dind = 0;
+ unsigned long ind_block[MINIX_BLOCK_SIZE >> 2];
+ unsigned long dind_block[MINIX_BLOCK_SIZE >> 2];
+
+ if (!ctl->fs_bad_blocks)
+ return;
+ mark_inode (MINIX_BAD_INO);
+ inode->i_nlinks = 1;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = mkfs_minix_time(NULL);
+ inode->i_mode = S_IFREG + 0000;
+ inode->i_size = ctl->fs_bad_blocks * MINIX_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 (ctl);
+ memset (ind_block, 0, MINIX_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 (ctl);
+ memset (dind_block, 0, MINIX_BLOCK_SIZE);
+ for (i = 0; i < 256; i++) {
+ write_block (ctl, ind, (char *) ind_block);
+ dind_block[i] = ind = get_free_block (ctl);
+ memset (ind_block, 0, MINIX_BLOCK_SIZE);
+ for (j = 0; j < 256; j++) {
+ ind_block[j] = zone;
+ if (!NEXT_BAD)
+ goto end_bad;
+ }
+ }
+ /* Could make triple indirect block here */
+ errx(MKFS_EX_ERROR, _("%s: too many bad blocks"), ctl->device_name);
+ end_bad:
+ if (ind)
+ write_block (ctl, ind, (char *) ind_block);
+ if (dind)
+ write_block (ctl, dind, (char *) dind_block);
+}
+
+static void make_bad_inode(struct fs_control *ctl)
+{
+ if (fs_version < 2) {
+ make_bad_inode_v1(ctl);
+ return;
+ }
+ make_bad_inode_v2_v3(ctl);
+}
+
+static void make_root_inode_v1(struct fs_control *ctl) {
+ struct minix_inode * inode = &Inode[MINIX_ROOT_INO];
+
+ mark_inode(MINIX_ROOT_INO);
+ inode->i_zone[0] = get_free_block(ctl);
+ inode->i_nlinks = 2;
+ inode->i_time = mkfs_minix_time(NULL);
+ if (ctl->fs_bad_blocks)
+ inode->i_size = 3 * ctl->fs_dirsize;
+ else {
+ memset(&root_block[2 * ctl->fs_dirsize], 0, ctl->fs_dirsize);
+ inode->i_size = 2 * ctl->fs_dirsize;
+ }
+ inode->i_mode = S_IFDIR + 0755;
+ inode->i_uid = getuid();
+ if (inode->i_uid)
+ inode->i_gid = getgid();
+ write_block(ctl, inode->i_zone[0],root_block);
+}
+
+static void make_root_inode_v2_v3 (struct fs_control *ctl) {
+ struct minix2_inode *inode = &Inode2[MINIX_ROOT_INO];
+
+ mark_inode (MINIX_ROOT_INO);
+ inode->i_zone[0] = get_free_block (ctl);
+ inode->i_nlinks = 2;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = mkfs_minix_time(NULL);
+
+ if (ctl->fs_bad_blocks)
+ inode->i_size = 3 * ctl->fs_dirsize;
+ else {
+ memset(&root_block[2 * ctl->fs_dirsize], 0, ctl->fs_dirsize);
+ inode->i_size = 2 * ctl->fs_dirsize;
+ }
+
+ inode->i_mode = S_IFDIR + 0755;
+ inode->i_uid = getuid();
+ if (inode->i_uid)
+ inode->i_gid = getgid();
+ write_block (ctl, inode->i_zone[0], root_block);
+}
+
+static void make_root_inode(struct fs_control *ctl)
+{
+ char *tmp = root_block;
+
+ if (fs_version == 3) {
+ *(uint32_t *) tmp = 1;
+ strcpy(tmp + 4, ".");
+ tmp += ctl->fs_dirsize;
+ *(uint32_t *) tmp = 1;
+ strcpy(tmp + 4, "..");
+ tmp += ctl->fs_dirsize;
+ *(uint32_t *) tmp = 2;
+ strcpy(tmp + 4, ".badblocks");
+ } else {
+ *(uint16_t *) tmp = 1;
+ strcpy(tmp + 2, ".");
+ tmp += ctl->fs_dirsize;
+ *(uint16_t *) tmp = 1;
+ strcpy(tmp + 2, "..");
+ tmp += ctl->fs_dirsize;
+ *(uint16_t *) tmp = 2;
+ strcpy(tmp + 2, ".badblocks");
+ }
+ if (fs_version < 2) {
+ make_root_inode_v1(ctl);
+ return;
+ }
+ make_root_inode_v2_v3(ctl);
+}
+
+static void super_set_nzones(const struct fs_control *ctl)
+{
+ switch (fs_version) {
+ case 3:
+ Super3.s_zones = ctl->fs_blocks;
+ break;
+ case 2:
+ Super.s_zones = ctl->fs_blocks;
+ break;
+ default: /* v1 */
+ Super.s_nzones = ctl->fs_blocks;
+ break;
+ }
+}
+
+static void super_init_maxsize(void)
+{
+ switch (fs_version) {
+ case 3:
+ Super3.s_max_size = 2147483647L;
+ break;
+ case 2:
+ Super.s_max_size = 0x7fffffff;
+ break;
+ default: /* v1 */
+ Super.s_max_size = (7+512+512*512)*1024;
+ break;
+ }
+}
+
+static void super_set_map_blocks(const struct fs_control *ctl, unsigned long inodes)
+{
+ switch (fs_version) {
+ case 3:
+ Super3.s_imap_blocks = UPPER(inodes + 1, BITS_PER_BLOCK);
+ Super3.s_zmap_blocks = UPPER(ctl->fs_blocks - (1 + get_nimaps() + inode_blocks()),
+ BITS_PER_BLOCK + 1);
+ Super3.s_firstdatazone = first_zone_data();
+ break;
+ default:
+ Super.s_imap_blocks = UPPER(inodes + 1, BITS_PER_BLOCK);
+ Super.s_zmap_blocks = UPPER(ctl->fs_blocks - (1 + get_nimaps() + inode_blocks()),
+ BITS_PER_BLOCK + 1);
+ Super.s_firstdatazone = first_zone_data();
+ break;
+ }
+}
+
+static void super_set_magic(const struct fs_control *ctl)
+{
+ switch (fs_version) {
+ case 3:
+ Super3.s_magic = ctl->fs_magic;
+ break;
+ default:
+ Super.s_magic = ctl->fs_magic;
+ break;
+ }
+}
+
+static void setup_tables(const struct fs_control *ctl) {
+ unsigned long inodes, zmaps, imaps, zones, i;
+
+ super_block_buffer = xcalloc(1, MINIX_BLOCK_SIZE);
+
+ memset(boot_block_buffer,0,512);
+ super_set_magic(ctl);
+
+ if (fs_version == 3) {
+ Super3.s_log_zone_size = 0;
+ Super3.s_blocksize = MINIX_BLOCK_SIZE;
+ }
+ else {
+ Super.s_log_zone_size = 0;
+ }
+
+ super_init_maxsize();
+ super_set_nzones(ctl);
+ zones = get_nzones();
+
+ /* some magic nrs: 1 inode / 3 blocks for smaller filesystems,
+ * for one inode / 16 blocks for large ones. mkfs will eventually
+ * crab about too far when getting close to the maximum size. */
+ if (ctl->fs_inodes == 0)
+ if (2048 * 1024 < ctl->fs_blocks) /* 2GB */
+ inodes = ctl->fs_blocks / 16;
+ else if (512 * 1024 < ctl->fs_blocks) /* 0.5GB */
+ inodes = ctl->fs_blocks / 8;
+ else
+ inodes = ctl->fs_blocks / 3;
+ else
+ inodes = ctl->fs_inodes;
+ /* Round up inode count to fill block size */
+ if (fs_version == 2 || fs_version == 3)
+ inodes = ((inodes + MINIX2_INODES_PER_BLOCK - 1) &
+ ~(MINIX2_INODES_PER_BLOCK - 1));
+ else
+ inodes = ((inodes + MINIX_INODES_PER_BLOCK - 1) &
+ ~(MINIX_INODES_PER_BLOCK - 1));
+
+ if (fs_version == 3)
+ Super3.s_ninodes = inodes;
+ else {
+ if (inodes > MINIX_MAX_INODES)
+ inodes = MINIX_MAX_INODES;
+ Super.s_ninodes = inodes;
+ }
+ super_set_map_blocks(ctl, inodes);
+ if (MINIX_MAX_INODES < first_zone_data())
+ errx(MKFS_EX_ERROR,
+ _("First data block at %jd, which is too far (max %d).\n"
+ "Try specifying fewer inodes by passing --inodes <num>"),
+ (intmax_t)first_zone_data(),
+ MINIX_MAX_INODES);
+ imaps = get_nimaps();
+ zmaps = get_nzmaps();
+
+ inode_map = xmalloc(imaps * MINIX_BLOCK_SIZE);
+ zone_map = xmalloc(zmaps * MINIX_BLOCK_SIZE);
+ memset(inode_map,0xff,imaps * MINIX_BLOCK_SIZE);
+ memset(zone_map,0xff,zmaps * MINIX_BLOCK_SIZE);
+
+ for (i = get_first_zone() ; i<zones ; i++)
+ unmark_zone(i);
+ for (i = MINIX_ROOT_INO ; i<=inodes; i++)
+ unmark_inode(i);
+
+ inode_buffer = xmalloc(get_inode_buffer_size());
+ memset(inode_buffer,0, get_inode_buffer_size());
+
+ printf(P_("%lu inode\n", "%lu inodes\n", inodes), inodes);
+ printf(P_("%lu block\n", "%lu blocks\n", zones), zones);
+ printf(_("Firstdatazone=%jd (%jd)\n"),
+ (intmax_t)get_first_zone(), (intmax_t)first_zone_data());
+ printf(_("Zonesize=%zu\n"), (size_t) MINIX_BLOCK_SIZE << get_zone_size());
+ printf(_("Maxsize=%zu\n\n"),get_max_size());
+}
+
+/*
+ * Perform a test of a block; return the number of
+ * blocks readable/writable.
+ */
+static size_t do_check(const struct fs_control *ctl, char * buffer, int try, unsigned int current_block) {
+ ssize_t got;
+
+ /* Seek to the correct loc. */
+ if (lseek(ctl->device_fd, current_block * MINIX_BLOCK_SIZE, SEEK_SET) !=
+ current_block * MINIX_BLOCK_SIZE )
+ err(MKFS_EX_ERROR, _("%s: seek failed during testing of blocks"),
+ ctl->device_name);
+
+ /* Try the read */
+ got = read(ctl->device_fd, buffer, try * MINIX_BLOCK_SIZE);
+ if (got < 0) got = 0;
+ if (got & (MINIX_BLOCK_SIZE - 1 )) {
+ printf(_("Weird values in do_check: probably bugs\n"));
+ }
+ got /= MINIX_BLOCK_SIZE;
+ return got;
+}
+
+static unsigned int currently_testing = 0;
+
+static void alarm_intr(int alnum __attribute__ ((__unused__))) {
+ unsigned long zones = get_nzones();
+
+ if (currently_testing >= zones)
+ return;
+ signal(SIGALRM,alarm_intr);
+ alarm(5);
+ if (!currently_testing)
+ return;
+ printf("%d ...", currently_testing);
+ fflush(stdout);
+}
+
+static void check_blocks(struct fs_control *ctl) {
+ size_t try, got;
+ static char buffer[MINIX_BLOCK_SIZE * TEST_BUFFER_BLOCKS];
+ unsigned long zones = get_nzones();
+ unsigned long first_zone = get_first_zone();
+
+ currently_testing=0;
+ signal(SIGALRM,alarm_intr);
+ alarm(5);
+ while (currently_testing < zones) {
+ if (lseek(ctl->device_fd, currently_testing * MINIX_BLOCK_SIZE,SEEK_SET) !=
+ currently_testing*MINIX_BLOCK_SIZE)
+ errx(MKFS_EX_ERROR, _("%s: seek failed in check_blocks"),
+ ctl->device_name);
+ try = TEST_BUFFER_BLOCKS;
+ if (currently_testing + try > zones)
+ try = zones-currently_testing;
+ got = do_check(ctl, buffer, try, currently_testing);
+ currently_testing += got;
+ if (got == try)
+ continue;
+ if (currently_testing < first_zone)
+ errx(MKFS_EX_ERROR, _("%s: bad blocks before data-area: "
+ "cannot make fs"), ctl->device_name);
+ mark_zone(currently_testing);
+ ctl->fs_bad_blocks++;
+ currently_testing++;
+ }
+ if (ctl->fs_bad_blocks > 0)
+ printf(P_("%d bad block\n", "%d bad blocks\n", ctl->fs_bad_blocks), ctl->fs_bad_blocks);
+}
+
+static void get_list_blocks(struct fs_control *ctl, char *filename) {
+ FILE *listfile;
+ unsigned long blockno;
+
+ listfile = fopen(filename,"r");
+ if (listfile == NULL)
+ err(MKFS_EX_ERROR, _("%s: can't open file of bad blocks"),
+ ctl->device_name);
+
+ while (!feof(listfile)) {
+ if (fscanf(listfile,"%lu\n", &blockno) != 1) {
+ printf(_("badblock number input error on line %d\n"), ctl->fs_bad_blocks + 1);
+ errx(MKFS_EX_ERROR, _("%s: cannot read badblocks file"),
+ ctl->device_name);
+ }
+ mark_zone(blockno);
+ ctl->fs_bad_blocks++;
+ }
+ fclose(listfile);
+
+ if (ctl->fs_bad_blocks > 0)
+ printf(P_("%d bad block\n", "%d bad blocks\n", ctl->fs_bad_blocks), ctl->fs_bad_blocks);
+}
+
+static int find_super_magic(const struct fs_control *ctl)
+{
+ switch (fs_version) {
+ case 1:
+ if (ctl->fs_namelen == 14)
+ return MINIX_SUPER_MAGIC;
+ return MINIX_SUPER_MAGIC2;
+ case 2:
+ if (ctl->fs_namelen == 14)
+ return MINIX2_SUPER_MAGIC;
+ return MINIX2_SUPER_MAGIC2;
+ case 3:
+ return MINIX3_SUPER_MAGIC;
+ default:
+ abort();
+ }
+}
+
+static void determine_device_blocks(struct fs_control *ctl, const struct stat *statbuf)
+{
+ unsigned long long dev_blocks = 0;
+
+ if (S_ISBLK(statbuf->st_mode)) {
+ int sectorsize;
+
+ if (blkdev_get_sector_size(ctl->device_fd, &sectorsize) == -1)
+ sectorsize = DEFAULT_SECTOR_SIZE; /* kernel < 2.3.3 */
+ if (MINIX_BLOCK_SIZE < sectorsize)
+ errx(MKFS_EX_ERROR, _("block size smaller than physical "
+ "sector size of %s"), ctl->device_name);
+ if (blkdev_get_size(ctl->device_fd, &dev_blocks) == -1)
+ errx(MKFS_EX_ERROR, _("cannot determine size of %s"), ctl->device_name);
+ dev_blocks /= MINIX_BLOCK_SIZE;
+ } else if (!S_ISBLK(statbuf->st_mode))
+ dev_blocks = statbuf->st_size / MINIX_BLOCK_SIZE;
+ if (!ctl->fs_blocks)
+ ctl->fs_blocks = dev_blocks;
+ else if (dev_blocks < ctl->fs_blocks)
+ errx(MKFS_EX_ERROR,
+ _("%s: requested blocks (%llu) exceeds available (%llu) blocks\n"),
+ ctl->device_name, ctl->fs_blocks, dev_blocks);
+ if (ctl->fs_blocks < 10)
+ errx(MKFS_EX_ERROR, _("%s: number of blocks too small"), ctl->device_name);
+ if (fs_version == 1 && ctl->fs_blocks > MINIX_MAX_INODES)
+ ctl->fs_blocks = MINIX_MAX_INODES;
+ if (ctl->fs_blocks > (4 + ((MINIX_MAX_INODES - 4) * BITS_PER_BLOCK)))
+ ctl->fs_blocks = 4 + ((MINIX_MAX_INODES - 4) * BITS_PER_BLOCK); /* Utter maximum: Clip. */
+}
+
+static void check_user_instructions(struct fs_control *ctl)
+{
+ switch (fs_version) {
+ case 1:
+ case 2:
+ if (ctl->fs_namelen == 14 || ctl->fs_namelen == 30)
+ ctl->fs_dirsize = ctl->fs_namelen + 2;
+ else
+ errx(MKFS_EX_ERROR, _("unsupported name length: %d"), ctl->fs_namelen);
+ break;
+ case 3:
+ if (ctl->fs_namelen == 60)
+ ctl->fs_dirsize = ctl->fs_namelen + 4;
+ else
+ errx(MKFS_EX_ERROR, _("unsupported name length: %d"), ctl->fs_namelen);
+ break;
+ default:
+ errx(MKFS_EX_ERROR, _("unsupported minix file system version: %d"), fs_version);
+ }
+ ctl->fs_magic = find_super_magic(ctl);
+}
+
+int main(int argc, char ** argv)
+{
+ struct fs_control ctl = {
+ .fs_namelen = 30, /* keep in sync with DEFAULT_FS_VERSION */
+ 0
+ };
+ int i;
+ struct stat statbuf;
+ char * listfile = NULL;
+ static const struct option longopts[] = {
+ {"namelength", required_argument, NULL, 'n'},
+ {"inodes", required_argument, NULL, 'i'},
+ {"check", no_argument, NULL, 'c'},
+ {"badblocks", required_argument, NULL, 'l'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ strutils_set_exitcode(MKFS_EX_USAGE);
+
+ while ((i = getopt_long(argc, argv, "1v23n:i:cl:Vh", longopts, NULL)) != -1)
+ switch (i) {
+ case '1':
+ fs_version = 1;
+ break;
+ case 'v': /* kept for backwards compatibility */
+ warnx(_("-v is ambiguous, use '-2' instead"));
+ /* fallthrough */
+ case '2':
+ fs_version = 2;
+ break;
+ case '3':
+ fs_version = 3;
+ ctl.fs_namelen = 60;
+ break;
+ case 'n':
+ ctl.fs_namelen = strtou16_or_err(optarg,
+ _("failed to parse maximum length of filenames"));
+ break;
+ case 'i':
+ ctl.fs_inodes = strtoul_or_err(optarg,
+ _("failed to parse number of inodes"));
+ break;
+ case 'c':
+ ctl.check_blocks = 1;
+ break;
+ case 'l':
+ listfile = optarg;
+ break;
+ case 'V':
+ print_version(MKFS_EX_OK);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(MKFS_EX_USAGE);
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc > 0) {
+ ctl.device_name = argv[0];
+ argc--;
+ argv++;
+ }
+ if (argc > 0)
+ ctl.fs_blocks = strtoul_or_err(argv[0], _("failed to parse number of blocks"));
+
+ if (!ctl.device_name) {
+ warnx(_("no device specified"));
+ errtryhelp(MKFS_EX_USAGE);
+ }
+ check_user_instructions(&ctl);
+ if (is_mounted(ctl.device_name))
+ errx(MKFS_EX_ERROR, _("%s is mounted; will not make a filesystem here!"),
+ ctl.device_name);
+ if (stat(ctl.device_name, &statbuf) < 0)
+ err(MKFS_EX_ERROR, _("stat of %s failed"), ctl.device_name);
+ ctl.device_fd = open_blkdev_or_file(&statbuf, ctl.device_name, O_RDWR);
+ if (ctl.device_fd < 0)
+ err(MKFS_EX_ERROR, _("cannot open %s"), ctl.device_name);
+ determine_device_blocks(&ctl, &statbuf);
+ setup_tables(&ctl);
+ if (ctl.check_blocks)
+ check_blocks(&ctl);
+ else if (listfile)
+ get_list_blocks(&ctl, listfile);
+
+ make_root_inode(&ctl);
+ make_bad_inode(&ctl);
+
+ mark_good_blocks(&ctl);
+ write_tables(&ctl);
+ if (close_fd(ctl.device_fd) != 0)
+ err(MKFS_EX_ERROR, _("write failed"));
+
+ return MKFS_EX_OK;
+}
diff --git a/disk-utils/mkswap.8 b/disk-utils/mkswap.8
new file mode 100644
index 0000000..a8cbcc2
--- /dev/null
+++ b/disk-utils/mkswap.8
@@ -0,0 +1,158 @@
+.\" Copyright 1998 Andries E. Brouwer (aeb@cwi.nl)
+.\"
+.\" May be distributed under the GNU General Public License
+.\"
+.TH MKSWAP 8 "March 2009" "util-linux" "System Administration"
+.SH NAME
+mkswap \- set up a Linux swap area
+.SH SYNOPSIS
+.B mkswap
+[options]
+.I device
+.RI [ size ]
+.SH DESCRIPTION
+.B mkswap
+sets up a Linux swap area on a device or in a file.
+
+The
+.I device
+argument will usually be a disk partition (something like
+.IR /dev/sdb7 )
+but can also be a file.
+The Linux kernel does not look at partition IDs, but
+many installation scripts will assume that partitions
+of hex type 82 (LINUX_SWAP) are meant to be swap partitions.
+(\fBWarning: Solaris also uses this type. Be careful not to kill
+your Solaris partitions.\fP)
+
+The
+.I size
+parameter is superfluous but retained for backwards compatibility.
+(It specifies the desired size of the swap area in 1024-byte blocks.
+.B mkswap
+will use the entire partition or file if it is omitted.
+Specifying it is unwise \(en a typo may destroy your disk.)
+
+After creating the swap area, you need the
+.B swapon
+command to start using it. Usually swap areas are listed in
+.I /etc/fstab
+so that they can be taken into use at boot time by a
+.B swapon \-a
+command in some boot script.
+
+.SH WARNING
+The swap header does not touch the first block. A boot loader or disk label
+can be there, but it is not a recommended setup. The recommended setup is to
+use a separate partition for a Linux swap area.
+
+.BR mkswap ,
+like many others mkfs-like utils,
+.B erases the first partition block to make any previous filesystem invisible.
+
+However,
+.B mkswap
+refuses to erase the first block on a device with a disk
+label (SUN, BSD, \&...\&).
+
+.SH OPTIONS
+.TP
+.BR \-c , " \-\-check"
+Check the device (if it is a block device) for bad blocks
+before creating the swap area.
+If any bad blocks are found, the count is printed.
+.TP
+.BR \-f , " \-\-force"
+Go ahead even if the command is stupid.
+This allows the creation of a swap area larger than the file
+or partition it resides on.
+
+Also, without this option,
+.B mkswap
+will refuse to erase the first block on a device with a partition table.
+.TP
+.BR \-L , " \-\-label " \fIlabel\fR
+Specify a \fIlabel\fR for the device, to allow
+.B swapon
+by label.
+.TP
+\fB\-\-lock\fR[=\fImode\fR]
+Use exclusive BSD lock for device or file it operates. The optional argument
+\fImode\fP can be \fByes\fR, \fBno\fR (or 1 and 0) or \fBnonblock\fR. If the \fImode\fR
+argument is omitted, it defaults to \fB"yes"\fR. This option overwrites
+environment variable \fB$LOCK_BLOCK_DEVICE\fR. The default is not to use any
+lock at all, but it's recommended to avoid collisions with udevd or other
+tools.
+.TP
+.BR \-p , " \-\-pagesize " \fIsize\fR
+Specify the page \fIsize\fR (in bytes) to use. This option is usually unnecessary;
+.B mkswap
+reads the size from the kernel.
+.TP
+.BR \-U , " \-\-uuid " \fIUUID\fR
+Specify the \fIUUID\fR to use. The default is to generate a UUID.
+.TP
+.BR \-v , " \-\-swapversion 1"
+Specify the swap-space version. (This option is currently pointless, as the old
+.B \-v 0
+option has become obsolete and now only
+.B \-v 1
+is supported.
+The kernel has not supported v0 swap-space format since 2.5.22 (June 2002).
+The new version v1 is supported since 2.1.117 (August 1998).)
+.TP
+.BR \-h , " \-\-help"
+Display help text and exit.
+.TP
+.BR \-V , " \-\-version"
+Display version information and exit.
+
+.SH ENVIRONMENT
+.IP LIBBLKID_DEBUG=all
+enables libblkid debug output.
+.IP LOCK_BLOCK_DEVICE=<mode>
+use exclusive BSD lock. The mode is "1" or "0". See \fB\-\-lock\fR for more details.
+
+.SH NOTES
+The maximum useful size of a swap area depends on the architecture and
+the kernel version.
+
+The maximum number of the pages that is possible to address by swap area header
+is 4294967295 (32-bit unsigned int). The remaining space on the swap device is ignored.
+
+Presently, Linux allows 32 swap areas.
+The areas in use can be seen in the file
+.I /proc/swaps
+
+.B mkswap
+refuses areas smaller than 10 pages.
+
+If you don't know the page size that your machine uses, you may be
+able to look it up with "cat /proc/cpuinfo" (or you may not \(en
+the contents of this file depend on architecture and kernel version).
+
+To set up a swap file, it is necessary to create that file before
+initializing it with
+.BR mkswap ,
+e.g.\& using a command like
+
+.nf
+.RS
+# dd if=/dev/zero of=swapfile bs=1MiB count=$((8*1024))
+.RE
+.fi
+
+to create 8GiB swapfile.
+
+Please read notes from
+.BR swapon (8)
+about
+.B the swap file use restrictions
+(holes, preallocation and copy-on-write issues).
+
+.SH SEE ALSO
+.BR fdisk (8),
+.BR swapon (8)
+.SH AVAILABILITY
+The mkswap command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/mkswap.c b/disk-utils/mkswap.c
new file mode 100644
index 0000000..4fb8415
--- /dev/null
+++ b/disk-utils/mkswap.c
@@ -0,0 +1,563 @@
+/*
+ * mkswap.c - set up a linux swap device
+ *
+ * Copyright (C) 1991 Linus Torvalds
+ * 20.12.91 - time began. Got VM working yesterday by doing this by hand.
+ *
+ * Copyright (C) 1999 Jakub Jelinek <jj@ultra.linux.cz>
+ * Copyright (C) 2007-2014 Karel Zak <kzak@redhat.com>
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <getopt.h>
+#include <assert.h>
+#ifdef HAVE_LIBSELINUX
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#endif
+
+#include "linux_version.h"
+#include "swapheader.h"
+#include "strutils.h"
+#include "nls.h"
+#include "blkdev.h"
+#include "pathnames.h"
+#include "all-io.h"
+#include "xalloc.h"
+#include "c.h"
+#include "closestream.h"
+#include "ismounted.h"
+
+#ifdef HAVE_LIBUUID
+# include <uuid.h>
+#endif
+
+#ifdef HAVE_LIBBLKID
+# include <blkid.h>
+#endif
+
+#define MIN_GOODPAGES 10
+
+#define SELINUX_SWAPFILE_TYPE "swapfile_t"
+
+struct mkswap_control {
+ struct swap_header_v1_2 *hdr; /* swap header */
+ void *signature_page;/* buffer with swap header */
+
+ char *devname; /* device or file name */
+ const char *lockmode; /* as specified by --lock */
+ struct stat devstat; /* stat() result */
+ int fd; /* swap file descriptor */
+
+ unsigned long long npages; /* number of pages */
+ unsigned long nbadpages; /* number of bad pages */
+
+ int user_pagesize; /* --pagesize */
+ int pagesize; /* final pagesize used for the header */
+
+ char *opt_label; /* LABEL as specified on command line */
+ unsigned char *uuid; /* UUID parsed by libbuuid */
+
+ unsigned int check:1, /* --check */
+ force:1; /* --force */
+};
+
+static void init_signature_page(struct mkswap_control *ctl)
+{
+ const int kernel_pagesize = getpagesize();
+
+ if (ctl->user_pagesize) {
+ if (ctl->user_pagesize < 0 || !is_power_of_2(ctl->user_pagesize) ||
+ (size_t) ctl->user_pagesize < sizeof(struct swap_header_v1_2) + 10)
+ errx(EXIT_FAILURE,
+ _("Bad user-specified page size %u"),
+ ctl->user_pagesize);
+ if (ctl->user_pagesize != kernel_pagesize)
+ warnx(_("Using user-specified page size %d, "
+ "instead of the system value %d"),
+ ctl->user_pagesize, kernel_pagesize);
+ ctl->pagesize = ctl->user_pagesize;
+ } else
+ ctl->pagesize = kernel_pagesize;
+
+ ctl->signature_page = xcalloc(1, ctl->pagesize);
+ ctl->hdr = (struct swap_header_v1_2 *) ctl->signature_page;
+}
+
+static void deinit_signature_page(struct mkswap_control *ctl)
+{
+ free(ctl->signature_page);
+
+ ctl->hdr = NULL;
+ ctl->signature_page = NULL;
+}
+
+static void set_signature(const struct mkswap_control *ctl)
+{
+ char *sp = (char *) ctl->signature_page;
+
+ assert(sp);
+ memcpy(sp + ctl->pagesize - SWAP_SIGNATURE_SZ, SWAP_SIGNATURE, SWAP_SIGNATURE_SZ);
+}
+
+static void set_uuid_and_label(const struct mkswap_control *ctl)
+{
+ assert(ctl);
+ assert(ctl->hdr);
+
+ /* set UUID */
+ if (ctl->uuid)
+ memcpy(ctl->hdr->uuid, ctl->uuid, sizeof(ctl->hdr->uuid));
+
+ /* set LABEL */
+ if (ctl->opt_label) {
+ xstrncpy(ctl->hdr->volume_name,
+ ctl->opt_label, sizeof(ctl->hdr->volume_name));
+ if (strlen(ctl->opt_label) > strlen(ctl->hdr->volume_name))
+ warnx(_("Label was truncated."));
+ }
+
+ /* report results */
+ if (ctl->uuid || ctl->opt_label) {
+ if (ctl->opt_label)
+ printf("LABEL=%s, ", ctl->hdr->volume_name);
+ else
+ printf(_("no label, "));
+#ifdef HAVE_LIBUUID
+ if (ctl->uuid) {
+ char uuid_string[UUID_STR_LEN];
+ uuid_unparse(ctl->uuid, uuid_string);
+ printf("UUID=%s\n", uuid_string);
+ } else
+#endif
+ printf(_("no uuid\n"));
+ }
+}
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fprintf(out,
+ _("\nUsage:\n"
+ " %s [options] device [size]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Set up a Linux swap area.\n"), out);
+
+ fprintf(out, _(
+ "\nOptions:\n"
+ " -c, --check check bad blocks before creating the swap area\n"
+ " -f, --force allow swap size area be larger than device\n"
+ " -p, --pagesize SIZE specify page size in bytes\n"
+ " -L, --label LABEL specify label\n"
+ " -v, --swapversion NUM specify swap-space version number\n"
+ " -U, --uuid UUID specify the uuid to use\n"
+ ));
+ fprintf(out,
+ _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
+ printf(USAGE_HELP_OPTIONS(27));
+
+ printf(USAGE_MAN_TAIL("mkswap(8)"));
+ exit(EXIT_SUCCESS);
+}
+
+static void page_bad(struct mkswap_control *ctl, unsigned int page)
+{
+ const unsigned long max_badpages =
+ (ctl->pagesize - 1024 - 128 * sizeof(int) - 10) / sizeof(int);
+
+ if (ctl->nbadpages == max_badpages)
+ errx(EXIT_FAILURE, _("too many bad pages: %lu"), max_badpages);
+
+ ctl->hdr->badpages[ctl->nbadpages] = page;
+ ctl->nbadpages++;
+}
+
+static void check_blocks(struct mkswap_control *ctl)
+{
+ unsigned int current_page = 0;
+ int do_seek = 1;
+ char *buffer;
+
+ assert(ctl);
+ assert(ctl->fd > -1);
+
+ buffer = xmalloc(ctl->pagesize);
+ while (current_page < ctl->npages) {
+ ssize_t rc;
+ off_t offset = (off_t) current_page * ctl->pagesize;
+
+ if (do_seek && lseek(ctl->fd, offset, SEEK_SET) != offset)
+ errx(EXIT_FAILURE, _("seek failed in check_blocks"));
+
+ rc = read(ctl->fd, buffer, ctl->pagesize);
+ do_seek = (rc < 0 || rc != ctl->pagesize);
+ if (do_seek)
+ page_bad(ctl, current_page);
+ current_page++;
+ }
+ printf(P_("%lu bad page\n", "%lu bad pages\n", ctl->nbadpages), ctl->nbadpages);
+ free(buffer);
+}
+
+/* return size in pages */
+static unsigned long long get_size(const struct mkswap_control *ctl)
+{
+ int fd;
+ unsigned long long size;
+
+ fd = open(ctl->devname, O_RDONLY);
+ if (fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), ctl->devname);
+ if (blkdev_get_size(fd, &size) == 0)
+ size /= ctl->pagesize;
+
+ close(fd);
+ return size;
+}
+
+#ifdef HAVE_LIBBLKID
+static blkid_probe new_prober(const struct mkswap_control *ctl)
+{
+ blkid_probe pr = blkid_new_probe();
+ if (!pr)
+ errx(EXIT_FAILURE, _("unable to alloc new libblkid probe"));
+ if (blkid_probe_set_device(pr, ctl->fd, 0, 0))
+ errx(EXIT_FAILURE, _("unable to assign device to libblkid probe"));
+ return pr;
+}
+#endif
+
+static void open_device(struct mkswap_control *ctl)
+{
+ assert(ctl);
+ assert(ctl->devname);
+
+ if (stat(ctl->devname, &ctl->devstat) < 0)
+ err(EXIT_FAILURE, _("stat of %s failed"), ctl->devname);
+ ctl->fd = open_blkdev_or_file(&ctl->devstat, ctl->devname, O_RDWR);
+ if (ctl->fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), ctl->devname);
+
+ if (blkdev_lock(ctl->fd, ctl->devname, ctl->lockmode) != 0)
+ exit(EXIT_FAILURE);
+
+ if (ctl->check && S_ISREG(ctl->devstat.st_mode)) {
+ ctl->check = 0;
+ warnx(_("warning: checking bad blocks from swap file is not supported: %s"),
+ ctl->devname);
+ }
+}
+
+static void wipe_device(struct mkswap_control *ctl)
+{
+ char *type = NULL;
+ int zap = 1;
+#ifdef HAVE_LIBBLKID
+ blkid_probe pr = NULL;
+#endif
+ if (!ctl->force) {
+ const char *v = NULL;
+
+ if (lseek(ctl->fd, 0, SEEK_SET) != 0)
+ errx(EXIT_FAILURE, _("unable to rewind swap-device"));
+
+#ifdef HAVE_LIBBLKID
+ pr = new_prober(ctl);
+ blkid_probe_enable_partitions(pr, 1);
+ blkid_probe_enable_superblocks(pr, 0);
+
+ if (blkid_do_fullprobe(pr) == 0 &&
+ blkid_probe_lookup_value(pr, "PTTYPE", &v, NULL) == 0 && v) {
+ type = xstrdup(v);
+ zap = 0;
+ }
+#else
+ /* don't zap if compiled without libblkid */
+ zap = 0;
+#endif
+ }
+
+ if (zap) {
+ /*
+ * Wipe bootbits
+ */
+ char buf[1024] = { '\0' };
+
+ if (lseek(ctl->fd, 0, SEEK_SET) != 0)
+ errx(EXIT_FAILURE, _("unable to rewind swap-device"));
+
+ if (write_all(ctl->fd, buf, sizeof(buf)))
+ errx(EXIT_FAILURE, _("unable to erase bootbits sectors"));
+#ifdef HAVE_LIBBLKID
+ /*
+ * Wipe rest of the device
+ */
+ if (!pr)
+ pr = new_prober(ctl);
+
+ blkid_probe_enable_superblocks(pr, 1);
+ blkid_probe_enable_partitions(pr, 0);
+ blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC|BLKID_SUBLKS_TYPE);
+
+ while (blkid_do_probe(pr) == 0) {
+ const char *data = NULL;
+
+ if (blkid_probe_lookup_value(pr, "TYPE", &data, NULL) == 0 && data)
+ warnx(_("%s: warning: wiping old %s signature."), ctl->devname, data);
+ blkid_do_wipe(pr, 0);
+ }
+#endif
+ } else {
+ warnx(_("%s: warning: don't erase bootbits sectors"),
+ ctl->devname);
+ if (type)
+ fprintf(stderr, _(" (%s partition table detected). "), type);
+ else
+ fprintf(stderr, _(" (compiled without libblkid). "));
+ fprintf(stderr, _("Use -f to force.\n"));
+ }
+ free(type);
+#ifdef HAVE_LIBBLKID
+ blkid_free_probe(pr);
+#endif
+}
+
+#define SIGNATURE_OFFSET 1024
+
+static void write_header_to_device(struct mkswap_control *ctl)
+{
+ assert(ctl);
+ assert(ctl->fd > -1);
+ assert(ctl->signature_page);
+
+ if (lseek(ctl->fd, SIGNATURE_OFFSET, SEEK_SET) != SIGNATURE_OFFSET)
+ errx(EXIT_FAILURE, _("unable to rewind swap-device"));
+
+ if (write_all(ctl->fd, (char *) ctl->signature_page + SIGNATURE_OFFSET,
+ ctl->pagesize - SIGNATURE_OFFSET) == -1)
+ err(EXIT_FAILURE,
+ _("%s: unable to write signature page"),
+ ctl->devname);
+}
+
+int main(int argc, char **argv)
+{
+ struct mkswap_control ctl = { .fd = -1 };
+ int c, permMask;
+ uint64_t sz;
+ int version = SWAP_VERSION;
+ char *block_count = NULL, *strsz = NULL;
+#ifdef HAVE_LIBUUID
+ const char *opt_uuid = NULL;
+ uuid_t uuid_dat;
+#endif
+ enum {
+ OPT_LOCK = CHAR_MAX + 1,
+ };
+ static const struct option longopts[] = {
+ { "check", no_argument, NULL, 'c' },
+ { "force", no_argument, NULL, 'f' },
+ { "pagesize", required_argument, NULL, 'p' },
+ { "label", required_argument, NULL, 'L' },
+ { "swapversion", required_argument, NULL, 'v' },
+ { "uuid", required_argument, NULL, 'U' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
+ { "lock", optional_argument, NULL, OPT_LOCK },
+ { NULL, 0, NULL, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ while((c = getopt_long(argc, argv, "cfp:L:v:U:Vh", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'c':
+ ctl.check = 1;
+ break;
+ case 'f':
+ ctl.force = 1;
+ break;
+ case 'p':
+ ctl.user_pagesize = strtou32_or_err(optarg, _("parsing page size failed"));
+ break;
+ case 'L':
+ ctl.opt_label = optarg;
+ break;
+ case 'v':
+ version = strtos32_or_err(optarg, _("parsing version number failed"));
+ if (version != SWAP_VERSION)
+ errx(EXIT_FAILURE,
+ _("swapspace version %d is not supported"), version);
+ break;
+ case 'U':
+#ifdef HAVE_LIBUUID
+ opt_uuid = optarg;
+#else
+ warnx(_("warning: ignoring -U (UUIDs are unsupported by %s)"),
+ program_invocation_short_name);
+#endif
+ break;
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ break;
+ case OPT_LOCK:
+ ctl.lockmode = "1";
+ if (optarg) {
+ if (*optarg == '=')
+ optarg++;
+ ctl.lockmode = optarg;
+ }
+ break;
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ if (optind < argc)
+ ctl.devname = argv[optind++];
+ if (optind < argc)
+ block_count = argv[optind++];
+ if (optind != argc) {
+ warnx(_("only one device argument is currently supported"));
+ errtryhelp(EXIT_FAILURE);
+ }
+
+#ifdef HAVE_LIBUUID
+ if(opt_uuid) {
+ if (uuid_parse(opt_uuid, uuid_dat) != 0)
+ errx(EXIT_FAILURE, _("error: parsing UUID failed"));
+ } else
+ uuid_generate(uuid_dat);
+ ctl.uuid = uuid_dat;
+#endif
+
+ init_signature_page(&ctl); /* get pagesize and allocate signature page */
+
+ if (!ctl.devname) {
+ warnx(_("error: Nowhere to set up swap on?"));
+ errtryhelp(EXIT_FAILURE);
+ }
+ if (block_count) {
+ /* this silly user specified the number of blocks explicitly */
+ uint64_t blks = strtou64_or_err(block_count,
+ _("invalid block count argument"));
+ ctl.npages = blks / (ctl.pagesize / 1024);
+ }
+
+ sz = get_size(&ctl);
+ if (!ctl.npages)
+ ctl.npages = sz;
+ else if (ctl.npages > sz && !ctl.force)
+ errx(EXIT_FAILURE,
+ _("error: "
+ "size %llu KiB is larger than device size %"PRIu64" KiB"),
+ ctl.npages * (ctl.pagesize / 1024), sz * (ctl.pagesize / 1024));
+
+ if (ctl.npages < MIN_GOODPAGES)
+ errx(EXIT_FAILURE,
+ _("error: swap area needs to be at least %ld KiB"),
+ (long)(MIN_GOODPAGES * ctl.pagesize / 1024));
+ if (ctl.npages > UINT32_MAX) {
+ /* true when swap is bigger than 17.59 terabytes */
+ ctl.npages = UINT32_MAX;
+ warnx(_("warning: truncating swap area to %llu KiB"),
+ ctl.npages * ctl.pagesize / 1024);
+ }
+
+ if (is_mounted(ctl.devname))
+ errx(EXIT_FAILURE, _("error: "
+ "%s is mounted; will not make swapspace"),
+ ctl.devname);
+
+ open_device(&ctl);
+ permMask = S_ISBLK(ctl.devstat.st_mode) ? 07007 : 07077;
+ if ((ctl.devstat.st_mode & permMask) != 0)
+ warnx(_("%s: insecure permissions %04o, %04o suggested."),
+ ctl.devname, ctl.devstat.st_mode & 07777,
+ ~permMask & 0666);
+ if (getuid() == 0 && S_ISREG(ctl.devstat.st_mode) && ctl.devstat.st_uid != 0)
+ warnx(_("%s: insecure file owner %d, 0 (root) suggested."),
+ ctl.devname, ctl.devstat.st_uid);
+
+
+ if (ctl.check)
+ check_blocks(&ctl);
+
+ wipe_device(&ctl);
+
+ assert(ctl.hdr);
+ ctl.hdr->version = version;
+ ctl.hdr->last_page = ctl.npages - 1;
+ ctl.hdr->nr_badpages = ctl.nbadpages;
+
+ if ((ctl.npages - MIN_GOODPAGES) < ctl.nbadpages)
+ errx(EXIT_FAILURE, _("Unable to set up swap-space: unreadable"));
+
+ sz = (ctl.npages - ctl.nbadpages - 1) * ctl.pagesize;
+ strsz = size_to_human_string(SIZE_SUFFIX_SPACE | SIZE_SUFFIX_3LETTER, sz);
+
+ printf(_("Setting up swapspace version %d, size = %s (%"PRIu64" bytes)\n"),
+ version, strsz, sz);
+ free(strsz);
+
+ set_signature(&ctl);
+ set_uuid_and_label(&ctl);
+
+ write_header_to_device(&ctl);
+
+ deinit_signature_page(&ctl);
+
+#ifdef HAVE_LIBSELINUX
+ if (S_ISREG(ctl.devstat.st_mode) && is_selinux_enabled() > 0) {
+ security_context_t context_string;
+ security_context_t oldcontext;
+ context_t newcontext;
+
+ if (fgetfilecon(ctl.fd, &oldcontext) < 0) {
+ if (errno != ENODATA)
+ err(EXIT_FAILURE,
+ _("%s: unable to obtain selinux file label"),
+ ctl.devname);
+ if (matchpathcon(ctl.devname, ctl.devstat.st_mode, &oldcontext))
+ errx(EXIT_FAILURE, _("unable to matchpathcon()"));
+ }
+ if (!(newcontext = context_new(oldcontext)))
+ errx(EXIT_FAILURE, _("unable to create new selinux context"));
+ if (context_type_set(newcontext, SELINUX_SWAPFILE_TYPE))
+ errx(EXIT_FAILURE, _("couldn't compute selinux context"));
+
+ context_string = context_str(newcontext);
+
+ if (strcmp(context_string, oldcontext)!=0) {
+ if (fsetfilecon(ctl.fd, context_string) && errno != ENOTSUP)
+ err(EXIT_FAILURE, _("unable to relabel %s to %s"),
+ ctl.devname, context_string);
+ }
+ context_free(newcontext);
+ freecon(oldcontext);
+ }
+#endif
+ /*
+ * A subsequent swapon() will fail if the signature
+ * is not actually on disk. (This is a kernel bug.)
+ * The fsync() in close_fd() will take care of writing.
+ */
+ if (close_fd(ctl.fd) != 0)
+ err(EXIT_FAILURE, _("write failed"));
+ return EXIT_SUCCESS;
+}
diff --git a/disk-utils/partx.8 b/disk-utils/partx.8
new file mode 100644
index 0000000..e70d1fa
--- /dev/null
+++ b/disk-utils/partx.8
@@ -0,0 +1,198 @@
+.\" partx.8 -- man page for partx
+.\" Copyright 2007 Karel Zak <kzak@redhat.com>
+.\" Copyright 2007 Red Hat, Inc.
+.\" Copyright 2010 Davidlohr Bueso <dave@gnu.org>
+.\" May be distributed under the GNU General Public License
+.\"
+.TH PARTX "8" "December 2014" "util-linux" "System Administration"
+.SH NAME
+partx \- tell the kernel about the presence and numbering of on-disk partitions
+.SH SYNOPSIS
+.B partx
+.RB [ \-a | \-d | \-P | \-r | \-s | \-u ]
+.RB [ \-t " \fItype\fR]"
+.RB [ \-n " \fIM" : \fIN\fR]
+.RB [ \- "] " \fIdisk
+.br
+.B partx
+.RB [ \-a | \-d | \-P | \-r | \-s | \-u ]
+.RB [ \-t " \fItype\fR]"
+.IR partition " [" disk ]
+.SH DESCRIPTION
+Given a device or disk-image,
+.B partx
+tries to parse the partition table and list its contents. It
+can also tell the kernel to add or remove partitions from its
+bookkeeping.
+.PP
+The
+.I disk
+argument is optional when a
+.I partition
+argument is provided. To force scanning a partition as if it were a whole disk
+(for example to list nested subpartitions), use the argument "\-" (hyphen-minus).
+For example:
+
+.RS 7
+.TP
+partx \-\-show \- /dev/sda3
+.RE
+.PP
+This will see sda3 as a whole-disk rather than as a partition.
+.PP
+.B partx is not an fdisk program
+\(en adding and removing partitions does not change the disk, it just
+tells the kernel about the presence and numbering of on-disk
+partitions.
+.SH OPTIONS
+.TP
+.BR \-a , " \-\-add"
+Add the specified partitions, or read the disk and add all partitions.
+.TP
+.BR \-b , " \-\-bytes"
+Print the SIZE column in bytes rather than in human-readable format.
+.TP
+.BR \-d , " \-\-delete"
+Delete the specified partitions or all partitions. It is not error to
+remove non-existing partitions, so this option is possible to use together with
+large \fB\-\-nr\fR ranges without care about the current partitions set on
+the device.
+.TP
+.BR \-g , " \-\-noheadings"
+Do not print a header line with \fB\-\-show\fR or \fB\-\-raw\fR.
+.TP
+.BR \-l , " \-\-list"
+List the partitions. Note that all numbers are in 512-byte sectors.
+This output format is DEPRECATED in favour of
+.BR \-\-show .
+Do not use it in newly written scripts.
+.TP
+.BR \-n , " \-\-nr " \fIM : \fIN
+Specify the range of partitions. For backward compatibility also the
+format \fIM\fB\-\fIN\fR is supported.
+The range may contain negative numbers, for example
+.B \-\-nr \-1:\-1
+means the last partition, and
+.B \-\-nr \-2:\-1
+means the last two partitions. Supported range specifications are:
+.RS 14
+.TP
+.I M
+Specifies just one partition (e.g.\& \fB\-\-nr 3\fR).
+.TP
+.IB M :
+Specifies the lower limit only (e.g.\& \fB\-\-nr 2:\fR).
+.TP
+.BI : N
+Specifies the upper limit only (e.g.\& \fB\-\-nr :4\fR).
+.TP
+.IB M : N
+Specifies the lower and upper limits (e.g.\& \fB\-\-nr 2:4\fR).
+.RE
+.TP
+.BR \-o , " \-\-output " \fIlist
+Define the output columns to use for
+.BR \-\-show ,
+.B \-\-pairs
+and
+.B \-\-raw
+output. If no output arrangement is specified, then a default set is
+used. Use
+.B \-\-help
+to get
+.I list
+of all supported columns. This option cannot be combined with the
+.BR \-\-add ,
+.BR \-\-delete ,
+.B \-\-update
+or
+.B \-\-list
+options.
+.TP
+.B \-\-output\-all
+Output all available columns.
+.TP
+.BR \-P , " \-\-pairs"
+List the partitions using the KEY="value" format.
+.TP
+.BR \-r , " \-\-raw"
+List the partitions using the raw output format.
+.TP
+.BR \-s , " \-\-show"
+List the partitions.
+The output columns can be selected and rearranged with the
+\fB\-\-output\fR option.
+All numbers (except SIZE) are in 512-byte sectors.
+.TP
+.BR \-t , " \-\-type " \fItype
+Specify the partition table type.
+.TP
+.B \-\-list\-types
+List supported partition types and exit.
+.TP
+.BR \-u , " \-\-update"
+Update the specified partitions.
+.TP
+.BR \-S , " \-\-sector\-size " \fIsize
+Overwrite default sector size.
+.TP
+.BR \-v , " \-\-verbose"
+Verbose mode.
+.TP
+.BR \-V , " \-\-version"
+Display version information and exit.
+.TP
+.BR \-h , " \-\-help"
+Display help text and exit.
+.SH ENVIRONMENT
+.IP LIBBLKID_DEBUG=all
+enables libblkid debug output.
+.SH EXAMPLE
+.TP
+partx \-\-show /dev/sdb3
+.TQ
+partx \-\-show \-\-nr 3 /dev/sdb
+.TQ
+partx \-\-show /dev/sdb3 /dev/sdb
+All three commands list partition 3 of /dev/sdb.
+.TP
+partx \-\-show \- /dev/sdb3
+Lists all subpartitions on /dev/sdb3 (the device is used as
+whole-disk).
+.TP
+partx \-o START \-g \-\-nr 5 /dev/sdb
+Prints the start sector of partition 5 on /dev/sdb without header.
+.TP
+partx \-o SECTORS,SIZE /dev/sda5 /dev/sda
+Lists the length in sectors and human-readable size of partition 5 on
+/dev/sda.
+.TP
+partx \-\-add \-\-nr 3:5 /dev/sdd
+Adds all available partitions from 3 to 5 (inclusive) on /dev/sdd.
+.TP
+partx \-d \-\-nr :\-1 /dev/sdd
+Removes the last partition on /dev/sdd.
+.SH AUTHORS
+.MT dave@gnu.org
+Davidlohr Bueso
+.ME
+.br
+.MT kzak@redhat.com
+Karel Zak
+.ME
+.PP
+The original version was written by
+.MT aeb@cwi.nl
+Andries E.\& Brouwer
+.ME .
+.SH SEE ALSO
+.BR addpart (8),
+.BR delpart (8),
+.BR fdisk (8),
+.BR parted (8),
+.BR partprobe (8)
+.SH AVAILABILITY
+The partx command is part of the util-linux package and is available from
+.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/disk-utils/partx.c b/disk-utils/partx.c
new file mode 100644
index 0000000..07b3e28
--- /dev/null
+++ b/disk-utils/partx.c
@@ -0,0 +1,1071 @@
+/*
+ * partx: tell the kernel about your disk's partitions
+ * [This is not an fdisk - adding and removing partitions
+ * is not a change of the disk, but just telling the kernel
+ * about presence and numbering of on-disk partitions.]
+ *
+ * aeb, 2000-03-21 -- sah is 42 now
+ *
+ * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
+ * Rewritten to use libblkid for util-linux
+ * based on ideas from Karel Zak <kzak@redhat.com>
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <assert.h>
+#include <dirent.h>
+
+#include <blkid.h>
+#include <libsmartcols.h>
+
+#include "c.h"
+#include "pathnames.h"
+#include "nls.h"
+#include "blkdev.h"
+#include "strutils.h"
+#include "xalloc.h"
+#include "partx.h"
+#include "sysfs.h"
+#include "loopdev.h"
+#include "closestream.h"
+#include "optutils.h"
+
+/* this is the default upper limit, could be modified by --nr */
+#define SLICES_MAX 256
+
+/* basic table settings */
+enum {
+ PARTX_RAW = (1 << 0),
+ PARTX_NOHEADINGS = (1 << 1),
+ PARTX_EXPORT = (1 << 2),
+};
+
+/* all the columns (-o option) */
+enum {
+ COL_PARTNO,
+ COL_START,
+ COL_END,
+ COL_SECTORS,
+ COL_SIZE,
+ COL_NAME,
+ COL_UUID,
+ COL_TYPE,
+ COL_FLAGS,
+ COL_SCHEME,
+};
+
+#define ACT_ERROR "--{add,delete,show,list,raw,pairs}"
+enum {
+ ACT_NONE,
+ ACT_LIST,
+ ACT_SHOW,
+ ACT_ADD,
+ ACT_UPD,
+ ACT_DELETE
+};
+
+enum {
+ FL_BYTES = (1 << 1)
+};
+
+/* column names */
+struct colinfo {
+ const char *name; /* header */
+ double whint; /* width hint (N < 1 is in percent of termwidth) */
+ int flags; /* SCOLS_FL_* */
+ const char *help;
+};
+
+/* columns descriptions */
+static struct colinfo infos[] = {
+ [COL_PARTNO] = { "NR", 0.25, SCOLS_FL_RIGHT, N_("partition number") },
+ [COL_START] = { "START", 0.30, SCOLS_FL_RIGHT, N_("start of the partition in sectors") },
+ [COL_END] = { "END", 0.30, SCOLS_FL_RIGHT, N_("end of the partition in sectors") },
+ [COL_SECTORS] = { "SECTORS", 0.30, SCOLS_FL_RIGHT, N_("number of sectors") },
+ [COL_SIZE] = { "SIZE", 0.30, SCOLS_FL_RIGHT, N_("human readable size") },
+ [COL_NAME] = { "NAME", 0.30, SCOLS_FL_TRUNC, N_("partition name") },
+ [COL_UUID] = { "UUID", 36, 0, N_("partition UUID")},
+ [COL_SCHEME] = { "SCHEME", 0.1, SCOLS_FL_TRUNC, N_("partition table type (dos, gpt, ...)")},
+ [COL_FLAGS] = { "FLAGS", 0.1, SCOLS_FL_TRUNC, N_("partition flags")},
+ [COL_TYPE] = { "TYPE", 1, SCOLS_FL_RIGHT, N_("partition type (a string, a UUID, or hex)")},
+};
+
+#define NCOLS ARRAY_SIZE(infos)
+
+/* array with IDs of enabled columns */
+static int columns[NCOLS];
+static size_t ncolumns;
+
+static int verbose;
+static int partx_flags;
+static struct loopdev_cxt lc;
+static int loopdev;
+
+static void assoc_loopdev(const char *fname)
+{
+ int rc;
+
+ if (loopcxt_init(&lc, 0))
+ err(EXIT_FAILURE, _("failed to initialize loopcxt"));
+
+ rc = loopcxt_find_unused(&lc);
+ if (rc)
+ err(EXIT_FAILURE, _("%s: failed to find unused loop device"),
+ fname);
+
+ if (verbose)
+ printf(_("Trying to use '%s' for the loop device\n"),
+ loopcxt_get_device(&lc));
+
+ if (loopcxt_set_backing_file(&lc, fname))
+ err(EXIT_FAILURE, _("%s: failed to set backing file"), fname);
+
+ rc = loopcxt_setup_device(&lc);
+
+ if (rc == -EBUSY)
+ err(EXIT_FAILURE, _("%s: failed to set up loop device"), fname);
+
+ loopdev = 1;
+}
+
+static inline int get_column_id(int num)
+{
+ assert(ARRAY_SIZE(columns) == NCOLS);
+ assert((size_t)num < ncolumns);
+ assert(columns[num] < (int) NCOLS);
+ return columns[num];
+}
+
+static inline struct colinfo *get_column_info(int num)
+{
+ return &infos[ get_column_id(num) ];
+}
+
+static int column_name_to_id(const char *name, size_t namesz)
+{
+ size_t i;
+
+ assert(name);
+
+ for (i = 0; i < NCOLS; i++) {
+ const char *cn = infos[i].name;
+
+ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+ return i;
+ }
+ warnx(_("unknown column: %s"), name);
+ return -1;
+}
+
+/*
+ * Given a partition return the corresponding partition number.
+ *
+ * Note that this function tries to use sysfs, otherwise it assumes that the
+ * last characters are always numeric (sda1, sdc20, etc).
+ */
+static int get_partno_from_device(char *partition, dev_t devno)
+{
+ int partno = 0;
+ size_t sz;
+ char *p, *end = NULL;
+
+ assert(partition);
+
+ if (devno) {
+ struct path_cxt *pc;
+ int rc;
+
+ pc = ul_new_sysfs_path(devno, NULL, NULL);
+ if (!pc)
+ goto err;
+
+ rc = ul_path_read_s32(pc, &partno, "partition");
+ ul_unref_path(pc);
+
+ if (rc == 0)
+ return partno;
+ }
+
+ sz = strlen(partition);
+ p = partition + sz - 1;
+
+ if (!isdigit((unsigned char) *p))
+ goto err;
+
+ while (isdigit((unsigned char) *(p - 1))) p--;
+
+ errno = 0;
+ partno = strtol(p, &end, 10);
+ if (errno || !end || *end || p == end)
+ goto err;
+
+ return partno;
+err:
+ errx(EXIT_FAILURE, _("%s: failed to get partition number"), partition);
+}
+
+static int get_max_partno(const char *disk, dev_t devno)
+{
+ char path[PATH_MAX], *parent, *dirname = NULL;
+ struct stat st;
+ DIR *dir;
+ struct dirent *d;
+ int partno = 0;
+
+ if (!devno && !stat(disk, &st))
+ devno = st.st_rdev;
+ if (!devno)
+ goto dflt;
+ parent = strrchr(disk, '/');
+ if (!parent)
+ goto dflt;
+ parent++;
+
+ snprintf(path, sizeof(path), _PATH_SYS_DEVBLOCK "/%d:%d/",
+ major(devno), minor(devno));
+
+ dir = opendir(path);
+ if (!dir)
+ goto dflt;
+
+ dirname = xstrdup(path);
+
+ while ((d = readdir(dir))) {
+ int fd;
+
+ if (!strcmp(d->d_name, ".") ||
+ !strcmp(d->d_name, ".."))
+ continue;
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d->d_type != DT_DIR && d->d_type != DT_UNKNOWN)
+ continue;
+#endif
+ if (strncmp(parent, d->d_name, strlen(parent)) != 0)
+ continue;
+ snprintf(path, sizeof(path), "%s/partition", d->d_name);
+
+ fd = openat(dirfd(dir), path, O_RDONLY);
+ if (fd) {
+ int x = 0;
+ FILE *f = fdopen(fd, "r");
+ if (f) {
+ if (fscanf(f, "%d", &x) == 1 && x > partno)
+ partno = x;
+ fclose(f);
+ }
+ }
+ }
+
+ free(dirname);
+ closedir(dir);
+ return partno;
+dflt:
+ return SLICES_MAX;
+}
+
+static int recount_range_by_pt(blkid_partlist ls, int *lower, int *upper)
+{
+ int n = 0, i, nparts = blkid_partlist_numof_partitions(ls);
+
+ for (i = 0; i < nparts; i++) {
+ blkid_partition par = blkid_partlist_get_partition(ls, i);
+ int partno = blkid_partition_get_partno(par);
+ n = max(partno, n);
+ }
+
+ if (*lower < 0)
+ *lower = n + *lower + 1;
+ if (*upper < 0)
+ *upper = n + *upper + 1;
+
+ if (*lower > *upper && *upper != 0) {
+ warnx(_("specified range <%d:%d> does not make sense"), *lower, *upper);
+ return -EINVAL;
+ }
+ if (verbose)
+ printf(_("range recount: max partno=%d, lower=%d, upper=%d\n"), n, *lower, *upper);
+ return 0;
+}
+
+static void del_parts_warnx(const char *device, int first, int last)
+{
+ if (first == last)
+ warnx(_("%s: error deleting partition %d"), device, first);
+ else
+ warnx(_("%s: error deleting partitions %d-%d"),
+ device, first, last);
+}
+
+static int del_parts(int fd, const char *device, dev_t devno,
+ int lower, int upper)
+{
+ int rc = 0, i, errfirst = 0, errlast = 0;
+
+ assert(fd >= 0);
+ assert(device);
+
+ /* recount range by information in /sys */
+ if (!lower)
+ lower = 1;
+ if (!upper || lower < 0 || upper < 0) {
+ int n = get_max_partno(device, devno);
+ if (!upper)
+ upper = n;
+ else if (upper < 0)
+ upper = n + upper + 1;
+ if (lower < 0)
+ lower = n + lower + 1;
+ }
+ if (lower > upper) {
+ warnx(_("specified range <%d:%d> "
+ "does not make sense"), lower, upper);
+ return -1;
+ }
+
+ for (i = lower; i <= upper; i++) {
+ if (partx_del_partition(fd, i) == 0) {
+ if (verbose)
+ printf(_("%s: partition #%d removed\n"), device, i);
+ continue;
+ }
+
+ if (errno == ENXIO) {
+ if (verbose)
+ printf(_("%s: partition #%d doesn't exist\n"), device, i);
+ continue;
+ }
+ rc = -1;
+ if (verbose)
+ warn(_("%s: deleting partition #%d failed"), device, i);
+ if (!errfirst)
+ errlast = errfirst = i;
+ else if (errlast + 1 == i)
+ errlast++;
+ else {
+ del_parts_warnx(device, errfirst, errlast);
+ errlast = errfirst = i;
+ }
+ }
+
+ if (errfirst)
+ del_parts_warnx(device, errfirst, errlast);
+ return rc;
+}
+
+
+static void add_parts_warnx(const char *device, int first, int last)
+{
+ if (first == last)
+ warnx(_("%s: error adding partition %d"), device, first);
+ else
+ warnx(_("%s: error adding partitions %d-%d"),
+ device, first, last);
+}
+
+static int add_parts(int fd, const char *device,
+ blkid_partlist ls, int lower, int upper)
+{
+ int i, nparts, rc, errfirst = 0, errlast = 0;
+
+ assert(fd >= 0);
+ assert(device);
+ assert(ls);
+
+ rc = recount_range_by_pt(ls, &lower, &upper);
+ if (rc)
+ return rc;
+
+ nparts = blkid_partlist_numof_partitions(ls);
+
+ for (i = 0; i < nparts; i++) {
+ blkid_partition par = blkid_partlist_get_partition(ls, i);
+ int n = blkid_partition_get_partno(par);
+ uintmax_t start, size;
+
+ if (lower && n < lower)
+ continue;
+ if (upper && n > upper)
+ continue;
+
+ start = blkid_partition_get_start(par);
+ size = blkid_partition_get_size(par);
+
+ if (blkid_partition_is_extended(par))
+ /*
+ * Let's follow the Linux kernel and reduce
+ * DOS extended partition to 1 or 2 sectors.
+ */
+ size = min(size, (uintmax_t) 2);
+
+ if (partx_add_partition(fd, n, start, size) == 0) {
+ if (verbose)
+ printf(_("%s: partition #%d added\n"), device, n);
+ continue;
+ }
+ rc = -1;
+ if (verbose)
+ warn(_("%s: adding partition #%d failed"), device, n);
+ if (!errfirst)
+ errlast = errfirst = n;
+ else if (errlast + 1 == n)
+ errlast++;
+ else {
+ add_parts_warnx(device, errfirst, errlast);
+ errlast = errfirst = n;
+ }
+ }
+
+ if (errfirst)
+ add_parts_warnx(device, errfirst, errlast);
+
+ /*
+ * The kernel with enabled partitions scanner for loop devices add *all*
+ * partitions, so we should delete any extra, unwanted ones, when the -n
+ * option is passed.
+ */
+ if (loopdev && loopcxt_is_partscan(&lc) && (lower || upper)) {
+ for (i = 0; i < nparts; i++) {
+ blkid_partition par = blkid_partlist_get_partition(ls, i);
+ int n = blkid_partition_get_partno(par);
+
+ if (n < lower || n > upper)
+ partx_del_partition(fd, n);
+ }
+ }
+
+ return rc;
+}
+
+static void upd_parts_warnx(const char *device, int first, int last)
+{
+ if (first == last)
+ warnx(_("%s: error updating partition %d"), device, first);
+ else
+ warnx(_("%s: error updating partitions %d-%d"),
+ device, first, last);
+}
+
+static int upd_parts(int fd, const char *device, dev_t devno,
+ blkid_partlist ls, int lower, int upper)
+{
+ int n, nparts, rc = 0, errfirst = 0, errlast = 0, err;
+ blkid_partition par;
+ uintmax_t start, size;
+
+ assert(fd >= 0);
+ assert(device);
+ assert(ls);
+
+ /* recount range by information in /sys, if on disk number of
+ * partitions is greater than in /sys the use on-disk limit */
+ nparts = blkid_partlist_numof_partitions(ls);
+ if (!lower)
+ lower = 1;
+ if (!upper || lower < 0 || upper < 0) {
+ n = get_max_partno(device, devno);
+ if (!upper)
+ upper = n > nparts ? n : nparts;
+ else if (upper < 0)
+ upper = n + upper + 1;
+ if (lower < 0)
+ lower = n + lower + 1;
+ }
+ if (lower > upper) {
+ warnx(_("specified range <%d:%d> "
+ "does not make sense"), lower, upper);
+ return -1;
+ }
+
+ for (n = lower; n <= upper; n++) {
+ par = blkid_partlist_get_partition_by_partno(ls, n);
+ if (!par) {
+ if (verbose)
+ warn(_("%s: no partition #%d"), device, n);
+ continue;
+ }
+
+ start = blkid_partition_get_start(par);
+ size = blkid_partition_get_size(par);
+ if (blkid_partition_is_extended(par))
+ /*
+ * Let's follow the Linux kernel and reduce
+ * DOS extended partition to 1 or 2 sectors.
+ */
+ size = min(size, (uintmax_t) 2);
+
+ err = partx_del_partition(fd, n);
+ if (err == -1 && errno == ENXIO)
+ err = 0; /* good, it already doesn't exist */
+ if (err == -1 && errno == EBUSY)
+ {
+ /* try to resize */
+ err = partx_resize_partition(fd, n, start, size);
+ if (verbose)
+ printf(_("%s: partition #%d resized\n"), device, n);
+ if (err == 0)
+ continue;
+ }
+ if (err == 0 && partx_add_partition(fd, n, start, size) == 0) {
+ if (verbose)
+ printf(_("%s: partition #%d added\n"), device, n);
+ continue;
+ }
+
+ if (err == 0)
+ continue;
+ rc = -1;
+ if (verbose)
+ warn(_("%s: updating partition #%d failed"), device, n);
+ if (!errfirst)
+ errlast = errfirst = n;
+ else if (errlast + 1 == n)
+ errlast++;
+ else {
+ upd_parts_warnx(device, errfirst, errlast);
+ errlast = errfirst = n;
+ }
+ }
+
+ if (errfirst)
+ upd_parts_warnx(device, errfirst, errlast);
+ return rc;
+}
+
+static int list_parts(blkid_partlist ls, int lower, int upper)
+{
+ int i, nparts, rc;
+
+ assert(ls);
+
+ rc = recount_range_by_pt(ls, &lower, &upper);
+ if (rc)
+ return rc;
+
+ nparts = blkid_partlist_numof_partitions(ls);
+
+ for (i = 0; i < nparts; i++) {
+ blkid_partition par = blkid_partlist_get_partition(ls, i);
+ int n = blkid_partition_get_partno(par);
+ uintmax_t start, size;
+
+ if (lower && n < lower)
+ continue;
+ if (upper && n > upper)
+ continue;
+
+ start = blkid_partition_get_start(par);
+ size = blkid_partition_get_size(par);
+
+ printf(P_("#%2d: %9ju-%9ju (%9ju sector, %6ju MB)\n",
+ "#%2d: %9ju-%9ju (%9ju sectors, %6ju MB)\n",
+ size),
+ n, start, start + size -1,
+ size, (size << 9) / 1000000);
+ }
+ return 0;
+}
+
+static int add_scols_line(struct libscols_table *table, blkid_partition par)
+{
+ struct libscols_line *line;
+ int i, rc = 0;
+
+ assert(table);
+ assert(par);
+
+ line = scols_table_new_line(table, NULL);
+ if (!line) {
+ warn(_("failed to allocate output line"));
+ return -ENOMEM;
+ }
+
+ for (i = 0; (size_t)i < ncolumns; i++) {
+ char *str = NULL; /* allocated string */
+ const char *cstr = NULL; /* foreign string */
+
+ switch (get_column_id(i)) {
+ case COL_PARTNO:
+ xasprintf(&str, "%d", blkid_partition_get_partno(par));
+ break;
+ case COL_START:
+ xasprintf(&str, "%ju", blkid_partition_get_start(par));
+ break;
+ case COL_END:
+ xasprintf(&str, "%ju",
+ blkid_partition_get_start(par) +
+ blkid_partition_get_size(par) - 1);
+ break;
+ case COL_SECTORS:
+ xasprintf(&str, "%ju", blkid_partition_get_size(par));
+ break;
+ case COL_SIZE:
+ if (partx_flags & FL_BYTES)
+ xasprintf(&str, "%ju", (uintmax_t)
+ blkid_partition_get_size(par) << 9);
+ else
+ str = size_to_human_string(SIZE_SUFFIX_1LETTER,
+ blkid_partition_get_size(par) << 9);
+ break;
+ case COL_NAME:
+ cstr = blkid_partition_get_name(par);
+ break;
+ case COL_UUID:
+ cstr = blkid_partition_get_uuid(par);
+ break;
+ case COL_TYPE:
+ if (blkid_partition_get_type_string(par))
+ cstr = blkid_partition_get_type_string(par);
+ else
+ xasprintf(&str, "0x%x",
+ blkid_partition_get_type(par));
+ break;
+ case COL_FLAGS:
+ xasprintf(&str, "0x%llx", blkid_partition_get_flags(par));
+ break;
+ case COL_SCHEME:
+ {
+ blkid_parttable tab = blkid_partition_get_table(par);
+ if (tab)
+ cstr = blkid_parttable_get_type(tab);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (cstr)
+ rc = scols_line_set_data(line, i, cstr);
+ else if (str)
+ rc = scols_line_refer_data(line, i, str);
+ if (rc) {
+ warn(_("failed to add output data"));
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int show_parts(blkid_partlist ls, int scols_flags, int lower, int upper)
+{
+ int i, rc = -1;
+ struct libscols_table *table;
+ int nparts;
+
+ assert(ls);
+
+ nparts = blkid_partlist_numof_partitions(ls);
+ if (!nparts)
+ return 0;
+
+ scols_init_debug(0);
+ table = scols_new_table();
+ if (!table) {
+ warn(_("failed to allocate output table"));
+ return -1;
+ }
+ scols_table_enable_raw(table, !!(scols_flags & PARTX_RAW));
+ scols_table_enable_export(table, !!(scols_flags & PARTX_EXPORT));
+ scols_table_enable_noheadings(table, !!(scols_flags & PARTX_NOHEADINGS));
+
+ for (i = 0; (size_t)i < ncolumns; i++) {
+ struct colinfo *col = get_column_info(i);
+
+ if (!scols_table_new_column(table, col->name, col->whint, col->flags)) {
+ warnx(_("failed to allocate output column"));
+ goto done;
+ }
+ }
+
+ rc = recount_range_by_pt(ls, &lower, &upper);
+ if (rc)
+ goto done;
+
+ for (i = 0; i < nparts; i++) {
+ blkid_partition par = blkid_partlist_get_partition(ls, i);
+ int n = blkid_partition_get_partno(par);
+
+ if (lower && n < lower)
+ continue;
+ if (upper && n > upper)
+ continue;
+
+ rc = add_scols_line(table, par);
+ if (rc)
+ break;
+ }
+
+ rc = 0;
+ scols_print_table(table);
+done:
+ scols_unref_table(table);
+ return rc;
+}
+
+static blkid_partlist get_partlist(blkid_probe pr,
+ const char *device, char *type)
+{
+ blkid_partlist ls;
+ blkid_parttable tab;
+
+ assert(pr);
+ assert(device);
+
+ if (type) {
+ char *name[] = { type, NULL };
+
+ if (blkid_probe_filter_partitions_type(pr,
+ BLKID_FLTR_ONLYIN, name)) {
+ warnx(_("failed to initialize blkid "
+ "filter for '%s'"), type);
+ return NULL;
+ }
+ }
+
+ ls = blkid_probe_get_partitions(pr);
+ if (!ls) {
+ warnx(_("%s: failed to read partition table"), device);
+ return NULL;
+ }
+
+ tab = blkid_partlist_get_table(ls);
+ if (verbose && tab) {
+ printf(_("%s: partition table type '%s' detected\n"),
+ device, blkid_parttable_get_type(tab));
+
+ if (!blkid_partlist_numof_partitions(ls))
+ printf(_("%s: partition table with no partitions"), device);
+ }
+
+ return ls;
+}
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ size_t i;
+
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %s [-a|-d|-s|-u] [--nr <n:m> | <partition>] <disk>\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Tell the kernel about the presence and numbering of partitions.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -a, --add add specified partitions or all of them\n"), out);
+ fputs(_(" -d, --delete delete specified partitions or all of them\n"), out);
+ fputs(_(" -u, --update update specified partitions or all of them\n"), out);
+ fputs(_(" -s, --show list partitions\n\n"), out);
+ fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out);
+ fputs(_(" -g, --noheadings don't print headings for --show\n"), out);
+ fputs(_(" -n, --nr <n:m> specify the range of partitions (e.g. --nr 2:4)\n"), out);
+ fputs(_(" -o, --output <list> define which output columns to use\n"), out);
+ fputs(_(" --output-all output all columns\n"), out);
+ fputs(_(" -P, --pairs use key=\"value\" output format\n"), out);
+ fputs(_(" -r, --raw use raw output format\n"), out);
+ fputs(_(" -S, --sector-size <num> overwrite sector size\n"), out);
+ fputs(_(" -t, --type <type> specify the partition type\n"), out);
+ fputs(_(" --list-types list supported partition types and exit\n"), out);
+ fputs(_(" -v, --verbose verbose mode\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ printf(USAGE_HELP_OPTIONS(22));
+
+ fputs(USAGE_COLUMNS, out);
+ for (i = 0; i < NCOLS; i++)
+ fprintf(out, " %10s %s\n", infos[i].name, _(infos[i].help));
+
+ printf(USAGE_MAN_TAIL("partx(8)"));
+
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int fd, c, what = ACT_NONE, lower = 0, upper = 0, rc = 0;
+ int scols_flags = 0;
+ char *type = NULL;
+ char *device = NULL; /* pointer to argv[], ie: /dev/sda1 */
+ char *wholedisk = NULL; /* allocated, ie: /dev/sda */
+ char *outarg = NULL;
+ dev_t disk_devno = 0, part_devno = 0;
+ unsigned int sector_size = 0;
+
+ enum {
+ OPT_LIST_TYPES = CHAR_MAX + 1,
+ OPT_OUTPUT_ALL
+ };
+ static const struct option long_opts[] = {
+ { "bytes", no_argument, NULL, 'b' },
+ { "noheadings", no_argument, NULL, 'g' },
+ { "raw", no_argument, NULL, 'r' },
+ { "list", no_argument, NULL, 'l' },
+ { "show", no_argument, NULL, 's' },
+ { "add", no_argument, NULL, 'a' },
+ { "delete", no_argument, NULL, 'd' },
+ { "update", no_argument, NULL, 'u' },
+ { "type", required_argument, NULL, 't' },
+ { "list-types", no_argument, NULL, OPT_LIST_TYPES },
+ { "nr", required_argument, NULL, 'n' },
+ { "output", required_argument, NULL, 'o' },
+ { "output-all", no_argument, NULL, OPT_OUTPUT_ALL },
+ { "pairs", no_argument, NULL, 'P' },
+ { "sector-size",required_argument, NULL, 'S' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "verbose", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
+ { 'P','a','d','l','r','s','u' },
+ { 0 }
+ };
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ while ((c = getopt_long(argc, argv,
+ "abdglrsuvn:t:o:PS:hV", long_opts, NULL)) != -1) {
+
+ err_exclusive_options(c, long_opts, excl, excl_st);
+
+ switch(c) {
+ case 'a':
+ what = ACT_ADD;
+ break;
+ case 'b':
+ partx_flags |= FL_BYTES;
+ break;
+ case 'd':
+ what = ACT_DELETE;
+ break;
+ case 'g':
+ scols_flags |= PARTX_NOHEADINGS;
+ break;
+ case 'l':
+ what = ACT_LIST;
+ break;
+ case 'n':
+ if (parse_range(optarg, &lower, &upper, 0))
+ errx(EXIT_FAILURE, _("failed to parse --nr <M-N> range"));
+ break;
+ case 'o':
+ outarg = optarg;
+ break;
+ case OPT_OUTPUT_ALL:
+ for (ncolumns = 0; ncolumns < ARRAY_SIZE(infos); ncolumns++)
+ columns[ncolumns] = ncolumns;
+ break;
+ case 'P':
+ scols_flags |= PARTX_EXPORT;
+ what = ACT_SHOW;
+ break;
+ case 'r':
+ scols_flags |= PARTX_RAW;
+ what = ACT_SHOW;
+ break;
+ case 's':
+ what = ACT_SHOW;
+ break;
+ case 'S':
+ sector_size = strtou32_or_err(optarg, _("invalid sector size argument"));
+ break;
+ case 't':
+ type = optarg;
+ break;
+ case 'u':
+ what = ACT_UPD;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case OPT_LIST_TYPES:
+ {
+ size_t idx = 0;
+ const char *name = NULL;
+
+ while (blkid_partitions_get_name(idx++, &name) == 0)
+ puts(name);
+ return EXIT_SUCCESS;
+ }
+ case 'h':
+ usage();
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ if (what == ACT_NONE)
+ what = ACT_SHOW;
+
+ /* --show default, could by modified by -o */
+ if (what == ACT_SHOW && !ncolumns) {
+ columns[ncolumns++] = COL_PARTNO;
+ columns[ncolumns++] = COL_START;
+ columns[ncolumns++] = COL_END;
+ columns[ncolumns++] = COL_SECTORS;
+ columns[ncolumns++] = COL_SIZE;
+ columns[ncolumns++] = COL_NAME;
+ columns[ncolumns++] = COL_UUID;
+ }
+
+ if (what == ACT_SHOW && outarg &&
+ string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
+ &ncolumns, column_name_to_id) < 0)
+ return EXIT_FAILURE;
+
+ /*
+ * Note that 'partx /dev/sda1' == 'partx /dev/sda1 /dev/sda'
+ * so assume that the device and/or disk are always the last
+ * arguments to be passed to partx.
+ */
+ if (optind == argc - 2) {
+ /* passed 2 arguments:
+ * /dev/sda1 /dev/sda : partition + whole-disk
+ * -- /dev/sda1 : partition that should be used as a whole-disk
+ */
+ device = argv[optind];
+
+ if (strcmp(device, "-") == 0) {
+ device = NULL;
+ wholedisk = xstrdup(argv[optind + 1]);
+ } else {
+ device = argv[optind];
+ wholedisk = xstrdup(argv[optind + 1]);
+
+ if (device && wholedisk && !startswith(device, wholedisk))
+ errx(EXIT_FAILURE, _("partition and disk name do not match"));
+ }
+ } else if (optind == argc - 1) {
+ /* passed only one arg (ie: /dev/sda3 or /dev/sda) */
+ struct stat sb;
+
+ device = argv[optind];
+
+ if (stat(device, &sb))
+ err(EXIT_FAILURE, _("stat of %s failed"), device);
+
+ part_devno = sb.st_rdev;
+
+ if (blkid_devno_to_wholedisk(part_devno,
+ NULL, 0, &disk_devno) == 0 &&
+ part_devno != disk_devno)
+ wholedisk = blkid_devno_to_devname(disk_devno);
+
+ if (!wholedisk) {
+ wholedisk = xstrdup(device);
+ disk_devno = part_devno;
+ device = NULL;
+ part_devno = 0;
+ }
+ } else {
+ warnx(_("bad usage"));
+ errtryhelp(EXIT_FAILURE);
+ }
+ if (device && (upper || lower))
+ errx(EXIT_FAILURE, _("--nr and <partition> are mutually exclusive"));
+
+ assert(wholedisk);
+
+ if (device) {
+ /* use partno from given partition instead of --nr range, e.g:
+ * partx -d /dev/sda3
+ * is the same like:
+ * partx -d --nr 3 /dev/sda
+ */
+ struct stat sb;
+
+ if (!part_devno && !stat(device, &sb))
+ part_devno = sb.st_rdev;
+
+ lower = upper = get_partno_from_device(device, part_devno);
+ }
+
+ if (verbose)
+ printf(_("partition: %s, disk: %s, lower: %d, upper: %d\n"),
+ device ? device : "none", wholedisk, lower, upper);
+
+ if (what == ACT_ADD || what == ACT_DELETE) {
+ struct stat x;
+
+ if (stat(wholedisk, &x))
+ errx(EXIT_FAILURE, "%s", wholedisk);
+
+ if (S_ISREG(x.st_mode)) {
+ /* not a blkdev, try to associate it to a loop device */
+ if (what == ACT_DELETE)
+ errx(EXIT_FAILURE, _("%s: cannot delete partitions"),
+ wholedisk);
+ if (!loopmod_supports_partscan())
+ errx(EXIT_FAILURE, _("%s: partitioned loop devices unsupported"),
+ wholedisk);
+ assoc_loopdev(wholedisk);
+ wholedisk = xstrdup(lc.device);
+ } else if (!S_ISBLK(x.st_mode))
+ errx(EXIT_FAILURE, _("%s: not a block device"), wholedisk);
+ }
+ if ((fd = open(wholedisk, O_RDONLY)) == -1)
+ err(EXIT_FAILURE, _("cannot open %s"), wholedisk);
+
+ if (what == ACT_DELETE)
+ rc = del_parts(fd, wholedisk, disk_devno, lower, upper);
+ else {
+ blkid_probe pr = blkid_new_probe();
+ blkid_partlist ls = NULL;
+
+ if (!pr || blkid_probe_set_device(pr, fd, 0, 0))
+ warnx(_("%s: failed to initialize blkid prober"),
+ wholedisk);
+ else {
+ if (sector_size)
+ blkid_probe_set_sectorsize(pr, sector_size);
+
+ ls = get_partlist(pr, wholedisk, type);
+ }
+
+ if (ls) {
+ switch (what) {
+ case ACT_SHOW:
+ rc = show_parts(ls, scols_flags, lower, upper);
+ break;
+ case ACT_LIST:
+ rc = list_parts(ls, lower, upper);
+ break;
+ case ACT_ADD:
+ rc = add_parts(fd, wholedisk, ls, lower, upper);
+ break;
+ case ACT_UPD:
+ rc = upd_parts(fd, wholedisk, disk_devno, ls, lower, upper);
+ break;
+ case ACT_NONE:
+ break;
+ default:
+ abort();
+ }
+ } else
+ rc = 1;
+
+ blkid_free_probe(pr);
+ }
+
+ if (loopdev)
+ loopcxt_deinit(&lc);
+
+ if (close_fd(fd) != 0)
+ err(EXIT_FAILURE, _("write failed"));
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/disk-utils/raw.8 b/disk-utils/raw.8
new file mode 100644
index 0000000..ba9fd25
--- /dev/null
+++ b/disk-utils/raw.8
@@ -0,0 +1,99 @@
+.TH RAW 8 "August 1999" "util-linux" "System Administration"
+.SH NAME
+raw \- bind a Linux raw character device
+.SH SYNOPSIS
+.B raw
+.I /dev/raw/raw<N> <major> <minor>
+.PP
+.B raw
+.I /dev/raw/raw<N> /dev/<blockdev>
+.PP
+.B raw \-q
+.I /dev/raw/raw<N>
+.PP
+.B raw \-qa
+.SH DESCRIPTION
+.B raw
+is used to bind a Linux raw character device to a block device. Any
+block device may be used: at the time of binding, the device driver does
+not even have to be accessible (it may be loaded on demand as a kernel
+module later).
+.PP
+.B raw
+is used in two modes: it either sets raw device bindings, or it queries
+existing bindings. When setting a raw device,
+.I /dev/raw/raw<N>
+is the device name of an existing raw device node in the filesystem.
+The block device to which it is to be bound can be specified either in
+terms of its
+.I major
+and
+.I minor
+device numbers, or as a path name
+.I /dev/<blockdev>
+to an existing block device file.
+.PP
+The bindings already in existence can be queried with the
+.I \-q
+option, which is used either with a raw device filename to query that one
+device, or with the
+.I \-a
+option to query all bound raw devices.
+.PP
+Unbinding can be done by specifying major and minor 0.
+.PP
+Once bound to a block device, a raw device can be opened, read and
+written, just like the block device it is bound to. However, the raw
+device does not behave exactly like the block device. In particular,
+access to the raw device bypasses the kernel's block buffer cache
+entirely: all I/O is done directly to and from the address space of the
+process performing the I/O. If the underlying block device driver can
+support DMA, then no data copying at all is required to complete the
+I/O.
+.PP
+Because raw I/O involves direct hardware access to a process's memory, a
+few extra restrictions must be observed. All I/Os must be correctly
+aligned in memory and on disk: they must start at a sector offset on
+disk, they must be an exact number of sectors long, and the data buffer
+in virtual memory must also be aligned to a multiple of the sector
+size. The sector size is 512 bytes for most devices.
+.SH OPTIONS
+.TP
+\fB\-q\fR, \fB\-\-query\fR
+Set query mode.
+.B raw
+will query an existing binding instead of setting a new one.
+.TP
+\fB\-a\fR, \fB\-\-all\fR
+With
+.B \-q
+, specify that all bound raw devices should be queried.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help text and exit.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display version information and exit.
+
+.SH NOTES
+Rather than using raw devices applications should prefer
+.BR open (2)
+devices, such as /dev/sda1, with the O_DIRECT flag.
+.SH BUGS
+The Linux
+.BR dd (1)
+command should be used without the \fBbs=\fR option, or the blocksize
+needs to be a multiple of the sector size of the device (512 bytes usually),
+otherwise it will fail with "Invalid Argument" messages (EINVAL).
+
+.PP
+Raw I/O devices do not maintain cache coherency with the Linux block
+device buffer cache. If you use raw I/O to overwrite data already in
+the buffer cache, the buffer cache will no longer correspond to the
+contents of the actual storage device underneath. This is deliberate,
+but is regarded either a bug or a feature depending on who you ask!
+.SH AUTHORS
+Stephen Tweedie (sct@redhat.com)
+.SH AVAILABILITY
+The raw command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/raw.c b/disk-utils/raw.c
new file mode 100644
index 0000000..b44a581
--- /dev/null
+++ b/disk-utils/raw.c
@@ -0,0 +1,276 @@
+/*
+ * raw.c: User mode tool to bind and query raw character devices.
+ *
+ * Stephen Tweedie, 1999, 2000
+ *
+ * This file may be redistributed under the terms of the GNU General
+ * Public License, version 2.
+ *
+ * Copyright Red Hat Software, 1999, 2000
+ *
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/major.h>
+#include <linux/raw.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "pathnames.h"
+
+#define EXIT_RAW_ACCESS 3
+#define EXIT_RAW_IOCTL 4
+
+#define RAW_NR_MINORS 8192
+
+static int do_query;
+static int do_query_all;
+
+static int master_fd;
+static int raw_minor;
+
+void open_raw_ctl(void);
+static int query(int minor_raw, const char *raw_name, int quiet);
+static int bind(int minor_raw, int block_major, int block_minor);
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %1$s %2$srawN <major> <minor>\n"
+ " %1$s %2$srawN /dev/<blockdevice>\n"
+ " %1$s -q %2$srawN\n"
+ " %1$s -qa\n"), program_invocation_short_name,
+ _PATH_RAWDEVDIR);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Bind a raw character device to a block device.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -q, --query set query mode\n"), out);
+ fputs(_(" -a, --all query all raw devices\n"), out);
+ printf(USAGE_HELP_OPTIONS(16));
+ printf(USAGE_MAN_TAIL("raw(8)"));
+ exit(EXIT_SUCCESS);
+}
+
+static long strtol_octal_or_err(const char *str, const char *errmesg)
+{
+ long num;
+ char *end = NULL;
+
+ if (str == NULL || *str == '\0')
+ goto err;
+ errno = 0;
+ num = strtol(str, &end, 0);
+
+ if (errno || str == end || (end && *end))
+ goto err;
+
+ return num;
+ err:
+ if (errno)
+ err(EXIT_FAILURE, "%s: '%s'", errmesg, str);
+ else
+ errx(EXIT_FAILURE, "%s: '%s'", errmesg, str);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ char *raw_name;
+ char *block_name;
+ int retval;
+ int block_major, block_minor;
+ int i, rc;
+
+ struct stat statbuf;
+
+ static const struct option longopts[] = {
+ {"query", no_argument, NULL, 'q'},
+ {"all", no_argument, NULL, 'a'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, '0'},
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ while ((c = getopt_long(argc, argv, "qaVh", longopts, NULL)) != -1)
+ switch (c) {
+ case 'q':
+ do_query = 1;
+ break;
+ case 'a':
+ do_query_all = 1;
+ break;
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ /*
+ * Check for, and open, the master raw device, /dev/raw
+ */
+ open_raw_ctl();
+
+ if (do_query_all) {
+ if (optind < argc) {
+ warnx(_("bad usage"));
+ errtryhelp(EXIT_FAILURE);
+ }
+ for (i = 1; i < RAW_NR_MINORS; i++)
+ query(i, NULL, 1);
+ exit(EXIT_SUCCESS);
+ }
+
+ /*
+ * It's a bind or a single query. Either way we need a raw device.
+ */
+
+ if (optind >= argc) {
+ warnx(_("bad usage"));
+ errtryhelp(EXIT_FAILURE);
+ }
+ raw_name = argv[optind++];
+
+ /*
+ * try to check the device name before stat(), because on systems with
+ * udev the raw0 causes a create udev event for char 162/0, which
+ * causes udev to *remove* /dev/rawctl
+ */
+ rc = sscanf(raw_name, _PATH_RAWDEVDIR "raw%d", &raw_minor);
+ if (rc != 1) {
+ warnx(_("bad usage"));
+ errtryhelp(EXIT_FAILURE);
+ }
+ if (raw_minor == 0)
+ errx(EXIT_RAW_ACCESS,
+ _("Device '%s' is the control raw device "
+ "(use raw<N> where <N> is greater than zero)"),
+ raw_name);
+
+ if (do_query)
+ return query(raw_minor, raw_name, 0);
+
+ /*
+ * It's not a query, so we still have some parsing to do. Have we been
+ * given a block device filename or a major/minor pair?
+ */
+ switch (argc - optind) {
+ case 1:
+ block_name = argv[optind];
+ retval = stat(block_name, &statbuf);
+ if (retval)
+ err(EXIT_RAW_ACCESS,
+ _("Cannot locate block device '%s'"), block_name);
+ if (!S_ISBLK(statbuf.st_mode))
+ errx(EXIT_RAW_ACCESS,
+ _("Device '%s' is not a block device"),
+ block_name);
+ block_major = major(statbuf.st_rdev);
+ block_minor = minor(statbuf.st_rdev);
+ break;
+
+ case 2:
+ block_major =
+ strtol_octal_or_err(argv[optind],
+ _("failed to parse argument"));
+ block_minor =
+ strtol_octal_or_err(argv[optind + 1],
+ _("failed to parse argument"));
+ break;
+
+ default:
+ warnx(_("bad usage"));
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ return bind(raw_minor, block_major, block_minor);
+}
+
+void open_raw_ctl(void)
+{
+ master_fd = open(_PATH_RAWDEVCTL, O_RDWR, 0);
+ if (master_fd < 0) {
+ master_fd = open(_PATH_RAWDEVCTL_OLD, O_RDWR, 0);
+ if (master_fd < 0)
+ err(EXIT_RAW_ACCESS,
+ _("Cannot open master raw device '%s'"),
+ _PATH_RAWDEVCTL);
+ }
+}
+
+static int query(int minor_raw, const char *raw_name, int quiet)
+{
+ struct raw_config_request rq;
+ static int has_worked = 0;
+
+ if (raw_name) {
+ struct stat statbuf;
+
+ if (stat(raw_name, &statbuf) != 0)
+ err(EXIT_RAW_ACCESS,
+ _("Cannot locate raw device '%s'"), raw_name);
+ if (!S_ISCHR(statbuf.st_mode))
+ errx(EXIT_RAW_ACCESS,
+ _("Raw device '%s' is not a character dev"),
+ raw_name);
+ if (major(statbuf.st_rdev) != RAW_MAJOR)
+ errx(EXIT_RAW_ACCESS,
+ _("Device '%s' is not a raw dev"), raw_name);
+ minor_raw = minor(statbuf.st_rdev);
+ }
+
+ rq.raw_minor = minor_raw;
+ if (ioctl(master_fd, RAW_GETBIND, &rq) < 0) {
+ if (quiet && errno == ENODEV)
+ return 3;
+ if (has_worked && errno == EINVAL)
+ return 0;
+ err(EXIT_RAW_IOCTL, _("Error querying raw device"));
+ }
+
+ /* If one query has worked, mark that fact so that we don't report
+ * spurious fatal errors if raw(8) has been built to support more raw
+ * minor numbers than the kernel has. */
+ has_worked = 1;
+ if (quiet && !rq.block_major && !rq.block_minor)
+ return 0;
+ printf(_("%sraw%d: bound to major %d, minor %d\n"),
+ _PATH_RAWDEVDIR, minor_raw, (int)rq.block_major,
+ (int)rq.block_minor);
+ return 0;
+}
+
+static int bind(int minor_raw, int block_major, int block_minor)
+{
+ struct raw_config_request rq;
+
+ rq.raw_minor = minor_raw;
+ rq.block_major = block_major;
+ rq.block_minor = block_minor;
+ if (ioctl(master_fd, RAW_SETBIND, &rq) < 0)
+ err(EXIT_RAW_IOCTL, _("Error setting raw device"));
+ printf(_("%sraw%d: bound to major %d, minor %d\n"),
+ _PATH_RAWDEVDIR, raw_minor, (int)rq.block_major,
+ (int)rq.block_minor);
+ return 0;
+}
diff --git a/disk-utils/resizepart.8 b/disk-utils/resizepart.8
new file mode 100644
index 0000000..a3ea0d2
--- /dev/null
+++ b/disk-utils/resizepart.8
@@ -0,0 +1,38 @@
+.\" resizepart.8 -- man page for resizepart
+.\" Copyright 2012 Vivek Goyal <vgoyal@redhat.com>
+.\" Copyright 2012 Red Hat, Inc.
+.\" May be distributed under the GNU General Public License
+.TH RESIZEPART 8 "January 2015" "util-linux" "System Administration"
+.SH NAME
+resizepart \- tell the kernel about the new size of a partition
+.SH SYNOPSIS
+.B resizepart
+.I device partition length
+.SH DESCRIPTION
+.B resizepart
+tells the Linux kernel about the new size of the specified partition.
+The command is a simple wrapper around the "resize partition" ioctl.
+
+This command doesn't manipulate partitions on a block device.
+
+.SH PARAMETERS
+.TP
+.I device
+The disk device.
+.TP
+.I partition
+The partition number.
+.TP
+.I length
+The new length of the partition (in 512-byte sectors).
+
+.SH SEE ALSO
+.BR addpart (8),
+.BR delpart (8),
+.BR fdisk (8),
+.BR parted (8),
+.BR partprobe (8),
+.BR partx (8)
+.SH AVAILABILITY
+The resizepart command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/resizepart.c b/disk-utils/resizepart.c
new file mode 100644
index 0000000..b273827
--- /dev/null
+++ b/disk-utils/resizepart.c
@@ -0,0 +1,118 @@
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "nls.h"
+#include "partx.h"
+#include "sysfs.h"
+#include "strutils.h"
+#include "closestream.h"
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s <disk device> <partition number> <length>\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Tell the kernel about the new size of a partition.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ printf(USAGE_HELP_OPTIONS(16));
+ printf(USAGE_MAN_TAIL("resizepart(8)"));
+ exit(EXIT_SUCCESS);
+}
+
+static int get_partition_start(int fd, int partno, uint64_t *start)
+{
+ struct stat st;
+ struct path_cxt *disk = NULL, *part = NULL;
+ dev_t devno = 0;
+ int rc = -1;
+
+ /*
+ * wholedisk
+ */
+ if (fstat(fd, &st) || !S_ISBLK(st.st_mode))
+ goto done;
+ devno = st.st_rdev;
+ disk = ul_new_sysfs_path(devno, NULL, NULL);
+ if (!disk)
+ goto done;
+ /*
+ * partition
+ */
+ devno = sysfs_blkdev_partno_to_devno(disk, partno);
+ if (!devno)
+ goto done;
+
+ part = ul_new_sysfs_path(devno, disk, NULL);
+ if (!part)
+ goto done;
+ if (ul_path_read_u64(part, start, "start"))
+ goto done;
+
+ rc = 0;
+done:
+ ul_unref_path(part);
+ ul_unref_path(disk);
+ return rc;
+}
+
+int main(int argc, char **argv)
+{
+ int c, fd, partno;
+ const char *wholedisk;
+ uint64_t start;
+
+ static const struct option longopts[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, '0'},
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ while ((c = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
+ switch (c) {
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ if (argc != 4) {
+ warnx(_("not enough arguments"));
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ wholedisk = argv[1];
+ partno = strtou32_or_err(argv[2], _("invalid partition number argument"));
+
+ if ((fd = open(wholedisk, O_RDONLY)) < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), wholedisk);
+
+ if (get_partition_start(fd, partno, &start))
+ err(EXIT_FAILURE, _("%s: failed to get start of the partition number %s"),
+ wholedisk, argv[2]);
+
+ if (partx_resize_partition(fd, partno, start,
+ strtou64_or_err(argv[3], _("invalid length argument"))))
+ err(EXIT_FAILURE, _("failed to resize partition"));
+
+ if (close_fd(fd) != 0)
+ err(EXIT_FAILURE, _("write failed"));
+
+ return 0;
+}
diff --git a/disk-utils/sfdisk.8 b/disk-utils/sfdisk.8
new file mode 100644
index 0000000..12c94eb
--- /dev/null
+++ b/disk-utils/sfdisk.8
@@ -0,0 +1,661 @@
+.\" sfdisk.8 -- man page for sfdisk
+.\" Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one.
+.\"
+.TH SFDISK 8 "June 2015" "util-linux" "System Administration"
+.SH NAME
+sfdisk \- display or manipulate a disk partition table
+.SH SYNOPSIS
+.B sfdisk
+[options]
+.I device
+.RB [ \-N
+.IR partition-number ]
+.sp
+.B sfdisk
+[options]
+.I command
+.SH DESCRIPTION
+.B sfdisk
+is a script-oriented tool for partitioning any block device. It
+runs in interactive mode if executed on terminal (stdin refers to a terminal).
+
+Since version 2.26
+.B sfdisk
+supports MBR (DOS), GPT, SUN and SGI disk labels, but no longer provides any
+functionality for CHS (Cylinder-Head-Sector) addressing. CHS has
+never been important for Linux, and this addressing concept does not make any
+sense for new devices.
+.sp
+.B sfdisk
+(since version 2.26)
+.B aligns the start and end of partitions
+to block-device I/O limits when relative sizes are specified, when the default
+values are used or when multiplicative suffixes (e.g., MiB) are used for sizes.
+It is possible that partition size will be optimized (reduced or enlarged) due
+to alignment if the start offset is specified exactly in sectors and partition
+size relative or by multiplicative suffixes.
+
+The recommended way is not to specify start offsets at all and specify
+partition size in MiB, GiB (or so). In this case sfdisk align all partitions
+to block-device I/O limits (or when I/O limits are too small then to megabyte
+boundary to keep disk layout portable). If this default behaviour is unwanted
+(usually for very small partitions) then specify offsets and sizes in
+sectors. In this case sfdisk entirely follows specified numbers without any
+optimization.
+.sp
+.B sfdisk
+does not create the standard system partitions for SGI and SUN disk labels like
+.BR fdisk (8)
+does.
+It is necessary to explicitly create all partitions including whole-disk system
+partitions.
+
+.B sfdisk
+uses BLKRRPART (reread partition table) ioctl to make sure that the device is
+not used by system or other tools (see also \-\-no-reread). It's possible that
+this feature or another sfdisk activity races with \fBudevd\fR. The recommended way
+how to avoid possible collisions is to use \fB\-\-lock\fR option.
+The exclusive lock will cause udevd to skip the event handling on the device.
+.PP
+The sfdisk prompt is only a hint for users and a displayed partition number does
+not mean that the same partition table entry will be created (if -N not
+specified), especially for tables with gaps.
+
+.SH COMMANDS
+The commands are mutually exclusive.
+.TP
+.RB [ \-N " \fIpartition-number\fR] " \fIdevice\fR
+The default \fBsfdisk\fR command is to read the specification for the desired
+partitioning of \fIdevice\fR from standard input, and then create a partition
+table according to the specification. See below for the description of the
+input format. If standard input is a terminal, then \fBsfdisk\fR starts an
+interactive session.
+.sp
+If the option \fB\-N\fR is specified, then the changes are applied to
+the partition addressed by \fIpartition-number\fR. The unspecified fields
+of the partition are not modified.
+.sp
+Note that it's possible to address an unused partition with \fB\-N\fR.
+For example, an MBR always contains 4 partitions, but the number of used
+partitions may be smaller. In this case \fBsfdisk\fR follows the default
+values from the partition table and does not use built-in defaults for the
+unused partition given with \fB\-N\fR. See also \fB\-\-append\fR.
+.TP
+.BR \-A , " \-\-activate \fIdevice " [ \fIpartition-number...]
+Switch on the bootable flag for the specified partitions and switch off the
+bootable flag on all unspecified partitions. The special placeholder '\-'
+may be used instead of the partition numbers to switch off the bootable flag
+on all partitions.
+
+The activation command is supported for MBR and PMBR only. If GPT label is detected
+than sfdisk prints warning and automatically enter PMBR.
+
+If no \fIpartition-number\fR is specified, then list the partitions with an
+enabled flag.
+.TP
+.BR "\-\-delete \fIdevice " [ \fIpartition-number ...]
+Delete all or the specified partitions.
+.TP
+.BR \-d , " \-\-dump " \fIdevice\fR
+Dump the partitions of a device in a format that is usable as input to \fBsfdisk\fR.
+See the section \fBBACKING UP THE PARTITION TABLE\fR.
+.TP
+.BR \-g , " \-\-show\-geometry " [ \fIdevice ...]
+List the geometry of all or the specified devices. For backward
+compatibility the deprecated option \fB\-\-show\-pt\-geometry\fR have the same
+meaning as this one.
+.TP
+.BR \-J , " \-\-json " \fIdevice\fR
+Dump the partitions of a device in JSON format. Note that \fBsfdisk\fR is
+not able to use JSON as input format.
+.TP
+.BR \-l , " \-\-list " [ \fIdevice ...]
+List the partitions of all or the specified devices. This command can be used
+together with \fB\-\-verify\fR.
+.TP
+.BR \-F , " \-\-list-free " [ \fIdevice ...]
+List the free unpartitioned areas on all or the specified devices.
+.TP
+.BR "\-\-part\-attrs \fIdevice partition-number " [ \fIattributes ]
+Change the GPT partition attribute bits. If \fIattributes\fR is not specified,
+then print the current partition settings. The \fIattributes\fR argument is a
+comma- or space-delimited list of bits numbers or bit names. For example, the
+string "RequiredPartition,50,51" sets three bits. The currently supported
+attribute bits are:
+.RS
+.TP
+.BR "Bit 0 (RequiredPartition)"
+If this bit is set, the partition is required for the platform to function. The
+creator of the partition indicates that deletion or modification of the contents
+can result in loss of platform features or failure for the platform to boot or
+operate. The system cannot function normally if this partition is removed, and it
+should be considered part of the hardware of the system.
+.TP
+.BR "Bit 1 (NoBlockIOProtocol)"
+EFI firmware should ignore the content of the partition and not try to read from it.
+.TP
+.BR "Bit 2 (LegacyBIOSBootable)"
+The partition may be bootable by legacy BIOS firmware.
+.TP
+.BR "Bits 3-47"
+Undefined and must be zero. Reserved for expansion by future versions of the
+UEFI specification.
+.TP
+.BR "Bits 48-63"
+Reserved for GUID specific use. The use of these bits will vary depending on
+the partition type. For example Microsoft uses bit 60 to indicate read-only,
+61 for shadow copy of another partition, 62 for hidden partitions and 63 to
+disable automount.
+.RE
+.sp
+.TP
+.BR "\-\-part\-label \fIdevice partition-number " [ \fIlabel ]
+Change the GPT partition name (label). If \fIlabel\fR is not specified,
+then print the current partition label.
+.TP
+.BR "\-\-part\-type \fIdevice partition-number " [ \fItype ]
+Change the partition type. If \fItype\fR is not specified, then print the
+current partition type.
+.sp
+The \fItype\fR argument is hexadecimal for MBR,
+GUID for GPT, type alias (e.g. "linux") or type shortcut (e.g. 'L').
+For backward compatibility the options \fB\-c\fR and
+\fB\-\-id\fR have the same meaning as this one.
+.TP
+.BR "\-\-part\-uuid \fIdevice partition-number " [ \fIuuid ]
+Change the GPT partition UUID. If \fIuuid\fR is not specified,
+then print the current partition UUID.
+.TP
+.BR "\-\-disk\-id \fIdevice " [ \fIid ]
+Change the disk identifier. If \fIid\fR is not specified,
+then print the current identifier. The identifier is UUID for GPT
+or unsigned integer for MBR.
+.TP
+.BR \-r , " \-\-reorder " \fIdevice
+Renumber the partitions, ordering them by their start offset.
+.TP
+.BR \-s , " \-\-show\-size " [ \fIdevice ...]
+List the sizes of all or the specified devices in units of 1024 byte size.
+This command is DEPRECATED in favour of
+.BR blockdev (8).
+.TP
+.BR \-T , " \-\-list\-types"
+Print all supported types for the current disk label or the label specified by
+\fB\-\-label\fR.
+.TP
+.BR \-V , " \-\-verify " [ \fIdevice ...]
+Test whether the partition table and partitions seem correct.
+.TP
+.BR "\-\-relocate \fIoper " \fIdevice
+Relocate partition table header. This command is currently supported for GPT header only.
+The argument \fIoper\fP can be:
+.RS
+.TP
+.B gpt-bak-std
+Move GPT backup header to the standard location at the end of the device.
+.TP
+.B gpt-bak-mini
+Move GPT backup header behind the last partition. Note that UEFI
+standard requires the backup header at the end of the device and partitioning
+tools can automatically relocate the header to follow the standard.
+.RE
+.SH OPTIONS
+.TP
+.BR \-a , " \-\-append"
+Don't create a new partition table, but only append the specified partitions.
+.sp
+Note that unused partition maybe be re-used in this case although it is not the
+last partition in the partition table. See also \fB\-N\fR to specify entry in
+the partition table.
+.TP
+.BR \-b , " \-\-backup"
+Back up the current partition table sectors before starting the partitioning.
+The default backup file name is ~/sfdisk-<device>-<offset>.bak; to use another
+name see option \fB\-O\fR, \fB\-\-backup\-file\fR.
+.TP
+.BR \-\-color [ =\fIwhen ]
+Colorize the output. The optional argument \fIwhen\fP
+can be \fBauto\fR, \fBnever\fR or \fBalways\fR. If the \fIwhen\fR argument is omitted,
+it defaults to \fBauto\fR. The colors can be disabled; for the current built-in default
+see the \fB\-\-help\fR output. See also the \fBCOLORS\fR section.
+.TP
+.BR \-f , " \-\-force"
+Disable all consistency checking.
+.TP
+.B \-\-Linux
+Deprecated and ignored option. Partitioning that is compatible with
+Linux (and other modern operating systems) is the default.
+.TP
+\fB\-\-lock\fR[=\fImode\fR]
+Use exclusive BSD lock for device or file it operates. The optional argument
+\fImode\fP can be \fByes\fR, \fBno\fR (or 1 and 0) or \fBnonblock\fR. If the \fImode\fR
+argument is omitted, it defaults to \fB"yes"\fR. This option overwrites
+environment variable \fB$LOCK_BLOCK_DEVICE\fR. The default is not to use any
+lock at all, but it's recommended to avoid collisions with udevd or other
+tools.
+.TP
+.BR \-n , " \-\-no\-act"
+Do everything except writing to the device.
+.TP
+.B \-\-no\-reread
+Do not check through the re-read-partition-table ioctl whether the device is in use.
+.TP
+.B \-\-no\-tell\-kernel
+Don't tell the kernel about partition changes. This option is recommended together
+with \fB\-\-no\-reread\fR to modify a partition on used disk. The modified partition
+should not be used (e.g., mounted).
+.TP
+.BR \-O , " \-\-backup\-file " \fIpath
+Override the default backup file name. Note that the device name and offset
+are always appended to the file name.
+.TP
+.BR \-\-move-data [ =\fIpath ]
+Move data after partition relocation, for example when moving the beginning
+of a partition to another place on the disk. The size of the partition has
+to remain the same, the new and old location may overlap. This option requires
+option \fB\-N\fR in order to be processed on one specific partition only.
+
+The optional \fIpath\fR specifies log file name. The log file contains information
+about all read/write operations on the partition data. The word "@default" as
+a \fIpath\fR forces sfdisk to use ~/sfdisk-<devname>.move for the log. The log is
+optional since v2.35.
+
+Note that this operation is risky and not atomic. \fBDon't forget to backup your data!\fR
+
+See also \fB\-\-move\-use\-fsync\fR.
+
+In the example below, the first command creates a 100MiB free area before
+the first partition and moves the data it contains (e.g., a filesystem),
+the next command creates a new partition from the free space (at offset 2048),
+and the last command reorders partitions to match disk order
+(the original sdc1 will become sdc2).
+.RS
+.sp
+.B "echo '+100M,' | sfdisk \-\-move-data /dev/sdc \-N 1"
+.br
+.B "echo '2048,' | sfdisk /dev/sdc \-\-append"
+.br
+.B sfdisk /dev/sdc \-\-reorder
+.sp
+.RE
+
+.TP
+.B \-\-move\-use\-fsync
+Use fsync system call after each write when move data to a new location by
+\fB\-\-move\-data\fR.
+.TP
+.BR \-o , " \-\-output " \fIlist
+Specify which output columns to print. Use
+.B \-\-help
+to get a list of all supported columns.
+.sp
+The default list of columns may be extended if \fIlist\fP is
+specified in the format \fI+list\fP (e.g., \fB\-o +UUID\fP).
+.TP
+.BR \-q , " \-\-quiet"
+Suppress extra info messages.
+.TP
+.BR \-u , " \-\-unit S"
+Deprecated option. Only the sector unit is supported. This option is not
+supported when using the \-\-show-size command.
+.TP
+.BR \-X , " \-\-label " \fItype
+Specify the disk label type (e.g., \fBdos\fR, \fBgpt\fR, ...). If this option
+is not given, then \fBsfdisk\fR defaults to the existing label, but if there
+is no label on the device yet, then the type defaults to \fBdos\fR. The default
+or the current label may be overwritten by the "label: <name>" script header
+line. The option \fB\-\-label\fR does not force \fBsfdisk\fR to create empty
+disk label (see the \fBEMPTY DISK LABEL\fR section below).
+.TP
+.BR \-Y , " \-\-label\-nested " \fItype
+Force editing of a nested disk label. The primary disk label has to exist already.
+This option allows editing for example a hybrid/protective MBR on devices with GPT.
+.TP
+.BR \-w , " \-\-wipe "\fIwhen
+Wipe filesystem, RAID and partition-table signatures from the device, in order
+to avoid possible collisions. The argument \fIwhen\fR can be \fBauto\fR,
+\fBnever\fR or \fBalways\fR. When this option is not given, the default is
+\fBauto\fR, in which case signatures are wiped only when in interactive mode;
+except the old partition-table signatures which are always wiped before create
+a new partition-table if the argument \fIwhen\fR is not \fBnever\fR. In all
+cases detected signatures are reported by warning messages before a new
+partition table is created. See also
+.BR wipefs (8)
+command.
+
+.TP
+.BR \-W , " \-\-wipe-partitions "\fIwhen
+Wipe filesystem, RAID and partition-table signatures from a newly created
+partitions, in order to avoid possible collisions. The argument \fIwhen\fR can
+be \fBauto\fR, \fBnever\fR or \fBalways\fR. When this option is not given, the
+default is \fBauto\fR, in which case signatures are wiped only when in
+interactive mode and after confirmation by user. In all cases detected
+signatures are reported by warning messages after a new partition is created.
+See also
+.BR wipefs (8)
+command.
+
+.TP
+.BR \-v , " \-\-version"
+Display version information and exit.
+.TP
+.BR \-h , " \-\-help"
+Display help text and exit.
+
+.SH INPUT FORMATS
+.B sfdisk
+supports two input formats and generic header lines.
+
+.B Header lines
+.RS
+The optional header lines specify generic information that apply to the partition
+table. The header-line format is:
+.RS
+.sp
+.B "<name>: <value>"
+.sp
+.RE
+The currently recognized headers are:
+.RS
+.TP
+.B unit
+Specify the partitioning unit. The only supported unit is \fBsectors\fR.
+.TP
+.B label
+Specify the partition table type. For example \fBdos\fR or \fBgpt\fR.
+.TP
+.B label-id
+Specify the partition table identifier. It should be a hexadecimal number
+(with a 0x prefix) for MBR and a UUID for GPT.
+.TP
+.B first-lba
+Specify the first usable sector for GPT partitions.
+.TP
+.B last-lba
+Specify the last usable sector for GPT partitions.
+.TP
+.B table-length
+Specify the maximal number of GPT partitions.
+.TP
+.B grain
+Specify minimal size in bytes used to calculate partitions alignment. The
+default is 1MiB and it's strongly recommended to use the default. Do not
+modify this variable if you're not sure.
+.TP
+.B sector-size
+Specify sector size. This header is informative only and it is not used when
+sfdisk creates a new partition table, in this case the real device specific
+value is always used and sector size from the dump is ignored.
+.RE
+.sp
+Note that it is only possible to use header lines before the first partition
+is specified in the input.
+.RE
+
+.B Unnamed-fields format
+.RS
+.RS
+.sp
+.I start size type bootable
+.sp
+.RE
+where each line fills one partition descriptor.
+.sp
+Fields are separated by whitespace, comma or semicolon possibly
+followed by whitespace; initial and trailing whitespace is ignored.
+Numbers can be octal, decimal or hexadecimal; decimal is the default.
+When a field is absent, empty or specified as '\-' a default value is
+used. But when the \fB\-N\fR option (change a single partition) is
+given, the default for each field is its previous value.
+.sp
+The default value of
+.I start
+is the first non-assigned sector aligned according to device I/O limits.
+The default start offset for the first partition is 1 MiB. The offset may
+be followed by the multiplicative suffixes (KiB, MiB, GiB, TiB, PiB,
+EiB, ZiB and YiB) then the number is interpreted as offset in bytes.
+.sp
+The default value of
+.I size
+indicates "as much as possible"; i.e., until the next partition or
+end-of-device. A numerical argument is by default interpreted as a
+number of sectors, however if the size is followed by one of the
+multiplicative suffixes (KiB, MiB, GiB, TiB, PiB, EiB, ZiB and YiB)
+then the number is interpreted as the size of the partition in bytes
+and it is then aligned according to the device I/O limits. A '+' can
+be used instead of a number to enlarge the partition as much as
+possible. Note '+' is equivalent to the default behaviour for a new
+partition; existing partitions will be resized as required.
+.sp
+The partition
+.I type
+is given in hex for MBR (DOS) where 0x prefix is optional; a GUID string for
+GPT; a shortcut or an alias. It's recommended to use two letters for MBR hex codes to
+avoid collision between deprecated shortcut 'E' and '0E' MBR hex code. For backward
+compatibility sfdisk tries to interpret
+.I type
+as a shortcut as a first possibility in partitioning scripts although on other places (e.g.
+\fB\-\-part-type command)\fR it tries shortcuts as the last possibility.
+
+Since v2.36 libfdisk supports partition type aliases as extension to shortcuts. The alias is a
+simple human readable word (e.g. "linux").
+
+Supported shortcuts and aliases:
+.RS
+.TP
+.B L - alias 'linux'
+Linux; means 83 for MBR and 0FC63DAF-8483-4772-8E79-3D69D8477DE4 for GPT.
+.TP
+.B S - alias 'swap'
+swap area; means 82 for MBR and 0657FD6D-A4AB-43C4-84E5-0933C84B4F4F for GPT
+.TP
+.B Ex - alias 'extended'
+MBR extended partition; means 05 for MBR. The original shortcut 'E' is deprecated due to collision with
+0x0E MBR partition type.
+.TP
+.B H - alias 'home'
+home partition; means 933AC7E1-2EB4-4F13-B844-0E14E2AEF915 for GPT
+.TP
+.B U - alias 'uefi'
+EFI System partition, means EF for MBR and C12A7328-F81F-11D2-BA4B-00A0C93EC93B for GPT
+.TP
+.B R - alias 'raid'
+Linux RAID; means FD for MBR and A19D880F-05FC-4D3B-A006-743F0F84911E for GPT
+.TP
+.B V - alias 'lvm'
+LVM; means 8E for MBR and E6D6D379-F507-44C2-A23C-238F2A3DF928 for GPT
+.RE
+.PP
+The default
+.I type
+value is
+.I linux
+.sp
+The shortcut 'X' for Linux extended partition (85) is deprecated in favour of 'Ex'.
+
+.I bootable
+is specified as [\fB*\fR|\fB-\fR], with as default not-bootable. The
+value of this field is irrelevant for Linux - when Linux runs it has
+been booted already - but it might play a role for certain boot
+loaders and for other operating systems.
+.RE
+
+.B Named-fields format
+.RS
+This format is more readable, robust, extensible and allows specifying additional
+information (e.g., a UUID). It is recommended to use this format to keep your scripts
+more readable.
+.RS
+.sp
+.RI [ "device \fB:" ] " name" [\fB= value "], ..."
+.sp
+.RE
+The
+.I device
+field is optional. \fBsfdisk\fR extracts the partition number from the
+device name. It allows specifying the partitions in random order.
+This functionality is mostly used by \fB\-\-dump\fR.
+Don't use it if you are not sure.
+
+The
+.I value
+can be between quotation marks (e.g., name="This is partition name").
+The currently supported fields are:
+.RS
+.TP
+.BI start= number
+The first non-assigned sector aligned according to device I/O limits. The default
+start offset for the first partition is 1 MiB. The offset may be followed by
+the multiplicative suffixes (KiB, MiB, GiB, TiB, PiB, EiB, ZiB and YiB) then
+the number is interpreted as offset in bytes.
+.TP
+.BI size= number
+Specify the partition size in sectors. The number may be followed by the multiplicative
+suffixes (KiB, MiB, GiB, TiB, PiB, EiB, ZiB and YiB), then it's interpreted as size
+in bytes and the size is aligned according to device I/O limits.
+.TP
+.B bootable
+Mark the partition as bootable.
+.TP
+.BI attrs= string
+Partition attributes, usually GPT partition attribute bits. See
+\fB\-\-part\-attrs\fR for more details about the GPT-bits string format.
+.TP
+.BI uuid= string
+GPT partition UUID.
+.TP
+.BI name= string
+GPT partition name.
+.TP
+.BI type= code
+A hexadecimal number (without 0x) for an MBR partition, a GUID for a GPT partition,
+or a shortcut as for unnamed-fields format.
+For backward compatibility the \fBId=\fR field has the same meaning.
+.RE
+.RE
+
+.SH EMPTY DISK LABEL
+.B sfdisk
+does not create partition table without partitions by default. The lines with
+partitions are expected in the script by default. The empty partition table has
+to be explicitly requested by "label: <name>" script header line without any
+partitions lines. For example:
+.RS
+.sp
+.B "echo 'label: gpt' | sfdisk /dev/sdb"
+.sp
+.RE
+creates empty GPT partition table. Note that the \fB\-\-append\fR disables this feature.
+
+.SH BACKING UP THE PARTITION TABLE
+It is recommended to save the layout of your devices.
+.B sfdisk
+supports two ways.
+.sp
+Use the \fB\-\-dump\fR option to save a description of the device layout
+to a text file. The dump format is suitable for later \fBsfdisk\fR input.
+For example:
+.RS
+.sp
+.B "sfdisk \-\-dump /dev/sda > sda.dump"
+.sp
+.RE
+This can later be restored by:
+.RS
+.sp
+.B "sfdisk /dev/sda < sda.dump"
+.RE
+
+If you want to do a full (binary) backup of all sectors where the
+partition table is stored,
+then use the \fB\-\-backup\fR option. It writes the sectors to
+~/sfdisk-<device>-<offset>.bak files. The default name of the backup file can
+be changed with the \fB\-\-backup\-file\fR option. The backup files
+contain only raw data from the \fIdevice\fR.
+Note that the same concept of backup files is used by
+.BR wipefs (8).
+For example:
+.RS
+.sp
+.B "sfdisk \-\-backup /dev/sda"
+.sp
+.RE
+The GPT header can later be restored by:
+.RS
+.sp
+.nf
+.B "dd if=~/sfdisk-sda-0x00000200.bak of=/dev/sda \e"
+.B " seek=$((0x00000200)) bs=1 conv=notrunc"
+.fi
+.sp
+.RE
+Note that \fBsfdisk\fR since version 2.26 no longer provides the \fB\-I\fR option to
+restore sectors.
+.BR dd (1)
+provides all necessary functionality.
+
+.SH COLORS
+Implicit coloring can be disabled by an empty file \fI/etc/terminal-colors.d/sfdisk.disable\fR.
+
+See
+.BR terminal-colors.d (5)
+for more details about colorization configuration. The logical color names
+supported by
+.B sfdisk
+are:
+.TP
+.B header
+The header of the output tables.
+.TP
+.B warn
+The warning messages.
+.TP
+.B welcome
+The welcome message.
+
+.SH ENVIRONMENT
+.IP SFDISK_DEBUG=all
+enables sfdisk debug output.
+.IP LIBFDISK_DEBUG=all
+enables libfdisk debug output.
+.IP LIBBLKID_DEBUG=all
+enables libblkid debug output.
+.IP LIBSMARTCOLS_DEBUG=all
+enables libsmartcols debug output.
+.IP LOCK_BLOCK_DEVICE=<mode>
+use exclusive BSD lock. The mode is "1" or "0". See \fB\-\-lock\fR for more details.
+
+.SH NOTES
+Since version 2.26 \fBsfdisk\fR no longer provides the \fB\-R\fR or
+\fB\-\-re\-read\fR option to force the kernel to reread the partition table.
+Use \fBblockdev \-\-rereadpt\fR instead.
+.PP
+Since version 2.26 \fBsfdisk\fR does not provide the \fB\-\-DOS\fR, \fB\-\-IBM\fR, \fB\-\-DOS\-extended\fR,
+\fB\-\-unhide\fR, \fB\-\-show\-extended\fR, \fB\-\-cylinders\fR, \fB\-\-heads\fR, \fB\-\-sectors\fR,
+\fB\-\-inside\-outer\fR, \fB\-\-not\-inside\-outer\fR options.
+
+.SH AUTHORS
+Karel Zak <kzak@redhat.com>
+.PP
+The current sfdisk implementation is based on the original sfdisk
+from Andries E. Brouwer.
+
+.SH SEE ALSO
+.BR fdisk (8),
+.BR cfdisk (8),
+.BR parted (8),
+.BR partprobe (8),
+.BR partx (8)
+
+.SH AVAILABILITY
+The sfdisk command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/sfdisk.c b/disk-utils/sfdisk.c
new file mode 100644
index 0000000..5f00bd3
--- /dev/null
+++ b/disk-utils/sfdisk.c
@@ -0,0 +1,2438 @@
+/*
+ * Copyright (C) 1995 Andries E. Brouwer (aeb@cwi.nl)
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software. You can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation: either Version 1
+ * or (at your option) any later version.
+ *
+ * A.V. Le Blanc (LeBlanc@mcc.ac.uk) wrote Linux fdisk 1992-1994,
+ * patched by various people (faith@cs.unc.edu, martin@cs.unc.edu,
+ * leisner@sdsp.mc.xerox.com, esr@snark.thyrsus.com, aeb@cwi.nl)
+ * 1993-1995, with version numbers (as far as I have seen) 0.93 - 2.0e.
+ * This program had (head,sector,cylinder) as basic unit, and was
+ * (therefore) broken in several ways for the use on larger disks -
+ * for example, my last patch (from 2.0d to 2.0e) was required
+ * to allow a partition to cross cylinder 8064, and to write an
+ * extended partition past the 4GB mark.
+ *
+ * Karel Zak wrote new sfdisk based on libfdisk from util-linux
+ * in 2014.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <libsmartcols.h>
+#ifdef HAVE_LIBREADLINE
+# define _FUNCTION_DEF
+# include <readline/readline.h>
+#endif
+#include <libgen.h>
+#include <sys/time.h>
+
+#include "c.h"
+#include "xalloc.h"
+#include "nls.h"
+#include "debug.h"
+#include "strutils.h"
+#include "closestream.h"
+#include "colors.h"
+#include "blkdev.h"
+#include "all-io.h"
+#include "rpmatch.h"
+#include "optutils.h"
+#include "ttyutils.h"
+
+#include "libfdisk.h"
+#include "fdisk-list.h"
+
+/*
+ * sfdisk debug stuff (see fdisk.h and include/debug.h)
+ */
+static UL_DEBUG_DEFINE_MASK(sfdisk);
+UL_DEBUG_DEFINE_MASKNAMES(sfdisk) = UL_DEBUG_EMPTY_MASKNAMES;
+
+#define SFDISKPROG_DEBUG_INIT (1 << 1)
+#define SFDISKPROG_DEBUG_PARSE (1 << 2)
+#define SFDISKPROG_DEBUG_MISC (1 << 3)
+#define SFDISKPROG_DEBUG_ASK (1 << 4)
+#define SFDISKPROG_DEBUG_ALL 0xFFFF
+
+#define DBG(m, x) __UL_DBG(sfdisk, SFDISKPROG_DEBUG_, m, x)
+#define ON_DBG(m, x) __UL_DBG_CALL(sfdisk, SFDISKPROG_DEBUG_, m, x)
+
+enum {
+ ACT_FDISK = 1,
+ ACT_ACTIVATE,
+ ACT_CHANGE_ID,
+ ACT_DUMP,
+ ACT_LIST,
+ ACT_LIST_FREE,
+ ACT_LIST_TYPES,
+ ACT_REORDER,
+ ACT_RELOCATE,
+ ACT_SHOW_SIZE,
+ ACT_SHOW_GEOM,
+ ACT_VERIFY,
+ ACT_PARTTYPE,
+ ACT_PARTUUID,
+ ACT_PARTLABEL,
+ ACT_PARTATTRS,
+ ACT_DISKID,
+ ACT_DELETE
+};
+
+struct sfdisk {
+ int act; /* ACT_* */
+ int partno; /* -N <partno>, default -1 */
+ int wipemode; /* remove foreign signatures from disk */
+ int pwipemode; /* remove foreign signatures from partitions */
+ const char *lockmode; /* as specified by --lock */
+ const char *label; /* --label <label> */
+ const char *label_nested; /* --label-nested <label> */
+ const char *backup_file; /* -O <path> */
+ const char *move_typescript; /* --movedata <typescript> */
+ char *prompt;
+
+ struct fdisk_context *cxt; /* libfdisk context */
+ struct fdisk_partition *orig_pa; /* -N <partno> before the change */
+
+ unsigned int verify : 1, /* call fdisk_verify_disklabel() */
+ quiet : 1, /* suppress extra messages */
+ interactive : 1, /* running on tty */
+ noreread : 1, /* don't check device is in use */
+ force : 1, /* do also stupid things */
+ backup : 1, /* backup sectors before write PT */
+ container : 1, /* PT contains container (MBR extended) partitions */
+ unused : 1, /* PT contains unused partition */
+ append : 1, /* don't create new PT, append partitions only */
+ json : 1, /* JSON dump */
+ movedata: 1, /* move data after resize */
+ movefsync: 1, /* use fsync() after each write() */
+ notell : 1, /* don't tell kernel aout new PT */
+ noact : 1; /* do not write to device */
+};
+
+#define SFDISK_PROMPT ">>> "
+
+static void sfdiskprog_init_debug(void)
+{
+ __UL_INIT_DEBUG_FROM_ENV(sfdisk, SFDISKPROG_DEBUG_, 0, SFDISK_DEBUG);
+}
+
+
+static int get_user_reply(const char *prompt, char *buf, size_t bufsz)
+{
+ char *p;
+ size_t sz;
+
+#ifdef HAVE_LIBREADLINE
+ if (isatty(STDIN_FILENO)) {
+ p = readline(prompt);
+ if (!p)
+ return 1;
+ xstrncpy(buf, p, bufsz);
+ free(p);
+ } else
+#endif
+ {
+ fputs(prompt, stdout);
+ fflush(stdout);
+
+ if (!fgets(buf, bufsz, stdin))
+ return 1;
+ }
+
+ for (p = buf; *p && !isgraph(*p); p++); /* get first non-blank */
+
+ if (p > buf)
+ memmove(buf, p, p - buf); /* remove blank space */
+ sz = strlen(buf);
+ if (sz && *(buf + sz - 1) == '\n')
+ *(buf + sz - 1) = '\0';
+
+ DBG(ASK, ul_debug("user's reply: >>>%s<<<", buf));
+ return 0;
+}
+
+static int ask_callback(struct fdisk_context *cxt __attribute__((__unused__)),
+ struct fdisk_ask *ask,
+ void *data)
+{
+ struct sfdisk *sf = (struct sfdisk *) data;
+ int rc = 0;
+
+ assert(ask);
+
+ switch(fdisk_ask_get_type(ask)) {
+ case FDISK_ASKTYPE_INFO:
+ if (sf->quiet)
+ break;
+ fputs(fdisk_ask_print_get_mesg(ask), stdout);
+ fputc('\n', stdout);
+ break;
+ case FDISK_ASKTYPE_WARNX:
+ fflush(stdout);
+ color_scheme_fenable("warn", UL_COLOR_RED, stderr);
+ fputs(fdisk_ask_print_get_mesg(ask), stderr);
+ color_fdisable(stderr);
+ fputc('\n', stderr);
+ break;
+ case FDISK_ASKTYPE_WARN:
+ fflush(stdout);
+ color_scheme_fenable("warn", UL_COLOR_RED, stderr);
+ fputs(fdisk_ask_print_get_mesg(ask), stderr);
+ errno = fdisk_ask_print_get_errno(ask);
+ fprintf(stderr, ": %m\n");
+ color_fdisable(stderr);
+ break;
+ case FDISK_ASKTYPE_YESNO:
+ {
+ char buf[BUFSIZ] = { '\0' };
+ fputc('\n', stdout);
+ do {
+ int x;
+ fputs(fdisk_ask_get_query(ask), stdout);
+ rc = get_user_reply(_(" [Y]es/[N]o: "), buf, sizeof(buf));
+ if (rc)
+ break;
+ x = rpmatch(buf);
+ if (x == RPMATCH_YES || x == RPMATCH_NO) {
+ fdisk_ask_yesno_set_result(ask, x);
+ break;
+ }
+ } while(1);
+ DBG(ASK, ul_debug("yes-no ask: reply '%s' [rc=%d]", buf, rc));
+ break;
+ }
+ default:
+ break;
+ }
+ return rc;
+}
+
+static void sfdisk_init(struct sfdisk *sf)
+{
+ fdisk_init_debug(0);
+ scols_init_debug(0);
+ sfdiskprog_init_debug();
+
+ sf->cxt = fdisk_new_context();
+ if (!sf->cxt)
+ err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
+ fdisk_set_ask(sf->cxt, ask_callback, (void *) sf);
+ fdisk_enable_bootbits_protection(sf->cxt, 1);
+
+ if (sf->label_nested) {
+ struct fdisk_context *x = fdisk_new_nested_context(sf->cxt,
+ sf->label_nested);
+ if (!x)
+ err(EXIT_FAILURE, _("failed to allocate nested libfdisk context"));
+ /* the original context is available by fdisk_get_parent() */
+ sf->cxt = x;
+ }
+}
+
+static int sfdisk_deinit(struct sfdisk *sf)
+{
+ struct fdisk_context *parent;
+
+ assert(sf);
+ assert(sf->cxt);
+
+ parent = fdisk_get_parent(sf->cxt);
+ if (parent) {
+ fdisk_unref_context(sf->cxt);
+ sf->cxt = parent;
+ }
+
+ fdisk_unref_context(sf->cxt);
+ free(sf->prompt);
+
+ memset(sf, 0, sizeof(*sf));
+ return 0;
+}
+
+static struct fdisk_partition *get_partition(struct fdisk_context *cxt, size_t partno)
+{
+ struct fdisk_table *tb = NULL;
+ struct fdisk_partition *pa;
+
+ if (fdisk_get_partitions(cxt, &tb) != 0)
+ return NULL;
+
+ pa = fdisk_table_get_partition_by_partno(tb, partno);
+ if (pa)
+ fdisk_ref_partition(pa);
+ fdisk_unref_table(tb);
+ return pa;
+}
+
+static void backup_sectors(struct sfdisk *sf,
+ const char *tpl,
+ const char *name,
+ const char *devname,
+ uint64_t offset, size_t size)
+{
+ char *fname;
+ int fd, devfd;
+
+ devfd = fdisk_get_devfd(sf->cxt);
+ assert(devfd >= 0);
+
+ xasprintf(&fname, "%s0x%08"PRIx64".bak", tpl, offset);
+
+ fd = open(fname, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
+ if (fd < 0)
+ goto fail;
+
+ if (lseek(devfd, (off_t) offset, SEEK_SET) == (off_t) -1) {
+ fdisk_warn(sf->cxt, _("cannot seek %s"), devname);
+ goto fail;
+ } else {
+ unsigned char *buf = xmalloc(size);
+
+ if (read_all(devfd, (char *) buf, size) != (ssize_t) size) {
+ fdisk_warn(sf->cxt, _("cannot read %s"), devname);
+ free(buf);
+ goto fail;
+ }
+ if (write_all(fd, buf, size) != 0) {
+ fdisk_warn(sf->cxt, _("cannot write %s"), fname);
+ free(buf);
+ goto fail;
+ }
+ free(buf);
+ }
+
+ fdisk_info(sf->cxt, _("%12s (offset %5ju, size %5ju): %s"),
+ name, (uintmax_t) offset, (uintmax_t) size, fname);
+ close(fd);
+ free(fname);
+ return;
+fail:
+ errx(EXIT_FAILURE, _("%s: failed to create a backup"), devname);
+}
+
+static char *mk_backup_filename_tpl(const char *filename, const char *devname, const char *suffix)
+{
+ char *tpl = NULL;
+ char *name, *buf = xstrdup(devname);
+
+ name = basename(buf);
+
+ if (!filename || strcmp(filename, "@default") == 0) {
+ const char *home = getenv ("HOME");
+ if (!home)
+ errx(EXIT_FAILURE, _("failed to create a backup file, $HOME undefined"));
+ xasprintf(&tpl, "%s/sfdisk-%s%s", home, name, suffix);
+ } else
+ xasprintf(&tpl, "%s-%s%s", filename, name, suffix);
+
+ free(buf);
+ return tpl;
+}
+
+
+static void backup_partition_table(struct sfdisk *sf, const char *devname)
+{
+ const char *name;
+ char *tpl;
+ uint64_t offset = 0;
+ size_t size = 0;
+ int i = 0;
+
+ assert(sf);
+
+ if (!fdisk_has_label(sf->cxt))
+ return;
+
+ tpl = mk_backup_filename_tpl(sf->backup_file, devname, "-");
+
+ color_scheme_enable("header", UL_COLOR_BOLD);
+ fdisk_info(sf->cxt, _("Backup files:"));
+ color_disable();
+
+ while (fdisk_locate_disklabel(sf->cxt, i++, &name, &offset, &size) == 0 && size)
+ backup_sectors(sf, tpl, name, devname, offset, size);
+
+ if (!sf->quiet)
+ fputc('\n', stdout);
+ free(tpl);
+}
+
+static int assign_device(struct sfdisk *sf, const char *devname, int rdonly)
+{
+ struct fdisk_context *cxt = sf->cxt;
+
+ if (fdisk_assign_device(cxt, devname, rdonly) != 0)
+ err(EXIT_FAILURE, _("cannot open %s"), devname);
+
+ if (!fdisk_is_readonly(cxt)) {
+ if (blkdev_lock(fdisk_get_devfd(cxt), devname, sf->lockmode) != 0) {
+ fdisk_deassign_device(cxt, 1);
+ exit(EXIT_FAILURE);
+ }
+ if (sf->backup)
+ backup_partition_table(sf, devname);
+ }
+ return 0;
+}
+
+
+static int move_partition_data(struct sfdisk *sf, size_t partno, struct fdisk_partition *orig_pa)
+{
+ struct fdisk_partition *pa = get_partition(sf->cxt, partno);
+ char *devname = NULL, *typescript = NULL, *buf = NULL;
+ FILE *f = NULL;
+ int ok = 0, fd, backward = 0;
+ fdisk_sector_t nsectors, from, to, step, i, prev;
+ size_t io, ss, step_bytes, cc, ioerr = 0;
+ uintmax_t src, dst, nbytes;
+ int progress = 0, rc = 0;
+ struct timeval prev_time;
+ uint64_t bytes_per_sec = 0;
+
+ assert(sf->movedata);
+
+ if (!pa)
+ warnx(_("failed to read new partition from device; ignoring --move-data"));
+ else if (!fdisk_partition_has_size(pa))
+ warnx(_("failed to get size of the new partition; ignoring --move-data"));
+ else if (!fdisk_partition_has_start(pa))
+ warnx(_("failed to get start of the new partition; ignoring --move-data"));
+ else if (!fdisk_partition_has_size(orig_pa))
+ warnx(_("failed to get size of the old partition; ignoring --move-data"));
+ else if (!fdisk_partition_has_start(orig_pa))
+ warnx(_("failed to get start of the old partition; ignoring --move-data"));
+ else if (fdisk_partition_get_start(pa) == fdisk_partition_get_start(orig_pa))
+ warnx(_("start of the partition has not been moved; ignoring --move-data"));
+ else if (fdisk_partition_get_size(orig_pa) < fdisk_partition_get_size(pa))
+ warnx(_("new partition is smaller than original; ignoring --move-data"));
+ else
+ ok = 1;
+ if (!ok)
+ return -EINVAL;
+
+ DBG(MISC, ul_debug("moving data"));
+
+ fd = fdisk_get_devfd(sf->cxt);
+
+ /* set move direction and overlay */
+ nsectors = fdisk_partition_get_size(orig_pa);
+ from = fdisk_partition_get_start(orig_pa);
+ to = fdisk_partition_get_start(pa);
+
+
+ if ((to >= from && from + nsectors >= to) ||
+ (from >= to && to + nsectors >= from)) {
+ /* source and target overlay, check if we need to copy
+ * backwardly from end of the source */
+ DBG(MISC, ul_debug("overlay between source and target"));
+ backward = from < to;
+ DBG(MISC, ul_debug(" copy order: %s", backward ? "backward" : "forward"));
+ }
+
+ /* set optimal step size -- nearest to 1MiB aligned to optimal I/O */
+ io = fdisk_get_optimal_iosize(sf->cxt);
+ ss = fdisk_get_sector_size(sf->cxt);
+ if (!io)
+ io = ss;
+ if (io < 1024 * 1024)
+ step_bytes = ((1024 * 1024) + io/2) / io * io;
+ else
+ step_bytes = io;
+
+ step = step_bytes / ss;
+ nbytes = nsectors * ss;
+
+ DBG(MISC, ul_debug(" step: %ju (%zu bytes)", (uintmax_t)step, step_bytes));
+
+#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE)
+ if (!backward)
+ posix_fadvise(fd, from * ss, nsectors * ss, POSIX_FADV_SEQUENTIAL);
+#endif
+ devname = fdisk_partname(fdisk_get_devname(sf->cxt), partno+1);
+ if (sf->move_typescript)
+ typescript = mk_backup_filename_tpl(sf->move_typescript, devname, ".move");
+
+ if (!sf->quiet) {
+ fdisk_info(sf->cxt,"");
+ color_scheme_enable("header", UL_COLOR_BOLD);
+ fdisk_info(sf->cxt, sf->noact ? _("Data move: (--no-act)") : _("Data move:"));
+ color_disable();
+ if (typescript)
+ fdisk_info(sf->cxt, _(" typescript file: %s"), typescript);
+ printf(_(" start sector: (from/to) %ju / %ju\n"), (uintmax_t) from, (uintmax_t) to);
+ printf(_(" sectors: %ju\n"), (uintmax_t) nsectors);
+ printf(_(" step size: %zu bytes\n"), step_bytes);
+ putchar('\n');
+ fflush(stdout);
+
+ if (isatty(fileno(stdout)))
+ progress = 1;
+ }
+
+ if (sf->interactive) {
+ int yes = 0;
+ fdisk_ask_yesno(sf->cxt, _("Do you want to move partition data?"), &yes);
+ if (!yes) {
+ fdisk_info(sf->cxt, _("Leaving."));
+ return 0;
+ }
+ }
+
+ if (typescript) {
+ f = fopen(typescript, "w");
+ if (!f) {
+ rc = -errno;
+ fdisk_warn(sf->cxt, _("cannot open %s"), typescript);
+ goto done;
+ }
+
+ /* don't translate */
+ fprintf(f, "# sfdisk: " PACKAGE_STRING "\n");
+ fprintf(f, "# Disk: %s\n", devname);
+ fprintf(f, "# Partition: %zu\n", partno + 1);
+ fprintf(f, "# Operation: move data\n");
+ fprintf(f, "# Sector size: %zu\n", ss);
+ fprintf(f, "# Original start offset (sectors/bytes): %ju/%ju\n",
+ (uintmax_t)from, (uintmax_t)from * ss);
+ fprintf(f, "# New start offset (sectors/bytes): %ju/%ju\n",
+ (uintmax_t)to, (uintmax_t)to * ss);
+ fprintf(f, "# Area size (sectors/bytes): %ju/%ju\n",
+ (uintmax_t)nsectors, (uintmax_t)nsectors * ss);
+ fprintf(f, "# Step size (sectors/bytes): %zu/%zu\n", step, step_bytes);
+ fprintf(f, "# Steps: %ju\n", ((uintmax_t) nsectors / step) + 1);
+ fprintf(f, "#\n");
+ fprintf(f, "# <step>: <from> <to> (step offsets in bytes)\n");
+ }
+
+ src = (backward ? from + nsectors : from) * ss;
+ dst = (backward ? to + nsectors : to) * ss;
+ buf = xmalloc(step_bytes);
+
+ DBG(MISC, ul_debug(" initial: src=%ju dst=%ju", src, dst));
+
+ gettimeofday(&prev_time, NULL);
+ prev = 0;
+
+ for (cc = 1, i = 0; i < nsectors && nbytes > 0; i += step, cc++) {
+
+ if (nbytes < step_bytes) {
+ DBG(MISC, ul_debug("aligning step #%05zu from %ju to %ju",
+ cc, step_bytes, nbytes));
+ step_bytes = nbytes;
+ }
+ nbytes -= step_bytes;
+
+ if (backward)
+ src -= step_bytes, dst -= step_bytes;
+
+ DBG(MISC, ul_debug("#%05zu: src=%ju dst=%ju", cc, src, dst));
+
+ if (!sf->noact) {
+ /* read source */
+ if (lseek(fd, src, SEEK_SET) == (off_t) -1 ||
+ read_all(fd, buf, step_bytes) != (ssize_t) step_bytes) {
+ if (f)
+ fprintf(f, "%05zu: read error %12ju %12ju\n", cc, src, dst);
+ fdisk_warn(sf->cxt,
+ _("cannot read at offset: %zu; continue"), src);
+ ioerr++;
+ goto next;
+ }
+
+ /* write target */
+ if (lseek(fd, dst, SEEK_SET) == (off_t) -1 ||
+ write_all(fd, buf, step_bytes) != 0) {
+ if (f)
+ fprintf(f, "%05zu: write error %12ju %12ju\n", cc, src, dst);
+ fdisk_warn(sf->cxt,
+ _("cannot write at offset: %zu; continue"), dst);
+ ioerr++;
+ goto next;
+ }
+ if (sf->movefsync)
+ fsync(fd);
+ }
+
+ /* write log */
+ if (f)
+ fprintf(f, "%05zu: %12ju %12ju\n", cc, src, dst);
+
+ if (progress && i % 10 == 0) {
+ unsigned int elapsed = 0; /* usec */
+ struct timeval cur_time;
+
+ gettimeofday(&cur_time, NULL);
+ if (cur_time.tv_sec - prev_time.tv_sec > 1) {
+ elapsed = ((cur_time.tv_sec - prev_time.tv_sec) * 1000000) +
+ (cur_time.tv_usec - prev_time.tv_usec);
+
+ bytes_per_sec = ((i - prev) * ss) / elapsed; /* per usec */
+ bytes_per_sec *= 1000000; /* per sec */
+
+ prev_time = cur_time;
+ prev = i;
+ }
+
+ if (bytes_per_sec)
+ fprintf(stdout, _("Moved %ju from %ju sectors (%.3f%%, %.1f MiB/s)."),
+ i + 1, nsectors,
+ 100.0 / ((double) nsectors/(i+1)),
+ (double) (bytes_per_sec / (1024 * 1024)));
+ else
+ fprintf(stdout, _("Moved %ju from %ju sectors (%.3f%%)."),
+ i + 1, nsectors,
+ 100.0 / ((double) nsectors/(i+1)));
+ fflush(stdout);
+ fputc('\r', stdout);
+
+ }
+next:
+ if (!backward)
+ src += step_bytes, dst += step_bytes;
+ }
+
+ if (progress) {
+ int x = get_terminal_width(80);
+ for (; x > 0; x--)
+ fputc(' ', stdout);
+ fflush(stdout);
+ fputc('\r', stdout);
+
+ if (i > nsectors)
+ /* see for() above; @i has to be greater than @nsectors
+ * on success due to i += step */
+ i = nsectors;
+
+ fprintf(stdout, _("Moved %ju from %ju sectors (%.0f%%)."),
+ i, nsectors,
+ 100.0 / ((double) nsectors/(i+1)));
+ fputc('\n', stdout);
+ }
+ rc = 0;
+done:
+ if (f)
+ fclose(f);
+ free(buf);
+ free(typescript);
+
+ if (sf->noact)
+ fdisk_info(sf->cxt, _("Your data has not been moved (--no-act)."));
+ if (ioerr) {
+ fdisk_info(sf->cxt, _("%zu I/O errors detected!"), ioerr);
+ rc = -EIO;
+ } else if (rc)
+ warn(_("%s: failed to move data"), devname);
+
+ free(devname);
+
+ return rc;
+}
+
+static int write_changes(struct sfdisk *sf)
+{
+ int rc = 0;
+
+ if (sf->noact)
+ fdisk_info(sf->cxt, _("The partition table is unchanged (--no-act)."));
+ else
+ rc = fdisk_write_disklabel(sf->cxt);
+
+ if (rc == 0 && sf->movedata && sf->orig_pa)
+ rc = move_partition_data(sf, sf->partno, sf->orig_pa);
+
+ if (!sf->noact && !rc) {
+ fdisk_info(sf->cxt, _("\nThe partition table has been altered."));
+ if (!sf->notell) {
+ /* Let's wait a little bit. It's possible that our
+ * system is still busy with a previous re-read
+ * ioctl (on sfdisk start) or with another task
+ * related to the write to the device.
+ */
+ xusleep(250000);
+ fdisk_reread_partition_table(sf->cxt);
+ }
+ }
+
+ if (!rc)
+ rc = fdisk_deassign_device(sf->cxt,
+ sf->noact || sf->notell); /* no-sync */
+ return rc;
+}
+
+/*
+ * sfdisk --list [<device ..]
+ */
+static int command_list_partitions(struct sfdisk *sf, int argc, char **argv)
+{
+ int fail = 0;
+ fdisk_enable_listonly(sf->cxt, 1);
+
+ if (argc) {
+ int i;
+
+ for (i = 0; i < argc; i++)
+ if (print_device_pt(sf->cxt, argv[i], 1, sf->verify, i) != 0)
+ fail++;
+ } else
+ print_all_devices_pt(sf->cxt, sf->verify);
+
+ return fail;
+}
+
+/*
+ * sfdisk --list-free [<device ..]
+ */
+static int command_list_freespace(struct sfdisk *sf, int argc, char **argv)
+{
+ int fail = 0;
+ fdisk_enable_listonly(sf->cxt, 1);
+
+ if (argc) {
+ int i;
+
+ for (i = 0; i < argc; i++)
+ if (print_device_freespace(sf->cxt, argv[i], 1, i) != 0)
+ fail++;
+ } else
+ print_all_devices_freespace(sf->cxt);
+
+ return fail;
+}
+
+/*
+ * sfdisk --list-types
+ */
+static int command_list_types(struct sfdisk *sf)
+{
+ const struct fdisk_parttype *t;
+ struct fdisk_label *lb;
+ const char *name;
+ size_t i = 0;
+ int codes;
+
+ assert(sf);
+ assert(sf->cxt);
+
+ name = sf->label ? sf->label : "dos";
+ lb = fdisk_get_label(sf->cxt, name);
+ if (!lb)
+ errx(EXIT_FAILURE, _("unsupported label '%s'"), name);
+
+ codes = fdisk_label_has_code_parttypes(lb);
+ fputs(_("Id Name\n\n"), stdout);
+
+ while ((t = fdisk_label_get_parttype(lb, i++))) {
+ if (codes)
+ printf("%2x %s\n", fdisk_parttype_get_code(t),
+ fdisk_parttype_get_name(t));
+ else
+ printf("%s %s\n", fdisk_parttype_get_string(t),
+ fdisk_parttype_get_name(t));
+ }
+
+ return 0;
+}
+
+static int verify_device(struct sfdisk *sf, const char *devname)
+{
+ int rc = 1;
+
+ fdisk_enable_listonly(sf->cxt, 1);
+
+ assign_device(sf, devname, 1);
+
+ color_scheme_enable("header", UL_COLOR_BOLD);
+ fdisk_info(sf->cxt, "%s:", devname);
+ color_disable();
+
+ if (!fdisk_has_label(sf->cxt))
+ fdisk_info(sf->cxt, _("unrecognized partition table type"));
+ else
+ rc = fdisk_verify_disklabel(sf->cxt);
+
+ fdisk_deassign_device(sf->cxt, 1);
+ return rc;
+}
+
+/*
+ * sfdisk --verify [<device ..]
+ */
+static int command_verify(struct sfdisk *sf, int argc, char **argv)
+{
+ int nfails = 0, ct = 0;
+
+ if (argc) {
+ int i;
+ for (i = 0; i < argc; i++) {
+ if (i)
+ fdisk_info(sf->cxt, " ");
+ if (verify_device(sf, argv[i]) < 0)
+ nfails++;
+ }
+ } else {
+ FILE *f = NULL;
+ char *dev;
+
+ while ((dev = next_proc_partition(&f))) {
+ if (ct)
+ fdisk_info(sf->cxt, " ");
+ if (verify_device(sf, dev) < 0)
+ nfails++;
+ free(dev);
+ ct++;
+ }
+ }
+
+ return nfails;
+}
+
+static int get_size(const char *dev, int silent, uintmax_t *sz)
+{
+ int fd, rc = 0;
+
+ fd = open(dev, O_RDONLY);
+ if (fd < 0) {
+ if (!silent)
+ warn(_("cannot open %s"), dev);
+ return -errno;
+ }
+
+ if (blkdev_get_sectors(fd, (unsigned long long *) sz) == -1) {
+ if (!silent)
+ warn(_("Cannot get size of %s"), dev);
+ rc = -errno;
+ }
+
+ close(fd);
+ return rc;
+}
+
+/*
+ * sfdisk --show-size [<device ..]
+ *
+ * (silly, but just for backward compatibility)
+ */
+static int command_show_size(struct sfdisk *sf __attribute__((__unused__)),
+ int argc, char **argv)
+{
+ uintmax_t sz;
+
+ if (argc) {
+ int i;
+ for (i = 0; i < argc; i++) {
+ if (get_size(argv[i], 0, &sz) == 0)
+ printf("%ju\n", sz / 2);
+ }
+ } else {
+ FILE *f = NULL;
+ uintmax_t total = 0;
+ char *dev;
+
+ while ((dev = next_proc_partition(&f))) {
+ if (get_size(dev, 1, &sz) == 0) {
+ printf("%s: %9ju\n", dev, sz / 2);
+ total += sz / 2;
+ }
+ free(dev);
+ }
+ if (total)
+ printf(_("total: %ju blocks\n"), total);
+ }
+
+ return 0;
+}
+
+static int print_geom(struct sfdisk *sf, const char *devname)
+{
+ fdisk_enable_listonly(sf->cxt, 1);
+
+ assign_device(sf, devname, 1);
+
+ fdisk_info(sf->cxt, "%s: %ju cylinders, %ju heads, %ju sectors/track",
+ devname,
+ (uintmax_t) fdisk_get_geom_cylinders(sf->cxt),
+ (uintmax_t) fdisk_get_geom_heads(sf->cxt),
+ (uintmax_t) fdisk_get_geom_sectors(sf->cxt));
+
+ fdisk_deassign_device(sf->cxt, 1);
+ return 0;
+}
+
+/*
+ * sfdisk --show-geometry [<device ..]
+ */
+static int command_show_geometry(struct sfdisk *sf, int argc, char **argv)
+{
+ int nfails = 0;
+
+ if (argc) {
+ int i;
+ for (i = 0; i < argc; i++) {
+ if (print_geom(sf, argv[i]) < 0)
+ nfails++;
+ }
+ } else {
+ FILE *f = NULL;
+ char *dev;
+
+ while ((dev = next_proc_partition(&f))) {
+ if (print_geom(sf, dev) < 0)
+ nfails++;
+ free(dev);
+ }
+ }
+
+ return nfails;
+}
+
+/*
+ * sfdisk --activate <device> [<partno> ...]
+ */
+static int command_activate(struct sfdisk *sf, int argc, char **argv)
+{
+ int rc, nparts, i, listonly;
+ struct fdisk_partition *pa = NULL;
+ const char *devname = NULL;
+
+ if (argc < 1)
+ errx(EXIT_FAILURE, _("no disk device specified"));
+ devname = argv[0];
+
+ /* --activate <device> */
+ listonly = argc == 1;
+
+ assign_device(sf, devname, listonly);
+
+ if (fdisk_is_label(sf->cxt, GPT)) {
+ if (fdisk_gpt_is_hybrid(sf->cxt))
+ errx(EXIT_FAILURE, _("toggle boot flags is unsupported for Hybrid GPT/MBR"));
+
+ /* Switch from GPT to PMBR */
+ sf->cxt = fdisk_new_nested_context(sf->cxt, "dos");
+ if (!sf->cxt)
+ err(EXIT_FAILURE, _("cannot switch to PMBR"));
+ fdisk_info(sf->cxt, _("Activation is unsupported for GPT -- entering nested PMBR."));
+
+ } else if (!fdisk_is_label(sf->cxt, DOS))
+ errx(EXIT_FAILURE, _("toggle boot flags is supported for MBR or PMBR only"));
+
+ nparts = fdisk_get_npartitions(sf->cxt);
+ for (i = 0; i < nparts; i++) {
+ char *data = NULL;
+
+ /* note that fdisk_get_partition() reuses the @pa pointer, you
+ * don't have to (re)allocate it */
+ if (fdisk_get_partition(sf->cxt, i, &pa) != 0)
+ continue;
+
+ /* sfdisk --activate list bootable partitions */
+ if (listonly) {
+ if (!fdisk_partition_is_bootable(pa))
+ continue;
+ if (fdisk_partition_to_string(pa, sf->cxt,
+ FDISK_FIELD_DEVICE, &data) == 0) {
+ printf("%s\n", data);
+ free(data);
+ }
+
+ /* deactivate all active partitions */
+ } else if (fdisk_partition_is_bootable(pa))
+ fdisk_toggle_partition_flag(sf->cxt, i, DOS_FLAG_ACTIVE);
+ }
+
+ /* sfdisk --activate <partno> [..] */
+ for (i = 1; i < argc; i++) {
+ int n;
+
+ if (i == 1 && strcmp(argv[1], "-") == 0)
+ break;
+ n = strtou32_or_err(argv[i], _("failed to parse partition number"));
+
+ rc = fdisk_toggle_partition_flag(sf->cxt, n - 1, DOS_FLAG_ACTIVE);
+ if (rc)
+ errx(EXIT_FAILURE,
+ _("%s: partition %d: failed to toggle bootable flag"),
+ devname, i + 1);
+ }
+
+ fdisk_unref_partition(pa);
+
+ if (listonly)
+ rc = fdisk_deassign_device(sf->cxt, 1);
+ else
+ rc = write_changes(sf);
+ return rc;
+}
+
+/*
+ * sfdisk --delete <device> [<partno> ...]
+ */
+static int command_delete(struct sfdisk *sf, int argc, char **argv)
+{
+ size_t i;
+ const char *devname = NULL;
+
+ if (argc < 1)
+ errx(EXIT_FAILURE, _("no disk device specified"));
+ devname = argv[0];
+
+ assign_device(sf, devname, 0);
+
+ /* delete all */
+ if (argc == 1) {
+ size_t nparts = fdisk_get_npartitions(sf->cxt);
+ for (i = 0; i < nparts; i++) {
+ if (fdisk_is_partition_used(sf->cxt, i) &&
+ fdisk_delete_partition(sf->cxt, i) != 0)
+ errx(EXIT_FAILURE, _("%s: partition %zu: failed to delete"), devname, i + 1);
+ }
+ /* delete specified */
+ } else {
+ for (i = 1; i < (size_t) argc; i++) {
+ size_t n = strtou32_or_err(argv[i], _("failed to parse partition number"));
+
+ if (fdisk_delete_partition(sf->cxt, n - 1) != 0)
+ errx(EXIT_FAILURE, _("%s: partition %zu: failed to delete"), devname, n);
+ }
+ }
+
+ return write_changes(sf);
+}
+
+/*
+ * sfdisk --reorder <device>
+ */
+static int command_reorder(struct sfdisk *sf, int argc, char **argv)
+{
+ const char *devname = NULL;
+ int rc;
+
+ if (argc)
+ devname = argv[0];
+ if (!devname)
+ errx(EXIT_FAILURE, _("no disk device specified"));
+
+ assign_device(sf, devname, 0); /* read-write */
+
+ if (fdisk_reorder_partitions(sf->cxt) == 1) /* unchanged */
+ rc = fdisk_deassign_device(sf->cxt, 1);
+ else
+ rc = write_changes(sf);
+
+ return rc;
+}
+
+
+/*
+ * sfdisk --dump <device>
+ */
+static int command_dump(struct sfdisk *sf, int argc, char **argv)
+{
+ const char *devname = NULL;
+ struct fdisk_script *dp;
+ int rc;
+
+ if (argc)
+ devname = argv[0];
+ if (!devname)
+ errx(EXIT_FAILURE, _("no disk device specified"));
+
+ assign_device(sf, devname, 1); /* read-only */
+
+ if (!fdisk_has_label(sf->cxt))
+ errx(EXIT_FAILURE, _("%s: does not contain a recognized partition table"), devname);
+
+ dp = fdisk_new_script(sf->cxt);
+ if (!dp)
+ err(EXIT_FAILURE, _("failed to allocate dump struct"));
+
+ rc = fdisk_script_read_context(dp, NULL);
+ if (rc)
+ errx(EXIT_FAILURE, _("%s: failed to dump partition table"), devname);
+
+ if (sf->json)
+ fdisk_script_enable_json(dp, 1);
+ fdisk_script_write_file(dp, stdout);
+
+ fdisk_unref_script(dp);
+ fdisk_deassign_device(sf->cxt, 1); /* no-sync() */
+ return 0;
+}
+
+static void assign_device_partition(struct sfdisk *sf,
+ const char *devname,
+ size_t partno,
+ int rdonly)
+{
+ int rc;
+ size_t n;
+ struct fdisk_label *lb = NULL;
+
+ assert(sf);
+ assert(devname);
+
+ /* read-only when a new <type> undefined */
+ rc = fdisk_assign_device(sf->cxt, devname, rdonly);
+ if (rc)
+ err(EXIT_FAILURE, _("cannot open %s"), devname);
+
+ if (!fdisk_is_readonly(sf->cxt)
+ && blkdev_lock(fdisk_get_devfd(sf->cxt), devname, sf->lockmode) != 0) {
+ fdisk_deassign_device(sf->cxt, 1);
+ return;
+ }
+ lb = fdisk_get_label(sf->cxt, NULL);
+ if (!lb)
+ errx(EXIT_FAILURE, _("%s: no partition table found"), devname);
+
+ n = fdisk_get_npartitions(sf->cxt);
+ if (partno > n)
+ errx(EXIT_FAILURE, _("%s: partition %zu: partition table contains "
+ "only %zu partitions"), devname, partno, n);
+ if (!fdisk_is_partition_used(sf->cxt, partno - 1))
+ errx(EXIT_FAILURE, _("%s: partition %zu: partition is unused"),
+ devname, partno);
+}
+
+/*
+ * sfdisk --part-type <device> <partno> [<type>]
+ */
+static int command_parttype(struct sfdisk *sf, int argc, char **argv)
+{
+ size_t partno;
+ struct fdisk_parttype *type = NULL;
+ struct fdisk_label *lb;
+ const char *devname = NULL, *typestr = NULL;
+
+ if (!argc)
+ errx(EXIT_FAILURE, _("no disk device specified"));
+ devname = argv[0];
+
+ if (argc < 2)
+ errx(EXIT_FAILURE, _("no partition number specified"));
+ partno = strtou32_or_err(argv[1], _("failed to parse partition number"));
+
+ if (argc == 3)
+ typestr = argv[2];
+ else if (argc > 3)
+ errx(EXIT_FAILURE, _("unexpected arguments"));
+
+ /* read-only when a new <type> undefined */
+ assign_device_partition(sf, devname, partno, !typestr);
+
+ lb = fdisk_get_label(sf->cxt, NULL);
+
+ /* print partition type */
+ if (!typestr) {
+ const struct fdisk_parttype *t = NULL;
+ struct fdisk_partition *pa = NULL;
+
+ if (fdisk_get_partition(sf->cxt, partno - 1, &pa) == 0)
+ t = fdisk_partition_get_type(pa);
+ if (!t)
+ errx(EXIT_FAILURE, _("%s: partition %zu: failed to get partition type"),
+ devname, partno);
+
+ if (fdisk_label_has_code_parttypes(lb))
+ printf("%2x\n", fdisk_parttype_get_code(t));
+ else
+ printf("%s\n", fdisk_parttype_get_string(t));
+
+ fdisk_unref_partition(pa);
+ fdisk_deassign_device(sf->cxt, 1);
+ return 0;
+ }
+
+ if (sf->backup)
+ backup_partition_table(sf, devname);
+
+ /* parse <type> and apply to PT */
+ type = fdisk_label_advparse_parttype(lb, typestr,
+ FDISK_PARTTYPE_PARSE_DATA
+ | FDISK_PARTTYPE_PARSE_ALIAS
+ | FDISK_PARTTYPE_PARSE_SHORTCUT);
+ if (!type)
+ errx(EXIT_FAILURE, _("failed to parse %s partition type '%s'"),
+ fdisk_label_get_name(lb), typestr);
+
+ else if (fdisk_set_partition_type(sf->cxt, partno - 1, type) != 0)
+ errx(EXIT_FAILURE, _("%s: partition %zu: failed to set partition type"),
+ devname, partno);
+ fdisk_unref_parttype(type);
+ return write_changes(sf);
+}
+
+/*
+ * sfdisk --part-uuid <device> <partno> [<uuid>]
+ */
+static int command_partuuid(struct sfdisk *sf, int argc, char **argv)
+{
+ size_t partno;
+ struct fdisk_partition *pa = NULL;
+ const char *devname = NULL, *uuid = NULL;
+
+ if (!argc)
+ errx(EXIT_FAILURE, _("no disk device specified"));
+ devname = argv[0];
+
+ if (argc < 2)
+ errx(EXIT_FAILURE, _("no partition number specified"));
+ partno = strtou32_or_err(argv[1], _("failed to parse partition number"));
+
+ if (argc == 3)
+ uuid = argv[2];
+ else if (argc > 3)
+ errx(EXIT_FAILURE, _("unexpected arguments"));
+
+ /* read-only if uuid not given */
+ assign_device_partition(sf, devname, partno, !uuid);
+
+ /* print partition uuid */
+ if (!uuid) {
+ const char *str = NULL;
+
+ if (fdisk_get_partition(sf->cxt, partno - 1, &pa) == 0)
+ str = fdisk_partition_get_uuid(pa);
+ if (!str)
+ errx(EXIT_FAILURE, _("%s: partition %zu: failed to get partition UUID"),
+ devname, partno);
+ printf("%s\n", str);
+ fdisk_unref_partition(pa);
+ fdisk_deassign_device(sf->cxt, 1);
+ return 0;
+ }
+
+ if (sf->backup)
+ backup_partition_table(sf, devname);
+
+ pa = fdisk_new_partition();
+ if (!pa)
+ err(EXIT_FAILURE, _("failed to allocate partition object"));
+
+ if (fdisk_partition_set_uuid(pa, uuid) != 0 ||
+ fdisk_set_partition(sf->cxt, partno - 1, pa) != 0)
+ errx(EXIT_FAILURE, _("%s: partition %zu: failed to set partition UUID"),
+ devname, partno);
+ fdisk_unref_partition(pa);
+ return write_changes(sf);
+}
+
+/*
+ * sfdisk --part-label <device> <partno> [<label>]
+ */
+static int command_partlabel(struct sfdisk *sf, int argc, char **argv)
+{
+ size_t partno;
+ struct fdisk_partition *pa = NULL;
+ const char *devname = NULL, *name = NULL;
+
+ if (!argc)
+ errx(EXIT_FAILURE, _("no disk device specified"));
+ devname = argv[0];
+
+ if (argc < 2)
+ errx(EXIT_FAILURE, _("no partition number specified"));
+ partno = strtou32_or_err(argv[1], _("failed to parse partition number"));
+
+ if (argc == 3)
+ name = argv[2];
+ else if (argc > 3)
+ errx(EXIT_FAILURE, _("unexpected arguments"));
+
+ /* read-only if name not given */
+ assign_device_partition(sf, devname, partno, !name);
+
+ /* print partition name */
+ if (!name) {
+ const char *str = NULL;
+
+ if (fdisk_get_partition(sf->cxt, partno - 1, &pa) == 0)
+ str = fdisk_partition_get_name(pa);
+ if (!str)
+ errx(EXIT_FAILURE, _("%s: partition %zu: failed to get partition name"),
+ devname, partno);
+ printf("%s\n", str);
+ fdisk_unref_partition(pa);
+ fdisk_deassign_device(sf->cxt, 1);
+ return 0;
+ }
+
+ if (sf->backup)
+ backup_partition_table(sf, devname);
+
+ pa = fdisk_new_partition();
+ if (!pa)
+ err(EXIT_FAILURE, _("failed to allocate partition object"));
+
+ if (fdisk_partition_set_name(pa, name) != 0 ||
+ fdisk_set_partition(sf->cxt, partno - 1, pa) != 0)
+ errx(EXIT_FAILURE, _("%s: partition %zu: failed to set partition name"),
+ devname, partno);
+
+ fdisk_unref_partition(pa);
+ return write_changes(sf);
+}
+
+/*
+ * sfdisk --part-attrs <device> <partno> [<attrs>]
+ */
+static int command_partattrs(struct sfdisk *sf, int argc, char **argv)
+{
+ size_t partno;
+ struct fdisk_partition *pa = NULL;
+ const char *devname = NULL, *attrs = NULL;
+
+ if (!argc)
+ errx(EXIT_FAILURE, _("no disk device specified"));
+ devname = argv[0];
+
+ if (argc < 2)
+ errx(EXIT_FAILURE, _("no partition number specified"));
+ partno = strtou32_or_err(argv[1], _("failed to parse partition number"));
+
+ if (argc == 3)
+ attrs = argv[2];
+ else if (argc > 3)
+ errx(EXIT_FAILURE, _("unexpected arguments"));
+
+ /* read-only if name not given */
+ assign_device_partition(sf, devname, partno, !attrs);
+
+ /* print partition name */
+ if (!attrs) {
+ const char *str = NULL;
+
+ if (fdisk_get_partition(sf->cxt, partno - 1, &pa) == 0)
+ str = fdisk_partition_get_attrs(pa);
+ if (str)
+ printf("%s\n", str);
+ fdisk_unref_partition(pa);
+ fdisk_deassign_device(sf->cxt, 1);
+ return 0;
+ }
+
+ if (sf->backup)
+ backup_partition_table(sf, devname);
+
+ pa = fdisk_new_partition();
+ if (!pa)
+ err(EXIT_FAILURE, _("failed to allocate partition object"));
+
+ if (fdisk_partition_set_attrs(pa, attrs) != 0 ||
+ fdisk_set_partition(sf->cxt, partno - 1, pa) != 0)
+ errx(EXIT_FAILURE, _("%s: partition %zu: failed to set partition attributes"),
+ devname, partno);
+
+ fdisk_unref_partition(pa);
+ return write_changes(sf);
+}
+
+/*
+ * sfdisk --disk-id <device> [<str>]
+ */
+static int command_diskid(struct sfdisk *sf, int argc, char **argv)
+{
+ const char *devname = NULL;
+ char *str = NULL;
+
+ if (!argc)
+ errx(EXIT_FAILURE, _("no disk device specified"));
+ devname = argv[0];
+
+ if (argc == 2)
+ str = argv[1];
+ else if (argc > 2)
+ errx(EXIT_FAILURE, _("unexpected arguments"));
+
+ assign_device(sf, devname, !str);
+
+ /* print */
+ if (!str) {
+ fdisk_get_disklabel_id(sf->cxt, &str);
+ if (str)
+ printf("%s\n", str);
+ free(str);
+ fdisk_deassign_device(sf->cxt, 1);
+ return 0;
+ }
+
+ if (fdisk_set_disklabel_id_from_string(sf->cxt, str) != 0)
+ errx(EXIT_FAILURE, _("%s: failed to set disklabel ID"), devname);
+
+ return write_changes(sf);
+}
+
+/*
+ * sfdisk --relocate <mode> <device>
+ */
+static int command_relocate(struct sfdisk *sf, int argc, char **argv)
+{
+ const char *devname = NULL;
+ const char *oper = NULL;
+ struct fdisk_label *lb;
+
+ if (!argc)
+ errx(EXIT_FAILURE, _("no relocate operation specified"));
+ if (argc < 2)
+ errx(EXIT_FAILURE, _("no disk device specified"));
+ if (argc > 2)
+ errx(EXIT_FAILURE, _("unexpected arguments"));
+
+ oper = argv[0];
+ devname = argv[1];
+ lb = fdisk_get_label(sf->cxt, "gpt");
+
+ if (strcmp(oper, "gpt-bak-mini") == 0)
+ fdisk_gpt_enable_minimize(lb, 1);
+
+ else if (strcmp(oper, "gpt-bak-std") != 0)
+ errx(EXIT_FAILURE, _("unsupported relocation operation"));
+
+ assign_device(sf, devname, 0);
+
+ fdisk_label_set_changed(lb, 1);
+
+ return write_changes(sf);
+}
+
+static void sfdisk_print_partition(struct sfdisk *sf, size_t n)
+{
+ struct fdisk_partition *pa = NULL;
+ char *data;
+
+ assert(sf);
+
+ if (sf->quiet)
+ return;
+ if (fdisk_get_partition(sf->cxt, n, &pa) != 0)
+ return;
+
+ fdisk_partition_to_string(pa, sf->cxt, FDISK_FIELD_DEVICE, &data);
+ printf("%12s : ", data);
+
+ fdisk_partition_to_string(pa, sf->cxt, FDISK_FIELD_START, &data);
+ printf("%12s ", data);
+
+ fdisk_partition_to_string(pa, sf->cxt, FDISK_FIELD_END, &data);
+ printf("%12s ", data);
+
+ fdisk_partition_to_string(pa, sf->cxt, FDISK_FIELD_SIZE, &data);
+ printf("(%s) ", data);
+
+ fdisk_partition_to_string(pa, sf->cxt, FDISK_FIELD_TYPE, &data);
+ printf("%s\n", data);
+
+ fdisk_unref_partition(pa);
+}
+
+static void command_fdisk_help(void)
+{
+ fputs(_("\nHelp:\n"), stdout);
+
+ fputc('\n', stdout);
+ color_scheme_enable("help-title", UL_COLOR_BOLD);
+ fputs(_(" Commands:\n"), stdout);
+ color_disable();
+ fputs(_(" write write table to disk and exit\n"), stdout);
+ fputs(_(" quit show new situation and wait for user's feedback before write\n"), stdout);
+ fputs(_(" abort exit sfdisk shell\n"), stdout);
+ fputs(_(" print display the partition table\n"), stdout);
+ fputs(_(" help show this help text\n"), stdout);
+ fputc('\n', stdout);
+ fputs(_(" Ctrl-D the same as 'quit'\n"), stdout);
+
+ fputc('\n', stdout);
+ color_scheme_enable("help-title", UL_COLOR_BOLD);
+ fputs(_(" Input format:\n"), stdout);
+ color_disable();
+ fputs(_(" <start>, <size>, <type>, <bootable>\n"), stdout);
+
+ fputc('\n', stdout);
+ fputs(_(" <start> Beginning of the partition in sectors, or bytes if\n"
+ " specified in the format <number>{K,M,G,T,P,E,Z,Y}.\n"
+ " The default is the first free space.\n"), stdout);
+
+ fputc('\n', stdout);
+ fputs(_(" <size> Size of the partition in sectors, or bytes if\n"
+ " specified in the format <number>{K,M,G,T,P,E,Z,Y}.\n"
+ " The default is all available space.\n"), stdout);
+
+ fputc('\n', stdout);
+ fputs(_(" <type> The partition type. Default is a Linux data partition.\n"), stdout);
+ fputs(_(" MBR: hex or L,S,Ex,X,U,R,V shortcuts.\n"), stdout);
+ fputs(_(" GPT: UUID or L,S,H,U,R,V shortcuts.\n"), stdout);
+
+ fputc('\n', stdout);
+ fputs(_(" <bootable> Use '*' to mark an MBR partition as bootable.\n"), stdout);
+
+ fputc('\n', stdout);
+ color_scheme_enable("help-title", UL_COLOR_BOLD);
+ fputs(_(" Example:\n"), stdout);
+ color_disable();
+ fputs(_(" , 4G Creates a 4GiB partition at default start offset.\n"), stdout);
+ fputc('\n', stdout);
+}
+
+enum {
+ SFDISK_DONE_NONE = 0,
+ SFDISK_DONE_EOF,
+ SFDISK_DONE_ABORT,
+ SFDISK_DONE_WRITE,
+ SFDISK_DONE_ASK
+};
+
+/* returns: 0 on success, <0 on error, 1 successfully stop sfdisk */
+static int loop_control_commands(struct sfdisk *sf,
+ struct fdisk_script *dp,
+ char *buf)
+{
+ const char *p = skip_blank(buf);
+ int rc = SFDISK_DONE_NONE;
+
+ if (strcmp(p, "print") == 0)
+ list_disklabel(sf->cxt);
+ else if (strcmp(p, "help") == 0)
+ command_fdisk_help();
+ else if (strcmp(p, "quit") == 0)
+ rc = SFDISK_DONE_ASK;
+ else if (strcmp(p, "write") == 0)
+ rc = SFDISK_DONE_WRITE;
+ else if (strcmp(p, "abort") == 0)
+ rc = SFDISK_DONE_ABORT;
+ else {
+ if (sf->interactive)
+ fdisk_warnx(sf->cxt, _("unsupported command"));
+ else {
+ fdisk_warnx(sf->cxt, _("line %d: unsupported command"),
+ fdisk_script_get_nlines(dp));
+ rc = -EINVAL;
+ }
+ }
+ return rc;
+}
+
+static int has_container_or_unused(struct sfdisk *sf)
+{
+ size_t i, nparts;
+ struct fdisk_partition *pa = NULL;
+
+ if (sf->container || sf->unused)
+ return 1;
+
+ nparts = fdisk_get_npartitions(sf->cxt);
+ for (i = 0; i < nparts; i++) {
+
+ if (!fdisk_is_partition_used(sf->cxt, i)) {
+ sf->unused = 1;
+ continue;
+ }
+ if (fdisk_get_partition(sf->cxt, i, &pa) != 0)
+ continue;
+ if (fdisk_partition_is_container(pa))
+ sf->container = 1;
+ }
+
+ fdisk_unref_partition(pa);
+ return sf->container || sf->unused;
+}
+
+static size_t last_pt_partno(struct sfdisk *sf)
+{
+ size_t i, nparts, partno = 0;
+ struct fdisk_partition *pa = NULL;
+
+
+ nparts = fdisk_get_npartitions(sf->cxt);
+ for (i = 0; i < nparts; i++) {
+ size_t x;
+
+ if (fdisk_get_partition(sf->cxt, i, &pa) != 0 ||
+ !fdisk_partition_is_used(pa))
+ continue;
+ x = fdisk_partition_get_partno(pa);
+ if (x > partno)
+ partno = x;
+ }
+
+ fdisk_unref_partition(pa);
+ return partno;
+}
+
+#ifdef HAVE_LIBREADLINE
+static char *sfdisk_fgets(struct fdisk_script *dp,
+ char *buf, size_t bufsz, FILE *f)
+{
+ struct sfdisk *sf = (struct sfdisk *) fdisk_script_get_userdata(dp);
+
+ assert(dp);
+ assert(buf);
+ assert(bufsz > 2);
+
+ if (sf->interactive) {
+ char *p = readline(sf->prompt);
+ size_t len;
+
+ if (!p)
+ return NULL;
+ len = strlen(p);
+ if (len > bufsz - 2)
+ len = bufsz - 2;
+
+ memcpy(buf, p, len);
+ buf[len] = '\n'; /* append \n to be compatible with libc fgetc() */
+ buf[len + 1] = '\0';
+ free(p);
+ fflush(stdout);
+ return buf;
+ }
+ return fgets(buf, bufsz, f);
+}
+#endif
+
+static int ignore_partition(struct fdisk_partition *pa)
+{
+ /* incomplete partition setting */
+ if (!fdisk_partition_has_start(pa) && !fdisk_partition_start_is_default(pa))
+ return 1;
+ if (!fdisk_partition_has_size(pa) && !fdisk_partition_end_is_default(pa))
+ return 1;
+
+ /* probably dump from old sfdisk with start=0 size=0 */
+ if (fdisk_partition_has_start(pa) && fdisk_partition_get_start(pa) == 0 &&
+ fdisk_partition_has_size(pa) && fdisk_partition_get_size(pa) == 0)
+ return 1;
+
+ return 0;
+}
+
+static void follow_wipe_mode(struct sfdisk *sf)
+{
+ int dowipe = sf->wipemode == WIPEMODE_ALWAYS ? 1 : 0;
+
+ if (sf->interactive && sf->wipemode == WIPEMODE_AUTO)
+ dowipe = 1; /* do it in interactive mode */
+
+ if (fdisk_is_ptcollision(sf->cxt) && sf->wipemode != WIPEMODE_NEVER)
+ dowipe = 1; /* always wipe old PT */
+
+ fdisk_enable_wipe(sf->cxt, dowipe);
+ if (sf->quiet)
+ return;
+
+ if (dowipe) {
+ if (!fdisk_is_ptcollision(sf->cxt)) {
+ fdisk_warnx(sf->cxt, _(
+ "The device contains '%s' signature and it will be removed by a write command. "
+ "See sfdisk(8) man page and --wipe option for more details."),
+ fdisk_get_collision(sf->cxt));
+ fputc('\n', stdout);
+ }
+ } else {
+ fdisk_warnx(sf->cxt, _(
+ "The device contains '%s' signature and it may remain on the device. "
+ "It is recommended to wipe the device with wipefs(8) or "
+ "sfdisk --wipe, in order to avoid possible collisions."),
+ fdisk_get_collision(sf->cxt));
+ fputc('\n', stderr);
+ }
+}
+
+static int wipe_partition(struct sfdisk *sf, size_t partno)
+{
+ int rc, yes = 0;
+ char *fstype = NULL;
+ struct fdisk_partition *tmp = NULL;
+
+ DBG(MISC, ul_debug("checking for signature"));
+
+ rc = fdisk_get_partition(sf->cxt, partno, &tmp);
+ if (rc)
+ goto done;
+
+ rc = fdisk_partition_to_string(tmp, sf->cxt, FDISK_FIELD_FSTYPE, &fstype);
+ if (rc || fstype == NULL)
+ goto done;
+
+ fdisk_warnx(sf->cxt, _("Partition #%zu contains a %s signature."), partno + 1, fstype);
+
+ if (sf->pwipemode == WIPEMODE_AUTO && isatty(STDIN_FILENO))
+ fdisk_ask_yesno(sf->cxt, _("Do you want to remove the signature?"), &yes);
+ else if (sf->pwipemode == WIPEMODE_ALWAYS)
+ yes = 1;
+
+ if (yes) {
+ fdisk_info(sf->cxt, _("The signature will be removed by a write command."));
+ rc = fdisk_wipe_partition(sf->cxt, partno, TRUE);
+ }
+done:
+ fdisk_unref_partition(tmp);
+ free(fstype);
+ DBG(MISC, ul_debug("partition wipe check end [rc=%d]", rc));
+ return rc;
+}
+
+static void refresh_prompt_buffer(struct sfdisk *sf, const char *devname,
+ size_t next_partno, int created)
+{
+ if (created) {
+ char *partname = fdisk_partname(devname, next_partno + 1);
+ if (!partname)
+ err(EXIT_FAILURE, _("failed to allocate partition name"));
+
+ if (!sf->prompt || !startswith(sf->prompt, partname)) {
+ free(sf->prompt);
+ xasprintf(&sf->prompt,"%s: ", partname);
+ }
+ free(partname);
+ } else if (!sf->prompt || !startswith(sf->prompt, SFDISK_PROMPT)) {
+ free(sf->prompt);
+ sf->prompt = xstrdup(SFDISK_PROMPT);
+ }
+}
+
+/*
+ * sfdisk <device> [[-N] <partno>]
+ *
+ * Note that the option -N is there for backward compatibility only.
+ */
+static int command_fdisk(struct sfdisk *sf, int argc, char **argv)
+{
+ int rc = 0, partno = sf->partno, created = 0, unused = 0;
+ struct fdisk_script *dp;
+ struct fdisk_table *tb = NULL;
+ const char *devname = NULL, *label;
+ char buf[BUFSIZ];
+ size_t next_partno = (size_t) -1;
+
+ if (argc)
+ devname = argv[0];
+ if (partno < 0 && argc > 1)
+ partno = strtou32_or_err(argv[1],
+ _("failed to parse partition number"));
+ if (!devname)
+ errx(EXIT_FAILURE, _("no disk device specified"));
+
+ assign_device(sf, devname, 0);
+
+ dp = fdisk_new_script(sf->cxt);
+ if (!dp)
+ err(EXIT_FAILURE, _("failed to allocate script handler"));
+ fdisk_set_script(sf->cxt, dp);
+#ifdef HAVE_LIBREADLINE
+ fdisk_script_set_fgets(dp, sfdisk_fgets);
+#endif
+ fdisk_script_set_userdata(dp, (void *) sf);
+
+ /*
+ * Don't create a new disklabel when [-N] <partno> specified. In this
+ * case reuse already specified disklabel. Let's check that the disk
+ * really contains the partition.
+ */
+ if (partno >= 0) {
+ size_t n;
+
+ if (!fdisk_has_label(sf->cxt))
+ errx(EXIT_FAILURE, _("%s: cannot modify partition %d: "
+ "no partition table was found"),
+ devname, partno + 1);
+ n = fdisk_get_npartitions(sf->cxt);
+ if ((size_t) partno > n)
+ errx(EXIT_FAILURE, _("%s: cannot modify partition %d: "
+ "partition table contains only %zu "
+ "partitions"),
+ devname, partno + 1, n);
+
+ if (!fdisk_is_partition_used(sf->cxt, partno)) {
+ fdisk_warnx(sf->cxt, _("warning: %s: partition %d is not defined yet"),
+ devname, partno + 1);
+ unused = 1;
+ }
+ created = 1;
+ next_partno = partno;
+
+ if (sf->movedata)
+ sf->orig_pa = get_partition(sf->cxt, partno);
+ }
+
+ if (sf->append) {
+ created = 1;
+ next_partno = last_pt_partno(sf) + 1;
+ }
+
+ if (!sf->quiet && sf->interactive) {
+ color_scheme_enable("welcome", UL_COLOR_GREEN);
+ fdisk_info(sf->cxt, _("\nWelcome to sfdisk (%s)."), PACKAGE_STRING);
+ color_disable();
+ fdisk_info(sf->cxt, _("Changes will remain in memory only, until you decide to write them.\n"
+ "Be careful before using the write command.\n"));
+ }
+
+ if (!sf->noact && !sf->noreread) {
+ if (!sf->quiet)
+ fputs(_("Checking that no-one is using this disk right now ..."), stdout);
+ if (fdisk_device_is_used(sf->cxt)) {
+ if (!sf->quiet)
+ fputs(_(" FAILED\n\n"), stdout);
+
+ fdisk_warnx(sf->cxt, _(
+ "This disk is currently in use - repartitioning is probably a bad idea.\n"
+ "Umount all file systems, and swapoff all swap partitions on this disk.\n"
+ "Use the --no-reread flag to suppress this check.\n"));
+
+ if (!sf->force)
+ errx(EXIT_FAILURE, _("Use the --force flag to overrule all checks."));
+ } else if (!sf->quiet)
+ fputs(_(" OK\n\n"), stdout);
+ }
+
+ if (fdisk_get_collision(sf->cxt))
+ follow_wipe_mode(sf);
+
+ if (!sf->quiet) {
+ list_disk_geometry(sf->cxt);
+ if (fdisk_has_label(sf->cxt)) {
+ fdisk_info(sf->cxt, _("\nOld situation:"));
+ list_disklabel(sf->cxt);
+ }
+ }
+
+ if (sf->label)
+ label = sf->label;
+ else if (fdisk_has_label(sf->cxt))
+ label = fdisk_label_get_name(fdisk_get_label(sf->cxt, NULL));
+ else
+ label = "dos"; /* just for backward compatibility */
+
+ if (fdisk_script_set_header(dp, "label", label) != 0)
+ errx(EXIT_FAILURE, _("failed to set script header"));
+
+ if (!sf->quiet && sf->interactive) {
+ if (!fdisk_has_label(sf->cxt) && !sf->label)
+ fdisk_info(sf->cxt,
+ _("\nsfdisk is going to create a new '%s' disk label.\n"
+ "Use 'label: <name>' before you define a first partition\n"
+ "to override the default."), label);
+ fdisk_info(sf->cxt, _("\nType 'help' to get more information.\n"));
+ } else if (!sf->quiet)
+ fputc('\n', stdout);
+
+ tb = fdisk_script_get_table(dp);
+ assert(tb);
+
+ do {
+ size_t nparts;
+
+ DBG(PARSE, ul_debug("<---next-line--->"));
+ if (next_partno == (size_t) -1)
+ next_partno = fdisk_table_get_nents(tb);
+
+ if (created
+ && partno < 0
+ && next_partno == fdisk_get_npartitions(sf->cxt)
+ && !has_container_or_unused(sf)) {
+ fdisk_info(sf->cxt, _("All partitions used."));
+ rc = SFDISK_DONE_ASK;
+ break;
+ }
+
+ refresh_prompt_buffer(sf, devname, next_partno, created);
+
+
+ if (sf->prompt && (sf->interactive || !sf->quiet)) {
+#ifndef HAVE_LIBREADLINE
+ fputs(sf->prompt, stdout);
+#else
+ if (!sf->interactive)
+ fputs(sf->prompt, stdout);
+#endif
+ }
+
+ rc = fdisk_script_read_line(dp, stdin, buf, sizeof(buf));
+ if (rc == -ENOTSUP) {
+ buf[sizeof(buf) - 1] = '\0';
+ fdisk_warnx(sf->cxt, _("Unknown script header '%s' -- ignore."), buf);
+ continue;
+ }
+
+ if (rc < 0) {
+ DBG(PARSE, ul_debug("script parsing failed, trying sfdisk specific commands"));
+ buf[sizeof(buf) - 1] = '\0';
+ rc = loop_control_commands(sf, dp, buf);
+ if (rc)
+ break;
+ continue;
+ }
+
+ if (rc == 1) {
+ rc = SFDISK_DONE_EOF;
+ if (!sf->quiet)
+ fputs(_("Done.\n"), stdout);
+ break;
+ }
+
+ nparts = fdisk_table_get_nents(tb);
+ if (nparts) {
+ size_t cur_partno = (size_t) -1;
+ struct fdisk_partition *pa = fdisk_table_get_partition(tb, nparts - 1);
+
+ assert(pa);
+
+ if (ignore_partition(pa)) {
+ fdisk_info(sf->cxt, _("Ignoring partition."));
+ next_partno++;
+ continue;
+ }
+ if (!created) { /* create a new disklabel */
+ rc = fdisk_apply_script_headers(sf->cxt, dp);
+ created = !rc;
+ if (rc) {
+ errno = -rc;
+ fdisk_warn(sf->cxt, _(
+ "Failed to apply script headers, disk label not created"));
+ }
+
+ if (rc == 0 && fdisk_get_collision(sf->cxt))
+ follow_wipe_mode(sf);
+ }
+ if (!rc && partno >= 0) { /* -N <partno>, modify partition */
+ rc = fdisk_set_partition(sf->cxt, partno, pa);
+ rc = rc == 0 ? SFDISK_DONE_ASK : SFDISK_DONE_ABORT;
+ break;
+ }
+
+ if (!rc) { /* add partition */
+ if (!sf->interactive && !sf->quiet &&
+ (!sf->prompt || startswith(sf->prompt, SFDISK_PROMPT))) {
+ refresh_prompt_buffer(sf, devname, next_partno, created);
+ fputs(sf->prompt, stdout);
+ }
+ rc = fdisk_add_partition(sf->cxt, pa, &cur_partno);
+ if (rc) {
+ errno = -rc;
+ fdisk_warn(sf->cxt, _("Failed to add #%d partition"), next_partno + 1);
+ }
+ }
+
+ /* wipe partition on success
+ *
+ * Note that unused=1 means -N <partno> for unused,
+ * otherwise we wipe only newly created partitions.
+ */
+ if (rc == 0 && (unused || partno < 0)) {
+ rc = wipe_partition(sf, unused ? (size_t) partno : cur_partno);
+ if (rc)
+ errno = -rc;
+ }
+
+ if (!rc) {
+ /* success print result */
+ if (sf->interactive)
+ sfdisk_print_partition(sf, cur_partno);
+ next_partno = cur_partno + 1;
+ } else if (pa) /* error, drop partition from script */
+ fdisk_table_remove_partition(tb, pa);
+ } else
+ fdisk_info(sf->cxt, _("Script header accepted."));
+
+ if (rc && !sf->interactive) {
+ rc = SFDISK_DONE_ABORT;
+ break;
+ }
+ } while (1);
+
+ /* create empty disk label if label, but no partition specified */
+ if ((rc == SFDISK_DONE_EOF || rc == SFDISK_DONE_WRITE) && created == 0
+ && fdisk_script_has_force_label(dp) == 1
+ && fdisk_table_get_nents(tb) == 0
+ && fdisk_script_get_header(dp, "label")) {
+
+ int xrc = fdisk_apply_script_headers(sf->cxt, dp);
+ if (xrc) {
+ fdisk_warnx(sf->cxt, _(
+ "Failed to apply script headers, "
+ "disk label not created."));
+ rc = SFDISK_DONE_ABORT;
+ }
+ }
+
+ if (!sf->quiet && rc != SFDISK_DONE_ABORT) {
+ fdisk_info(sf->cxt, _("\nNew situation:"));
+ list_disk_identifier(sf->cxt);
+ list_disklabel(sf->cxt);
+ }
+
+ switch (rc) {
+ case SFDISK_DONE_ASK:
+ case SFDISK_DONE_EOF:
+ if (sf->interactive) {
+ int yes = 0;
+ fdisk_ask_yesno(sf->cxt, _("Do you want to write this to disk?"), &yes);
+ if (!yes) {
+ fdisk_info(sf->cxt, _("Leaving."));
+ rc = 0;
+ break;
+ }
+ }
+ /* fallthrough */
+ case SFDISK_DONE_WRITE:
+ rc = write_changes(sf);
+ break;
+ case SFDISK_DONE_ABORT:
+ default: /* rc < 0 on error */
+ fdisk_info(sf->cxt, _("Leaving.\n"));
+ break;
+ }
+
+ fdisk_set_script(sf->cxt, NULL);
+ fdisk_unref_script(dp);
+ return rc;
+}
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+
+ fprintf(out,
+ _(" %1$s [options] <dev> [[-N] <part>]\n"
+ " %1$s [options] <command>\n"), program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Display or manipulate a disk partition table.\n"), out);
+
+ fputs(USAGE_COMMANDS, out);
+ fputs(_(" -A, --activate <dev> [<part> ...] list or set bootable (P)MBR partitions\n"), out);
+ fputs(_(" -d, --dump <dev> dump partition table (usable for later input)\n"), out);
+ fputs(_(" -J, --json <dev> dump partition table in JSON format\n"), out);
+ fputs(_(" -g, --show-geometry [<dev> ...] list geometry of all or specified devices\n"), out);
+ fputs(_(" -l, --list [<dev> ...] list partitions of each device\n"), out);
+ fputs(_(" -F, --list-free [<dev> ...] list unpartitioned free areas of each device\n"), out);
+ fputs(_(" -r, --reorder <dev> fix partitions order (by start offset)\n"), out);
+ fputs(_(" -s, --show-size [<dev> ...] list sizes of all or specified devices\n"), out);
+ fputs(_(" -T, --list-types print the recognized types (see -X)\n"), out);
+ fputs(_(" -V, --verify [<dev> ...] test whether partitions seem correct\n"), out);
+ fputs(_(" --delete <dev> [<part> ...] delete all or specified partitions\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_(" --part-label <dev> <part> [<str>] print or change partition label\n"), out);
+ fputs(_(" --part-type <dev> <part> [<type>] print or change partition type\n"), out);
+ fputs(_(" --part-uuid <dev> <part> [<uuid>] print or change partition uuid\n"), out);
+ fputs(_(" --part-attrs <dev> <part> [<str>] print or change partition attributes\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_(" --disk-id <dev> [<str>] print or change disk label ID (UUID)\n"), out);
+ fputs(_(" --relocate <oper> <dev> move partition header\n"), out);
+
+ fputs(USAGE_ARGUMENTS, out);
+ fputs(_(" <dev> device (usually disk) path\n"), out);
+ fputs(_(" <part> partition number\n"), out);
+ fputs(_(" <type> partition type, GUID for GPT, hex for MBR\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -a, --append append partitions to existing partition table\n"), out);
+ fputs(_(" -b, --backup backup partition table sectors (see -O)\n"), out);
+ fputs(_(" --bytes print SIZE in bytes rather than in human readable format\n"), out);
+ fputs(_(" --move-data[=<typescript>] move partition data after relocation (requires -N)\n"), out);
+ fputs(_(" --move-use-fsync use fsync after each write when move data\n"), out);
+ fputs(_(" -f, --force disable all consistency checking\n"), out);
+
+ fprintf(out,
+ _(" --color[=<when>] colorize output (%s, %s or %s)\n"), "auto", "always", "never");
+ fprintf(out,
+ " %s\n", USAGE_COLORS_DEFAULT);
+ fprintf(out,
+ _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
+ fputs(_(" -N, --partno <num> specify partition number\n"), out);
+ fputs(_(" -n, --no-act do everything except write to device\n"), out);
+ fputs(_(" --no-reread do not check whether the device is in use\n"), out);
+ fputs(_(" --no-tell-kernel do not tell kernel about changes\n"), out);
+ fputs(_(" -O, --backup-file <path> override default backup file name\n"), out);
+ fputs(_(" -o, --output <list> output columns\n"), out);
+ fputs(_(" -q, --quiet suppress extra info messages\n"), out);
+ fprintf(out,
+ _(" -w, --wipe <mode> wipe signatures (%s, %s or %s)\n"), "auto", "always", "never");
+ fprintf(out,
+ _(" -W, --wipe-partitions <mode> wipe signatures from new partitions (%s, %s or %s)\n"), "auto", "always", "never");
+ fputs(_(" -X, --label <name> specify label type (dos, gpt, ...)\n"), out);
+ fputs(_(" -Y, --label-nested <name> specify nested label type (dos, bsd)\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_(" -G, --show-pt-geometry deprecated, alias to --show-geometry\n"), out);
+ fputs(_(" -L, --Linux deprecated, only for backward compatibility\n"), out);
+ fputs(_(" -u, --unit S deprecated, only sector unit is supported\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ printf( " -h, --help %s\n", USAGE_OPTSTR_HELP);
+ printf( " -v, --version %s\n", USAGE_OPTSTR_VERSION);
+
+ list_available_columns(out);
+
+ printf(USAGE_MAN_TAIL("sfdisk(8)"));
+ exit(EXIT_SUCCESS);
+}
+
+
+int main(int argc, char *argv[])
+{
+ const char *outarg = NULL;
+ int rc = -EINVAL, c, longidx = -1, bytes = 0;
+ int colormode = UL_COLORMODE_UNDEF;
+ struct sfdisk _sf = {
+ .partno = -1,
+ .wipemode = WIPEMODE_AUTO,
+ .pwipemode = WIPEMODE_AUTO,
+ .interactive = isatty(STDIN_FILENO) ? 1 : 0,
+ }, *sf = &_sf;
+
+ enum {
+ OPT_CHANGE_ID = CHAR_MAX + 1,
+ OPT_PRINT_ID,
+ OPT_ID,
+ OPT_NOREREAD,
+ OPT_PARTUUID,
+ OPT_PARTLABEL,
+ OPT_PARTTYPE,
+ OPT_PARTATTRS,
+ OPT_DISKID,
+ OPT_BYTES,
+ OPT_COLOR,
+ OPT_MOVEDATA,
+ OPT_MOVEFSYNC,
+ OPT_DELETE,
+ OPT_NOTELL,
+ OPT_RELOCATE,
+ OPT_LOCK,
+ };
+
+ static const struct option longopts[] = {
+ { "activate",no_argument, NULL, 'A' },
+ { "append", no_argument, NULL, 'a' },
+ { "backup", no_argument, NULL, 'b' },
+ { "backup-file", required_argument, NULL, 'O' },
+ { "bytes", no_argument, NULL, OPT_BYTES },
+ { "color", optional_argument, NULL, OPT_COLOR },
+ { "lock", optional_argument, NULL, OPT_LOCK },
+ { "delete", no_argument, NULL, OPT_DELETE },
+ { "dump", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "force", no_argument, NULL, 'f' },
+ { "json", no_argument, NULL, 'J' },
+ { "label", required_argument, NULL, 'X' },
+ { "label-nested", required_argument, NULL, 'Y' },
+ { "list", no_argument, NULL, 'l' },
+ { "list-free", no_argument, NULL, 'F' },
+ { "list-types", no_argument, NULL, 'T' },
+ { "no-act", no_argument, NULL, 'n' },
+ { "no-reread", no_argument, NULL, OPT_NOREREAD },
+ { "no-tell-kernel", no_argument, NULL, OPT_NOTELL },
+ { "move-data", optional_argument, NULL, OPT_MOVEDATA },
+ { "move-use-fsync", no_argument, NULL, OPT_MOVEFSYNC },
+ { "output", required_argument, NULL, 'o' },
+ { "partno", required_argument, NULL, 'N' },
+ { "reorder", no_argument, NULL, 'r' },
+ { "show-geometry", no_argument, NULL, 'g' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "verify", no_argument, NULL, 'V' },
+ { "version", no_argument, NULL, 'v' },
+ { "wipe", required_argument, NULL, 'w' },
+ { "wipe-partitions", required_argument, NULL, 'W' },
+
+ { "relocate", no_argument, NULL, OPT_RELOCATE },
+
+ { "part-uuid", no_argument, NULL, OPT_PARTUUID },
+ { "part-label", no_argument, NULL, OPT_PARTLABEL },
+ { "part-type", no_argument, NULL, OPT_PARTTYPE },
+ { "part-attrs", no_argument, NULL, OPT_PARTATTRS },
+
+ { "disk-id", no_argument, NULL, OPT_DISKID },
+
+ { "show-pt-geometry", no_argument, NULL, 'G' }, /* deprecated */
+ { "unit", required_argument, NULL, 'u' }, /* deprecated */
+ { "Linux", no_argument, NULL, 'L' }, /* deprecated */
+ { "show-size", no_argument, NULL, 's' }, /* deprecated */
+
+ { "change-id",no_argument, NULL, OPT_CHANGE_ID }, /* deprecated */
+ { "id", no_argument, NULL, 'c' }, /* deprecated */
+ { "print-id",no_argument, NULL, OPT_PRINT_ID }, /* deprecated */
+
+ { NULL, 0, NULL, 0 },
+ };
+ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
+ { 'F','d'}, /* --list-free --dump */
+ { 'F','J'}, /* --list-free --json */
+ { 's','u'}, /* --show-size --unit */
+ { 0 }
+ };
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ while ((c = getopt_long(argc, argv, "aAbcdfFgGhJlLo:O:nN:qrsTu:vVX:Y:w:W:",
+ longopts, &longidx)) != -1) {
+
+ err_exclusive_options(c, longopts, excl, excl_st);
+
+ switch(c) {
+ case 'A':
+ sf->act = ACT_ACTIVATE;
+ break;
+ case 'a':
+ sf->append = 1;
+ break;
+ case 'b':
+ sf->backup = 1;
+ break;
+ case OPT_CHANGE_ID:
+ case OPT_PRINT_ID:
+ case OPT_ID:
+ warnx(_("%s is deprecated in favour of --part-type"),
+ longopts[longidx].name);
+ sf->act = ACT_PARTTYPE;
+ break;
+ case 'c':
+ warnx(_("--id is deprecated in favour of --part-type"));
+ sf->act = ACT_PARTTYPE;
+ break;
+ case 'J':
+ sf->json = 1;
+ /* fallthrough */
+ case 'd':
+ sf->act = ACT_DUMP;
+ break;
+ case 'F':
+ sf->act = ACT_LIST_FREE;
+ break;
+ case 'f':
+ sf->force = 1;
+ break;
+ case 'G':
+ warnx(_("--show-pt-geometry is no more implemented. Using --show-geometry."));
+ /* fallthrough */
+ case 'g':
+ sf->act = ACT_SHOW_GEOM;
+ break;
+ case 'h':
+ usage();
+ break;
+ case 'l':
+ sf->act = ACT_LIST;
+ break;
+ case 'L':
+ warnx(_("--Linux option is unnecessary and deprecated"));
+ break;
+ case 'o':
+ outarg = optarg;
+ break;
+ case 'O':
+ sf->backup = 1;
+ sf->backup_file = optarg;
+ break;
+ case 'n':
+ sf->noact = 1;
+ break;
+ case 'N':
+ sf->partno = strtou32_or_err(optarg, _("failed to parse partition number")) - 1;
+ break;
+ case 'q':
+ sf->quiet = 1;
+ break;
+ case 'r':
+ sf->act = ACT_REORDER;
+ break;
+ case 's':
+ sf->act = ACT_SHOW_SIZE;
+ break;
+ case 'T':
+ sf->act = ACT_LIST_TYPES;
+ break;
+ case 'u':
+ if (*optarg != 'S')
+ errx(EXIT_FAILURE, _("unsupported unit '%c'"), *optarg);
+ break;
+ case 'v':
+ print_version(EXIT_SUCCESS);
+ case 'V':
+ sf->verify = 1;
+ break;
+ case 'w':
+ sf->wipemode = wipemode_from_string(optarg);
+ if (sf->wipemode < 0)
+ errx(EXIT_FAILURE, _("unsupported wipe mode"));
+ break;
+ case 'W':
+ sf->pwipemode = wipemode_from_string(optarg);
+ if (sf->pwipemode < 0)
+ errx(EXIT_FAILURE, _("unsupported wipe mode"));
+ break;
+ case 'X':
+ sf->label = optarg;
+ break;
+ case 'Y':
+ sf->label_nested = optarg;
+ break;
+
+ case OPT_PARTUUID:
+ sf->act = ACT_PARTUUID;
+ break;
+ case OPT_PARTTYPE:
+ sf->act = ACT_PARTTYPE;
+ break;
+ case OPT_PARTLABEL:
+ sf->act = ACT_PARTLABEL;
+ break;
+ case OPT_PARTATTRS:
+ sf->act = ACT_PARTATTRS;
+ break;
+ case OPT_DISKID:
+ sf->act = ACT_DISKID;
+ break;
+ case OPT_NOREREAD:
+ sf->noreread = 1;
+ break;
+ case OPT_BYTES:
+ bytes = 1;
+ break;
+ case OPT_COLOR:
+ colormode = UL_COLORMODE_AUTO;
+ if (optarg)
+ colormode = colormode_or_err(optarg,
+ _("unsupported color mode"));
+ break;
+ case OPT_MOVEDATA:
+ sf->movedata = 1;
+ sf->move_typescript = optarg;
+ break;
+ case OPT_MOVEFSYNC:
+ sf->movefsync = 1;
+ break;
+ case OPT_DELETE:
+ sf->act = ACT_DELETE;
+ break;
+ case OPT_NOTELL:
+ sf->notell = 1;
+ break;
+ case OPT_RELOCATE:
+ sf->act = ACT_RELOCATE;
+ break;
+ case OPT_LOCK:
+ sf->lockmode = "1";
+ if (optarg) {
+ if (*optarg == '=')
+ optarg++;
+ sf->lockmode = optarg;
+ }
+ break;
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ colors_init(colormode, "sfdisk");
+
+ sfdisk_init(sf);
+ if (bytes)
+ fdisk_set_size_unit(sf->cxt, FDISK_SIZEUNIT_BYTES);
+
+ if (outarg)
+ init_fields(NULL, outarg, NULL);
+
+ if (sf->verify && !sf->act)
+ sf->act = ACT_VERIFY; /* --verify make be used with --list too */
+ else if (!sf->act)
+ sf->act = ACT_FDISK; /* default */
+
+ if (sf->movedata && !(sf->act == ACT_FDISK && sf->partno >= 0))
+ errx(EXIT_FAILURE, _("--movedata requires -N"));
+
+ switch (sf->act) {
+ case ACT_ACTIVATE:
+ rc = command_activate(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_DELETE:
+ rc = command_delete(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_LIST:
+ rc = command_list_partitions(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_LIST_TYPES:
+ rc = command_list_types(sf);
+ break;
+
+ case ACT_LIST_FREE:
+ rc = command_list_freespace(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_FDISK:
+ rc = command_fdisk(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_DUMP:
+ rc = command_dump(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_SHOW_SIZE:
+ rc = command_show_size(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_SHOW_GEOM:
+ rc = command_show_geometry(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_VERIFY:
+ rc = command_verify(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_PARTTYPE:
+ rc = command_parttype(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_PARTUUID:
+ rc = command_partuuid(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_PARTLABEL:
+ rc = command_partlabel(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_PARTATTRS:
+ rc = command_partattrs(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_DISKID:
+ rc = command_diskid(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_REORDER:
+ rc = command_reorder(sf, argc - optind, argv + optind);
+ break;
+
+ case ACT_RELOCATE:
+ rc = command_relocate(sf, argc - optind, argv + optind);
+ break;
+ }
+
+ sfdisk_deinit(sf);
+
+ DBG(MISC, ul_debug("bye! [rc=%d]", rc));
+ return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
diff --git a/disk-utils/swaplabel.8 b/disk-utils/swaplabel.8
new file mode 100644
index 0000000..c92de00
--- /dev/null
+++ b/disk-utils/swaplabel.8
@@ -0,0 +1,69 @@
+.\" Copyright 2010 Jason Borden <jborden@bluehost.com>
+.\"
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.TH SWAPLABEL 8 "April 2010" "util-linux" "System Administration"
+.SH NAME
+swaplabel \- print or change the label or UUID of a swap area
+.SH SYNOPSIS
+.B swaplabel
+.RB [ \-L
+.IR label ]
+.RB [ \-U
+.IR UUID ]
+.I device
+.SH DESCRIPTION
+.B swaplabel
+will display or change the label or UUID of a swap partition located on
+.I device
+(or regular file).
+.PP
+If the optional arguments
+.B \-L
+and
+.B \-U
+are not given,
+.B swaplabel
+will simply display the current swap-area label and UUID of
+.IR device .
+.PP
+If an optional argument is present, then
+.B swaplabel
+will change the appropriate value on
+.IR device .
+These values can also be set during swap creation using
+.BR mkswap (8).
+The
+.B swaplabel
+utility allows changing the label or UUID on an actively used swap device.
+.SH OPTIONS
+.TP
+.BR \-h , " \-\-help"
+Display help text and exit.
+.TP
+.BR \-L , " \-\-label " \fIlabel\fR
+Specify a new \fIlabel\fR for the device.
+Swap partition labels can be at most 16 characters long. If
+.I label
+is longer than 16 characters,
+.B swaplabel
+will truncate it and print a warning message.
+.TP
+.BR \-U , " \-\-uuid " \fIUUID\fR
+Specify a new \fIUUID\fR for the device.
+The \fI UUID\fR
+must be in the standard 8-4-4-4-12 character format, such as is output by
+.BR uuidgen (1).
+.SH ENVIRONMENT
+.IP LIBBLKID_DEBUG=all
+enables libblkid debug output.
+.SH AUTHORS
+.B swaplabel
+was written by Jason Borden <jborden@bluehost.com> and Karel Zak <kzak@redhat.com>.
+.SH SEE ALSO
+.BR uuidgen (1),
+.BR mkswap (8),
+.BR swapon (8)
+.SH AVAILABILITY
+The swaplabel command is part of the util-linux package and is available from
+https://www.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/swaplabel.c b/disk-utils/swaplabel.c
new file mode 100644
index 0000000..e6ba7d6
--- /dev/null
+++ b/disk-utils/swaplabel.c
@@ -0,0 +1,194 @@
+/*
+ * swaplabel.c - Print or change the label / UUID of a swap partition
+ *
+ * Copyright (C) 2010 Jason Borden <jborden@bluehost.com>
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * Usage: swaplabel [-L label] [-U UUID] device
+ *
+ * This file may be redistributed under the terms of the GNU Public License
+ * version 2 or later.
+ *
+ */
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#ifdef HAVE_LIBUUID
+# include <uuid.h>
+#endif
+
+#include "c.h"
+#include "nls.h"
+#include "all-io.h"
+#include "strutils.h"
+#include "closestream.h"
+
+#include "swapheader.h"
+#include "swapprober.h"
+
+#define SWAP_UUID_OFFSET (offsetof(struct swap_header_v1_2, uuid))
+#define SWAP_LABEL_OFFSET (offsetof(struct swap_header_v1_2, volume_name))
+
+/* Print the swap partition information */
+static int print_info(blkid_probe pr)
+{
+ const char *data;
+
+ if (!blkid_probe_lookup_value(pr, "LABEL", &data, NULL))
+ printf("LABEL: %s\n", data);
+
+ if (!blkid_probe_lookup_value(pr, "UUID", &data, NULL))
+ printf("UUID: %s\n", data);
+
+ return 0;
+}
+
+/* Change the swap partition info */
+#ifdef HAVE_LIBUUID
+static int change_info(const char *devname, const char *label, const char *uuid)
+#else
+static int change_info(const char *devname, const char *label,
+ const char *uuid __attribute__((__unused__)))
+#endif
+{
+ int fd;
+
+ fd = open(devname, O_RDWR);
+ if (fd < 0) {
+ warn(_("cannot open %s"), devname);
+ goto err;
+ }
+#ifdef HAVE_LIBUUID
+ /* Write the uuid if it was provided */
+ if (uuid) {
+ uuid_t newuuid;
+
+ if (uuid_parse(uuid, newuuid) == -1)
+ warnx(_("failed to parse UUID: %s"), uuid);
+ else {
+ if (lseek(fd, SWAP_UUID_OFFSET, SEEK_SET) !=
+ SWAP_UUID_OFFSET) {
+ warn(_("%s: failed to seek to swap UUID"), devname);
+ goto err;
+
+ } else if (write_all(fd, newuuid, sizeof(newuuid))) {
+ warn(_("%s: failed to write UUID"), devname);
+ goto err;
+ }
+ }
+ }
+#endif
+ /* Write the label if it was provided */
+ if (label) {
+ char newlabel[SWAP_LABEL_LENGTH];
+
+ if (lseek(fd, SWAP_LABEL_OFFSET, SEEK_SET) != SWAP_LABEL_OFFSET) {
+ warn(_("%s: failed to seek to swap label "), devname);
+ goto err;
+ }
+ memset(newlabel, 0, sizeof(newlabel));
+ xstrncpy(newlabel, label, sizeof(newlabel));
+
+ if (strlen(label) > strlen(newlabel))
+ warnx(_("label is too long. Truncating it to '%s'"),
+ newlabel);
+ if (write_all(fd, newlabel, sizeof(newlabel))) {
+ warn(_("%s: failed to write label"), devname);
+ goto err;
+ }
+ }
+
+ if (close_fd(fd) != 0) {
+ warn(_("write failed: %s"), devname);
+ return -1;
+ }
+ return 0;
+err:
+ if (fd >= 0)
+ close(fd);
+ return -1;
+}
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s [options] <device>\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Display or change the label or UUID of a swap area.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -L, --label <label> specify a new label\n"
+ " -U, --uuid <uuid> specify a new uuid\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ printf(USAGE_HELP_OPTIONS(21));
+ printf(USAGE_MAN_TAIL("swaplabel(8)"));
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ blkid_probe pr = NULL;
+ char *uuid = NULL, *label = NULL, *devname;
+ int c, rc = -1;
+
+ static const struct option longopts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "label", required_argument, NULL, 'L' },
+ { "uuid", required_argument, NULL, 'U' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ while ((c = getopt_long(argc, argv, "hVL:U:", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'h':
+ usage();
+ break;
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'L':
+ label = optarg;
+ break;
+ case 'U':
+#ifdef HAVE_LIBUUID
+ uuid = optarg;
+#else
+ warnx(_("ignore -U (UUIDs are unsupported)"));
+#endif
+ break;
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ if (optind == argc) {
+ warnx(_("no device specified"));
+ errtryhelp(EXIT_FAILURE);
+ }
+ devname = argv[optind];
+ pr = get_swap_prober(devname);
+ if (pr) {
+ if (uuid || label)
+ rc = change_info(devname, label, uuid);
+ else
+ rc = print_info(pr);
+ blkid_free_probe(pr);
+ }
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+