summaryrefslogtreecommitdiff
path: root/disk-utils
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-11-02 20:15:39 +0400
committerIgor Pashev <pashev.igor@gmail.com>2012-11-02 20:15:39 +0400
commitb13154de3eca5ba28fbb4854d916cd0be5febeed (patch)
tree30f2e9e89ab71a2df837076ac68c3ba770230294 /disk-utils
downloadutil-linux-upstream.tar.gz
Imported Upstream version 2.22upstream/2.22upstream
Diffstat (limited to 'disk-utils')
-rw-r--r--disk-utils/Makemodule.am144
-rw-r--r--disk-utils/addpart.840
-rw-r--r--disk-utils/addpart.c61
-rw-r--r--disk-utils/blockdev.885
-rw-r--r--disk-utils/blockdev.c470
-rw-r--r--disk-utils/cramfs.h114
-rw-r--r--disk-utils/cramfs_common.c109
-rw-r--r--disk-utils/delpart.834
-rw-r--r--disk-utils/delpart.c60
-rw-r--r--disk-utils/elvtune.867
-rw-r--r--disk-utils/elvtune.c174
-rw-r--r--disk-utils/fdformat.865
-rw-r--r--disk-utils/fdformat.c168
-rw-r--r--disk-utils/fsck.8461
-rw-r--r--disk-utils/fsck.c1576
-rw-r--r--disk-utils/fsck.cramfs.c692
-rw-r--r--disk-utils/fsck.minix.8136
-rw-r--r--disk-utils/fsck.minix.c1391
-rw-r--r--disk-utils/isosize.835
-rw-r--r--disk-utils/isosize.c219
-rw-r--r--disk-utils/minix_programs.h118
-rw-r--r--disk-utils/mkfs.8110
-rw-r--r--disk-utils/mkfs.bfs.858
-rw-r--r--disk-utils/mkfs.bfs.c296
-rw-r--r--disk-utils/mkfs.c144
-rw-r--r--disk-utils/mkfs.cramfs.c913
-rw-r--r--disk-utils/mkfs.minix.886
-rw-r--r--disk-utils/mkfs.minix.c815
-rw-r--r--disk-utils/mkswap.8148
-rw-r--r--disk-utils/mkswap.c672
-rw-r--r--disk-utils/partx.8173
-rw-r--r--disk-utils/partx.c891
-rw-r--r--disk-utils/partx.h75
-rw-r--r--disk-utils/raw.892
-rw-r--r--disk-utils/raw.c270
-rw-r--r--disk-utils/resizepart.838
-rw-r--r--disk-utils/resizepart.c106
-rw-r--r--disk-utils/swaplabel.867
-rw-r--r--disk-utils/swaplabel.c228
39 files changed, 11401 insertions, 0 deletions
diff --git a/disk-utils/Makemodule.am b/disk-utils/Makemodule.am
new file mode 100644
index 0000000..f2360bf
--- /dev/null
+++ b/disk-utils/Makemodule.am
@@ -0,0 +1,144 @@
+
+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
+mkfs_minix_LDADD = $(LDADD) libcommon.la
+
+sbin_PROGRAMS += mkfs
+dist_man_MANS += disk-utils/mkfs.8
+mkfs_SOURCES = disk-utils/mkfs.c
+
+usrbin_exec_PROGRAMS += isosize
+dist_man_MANS += disk-utils/isosize.8
+isosize_SOURCES = disk-utils/isosize.c
+isosize_LDADD = $(LDADD) libcommon.la
+
+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
+
+sbin_PROGRAMS += mkswap
+dist_man_MANS += disk-utils/mkswap.8
+mkswap_SOURCES = \
+ disk-utils/mkswap.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
+
+
+if BUILD_SWAPLABEL
+sbin_PROGRAMS += swaplabel
+dist_man_MANS += disk-utils/swaplabel.8
+swaplabel_SOURCES = disk-utils/swaplabel.c
+
+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
+fsck_LDADD = $(LDADD) libmount.la libblkid.la
+fsck_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir) -I$(ul_libblkid_incdir)
+endif
+
+
+if BUILD_ELVTUNE
+sbin_PROGRAMS += elvtune
+dist_man_MANS += disk-utils/elvtune.8
+elvtune_SOURCES = disk-utils/elvtune.c
+elvtune_LDADD = $(LDADD) libcommon.la
+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
+
+sbin_PROGRAMS += mkfs.cramfs
+mkfs_cramfs_SOURCES = disk-utils/mkfs.cramfs.c $(cramfs_common_sources)
+mkfs_cramfs_LDADD = $(LDADD) -lz libcommon.la
+endif
+
+
+if LINUX
+sbin_PROGRAMS += blockdev
+dist_man_MANS += disk-utils/blockdev.8
+blockdev_SOURCES = disk-utils/blockdev.c
+blockdev_LDADD = $(LDADD) libcommon.la
+
+usrsbin_exec_PROGRAMS += fdformat
+dist_man_MANS += disk-utils/fdformat.8
+fdformat_SOURCES = disk-utils/fdformat.c
+endif # LINUX
+
+
+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 \
+ disk-utils/partx.h
+addpart_LDADD = $(LDADD) libcommon.la
+
+delpart_SOURCES = \
+ disk-utils/delpart.c \
+ disk-utils/partx.h
+delpart_LDADD = $(LDADD) libcommon.la
+
+resizepart_SOURCES = \
+ disk-utils/resizepart.c \
+ disk-utils/partx.h
+resizepart_LDADD = $(LDADD) libcommon.la
+
+partx_SOURCES = \
+ disk-utils/partx.c \
+ disk-utils/partx.h
+partx_CFLAGS = -I$(ul_libblkid_incdir)
+partx_LDADD = $(LDADD) libblkid.la libcommon.la
+
+endif # BUILD_PARTX
diff --git a/disk-utils/addpart.8 b/disk-utils/addpart.8
new file mode 100644
index 0000000..9dcd0a3
--- /dev/null
+++ b/disk-utils/addpart.8
@@ -0,0 +1,40 @@
+.\" addpart.8 --
+.\" 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 2007" "util-linux" "System Administration"
+.SH NAME
+addpart \-
+simple wrapper around the "add partition" ioctl
+.SH SYNOPSIS
+.B addpart
+.I device partition start length
+.SH DESCRIPTION
+.B addpart
+is a program that informs the Linux kernel of new partition.
+
+This command doesn't manipulate partitions on hard drive.
+
+.SH PARAMETERS
+.TP
+.I device
+Specify the disk device.
+.TP
+.I partition
+Specify the partition number.
+.TP
+.I start
+Specify the begin of the partition (in 512-byte sectors).
+.TP
+.I length
+Specify 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
+ftp://ftp.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..c0f25a5
--- /dev/null
+++ b/disk-utils/addpart.c
@@ -0,0 +1,61 @@
+#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(FILE * out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s <disk device> <partition number> <start> <length>\n"),
+ program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("addpart(8)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int c, fd;
+
+ static const struct option longopts[] = {
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {NULL, no_argument, 0, '0'},
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ while ((c = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
+ switch (c) {
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ if (argc != 5)
+ usage(stderr);
+
+ 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..2b3d64c
--- /dev/null
+++ b/disk-utils/blockdev.8
@@ -0,0 +1,85 @@
+.\" -*- nroff -*-
+.\" 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 ]
+.IR command
+.RI [ command ...]
+.IR device
+.RI [ device ...]
+.br
+.B blockdev
+.B \-\-report
+.RI [ device ...]
+.SH DESCRIPTION
+The utility
+.B blockdev
+allows one to call block device ioctls from the command line.
+.SH OPTIONS
+.IP "\fB\-V\fP"
+Print version and exit.
+.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.
+.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.
+.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 sectorsize in bytes - 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.
+.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.
+.IP "\fB\-\-setrw\fP"
+Set read-write.
+.SH AUTHOR
+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
+ftp://ftp.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..a9e8833
--- /dev/null
+++ b/disk-utils/blockdev.c
@@ -0,0 +1,470 @@
+/*
+ * 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"
+
+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")
+ },{
+ 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(FILE * out)
+{
+ size_t i;
+ fprintf(out, _("\nUsage:\n"
+ " %1$s -V\n"
+ " %1$s --report [devices]\n"
+ " %1$s [-v|-q] commands devices\n\n"
+ "Available commands:\n"), program_invocation_short_name);
+
+ fprintf(out, _(" %-25s get size in 512-byte sectors\n"), "--getsz");
+ for (i = 0; i < ARRAY_SIZE(bdcms); i++) {
+ if (bdcms[i].argname)
+ fprintf(out, " %s %-*s %s\n", bdcms[i].name,
+ (int)(24 - strlen(bdcms[i].name)),
+ bdcms[i].argname, _(bdcms[i].help));
+ else
+ fprintf(out, " %-25s %s\n", bdcms[i].name,
+ _(bdcms[i].help));
+ }
+ fputc('\n', out);
+ exit(out == stderr ? EXIT_FAILURE : 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);
+ atexit(close_stdout);
+
+ if (argc < 2)
+ usage(stderr);
+
+ /* -V not together with commands */
+ if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) {
+ printf(_("%s (%s)\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ }
+ if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
+ usage(stdout);
+
+ /* --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)
+ usage(stderr);
+
+ 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]);
+ usage(stderr);
+ }
+
+ 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);
+ usage(stderr);
+ }
+ 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) {
+ perror(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;
+ struct hd_geometry g;
+
+ fd = open(device, O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ if (!quiet)
+ warn(_("cannot open %s"), device);
+ return;
+ }
+
+ ro = ssz = bsz = 0;
+ g.start = ra = 0;
+ if (ioctl(fd, BLKROGET, &ro) == 0 &&
+ ioctl(fd, BLKRAGET, &ra) == 0 &&
+ ioctl(fd, BLKSSZGET, &ssz) == 0 &&
+ ioctl(fd, BLKBSZGET, &bsz) == 0 &&
+ ioctl(fd, HDIO_GETGEO, &g) == 0 &&
+ blkdev_get_size(fd, &bytes) == 0) {
+ printf("%s %5ld %5d %5d %10ld %15lld %s\n",
+ ro ? "ro" : "rw", ra, ssz, bsz, g.start, 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/cramfs.h b/disk-utils/cramfs.h
new file mode 100644
index 0000000..96904f7
--- /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 Michael 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..0dc59e7
--- /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 Michael 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..433582f
--- /dev/null
+++ b/disk-utils/delpart.8
@@ -0,0 +1,34 @@
+.\" delpart.8 --
+.\" 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 2007" "util-linux" "System Administration"
+.SH NAME
+delpart \-
+simple wrapper around the "del partition" ioctl
+.SH SYNOPSIS
+.B delpart
+.I device partition
+.SH DESCRIPTION
+.B delpart
+is a program that asks the Linux kernel to remove a partition.
+
+This command doesn't manipulate partitions on hard drive.
+
+.SH OPTIONS
+.TP
+.I device
+Specify the disk device.
+.TP
+.I partition
+Specify the partition number.
+
+.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
+ftp://ftp.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..e940e3b
--- /dev/null
+++ b/disk-utils/delpart.c
@@ -0,0 +1,60 @@
+#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(FILE * out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s <disk device> <partition number>\n"),
+ program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("delpart(8)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int c, fd;
+
+ static const struct option longopts[] = {
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {NULL, no_argument, 0, '0'},
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ while ((c = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
+ switch (c) {
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ if (argc != 3)
+ usage(stderr);
+
+
+ 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/elvtune.8 b/disk-utils/elvtune.8
new file mode 100644
index 0000000..f71b1d4
--- /dev/null
+++ b/disk-utils/elvtune.8
@@ -0,0 +1,67 @@
+.\" -*- nroff -*-
+.TH ELVTUNE 8 "March 2000" "util-linux" "System Administration"
+.SH NAME
+elvtune \- I/O elevator tuner
+.SH SYNOPSIS
+.B elvtune
+.RB [ \-r
+.IR r_lat ]
+.RB [ \-w
+.IR w_lat ]
+.RB [ \-b
+.IR b_max ]
+.IR device ...
+.br
+.B elvtune \-h
+.br
+.B elvtune \-v
+.SH DESCRIPTION
+.B elvtune
+allows to tune the I/O elevator per blockdevice queue. The
+tuning can be safely done at runtime. Tuning the elevator means
+being able to change disk performance and interactiveness.
+In the output of
+.B elvtune
+the address of the queue tuned will be shown;
+it can be considered as a queue ID.
+Multiple partitions on the same harddisk will
+share the same queue and so tuning one partition will be
+like tuning the whole HD.
+.SH OPTIONS
+.TP
+.BI -r \ r_lat
+Set the maximum latency that the I/O scheduler will provide on
+each read.
+.TP
+.BI -w \ w_lat
+Set the maximum latency that the I/O scheduler will provide on
+each write.
+.TP
+.BI -b \ b_max
+Set the maximum coalescing factor allowed on writes when there are reads
+pending in the queue.
+.TP
+.BI -h
+Display help text and exit.
+.TP
+.BI -v
+Display version version information and exit.
+.SH NOTE
+Actually, the only fields tunable are those relative
+to the IO scheduler. It's not possible to select
+a one-way or two-way elevator yet.
+.PP
+For logical blockdevices like LVM the tuning has to
+be done on the
+.I physical
+devices. Tuning the queue of the LVM logical device
+is useless.
+.SH RETURN VALUE
+0 on success and 1 on failure.
+.SH HISTORY
+Ioctls for tuning elevator behaviour were added in Linux 2.3.99-pre1.
+.SH AUTHORS
+Andrea Arcangeli <andrea@suse.de> SuSE
+.SH AVAILABILITY
+The elvtune command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/elvtune.c b/disk-utils/elvtune.c
new file mode 100644
index 0000000..9f7fbf3
--- /dev/null
+++ b/disk-utils/elvtune.c
@@ -0,0 +1,174 @@
+/*
+ * elvtune.c - I/O elevator tuner
+ *
+ * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
+ *
+ * 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.
+ */
+
+/*
+ * 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 <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include "nls.h"
+#include "blkdev.h"
+#include "closestream.h"
+#include "linux_version.h"
+
+/* this has to match with the kernel structure */
+/* current version for ac19 and 2.2.16 */
+typedef struct blkelv_ioctl_arg_s {
+ int queue_ID;
+ int read_latency;
+ int write_latency;
+ int max_bomb_segments;
+} blkelv_ioctl_arg_t;
+
+static void
+usage(void) {
+ fprintf(stderr, "elvtune (%s)\n", PACKAGE_STRING);
+ fprintf(stderr, _("usage:\n"));
+ fprintf(stderr, "\telvtune [-r r_lat] [-w w_lat] [-b b_lat]"
+ " /dev/blkdev1 [/dev/blkdev2...]\n");
+ fprintf(stderr, "\telvtune -h\n");
+ fprintf(stderr, "\telvtune -v\n");
+ fprintf(stderr, _("\tNOTE: elvtune only works with 2.4 kernels\n"));
+ /* (ioctls exist in 2.2.16 - 2.5.57) */
+}
+
+static void
+version(void) {
+ fprintf(stderr, "elvtune (%s)\n", PACKAGE_STRING);
+}
+
+int
+main(int argc, char * argv[]) {
+ int read_value = 0xbeefbeef, write_value = 0xbeefbeef, bomb_value = 0xbeefbeef;
+ int read_set, write_set, bomb_set, set;
+ char * devname;
+ int fd;
+ blkelv_ioctl_arg_t elevator;
+
+ read_set = write_set = bomb_set = set = 0;
+
+ setlocale(LC_MESSAGES, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ for (;;) {
+ int opt;
+
+ opt = getopt(argc, argv, "r:w:b:hv");
+ if (opt == -1)
+ break;
+ switch (opt) {
+ case 'r':
+ read_value = atoi(optarg);
+ read_set = set = 1;
+ break;
+ case 'w':
+ write_value = atoi(optarg);
+ write_set = set = 1;
+ break;
+ case 'b':
+ bomb_value = atoi(optarg);
+ bomb_set = set = 1;
+ break;
+
+ case 'h':
+ usage(), exit(0);
+ case 'v':
+ version(), exit(0);
+
+ default:
+ usage(), exit(1);
+ }
+ }
+
+ if (optind >= argc)
+ fprintf(stderr, _("missing blockdevice, use -h for help\n")), exit(1);
+
+ while (optind < argc) {
+ devname = argv[optind++];
+
+ fd = open(devname, O_RDONLY|O_NONBLOCK);
+ if (fd < 0) {
+ perror("open");
+ break;
+ }
+
+ /* mmj: If we get EINVAL it's not a 2.4 kernel, so warn about
+ that and exit. It should return ENOTTY however, so check for
+ that as well in case it gets corrected in the future */
+
+ if (ioctl(fd, BLKELVGET, &elevator) < 0) {
+ int errsv = errno;
+ perror("ioctl get");
+ if ((errsv == EINVAL || errsv == ENOTTY) &&
+ get_linux_version() >= KERNEL_VERSION(2,5,58)) {
+ fprintf(stderr,
+ _("\nelvtune is only useful on older "
+ "kernels;\nfor 2.6 use IO scheduler "
+ "sysfs tunables instead..\n"));
+ }
+ break;
+ }
+
+ if (set) {
+ if (read_set)
+ elevator.read_latency = read_value;
+ if (write_set)
+ elevator.write_latency = write_value;
+ if (bomb_set)
+ elevator.max_bomb_segments = bomb_value;
+
+ if (ioctl(fd, BLKELVSET, &elevator) < 0) {
+ perror("ioctl set");
+ break;
+ }
+ if (ioctl(fd, BLKELVGET, &elevator) < 0) {
+ perror("ioctl reget");
+ break;
+ }
+ }
+
+ printf("\n%s elevator ID\t\t%d\n", devname, elevator.queue_ID);
+ printf("\tread_latency:\t\t%d\n", elevator.read_latency);
+ printf("\twrite_latency:\t\t%d\n", elevator.write_latency);
+ printf("\tmax_bomb_segments:\t%d\n\n", elevator.max_bomb_segments);
+
+ if (close(fd) < 0) {
+ perror("close");
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/disk-utils/fdformat.8 b/disk-utils/fdformat.8
new file mode 100644
index 0000000..d02241e
--- /dev/null
+++ b/disk-utils/fdformat.8
@@ -0,0 +1,65 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH FDFORMAT 8 "July 2011" "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\-n\fR, \fB\-\-no\-verify\fR
+Skip the verification that is normally performed after the formatting.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Output version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help and exit.
+.SH "SEE ALSO"
+.BR fd (4),
+.BR setfdprm (8),
+.BR mkfs (8),
+.BR emkfs (8)
+.SH AUTHOR
+Werner Almesberger (almesber@nessie.cs.id.ethz.ch)
+.SH AVAILABILITY
+The fdformat command is part of the util-linux package and is available from
+ftp://ftp.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..e937a8e
--- /dev/null
+++ b/disk-utils/fdformat.c
@@ -0,0 +1,168 @@
+/*
+ * 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 "closestream.h"
+#include "nls.h"
+#include "xalloc.h"
+
+struct floppy_struct param;
+
+#define SECTOR_SIZE 512
+
+static void format_disk(int ctrl)
+{
+ struct format_descr descr;
+ unsigned int track;
+
+ printf(_("Formatting ... "));
+ fflush(stdout);
+ if (ioctl(ctrl, FDFMTBEG, NULL) < 0)
+ err(EXIT_FAILURE, "ioctl: FDFMTBEG");
+ for (track = 0; track < param.track; track++) {
+ descr.track = track;
+ descr.head = 0;
+ if (ioctl(ctrl, FDFMTTRK, (long) &descr) < 0)
+ err(EXIT_FAILURE, "ioctl: FDFMTTRK");
+
+ printf("%3d\b\b\b", track);
+ fflush(stdout);
+ if (param.head == 2) {
+ descr.head = 1;
+ if (ioctl(ctrl, FDFMTTRK, (long)&descr) < 0)
+ err(EXIT_FAILURE, "ioctl: FDFMTTRK");
+ }
+ }
+ if (ioctl(ctrl, FDFMTEND, NULL) < 0)
+ err(EXIT_FAILURE, "ioctl: FDFMTEND");
+ printf(_("done\n"));
+}
+
+static void verify_disk(char *name)
+{
+ unsigned char *data;
+ unsigned int cyl;
+ int fd, cyl_size, count;
+
+ cyl_size = param.sect * param.head * 512;
+ data = xmalloc(cyl_size);
+ printf(_("Verifying ... "));
+ fflush(stdout);
+ if ((fd = open(name, O_RDONLY)) < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), name);
+ for (cyl = 0; cyl < param.track; cyl++) {
+ int read_bytes;
+
+ printf("%3d\b\b\b", cyl);
+ fflush(stdout);
+ read_bytes = read(fd, data, cyl_size);
+ if (read_bytes != cyl_size) {
+ if (read_bytes < 0)
+ perror(_("Read: "));
+ fprintf(stderr,
+ _("Problem reading cylinder %d,"
+ " expected %d, read %d\n"),
+ cyl, cyl_size, read_bytes);
+ free(data);
+ exit(EXIT_FAILURE);
+ }
+ for (count = 0; count < cyl_size; count++)
+ if (data[count] != FD_FILL_BYTE) {
+ printf(_("bad data in cyl %d\n"
+ "Continuing ... "), cyl);
+ fflush(stdout);
+ break;
+ }
+ }
+ free(data);
+ printf(_("done\n"));
+ if (close(fd) < 0)
+ err(EXIT_FAILURE, "close");
+}
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fprintf(out, _("Usage: %s [options] device\n"),
+ program_invocation_short_name);
+
+ fprintf(out, _("\nOptions:\n"
+ " -n, --no-verify disable the verification after the format\n"
+ " -V, --version output version information and exit\n"
+ " -h, --help display this help and exit\n\n"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int ch;
+ int ctrl;
+ int verify = 1;
+ struct stat st;
+
+ static const struct option longopts[] = {
+ {"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);
+ atexit(close_stdout);
+
+ while ((ch = getopt_long(argc, argv, "nVh", longopts, NULL)) != -1)
+ switch (ch) {
+ case 'n':
+ verify = 0;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ exit(EXIT_SUCCESS);
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage(stderr);
+ if (stat(argv[0], &st) < 0)
+ err(EXIT_FAILURE, _("stat failed %s"), 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]);
+ if (access(argv[0], W_OK) < 0)
+ err(EXIT_FAILURE, _("cannot access file %s"), argv[0]);
+
+ ctrl = open(argv[0], O_WRONLY);
+ 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);
+ format_disk(ctrl);
+ close(ctrl);
+
+ if (verify)
+ verify_disk(argv[0]);
+ return EXIT_SUCCESS;
+}
diff --git a/disk-utils/fsck.8 b/disk-utils/fsck.8
new file mode 100644
index 0000000..4cfeb1f
--- /dev/null
+++ b/disk-utils/fsck.8
@@ -0,0 +1,461 @@
+.\" -*- nroff -*-
+.\" 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 [ \-lrsAVRTMNP ]
+.RB [ \-C
+.RI [ fd ]]
+.RB [ \-t
+.IR fstype ]
+.RI [ filesys ...]
+.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 ext2 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
+.B /etc/fstab
+serially. This is equivalent to the
+.B \-As
+options.
+.PP
+The exit code 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
+Fsck canceled by user request
+.TP
+.B 128
+Shared-library error
+.PD
+.RE
+.PP
+The exit code returned when multiple filesystems are checked
+is the bit-wise OR of the exit codes 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
+.I /sbin
+first, then in
+.I /etc/fs
+and
+.IR /etc ,
+and finally in the directories listed in the PATH environment
+variable. Please see the filesystem-specific checker manual pages for
+further details.
+.SH OPTIONS
+.TP
+.B \-l
+Lock the whole-disk device by an exclusive
+.BR flock (2).
+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
+.B 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) -- this feature is
+not implemented yet.
+.TP
+.B \-r
+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
+.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
+.B /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
+.B /etc/fstab
+will be checked.
+.sp
+For example, if
+.B opts=ro
+appears in
+.IR fslist ,
+then only filesystems listed in
+.B /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 can not 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,
+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 detemine 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 --- 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
+.B /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
+.B \-C\fR [ \fI "fd" \fR ]
+Display completion/progress bars for those filesystem checkers (currently
+only for ext2 and ext3) which support them. Fsck 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 code 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
+.B fs-specific-options
+Options which are not understood by
+.B fsck
+are passed to the filesystem-specific checker. These arguments
+.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.
+.IP
+Options and arguments which follow the
+.B \-\-
+are treated as filesystem-specific options to be passed to the
+filesystem-specific checker.
+.IP
+Please note that fsck 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.
+.PP
+Options to different filesystem-specific fsck's are not standardized.
+If in doubt, please consult the man pages of the filesystem-specific
+checker. Although not guaranteed, the following options are supported
+by most filesystem checkers:
+.TP
+.B \-a
+Automatically repair the filesystem without any questions (use
+this option with caution). Note that
+.BR e2fsck (8)
+supports
+.B \-a
+for backward compatibility only. This option is mapped to
+.BR e2fsck 's
+.B \-p
+option which is safe to use, unlike the
+.B \-a
+option that some filesystem checkers support.
+.TP
+.B \-n
+For some filesystem-specific checkers, the
+.B \-n
+option will cause the fs-specific fsck to avoid attempting to repair any
+problems, but simply report such problems to stdout. This is however
+not true for all filesystem-specific checkers. In particular,
+.BR fsck.reiserfs (8)
+will not report any corruption if given this option.
+.BR fsck.minix (8)
+does not support the
+.B \-n
+option at all.
+.TP
+.B \-r
+Interactively repair the filesystem (ask for confirmations). Note: It
+is generally a bad idea to use this option if multiple fsck's are being
+run in parallel. Also note that this is
+.BR e2fsck 's
+default behavior; it supports this option for backward compatibility
+reasons only.
+.TP
+.B \-y
+For some filesystem-specific checkers, the
+.B \-y
+option will cause the fs-specific fsck to always attempt to fix any
+detected filesystem corruption automatically. Sometimes an expert may
+be able to do better driving the fsck manually. Note that
+.B not
+all filesystem-specific checkers implement this option. In particular
+.BR fsck.minix (8)
+and
+.BR fsck.cramfs (8)
+do not support the
+.B -y
+option as of this writing.
+.SH AUTHOR
+.MT tytso@mit.edu
+Theodore Ts'o
+.ME
+.SH AVAILABILITY
+The fsck command is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
+.SH FILES
+.IR /etc/fstab .
+.SH ENVIRONMENT VARIABLES
+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. A set of
+system directories are searched first:
+.BR /sbin ,
+.BR /sbin/fs.d ,
+.BR /sbin/fs ,
+.BR /etc/fs ,
+and
+.BR /etc .
+Then the set of directories found in the
+.B PATH
+environment are searched.
+.TP
+.B FSTAB_FILE
+This environment variable allows the system administrator
+to override the standard location of the
+.B /etc/fstab
+file. It is also useful for developers who are testing
+.BR fsck .
+.SH SEE ALSO
+.BR fstab (5),
+.BR mkfs (8),
+.BR fsck.ext2 (8)
+or
+.BR fsck.ext3 (8)
+or
+.BR e2fsck (8),
+.BR cramfsck (8),
+.BR fsck.minix (8),
+.BR fsck.msdos (8),
+.BR fsck.jfs (8),
+.BR fsck.nfs (8),
+.BR fsck.vfat (8),
+.BR fsck.xfs (8),
+.BR fsck.xiafs (8),
+.BR reiserfsck (8).
diff --git a/disk-utils/fsck.c b/disk-utils/fsck.c
new file mode 100644
index 0000000..28a1d70
--- /dev/null
+++ b/disk-utils/fsck.c
@@ -0,0 +1,1576 @@
+/*
+ * 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, 2012 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 "closestream.h"
+
+#define XALLOC_EXIT_CODE FSCK_EX_ERROR
+#include "xalloc.h"
+
+#ifndef DEFAULT_FSTYPE
+# define DEFAULT_FSTYPE "ext2"
+#endif
+
+#define MAX_DEVICES 32
+#define MAX_ARGS 32
+
+static const char *ignored_types[] = {
+ "ignore",
+ "iso9660",
+ "sw",
+ NULL
+};
+
+static const char *really_wanted[] = {
+ "minix",
+ "ext2",
+ "ext3",
+ "ext4",
+ "ext4dev",
+ "jfs",
+ "reiserfs",
+ "xiafs",
+ "xfs",
+ NULL
+};
+
+/*
+ * Internal structure for mount tabel 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 whole disk file descriptor or -1 */
+ 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 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;
+
+static const char fsck_prefix_path[] = FS_SEARCH_PATH;
+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;
+ else
+ return (int) l;
+}
+
+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 + 1) > sizeof(path))
+ return 0;
+
+ f = fopen(path, "r");
+ if (!f)
+ return 0;
+
+ rc = fscanf(f, "%d", &x);
+ if (rc != 1) {
+ if (ferror(f))
+ warn(_("failed to 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 *diskname;
+
+ if (!disk || is_irrotational_disk(disk))
+ return;
+
+ diskname = blkid_devno_to_devname(disk);
+ if (!diskname)
+ return;
+
+ if (verbose)
+ printf(_("Locking disk %s ... "), diskname);
+
+ inst->lock = open(diskname, O_CLOEXEC | O_RDONLY);
+ 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"));
+
+ free(diskname);
+ return;
+}
+
+static void unlock_disk(struct fsck_instance *inst)
+{
+ if (inst->lock >= 0) {
+ /* explicitly unlock, don't rely on close(), maybe some library
+ * (e.g. liblkid) has still open the device.
+ */
+ flock(inst->lock, LOCK_UN);
+ close(inst->lock);
+
+ inst->lock = -1;
+ }
+}
+
+static void free_instance(struct fsck_instance *i)
+{
+ if (lockdisk)
+ unlock_disk(i);
+ free(i->prog);
+ free(i);
+ return;
+}
+
+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)
+ return fs;
+
+ mnt_free_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;
+
+ type = mnt_get_fstype(device, &ambi, mnt_table_get_cache(fstab));
+ if (!ambi)
+ mnt_fs_set_fstype(fs, type);
+ }
+}
+
+static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)),
+ const char *filename, int line)
+{
+ warnx(_("%s: parse error at line %d -- ignore"), filename, line);
+ return 0;
+}
+
+/*
+ * 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 defauls if $FSTAB_FILE is not specified
+ */
+ path = getenv("FSTAB_FILE");
+
+ if (mnt_table_parse_fstab(fstab, path)) {
+ if (!path)
+ path = mnt_get_fstab_path();
+ 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 char *find_fsck(const char *type)
+{
+ char *s;
+ const char *tpl;
+ static char prog[256];
+ char *p = xstrdup(fsck_path);
+ struct stat st;
+
+ /* 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, ":")) {
+ sprintf(prog, tpl, s, type);
+ if (stat(prog, &st) == 0)
+ break;
+ }
+ free(p);
+
+ return(s ? prog : NULL);
+}
+
+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)
+{
+ double time_diff;
+
+ if (!inst || !report_stats || noexecute)
+ return;
+
+ time_diff = (inst->end_time.tv_sec - inst->start_time.tv_sec)
+ + (inst->end_time.tv_usec - inst->start_time.tv_usec) / 1E6;
+
+ fprintf(stdout, "%s: status %d, rss %ld, "
+ "real %f, user %d.%06d, sys %d.%06d\n",
+ fs_get_device(inst->fs),
+ inst->exit_status,
+ inst->rusage.ru_maxrss,
+ time_diff,
+ (int)inst->rusage.ru_utime.tv_sec,
+ (int)inst->rusage.ru_utime.tv_usec,
+ (int)inst->rusage.ru_stime.tv_sec,
+ (int)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 *type, struct libmnt_fs *fs, int interactive)
+{
+ char *s, *argv[80], prog[80];
+ int argc, i;
+ struct fsck_instance *inst, *p;
+ pid_t pid;
+
+ inst = xcalloc(1, sizeof(*inst));
+
+ sprintf(prog, "fsck.%s", type);
+ argv[0] = xstrdup(prog);
+ argc = 1;
+
+ for (i=0; i <num_args; i++)
+ argv[argc++] = xstrdup(args[i]);
+
+ if (progress) {
+ if ((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] = 0;
+
+ s = find_fsck(prog);
+ if (s == NULL) {
+ warnx(_("%s: not found"), prog);
+ free(inst);
+ return ENOENT;
+ }
+
+ if (verbose || noexecute) {
+ const char *tgt = mnt_fs_get_target(fs);
+
+ if (!tgt)
+ tgt = fs_get_device(fs);
+ printf("[%s (%d) -- %s] ", s, num_running, tgt);
+ for (i=0; i < argc; i++)
+ printf("%s ", argv[i]);
+ printf("\n");
+ }
+
+ 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(inst);
+ return errno;
+ } else if (pid == 0) {
+ if (!interactive)
+ close(0);
+ execv(s, argv);
+ err(FSCK_EX_ERROR, _("%s: execute failed"), s);
+ }
+
+ for (i=0; i < argc; i++)
+ free(argv[i]);
+
+ inst->pid = pid;
+ inst->prog = xstrdup(prog);
+ inst->type = xstrdup(type);
+ gettimeofday(&inst->start_time, NULL);
+ 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;
+ 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 = 0;
+#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(_("waidpid failed"));
+ continue;
+ }
+ for (prev = 0, 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;
+ gettimeofday(&inst->end_time, NULL);
+ 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") &&
+ strcmp(inst2->type, "ext3") &&
+ strcmp(inst2->type, "ext4") &&
+ strcmp(inst2->type, "ext4dev"))
+ 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(0) + 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)
+{
+ 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) &&
+ strncmp(fstype, "opts=", 5) && strncmp(fstype, "loop", 4) &&
+ !strchr(fstype, ','))
+ type = fstype;
+ else
+ type = DEFAULT_FSTYPE;
+
+ num_running++;
+ retval = execute(type, fs, interactive);
+ if (retval) {
+ warnx(_("error %d while executing fsck.%s for %s"),
+ retval, type, fs_get_device(fs));
+ num_running--;
+ return FSCK_EX_ERROR;
+ }
+ return 0;
+}
+
+
+/*
+ * Deal with the fsck -t argument.
+ */
+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 == 0 || cmp->list[0] == 0)
+ 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 **ip, *type;
+ int wanted = 0;
+
+ /*
+ * 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;
+
+ /* Do we really really want to check this fs? */
+ for(ip = really_wanted; *ip; ip++)
+ if (strcmp(type, *ip) == 0) {
+ wanted = 1;
+ break;
+ }
+
+ /* See if the <fsck.fs> program is available. */
+ if (find_fsck(type) == NULL) {
+ if (wanted)
+ 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)) != 0) {
+#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 != 0);
+
+ 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 skep 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)
+{
+ printf(_("\nUsage:\n"
+ " %s [fsck-options] [fs-options] [filesys ...]\n"),
+ program_invocation_short_name);
+
+ puts(_( "\nOptions:\n"
+ " -A check all filesystems\n"
+ " -R skip root filesystem; useful only with `-A'\n"
+ " -M do not check mounted filesystems\n"
+ " -t <type> specify filesystem types to be checked;\n"
+ " type is allowed to be comma-separated list\n"
+ " -P check filesystems in parallel, including root\n"
+ " -r report statistics for each device fsck\n"
+ " -s serialize fsck operations\n"
+ " -l lock the device using flock()\n"
+ " -N do not execute, just show what would be done\n"
+ " -T do not show the title on startup\n"
+ " -C <fd> display progress bar; file descriptor is for GUIs\n"
+ " -V explain what is being done\n"
+ " -? display this help and exit\n\n"
+ "See fsck.* commands for fs-options."));
+
+ exit(FSCK_EX_USAGE);
+}
+
+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 = 0;
+ char options[128];
+ int opt = 0;
+ int opts_for_fsck = 0;
+ struct sigaction sa;
+
+ /*
+ * Set up signal action
+ */
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = signal_cancel;
+ sigaction(SIGINT, &sa, 0);
+ sigaction(SIGTERM, &sa, 0);
+
+ num_devices = 0;
+ num_args = 0;
+ instance_list = 0;
+
+ for (i=1; i < argc; i++) {
+ arg = argv[i];
+ if (!arg)
+ continue;
+ 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]) {
+ progress_fd = string_to_int(arg+j+1);
+ if (progress_fd < 0)
+ progress_fd = 0;
+ else
+ goto next_arg;
+ } else if ((i+1) < argc &&
+ !strncmp(argv[i+1], "-", 1) == 0) {
+ progress_fd = string_to_int(argv[i]);
+ 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;
+ break;
+ case 's':
+ serialize = 1;
+ break;
+ case 't':
+ tmp = 0;
+ if (fstype)
+ usage();
+ if (arg[j+1])
+ tmp = arg+j+1;
+ else if ((i+1) < argc)
+ tmp = argv[++i];
+ else
+ usage();
+ 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;
+ }
+ }
+ 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;
+ char *oldpath = getenv("PATH");
+ struct libmnt_fs *fs;
+
+ setvbuf(stdout, NULL, _IONBF, BUFSIZ);
+ setvbuf(stderr, NULL, _IONBF, BUFSIZ);
+
+ setlocale(LC_MESSAGES, "");
+ setlocale(LC_CTYPE, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ mnt_init_debug(0); /* init libmount debug mask */
+ mntcache = mnt_new_cache(); /* no fatal error if failed */
+
+ parse_argv(argc, argv);
+
+ if (!notitle)
+ printf(_("%s from %s\n"), program_invocation_short_name, PACKAGE_STRING);
+
+ load_fs_info();
+
+ /* Update our search path to include uncommon directories. */
+ if (oldpath) {
+ fsck_path = xmalloc (strlen (fsck_prefix_path) + 1 +
+ strlen (oldpath) + 1);
+ strcpy (fsck_path, fsck_prefix_path);
+ strcat (fsck_path, ":");
+ strcat (fsck_path, oldpath);
+ } else {
+ fsck_path = xstrdup(fsck_prefix_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_free_cache(mntcache);
+ mnt_free_table(fstab);
+ mnt_free_table(mtab);
+ return status;
+}
diff --git a/disk-utils/fsck.cramfs.c b/disk-utils/fsck.cramfs.c
new file mode 100644
index 0000000..04f970f
--- /dev/null
+++ b/disk-utils/fsck.cramfs.c
@@ -0,0 +1,692 @@
+/*
+ * 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)
+ */
+
+/* compile-time options */
+//#define INCLUDE_FS_TESTS /* include cramfs checking and extraction */
+
+#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 <utime.h>
+#include <fcntl.h>
+#include <zlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/sysmacros.h> /* for major, minor */
+
+#include "cramfs.h"
+#include "nls.h"
+#include "blkdev.h"
+#include "c.h"
+#include "exitcodes.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 */
+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) */
+
+char *extract_dir = ""; /* extraction directory (-x) */
+
+#define PAD_SIZE 512
+
+#ifdef INCLUDE_FS_TESTS
+
+static int opt_extract = 0; /* extract cramfs (-x) */
+
+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 *);
+#endif /* INCLUDE_FS_TESTS */
+
+static char *outbuffer;
+
+static size_t page_size;
+
+/* Input status of 0 to print help and exit without an error. */
+static void __attribute__((__noreturn__)) usage(int status)
+{
+ FILE *stream = status ? stderr : stdout;
+
+ fputs(USAGE_HEADER, stream);
+ fprintf(stream,
+ _(" %s [options] file\n"), program_invocation_short_name);
+ fputs(USAGE_OPTIONS, stream);
+ fputs(_(" -x, --destination <dir> extract into directory\n"), stream);
+ fputs(_(" -v, --verbose be more verbose\n"), stream);
+ fputs(USAGE_HELP, stream);
+ fputs(USAGE_VERSION, stream);
+ exit(status);
+}
+
+static int get_superblock_endianness(uint32_t magic)
+{
+ if (magic == CRAMFS_MAGIC) {
+ cramfs_is_big_endian = HOST_IS_BIG_ENDIAN;
+ return 0;
+ } else if (magic ==
+ u32_toggle_endianness(!HOST_IS_BIG_ENDIAN, CRAMFS_MAGIC)) {
+ cramfs_is_big_endian = !HOST_IS_BIG_ENDIAN;
+ return 0;
+ } else
+ 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 failed %s"), 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, _("read failed: %s"), filename);
+ if (get_superblock_endianness(super.magic) != -1)
+ *start = 0;
+ else if (*length >= (PAD_SIZE + sizeof(super))) {
+ lseek(fd, PAD_SIZE, SEEK_SET);
+ if (read(fd, &super, sizeof(super)) != sizeof(super))
+ err(FSCK_EX_ERROR, _("read failed: %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"));
+
+ if (super.size < page_size)
+ 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)
+ fprintf(stderr,
+ _("warning: file extends past end of filesystem\n"));
+ } else
+ fprintf(stderr, _("warning: old cramfs format\n"));
+}
+
+static void test_crc(int start)
+{
+ void *buf;
+ uint32_t crc;
+
+ if (!(super.flags & CRAMFS_FLAG_FSID_VERSION_2)) {
+#ifdef INCLUDE_FS_TESTS
+ return;
+#else
+ errx(FSCK_EX_USAGE, _("unable to test CRC: old cramfs format"));
+#endif
+ }
+
+ crc = crc32(0L, Z_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) {
+ lseek(fd, 0, SEEK_SET);
+ if (read(fd, buf, super.size) < 0)
+ err(FSCK_EX_ERROR, _("read failed: %s"), filename);
+ }
+ }
+ if (buf != MAP_FAILED) {
+ ((struct cramfs_super *)(buf + start))->fsid.crc =
+ crc32(0L, Z_NULL, 0);
+ crc = crc32(crc, buf + start, super.size - start);
+ munmap(buf, super.size);
+ } else {
+ int retval;
+ size_t length = 0;
+
+ buf = xmalloc(4096);
+ lseek(fd, start, SEEK_SET);
+ for (;;) {
+ retval = read(fd, buf, 4096);
+ if (retval < 0)
+ err(FSCK_EX_ERROR, _("read failed: %s"), filename);
+ else if (retval == 0)
+ break;
+ if (length == 0)
+ ((struct cramfs_super *)buf)->fsid.crc =
+ crc32(0L, Z_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"));
+}
+
+#ifdef INCLUDE_FS_TESTS
+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) {
+ read_buffer_block = block;
+ lseek(fd, block << ROMBUFFER_BITS, SEEK_SET);
+ read(fd, read_buffer, ROMBUFFERSIZE * 2);
+ }
+ 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, int len)
+{
+ int err;
+
+ stream.next_in = src;
+ stream.avail_in = len;
+
+ stream.next_out = (unsigned char *)outbuffer;
+ stream.avail_out = page_size * 2;
+
+ inflateReset(&stream);
+
+ if (len > page_size * 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 %p(%d): %s"),
+ zError(err), src, len);
+ return stream.total_out;
+}
+
+#if !HAVE_LCHOWN
+#define lchown chown
+#endif
+
+static void do_uncompress(char *path, int fd, unsigned long offset,
+ unsigned long size)
+{
+ unsigned long curr = offset + 4 * ((size + page_size - 1) / page_size);
+
+ do {
+ unsigned long out = page_size;
+ 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 %ld (%zd)\n"), curr,
+ page_size);
+ if (size < page_size)
+ out = size;
+ memset(outbuffer, 0x00, out);
+ } else {
+ if (opt_verbose > 1)
+ printf(_(" uncompressing block at %ld to %ld (%ld)\n"),
+ curr, next, next - curr);
+ out = uncompress_block(romfs_read(curr), next - curr);
+ }
+ if (size >= page_size) {
+ if (out != page_size)
+ 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 (opt_extract)
+ if (write(fd, 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)
+{
+ struct utimbuf epoch = { 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 (utime(path, &epoch) < 0)
+ err(FSCK_EX_ERROR, _("utime 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 (opt_extract) {
+ 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 fd = 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 (opt_extract) {
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, i->mode);
+ if (fd < 0)
+ err(FSCK_EX_ERROR, _("cannot open %s"), path);
+ }
+ if (i->size)
+ do_uncompress(path, fd, offset, i->size);
+ if (opt_extract) {
+ close(fd);
+ 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 %ld to %ld (%ld)\n"),
+ curr, next, next - curr);
+ free(str);
+ }
+ if (opt_extract) {
+ 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 (opt_extract) {
+ 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 (%ld) < sizeof(struct cramfs_super) + start (%ld)"),
+ start_data, sizeof(struct cramfs_super) + start);
+ if (end_dir != start_data)
+ errx(FSCK_EX_UNCORRECTED,
+ _("directory data end (%ld) != file data start (%ld)"),
+ end_dir, start_data);
+ }
+ if (super.flags & CRAMFS_FLAG_FSID_VERSION_2)
+ if (end_data > super.size)
+ errx(FSCK_EX_UNCORRECTED, _("invalid file data offset"));
+
+ iput(root); /* free(root) */
+}
+#endif /* INCLUDE_FS_TESTS */
+
+int main(int argc, char **argv)
+{
+ int c; /* for getopt */
+ int start = 0;
+ size_t length = 0;
+
+ static const struct option longopts[] = {
+ {"destination", required_argument, 0, 'x'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {NULL, no_argument, 0, '0'},
+ };
+
+ setlocale(LC_MESSAGES, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ page_size = getpagesize();
+
+ outbuffer = xmalloc(page_size * 2);
+
+ /* command line options */
+ while ((c = getopt_long(argc, argv, "x:vVh", longopts, NULL)) != EOF)
+ switch (c) {
+ case 'h':
+ usage(FSCK_EX_OK);
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'x':
+#ifdef INCLUDE_FS_TESTS
+ opt_extract = 1;
+ extract_dir = optarg;
+ break;
+#else
+ errx(FSCK_EX_USAGE, _("compiled without -x support"));
+#endif
+ case 'v':
+ opt_verbose++;
+ break;
+ default:
+ usage(FSCK_EX_USAGE);
+ }
+
+ if ((argc - optind) != 1)
+ usage(FSCK_EX_USAGE);
+ filename = argv[optind];
+
+ test_super(&start, &length);
+ test_crc(start);
+#ifdef INCLUDE_FS_TESTS
+ test_fs(start);
+#endif
+
+ 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..e84b82b
--- /dev/null
+++ b/disk-utils/fsck.minix.8
@@ -0,0 +1,136 @@
+.\" Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be freely distributed.
+.\" " for hilit19
+.TH FSCK 8 "July 1996" "util-linux" "System Administration"
+.SH NAME
+fsck.minix \- check consistency of Minix filesystem
+.SH SYNOPSIS
+.B fsck.minix
+.RB [ \-larvsmf ]
+.I device
+.SH DESCRIPTION
+.B fsck.minix
+performs a consistency check for the Linux MINIX filesystem. The current
+version supports the 14 character and 30 character filename options.
+
+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 (and remember that the kernel can write to it when it
+searches for files).
+
+The \fIdevice\fR name will usually have the following form:
+.nf
+.RS
+/dev/hda[1-63] (IDE disk 1)
+/dev/hdb[1-63] (IDE disk 2)
+/dev/sda[1-15] (SCSI disk 1)
+/dev/sdb[1-15] (SCSI disk 2)
+.RE
+.fi
+
+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. Since Linux does not currently have raw
+devices, there is
+.I no
+need to reboot at this time.
+.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 (i.e., the root filesystem), make sure nothing is
+writing to the disk, and that no files are "zombies" waiting for deletion.
+.SH OPTIONS
+.TP
+.B \-l
+List all filenames.
+.TP
+.B \-r
+Perform interactive repairs.
+.TP
+.B \-a
+Perform automatic repairs. (This option implies
+.B \-r
+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
+.B \-v
+Be verbose.
+.TP
+.B \-s
+Output super-block information.
+.TP
+.B \-m
+Activate MINIX-like "mode not cleared" warnings.
+.TP
+.B \-f
+Force a filesystem check even if the filesystem was marked as valid (this
+marking is done by the kernel when the filesystem is unmounted).
+.SH "SEE ALSO"
+.BR fsck (8),
+.BR fsck.ext (8),
+.BR fsck.ext2 (8),
+.BR fsck.xiafs (8),
+.BR mkfs (8),
+.BR mkfs.minix (8),
+.BR mkfs.ext (8),
+.BR mkfs.ext2 (8),
+.BR mkfs.xiafs (8),
+.BR reboot (8)
+.SH DIAGNOSTICS
+There are numerous diagnostic messages. The ones mentioned here are the
+most commonly seen in normal usage.
+
+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 CODES"
+The exit code returned by
+.B fsck.minix
+is the sum of the following:
+.IP 0
+No errors
+.IP 3
+Filesystem errors corrected, system should be rebooted if filesystem was
+mounted
+.IP 4
+Filesystem errors left uncorrected
+.IP 8
+Operational error
+.IP 16
+Usage or syntax error
+.PP
+In point of fact, only 0, 3, 4, 7, 8, and 16 can ever be returned.
+.SH AUTHOR
+Linus Torvalds (torvalds@cs.helsinki.fi)
+.br
+Error code values by Rik Faith (faith@cs.unc.edu)
+.br
+Added support for filesystem valid flag: Dr. Wettstein
+(greg%wind.uucp@plains.nodak.edu)
+.br
+Check to prevent fsck of mounted filesystem added by Daniel Quinlan
+(quinlan@yggdrasil.com)
+.br
+Minix v2 fs support by Andreas Schwab
+(schwab@issan.informatik.uni-dortmund.de), updated by Nicolai
+Langfeldt (janl@math.uio.no)
+.br
+Portability patch by Russell King (rmk@ecs.soton.ac.uk).
+.SH AVAILABILITY
+The fsck.minix command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/disk-utils/fsck.minix.c b/disk-utils/fsck.minix.c
new file mode 100644
index 0000000..f24a9f2
--- /dev/null
+++ b/disk-utils/fsck.minix.c
@@ -0,0 +1,1391 @@
+/*
+ * 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 rudimetary 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).
+ *
+ * Usuage: fsck [-larvsm] device
+ * -l for a listing of all the filenames
+ * -a for automatic repairs (not implemented)
+ * -r for repairs (interactive) (not implemented)
+ * -v for verbose (tells how many files)
+ * -s for super-block info
+ * -m for minix-like "mode not cleared" warnings
+ * -f force filesystem check even if filesystem marked as valid
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ */
+
+#include <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 <mntent.h>
+#include <sys/stat.h>
+#include <signal.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"
+
+#define ROOT_INO 1
+#define YESNO_LENGTH 64
+
+/* Global variables used in minix_programs.h inline fuctions */
+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 IN;
+static int repair, automatic, verbose, list, show, warn_mode, force;
+static int directory, regular, blockdev, chardev, links, symlinks, total;
+
+static int changed; /* flags if the filesystem has been changed */
+static int errors_uncorrected; /* flag if some error was not corrected */
+static 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];
+
+#define MAGIC (Super.s_magic)
+
+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(0, 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
+leave(int status) {
+ reset();
+ exit(status);
+}
+
+static void
+usage(void) {
+ fputs(USAGE_HEADER, stderr);
+ fprintf(stderr,
+ _(" %s [options] <device>\n"), program_invocation_short_name);
+ fputs(USAGE_OPTIONS, stderr);
+ fputs(_(" -l list all filenames\n"), stderr);
+ fputs(_(" -a automatic repair\n"), stderr);
+ fputs(_(" -r interactive repair\n"), stderr);
+ fputs(_(" -v be verbose\n"), stderr);
+ fputs(_(" -s output super-block information\n"), stderr);
+ fputs(_(" -m activate mode not cleared warnings\n"), stderr);
+ fputs(_(" -f force check\n"), stderr);
+ fputs(USAGE_SEPARATOR, stderr);
+ fputs(USAGE_VERSION, stderr);
+ fprintf(stderr, USAGE_MAN_TAIL("fsck.minix(8)"));
+ leave(FSCK_EX_USAGE);
+}
+
+static void die(const char *fmt, ...)
+ __attribute__ ((__format__(__printf__, 1, 2)));
+
+static void
+die(const char *fmt, ...) {
+ va_list ap;
+
+ fprintf(stderr, "%s: ", program_invocation_short_name);
+ 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 -1:
+ /* def = def */
+ break;
+ case 0:
+ case 1:
+ 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(0) && isatty(1))
+ cont = ask(_("Do you really want to continue"), 0);
+ else
+ cont = 0;
+ if (!cont) {
+ printf(_("check aborted.\n"));
+ exit(FSCK_EX_OK);
+ }
+ return;
+}
+
+/* check_zone_nr checks to see that *nr is a valid zone nr. If it isn't, it
+ * will possibly be repaired. Check_zone_nr sets *corrected if an error was
+ * corrected, and returns the zone (0 for no zone or a bad zone-number). */
+static int
+check_zone_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(IN, 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(IN, 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(IN, MINIX_BLOCK_SIZE * nr, SEEK_SET))
+ die(_("seek failed in write_block"));
+ if (MINIX_BLOCK_SIZE != write(IN, 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;
+
+ if (blknr < 7)
+ return check_zone_nr(inode->i_zone + blknr, &changed);
+ blknr -= 7;
+ if (blknr < 512) {
+ block = check_zone_nr(inode->i_zone + 7, &changed);
+ read_block(block, (char *)ind);
+ blk_chg = 0;
+ result = check_zone_nr(blknr + ind, &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *)ind);
+ return result;
+ }
+ blknr -= 512;
+ block = check_zone_nr(inode->i_zone + 8, &changed);
+ read_block(block, (char *)dind);
+ blk_chg = 0;
+ result = check_zone_nr(dind + (blknr / 512), &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *)dind);
+ block = result;
+ read_block(block, (char *)ind);
+ blk_chg = 0;
+ result = check_zone_nr(ind + (blknr % 512), &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *)ind);
+ return result;
+}
+
+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) {
+ /* 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(IN, MINIX_BLOCK_SIZE, SEEK_SET))
+ die(_("seek failed in write_super_block"));
+ if (MINIX_BLOCK_SIZE != write(IN, super_block_buffer, MINIX_BLOCK_SIZE))
+ die(_("unable to write super-block"));
+ return;
+}
+
+static void
+write_tables(void) {
+ write_super_block();
+ unsigned long buffsz = get_inode_buffer_size();
+ unsigned long imaps = get_nimaps();
+ unsigned long zmaps = get_nzmaps();
+
+ if (write_all(IN, inode_map, imaps * MINIX_BLOCK_SIZE))
+ die(_("Unable to write inode map"));
+
+ if (write_all(IN, zone_map, zmaps * MINIX_BLOCK_SIZE))
+ die(_("Unable to write zone map"));
+
+ if (write_all(IN, 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)
+ 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(IN, 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(IN, super_block_buffer, MINIX_BLOCK_SIZE))
+ die(_("unable to read super block"));
+ if (MAGIC == MINIX_SUPER_MAGIC) {
+ namelen = 14;
+ dirsize = 16;
+ fs_version = 1;
+ } else if (MAGIC == MINIX_SUPER_MAGIC2) {
+ namelen = 30;
+ dirsize = 32;
+ fs_version = 1;
+ } else if (MAGIC == MINIX2_SUPER_MAGIC) {
+ namelen = 14;
+ dirsize = 16;
+ fs_version = 2;
+ } else if (MAGIC == MINIX2_SUPER_MAGIC2) {
+ namelen = 30;
+ dirsize = 32;
+ fs_version = 2;
+ } 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_nimaps() * MINIX_BLOCK_SIZE * 8 < get_ninodes() + 1)
+ die(_("bad s_imap_blocks 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();
+ unsigned long buffsz = get_inode_buffer_size();
+ unsigned long norm_first_zone = first_zone_data();
+ unsigned long 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(IN, inode_map, imaps * MINIX_BLOCK_SIZE);
+ if (rc < 0 || imaps * MINIX_BLOCK_SIZE != (size_t) rc)
+ die(_("Unable to read inode map"));
+
+ rc = read(IN, zone_map, zmaps * MINIX_BLOCK_SIZE);
+ if (rc < 0 || zmaps * MINIX_BLOCK_SIZE != (size_t) rc)
+ die(_("Unable to read zone map"));
+
+ rc = read(IN, 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=%ld (%ld)\n"), first_zone, norm_first_zone);
+ printf(_("Zonesize=%d\n"), MINIX_BLOCK_SIZE << get_zone_size());
+ printf(_("Maxsize=%ld\n"), get_max_size());
+ 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];
+ 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)
+ strncpy(name_list[name_depth], name, namelen);
+ name_depth++;
+ inode = get_inode(ino);
+ name_depth--;
+ if (!offset) {
+ if (!inode || strcmp(".", name)) {
+ 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)) {
+ 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)
+ strncpy(name_list[name_depth], name, namelen);
+ 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--;
+ return;
+}
+
+static void
+check_file2(struct minix2_inode *dir, unsigned int offset) {
+ static char blk[MINIX_BLOCK_SIZE];
+ struct minix2_inode *inode;
+ unsigned long ino;
+ char *name;
+ int block;
+
+ block = map_block2(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)
+ strncpy(name_list[name_depth], name, namelen);
+ name_depth++;
+ inode = get_inode2(ino);
+ name_depth--;
+ if (!offset) {
+ if (!inode || strcmp(".", name)) {
+ 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)) {
+ 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("%6zd %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_zones2(ino);
+ if (inode && S_ISDIR(inode->i_mode))
+ recursive_check2(ino);
+ name_depth--;
+ return;
+}
+
+static void
+recursive_check(unsigned int ino) {
+ struct minix_inode *dir;
+ unsigned int offset;
+
+ dir = Inode + ino;
+ if (!S_ISDIR(dir->i_mode))
+ die(_("internal error"));
+ if (dir->i_size < 2 * dirsize) {
+ get_current_name();
+ printf(_("%s: bad directory: size < 32"), current_name);
+ errors_uncorrected = 1;
+ }
+ for (offset = 0; offset < dir->i_size; offset += dirsize)
+ check_file(dir, offset);
+}
+
+static void
+recursive_check2(unsigned int ino) {
+ struct minix2_inode *dir;
+ unsigned int offset;
+
+ dir = Inode2 + ino;
+ if (!S_ISDIR(dir->i_mode))
+ die(_("internal error"));
+ if (dir->i_size < 2 * dirsize) {
+ 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(IN, MINIX_BLOCK_SIZE * i, SEEK_SET))
+ die(_("seek failed in bad_zone"));
+ return (MINIX_BLOCK_SIZE != read(IN, 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 = 0;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ if (argc == 2 &&
+ (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version"))) {
+ printf(UTIL_LINUX_VERSION);
+ exit(FSCK_EX_OK);
+ }
+
+ 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 (argc-- > 1) {
+ argv++;
+ if (argv[0][0] != '-') {
+ if (device_name)
+ usage();
+ else
+ device_name = argv[0];
+ } else
+ while (*++argv[0])
+ switch (argv[0][0]) {
+ case 'l':
+ list = 1;
+ break;
+ case 'a':
+ automatic = 1;
+ repair = 1;
+ break;
+ case 'r':
+ automatic = 0;
+ repair = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 's':
+ show = 1;
+ break;
+ case 'm':
+ warn_mode = 1;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ if (!device_name)
+ usage();
+ check_mount(); /* trying to check a mounted filesystem? */
+ if (repair && !automatic) {
+ if (!isatty(0) || !isatty(1))
+ die(_("need terminal for interactive repairs"));
+ }
+ IN = open(device_name, repair ? O_RDWR : O_RDONLY);
+ if (IN < 0)
+ 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 (!(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;
+ } else if (force)
+ printf(_("Forcing filesystem check on %s.\n"), device_name);
+ else if (repair)
+ printf(_("Filesystem on %s is dirty, needs checking.\n"),
+ device_name);
+
+ read_tables();
+
+ /* 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(0, &termios);
+ tmp = termios;
+ tmp.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(0, TCSANOW, &tmp);
+ termios_set = 1;
+ }
+
+ if (fs_version == 2) {
+ check_root2();
+ check2();
+ } else {
+ check_root();
+ check();
+ }
+ if (verbose) {
+ unsigned long i, free;
+
+ for (i = 1, free = 0; i <= get_ninodes(); i++)
+ if (!inode_in_use(i))
+ free++;
+ printf(_("\n%6ld inodes used (%ld%%)\n"),
+ (get_ninodes() - free),
+ 100 * (get_ninodes() - free) / get_ninodes());
+ for (i = get_first_zone(), free = 0; i < get_nzones(); i++)
+ if (!zone_in_use(i))
+ 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(0, TCSANOW, &termios);
+
+ 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..6cbc1ff
--- /dev/null
+++ b/disk-utils/isosize.8
@@ -0,0 +1,35 @@
+.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
+.\" Add any additional description here
+.PP
+This command outputs the length of an iso9660 file system that
+is contained in the given file. That file may be a normal file or
+a block device (e.g. /dev/hdd or /dev/sr0). In the absence of
+any switches (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.
diff --git a/disk-utils/isosize.c b/disk-utils/isosize.c
new file mode 100644
index 0000000..e5defae
--- /dev/null
+++ b/disk-utils/isosize.c
@@ -0,0 +1,219 @@
+/*
+ * 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"
+
+#define ISODCL(from, to) (to - from + 1)
+
+static int isonum_721(unsigned char *p)
+{
+ return ((p[0] & 0xff)
+ | ((p[1] & 0xff) << 8));
+}
+
+static int isonum_722(unsigned char *p)
+{
+ return ((p[1] & 0xff)
+ | ((p[0] & 0xff) << 8));
+}
+
+static int isonum_723(unsigned char *p, int xflag)
+{
+ int le = isonum_721(p);
+ int be = isonum_722(p + 2);
+ if (xflag && le != be)
+ /* translation is useless */
+ warnx("723error: le=%d be=%d", le, be);
+ return (le);
+}
+
+static int isonum_731(unsigned char *p)
+{
+ return ((p[0] & 0xff)
+ | ((p[1] & 0xff) << 8)
+ | ((p[2] & 0xff) << 16)
+ | ((p[3] & 0xff) << 24));
+}
+
+static int isonum_732(unsigned char *p)
+{
+ return ((p[3] & 0xff)
+ | ((p[2] & 0xff) << 8)
+ | ((p[1] & 0xff) << 16)
+ | ((p[0] & 0xff) << 24));
+}
+
+static int isonum_733(unsigned char *p, int xflag)
+{
+ int le = isonum_731(p);
+ int be = isonum_732(p + 4);
+ if (xflag && le != be)
+ /* translation is useless */
+ warn("733error: le=%d be=%d", le, be);
+ return (le);
+}
+
+struct iso_primary_descriptor
+{
+ unsigned char type [ISODCL ( 1, 1)]; /* 711 */
+ unsigned char id [ISODCL ( 2, 6)];
+ unsigned char version [ISODCL ( 7, 7)]; /* 711 */
+ unsigned char unused1 [ISODCL ( 8, 8)];
+ unsigned char system_id [ISODCL ( 9, 40)]; /* auchars */
+ unsigned char volume_id [ISODCL ( 41, 72)]; /* duchars */
+ unsigned char unused2 [ISODCL ( 73, 80)];
+ unsigned char volume_space_size [ISODCL ( 81, 88)]; /* 733 */
+ unsigned char unused3 [ISODCL ( 89, 120)];
+ unsigned char volume_set_size [ISODCL ( 121, 124)]; /* 723 */
+ unsigned char volume_sequence_number [ISODCL ( 125, 128)]; /* 723 */
+ unsigned char logical_block_size [ISODCL ( 129, 132)]; /* 723 */
+ unsigned char path_table_size [ISODCL ( 133, 140)]; /* 733 */
+ unsigned char type_l_path_table [ISODCL ( 141, 144)]; /* 731 */
+ unsigned char opt_type_l_path_table [ISODCL ( 145, 148)]; /* 731 */
+ unsigned char type_m_path_table [ISODCL ( 149, 152)]; /* 732 */
+ unsigned char opt_type_m_path_table [ISODCL ( 153, 156)]; /* 732 */
+ unsigned char root_directory_record [ISODCL ( 157, 190)]; /* 9.1 */
+ unsigned char volume_set_id [ISODCL ( 191, 318)]; /* duchars */
+ unsigned char publisher_id [ISODCL ( 319, 446)]; /* achars */
+ unsigned char preparer_id [ISODCL ( 447, 574)]; /* achars */
+ unsigned char application_id [ISODCL ( 575, 702)]; /* achars */
+ unsigned char copyright_file_id [ISODCL ( 703, 739)]; /* 7.5 dchars */
+ unsigned char abstract_file_id [ISODCL ( 740, 776)]; /* 7.5 dchars */
+ unsigned char bibliographic_file_id [ISODCL ( 777, 813)]; /* 7.5 dchars */
+ unsigned char creation_date [ISODCL ( 814, 830)]; /* 8.4.26.1 */
+ unsigned char modification_date [ISODCL ( 831, 847)]; /* 8.4.26.1 */
+ unsigned char expiration_date [ISODCL ( 848, 864)]; /* 8.4.26.1 */
+ unsigned char effective_date [ISODCL ( 865, 881)]; /* 8.4.26.1 */
+ unsigned char file_structure_version [ISODCL ( 882, 882)]; /* 711 */
+ unsigned char unused4 [ISODCL ( 883, 883)];
+ unsigned char application_data [ISODCL ( 884, 1395)];
+ unsigned char unused5 [ISODCL (1396, 2048)];
+};
+
+static void isosize(char *filenamep, int xflag, long divisor)
+{
+ int fd, nsecs, ssize;
+ struct iso_primary_descriptor ipd;
+
+ if ((fd = open(filenamep, O_RDONLY)) < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), filenamep);
+
+ if (lseek(fd, 16 << 11, 0) == (off_t) - 1)
+ err(EXIT_FAILURE, _("seek error on %s"), filenamep);
+
+ if (read(fd, &ipd, sizeof(ipd)) < 0)
+ err(EXIT_FAILURE, _("read error on %s"), filenamep);
+
+ nsecs = isonum_733(ipd.volume_space_size, xflag);
+ /* isonum_723 returns nowadays always 2048 */
+ ssize = isonum_723(ipd.logical_block_size, xflag);
+
+ 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);
+ }
+
+ close(fd);
+}
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fprintf(out, _("\nUsage:\n"
+ " %s [options] iso9660_image_file\n"),
+ program_invocation_short_name);
+
+ fprintf(out, _("\nOptions:\n"
+ " -d, --divisor=NUM divide bytes NUM\n"
+ " -x, --sectors show sector count and size\n"
+ " -V, --version output version information and exit\n"
+ " -H, --help display this help and exit\n\n"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int j, ct, opt, xflag = 0;
+ long divisor = 0;
+
+ static const struct option longopts[] = {
+ {"divisor", no_argument, 0, 'd'},
+ {"sectors", no_argument, 0, 'x'},
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {NULL, 0, 0, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ 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':
+ printf(_("%s (%s)\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ ct = argc - optind;
+
+ if (ct <= 0)
+ usage(stderr);
+
+ for (j = optind; j < argc; j++) {
+ if (ct > 1)
+ printf("%s: ", argv[j]);
+ isosize(argv[j], xflag, divisor);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/disk-utils/minix_programs.h b/disk-utils/minix_programs.h
new file mode 100644
index 0000000..4af1a34
--- /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 (unsigned long)Super.s_ninodes;
+ }
+}
+
+static inline unsigned long get_nzones(void)
+{
+ switch (fs_version) {
+ case 3:
+ return (unsigned long)Super3.s_zones;
+ case 2:
+ return (unsigned long)Super.s_zones;
+ default:
+ return (unsigned long)Super.s_nzones;
+ }
+}
+
+static inline unsigned long get_nimaps(void)
+{
+ switch (fs_version) {
+ case 3:
+ return (unsigned long)Super3.s_imap_blocks;
+ default:
+ return (unsigned long)Super.s_imap_blocks;
+ }
+}
+
+static inline unsigned long get_nzmaps(void)
+{
+ switch (fs_version) {
+ case 3:
+ return (unsigned long)Super3.s_zmap_blocks;
+ default:
+ return (unsigned long)Super.s_zmap_blocks;
+ }
+}
+
+static inline unsigned long get_first_zone(void)
+{
+ switch (fs_version) {
+ case 3:
+ return (unsigned long)Super3.s_firstdatazone;
+ default:
+ return (unsigned long)Super.s_firstdatazone;
+ }
+}
+
+static inline unsigned long get_zone_size(void)
+{
+ switch (fs_version) {
+ case 3:
+ return (unsigned long)Super3.s_log_zone_size;
+ default:
+ return (unsigned long)Super.s_log_zone_size;
+ }
+}
+
+static inline unsigned long get_max_size(void)
+{
+ switch (fs_version) {
+ case 3:
+ return (unsigned long)Super3.s_max_size;
+ default:
+ return (unsigned long)Super.s_max_size;
+ }
+}
+
+static 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 unsigned long first_zone_data(void)
+{
+ return 2 + get_nimaps() + get_nzmaps() + inode_blocks();
+}
+
+static inline unsigned long 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..af9304b
--- /dev/null
+++ b/disk-utils/mkfs.8
@@ -0,0 +1,110 @@
+.\" -*- nroff -*-
+.TH MKFS 8 "June 2011" "util-linux" "System Administration"
+.SH NAME
+mkfs \- build a Linux filesystem
+.SH SYNOPSIS
+.SH SYNOPSIS
+.B mkfs
+.RI [ options ]
+.RB [ \-t
+.IR "type fs-options" ] " device " [ size ]
+.SH DESCRIPTION
+.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 code 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 in a number
+of directories, like perhaps
+.IR /sbin ,
+.IR /sbin/fs ,
+.IR /sbin/fs.d ,
+.IR /etc/fs ,
+.I /etc
+(the precise list is defined at compile time but at least
+contains
+.I /sbin
+and
+.IR /sbin/fs ),
+and finally in the directories
+listed in the PATH environment variable.
+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.
+Although not guaranteed, the following options are supported
+by most filesystem builders.
+.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 and exit.
+.SH BUGS
+All generic options must precede and not be combined with
+filesystem-specific options.
+Some filesystem-specific programs do not support the
+.B -V
+(verbose) option, nor return meaningful exit codes.
+Also, 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
+.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),
+.BR mkfs.xiafs (8)
+.SH AVAILABILITY
+The mkfs command is part of the util-linux package and is available from
+ftp://ftp.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..c1794e5
--- /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-512 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"
+Output version information and exit.
+Option
+.B \-V
+only works as
+.B \-\-version
+when it is the only option.
+.SH "EXIT CODES"
+The exit code 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
+ftp://ftp.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..d83f9e0
--- /dev/null
+++ b/disk-utils/mkfs.bfs.c
@@ -0,0 +1,296 @@
+/*
+ * mkfs.bfs - Create SCO BFS filesystem - aeb, 1999-09-07
+ *
+ */
+
+#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"
+
+#define BFS_ROOT_INO 2
+#define BFS_NAMELEN 14
+#define BFS_BLOCKSIZE 512
+#define BFS_SUPER_MAGIC 0x1badface
+
+/* superblock - 512 bytes */
+struct bfssb {
+ unsigned int s_magic;
+ unsigned int s_start; /* byte offset of start of data */
+ unsigned int s_end; /* sizeof(slice)-1 */
+
+ /* for recovery during compaction */
+ int s_from, s_to; /* src and dest block of current transfer */
+ int 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 {
+ unsigned short i_ino;
+ unsigned char i_pad1[2];
+ unsigned long i_first_block;
+ unsigned long i_last_block;
+ unsigned long i_bytes_to_end;
+ unsigned long i_type; /* 1: file, 2: the unique dir */
+ unsigned long i_mode;
+ unsigned long i_uid, i_gid;
+ unsigned long i_nlinks;
+ unsigned long i_atime, i_mtime, i_ctime;
+ unsigned char i_pad2[16];
+};
+
+#define BFS_DIR_TYPE 2
+
+/* directory entry - 16 bytes */
+struct bfsde {
+ unsigned short d_ino;
+ char d_name[BFS_NAMELEN];
+};
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fprintf(out,
+ _("Usage: %s [options] device [block-count]\n"),
+ program_invocation_short_name);
+ 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"
+ " -V, --version output version information and exit\n"
+ " -V as version must be only option\n"
+ " -h, --help display this help and exit\n\n"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static void __attribute__ ((__noreturn__)) print_version(void)
+{
+ printf(_("%s from %s\n"), program_invocation_short_name, PACKAGE_STRING);
+ 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;
+ 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);
+ atexit(close_stdout);
+
+ if (argc < 2)
+ usage(stderr);
+
+ if (argc == 2 && !strcmp(argv[1], "-V"))
+ print_version();
+
+ 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();
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+ }
+
+ if (optind == argc)
+ usage(stderr);
+
+ device = argv[optind++];
+
+ if (stat(device, &statbuf) < 0)
+ err(EXIT_FAILURE, _("stat failed %s"), device);
+
+ if (!S_ISBLK(statbuf.st_mode))
+ errx(EXIT_FAILURE, _("%s is not a block special device"), device);
+
+ fd = open(device, O_RDWR | O_EXCL);
+ 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)
+ usage(stderr);
+
+ 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 behaviour 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 = BFS_SUPER_MAGIC;
+ sb.s_start = ino_bytes + sizeof(struct bfssb);
+ sb.s_end = 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: %lu (in 1 block)\n"),
+ inodes);
+ else
+ fprintf(stderr, _("Inodes: %lu (in %llu blocks)\n"),
+ inodes, ino_blocks);
+ fprintf(stderr, _("Blocks: %lld\n"), total_blocks);
+ fprintf(stderr, _("Inode end: %d, Data end: %d\n"),
+ sb.s_start - 1, 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 = BFS_ROOT_INO;
+ ri.i_first_block = 1 + ino_blocks;
+ ri.i_last_block = ri.i_first_block +
+ (inodes * sizeof(de) - 1) / BFS_BLOCKSIZE;
+ ri.i_bytes_to_end = ri.i_first_block * BFS_BLOCKSIZE
+ + 2 * sizeof(struct bfsde) - 1;
+ ri.i_type = BFS_DIR_TYPE;
+ ri.i_mode = S_IFDIR | 0755; /* or just 0755 */
+ ri.i_uid = 0;
+ ri.i_gid = 1; /* random */
+ ri.i_nlinks = 2;
+ time(&now);
+ ri.i_atime = now;
+ ri.i_mtime = now;
+ ri.i_ctime = 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 = 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) < 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..dc2d2fd
--- /dev/null
+++ b/disk-utils/mkfs.c
@@ -0,0 +1,144 @@
+/*
+ * 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
+ *
+ */
+
+#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
+
+#define SEARCH_PATH "PATH=" FS_SEARCH_PATH
+#define PROGNAME "mkfs.%s"
+
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fprintf(out,
+ _("Usage: %s [options] [-t type fs-options] device [size]\n"),
+ program_invocation_short_name);
+
+ fprintf(out, _("\nOptions:\n"
+ " -t, --type=TYPE file system type, when undefined ext2 is used\n"
+ " fs-options parameters to real file system builder\n"
+ " device path to a device\n"
+ " size number of blocks on the device\n"
+ " -V, --verbose explain what is done\n"
+ " defining -V more than once will cause a dry-run\n"
+ " -V, --version output version information and exit\n"
+ " -V as version must be only option\n"
+ " -h, --help display this help and exit\n"));
+
+ fprintf(out, _("\nFor more information see mkfs(8).\n"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static void __attribute__ ((__noreturn__)) print_version(void)
+{
+ printf(_("%s (%s)\n"),
+ program_invocation_short_name, PACKAGE_STRING);
+ 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;
+ char *oldpath, *newpath;
+
+ enum { VERSION_OPTION = CHAR_MAX + 1 };
+
+ static const struct option longopts[] = {
+ {"type", required_argument, NULL, 't'},
+ {"version", no_argument, NULL, VERSION_OPTION},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ if (argc == 2 && !strcmp(argv[1], "-V"))
+ print_version();
+
+ /* 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(stdout);
+ case VERSION_OPTION:
+ print_version();
+ default:
+ optind--;
+ more = 1;
+ break; /* start of specific arguments */
+ }
+ if (optind == argc)
+ usage(stderr);
+
+ /* If -t wasn't specified, use the default */
+ if (fstype == NULL)
+ fstype = DEFAULT_FSTYPE;
+
+ /* Set PATH and program name */
+ oldpath = getenv("PATH");
+ if (!oldpath)
+ oldpath = "/bin";
+
+ newpath = xmalloc(strlen(oldpath) + sizeof(SEARCH_PATH) + 3);
+ sprintf(newpath, "%s:%s\n", SEARCH_PATH, oldpath);
+ putenv(newpath);
+
+ progname = xmalloc(sizeof(PROGNAME) + strlen(fstype) + 1);
+ sprintf(progname, PROGNAME, fstype);
+ argv[--optind] = progname;
+
+ if (verbose) {
+ printf(_("mkfs (%s)\n"), PACKAGE_STRING);
+ i = optind;
+ while (argv[i])
+ printf("%s ", argv[i++]);
+ printf("\n");
+ if (verbose > 1)
+ return EXIT_SUCCESS;
+ }
+
+ /* Execute the program */
+ execvp(progname, argv + optind);
+ perror(progname);
+ return EXIT_FAILURE;
+}
diff --git a/disk-utils/mkfs.cramfs.c b/disk-utils/mkfs.cramfs.c
new file mode 100644
index 0000000..f504a32
--- /dev/null
+++ b/disk-utils/mkfs.cramfs.c
@@ -0,0 +1,913 @@
+/*
+ * 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 instaed 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>
+#include <zlib.h>
+
+#include "c.h"
+#include "cramfs.h"
+#include "closestream.h"
+#include "md5.h"
+#include "nls.h"
+#include "exitcodes.h"
+#include "strutils.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; /* settable via -b option */
+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[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
+
+/* Input status of 0 to print help and exit without an error. */
+static void
+usage(int status) {
+ FILE *stream = status ? stderr : stdout;
+
+ fprintf(stream,
+ _("usage: %s [-h] [-v] [-b blksize] [-e edition] [-N endian] [-i file] "
+ "[-n name] dirname outfile\n"
+ " -h print this help\n"
+ " -v be verbose\n"
+ " -E make all warnings errors "
+ "(non-zero exit status)\n"
+ " -b blksize use this blocksize, must equal page size\n"
+ " -e edition set edition number (part of fsid)\n"
+ " -N endian set cramfs endianness (big|little|host), default host\n"
+ " -i file insert a file image into the filesystem "
+ "(requires >= 2.4.0)\n"
+ " -n name set name of cramfs filesystem\n"
+ " -p pad by %d bytes for boot code\n"
+ " -s sort directory entries (old option, ignored)\n"
+ " -z make explicit holes (requires >= 2.3.39)\n"
+ " dirname root of the filesystem to be compressed\n"
+ " outfile output file\n"),
+ program_invocation_short_name, PAD_SIZE);
+
+ exit(status);
+}
+
+static char *
+do_mmap(char *path, unsigned int size, unsigned int mode){
+ int fd;
+ char *start;
+
+ if (!size)
+ return NULL;
+
+ if (S_ISLNK(mode)) {
+ start = xmalloc(size);
+ if (readlink(path, start, size) < 0) {
+ perror(path);
+ warn_skip = 1;
+ start = NULL;
+ }
+ return start;
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ perror(path);
+ warn_skip = 1;
+ return NULL;
+ }
+
+ start = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (-1 == (int) (long) start)
+ err(MKFS_EX_ERROR, "mmap");
+ close(fd);
+
+ return start;
+}
+
+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) {
+ MD5_CTX ctx;
+ char *start;
+
+ start = do_mmap(e->path, e->size, e->mode);
+ if (start == NULL) {
+ e->flags |= CRAMFS_EFLAG_INVALID;
+ } else {
+ MD5Init(&ctx);
+ MD5Update(&ctx, (unsigned char *) start, e->size);
+ 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)
+ 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, 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, 0, 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] == '.') {
+ if (dirent->d_name[2] == '\0')
+ continue;
+ }
+ }
+ namelen = strlen(dirent->d_name);
+ if (namelen > MAX_INPUT_NAMELEN)
+ errx(MKFS_EX_ERROR,
+ _("Very long (%zu bytes) filename `%s' found.\n"
+ " Please increase MAX_INPUT_NAMELEN in "
+ "mkcramfs.c and recompile. Exiting."),
+ namelen, dirent->d_name);
+ memcpy(endpath, dirent->d_name, namelen + 1);
+
+ if (lstat(path, &st) < 0) {
+ perror(endpath);
+ warn_skip = 1;
+ continue;
+ }
+ entry = xcalloc(1, sizeof(struct entry));
+ entry->name = (unsigned char *)xstrdup(dirent->d_name);
+ if (namelen > 255) {
+ /* Can't happen when reading from ext2fs. */
+
+ /* TODO: we ought to avoid chopping in half
+ multi-byte UTF8 characters. */
+ entry->name[namelen = 255] = '\0';
+ warn_namelen = 1;
+ }
+ 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) {
+ 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, Z_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)
+ strncpy((char *)super->name, opt_name, sizeof(super->name));
+ else
+ strncpy((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))))))));
+ else
+ /* 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, Z_NULL, 0);
+ int c;
+ cramfs_is_big_endian = HOST_IS_BIG_ENDIAN; /* default is to use host order */
+
+ blksize = getpagesize();
+ total_blocks = 0;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ /* command line options */
+ while ((c = getopt(argc, argv, "hb:Ee:i:n:N:psVvz")) != EOF) {
+ switch (c) {
+ case 'h':
+ usage(MKFS_EX_OK);
+ 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, _("edition number argument failed"));
+ 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 failed %s"), 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':
+ printf(_("%s from %s\n"),
+ program_invocation_short_name, PACKAGE_STRING);
+ exit(MKFS_EX_OK);
+ case 'v':
+ verbose = 1;
+ break;
+ case 'z':
+ opt_holes = 1;
+ break;
+ default:
+ usage(FSCK_EX_USAGE);
+ }
+ }
+
+ if ((argc - optind) != 2)
+ usage(MKFS_EX_USAGE);
+ dirname = argv[optind];
+ outfile = argv[optind + 1];
+
+ if (stat(dirname, &st) < 0)
+ err(MKFS_EX_USAGE, _("stat failed %s"), 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);
+ close(fd);
+ if (written < 0)
+ err(MKFS_EX_ERROR, _("ROM image"));
+ if (offset != written)
+ errx(MKFS_EX_ERROR, _("ROM image write failed (%zd %zd)"),
+ written, offset);
+
+ /*
+ * (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 255 bytes."));
+ 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 EXIT_SUCCESS;
+}
diff --git a/disk-utils/mkfs.minix.8 b/disk-utils/mkfs.minix.8
new file mode 100644
index 0000000..a65a711
--- /dev/null
+++ b/disk-utils/mkfs.minix.8
@@ -0,0 +1,86 @@
+.\" Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be freely distributed.
+.TH MKFS.MINIX 8 "June 2011" "util-linux" "System Administration"
+.SH NAME
+mkfs.minix \- make a Minix filesystem
+.SH SYNOPSIS
+.B mkfs.minix
+.RB [ \-c | \-l
+.IR filename ]
+.RB [ \-n
+.IR namelength ]
+.RB [ \-i
+.IR inodecount ]
+.RB [ \-v ]
+.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-8] (IDE disk 1)
+/dev/hdb[1-8] (IDE disk 2)
+/dev/sda[1-8] (SCSI disk 1)
+/dev/sdb[1-8] (SCSI disk 2)
+.RE
+.fi
+
+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
+.B \-c
+Check the device for bad blocks before creating the filesystem. If any
+are found, the count is printed.
+.TP
+.BI \-n " namelength"
+Specify the maximum length of filenames.
+Currently, the only allowable values are 14 and 30.
+The default is 30. Note that kernels older than 0.99p7
+only accept namelength 14.
+.TP
+.BI \-i " inodecount"
+Specify the number of inodes for the filesystem.
+.TP
+.BI \-l " filename"
+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.
+.TP
+.BR \-2 , " \-v"
+Make a Minix version 2 filesystem.
+.TP
+.B \-3
+Make a Minix version 3 filesystem.
+.SH "EXIT CODES"
+The exit code 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 mkfs (8),
+.BR fsck (8),
+.BR reboot (8)
+.SH AVAILABILITY
+The mkfs.minix command is part of the util-linux package and is available from
+ftp://ftp.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..49626bf
--- /dev/null
+++ b/disk-utils/mkfs.minix.c
@@ -0,0 +1,815 @@
+/*
+ * 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>
+ *
+ * Usage: mkfs [-c | -l filename ] [-12v3] [-nXX] [-iXX] device [size-in-blocks]
+ *
+ * -c for readablility checking (SLOW!)
+ * -l for getting a list of bad blocks from a file.
+ * -n for namelength (currently the kernel only uses 14 or 30)
+ * -i for number of inodes
+ * -1 for v1 filesystem
+ * -2,-v for v2 filesystem
+ * -3 for v3 filesystem
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ */
+
+#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 <mntent.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"
+
+#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
+
+/*
+ * Global variables used in minix_programs.h inline fuctions
+ */
+int fs_version = 1;
+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)
+
+static char *program_name = "mkfs";
+static char *device_name;
+static int DEV = -1;
+static unsigned long long BLOCKS;
+static int check;
+static int badblocks;
+
+/*
+ * default (changed to 30, per Linus's
+ * suggestion, Sun Nov 21 08:05:07 1993)
+ * This should be changed in the future to 60,
+ * since v3 needs to be the default nowadays (2011)
+ */
+static size_t namelen = 30;
+static size_t dirsize = 32;
+static int magic = MINIX_SUPER_MAGIC2;
+
+static char root_block[MINIX_BLOCK_SIZE];
+
+static char boot_block_buffer[512];
+
+static unsigned short good_blocks_table[MAX_GOOD_BLOCKS];
+static int used_good_blocks;
+static unsigned long req_nr_inodes;
+
+
+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) {
+ errx(MKFS_EX_USAGE, _("Usage: %s [-c | -l filename] [-nXX] [-iXX] /dev/name [blocks]"),
+ program_name);
+}
+
+/*
+ * Check to make certain that our new filesystem won't be created on
+ * an already mounted partition. Code adapted from mke2fs, Copyright
+ * (C) 1994 Theodore Ts'o. Also licensed under GPL.
+ */
+static void check_mount(void) {
+ FILE * f;
+ struct mntent * mnt;
+
+ if ((f = setmntent (_PATH_MOUNTED, "r")) == NULL)
+ return;
+ while ((mnt = getmntent (f)) != NULL)
+ if (strcmp (device_name, mnt->mnt_fsname) == 0)
+ break;
+ endmntent (f);
+ if (!mnt)
+ return;
+
+ errx(MKFS_EX_ERROR, _("%s is mounted; will not make a filesystem here!"),
+ device_name);
+}
+
+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(void) {
+ unsigned long imaps = get_nimaps();
+ unsigned long zmaps = get_nzmaps();
+ unsigned long buffsz = get_inode_buffer_size();
+
+ /* Mark the super block valid. */
+ super_set_state();
+
+ if (lseek(DEV, 0, SEEK_SET))
+ err(MKFS_EX_ERROR, _("%s: seek to boot block failed "
+ " in write_tables"), device_name);
+ if (write_all(DEV, boot_block_buffer, 512))
+ err(MKFS_EX_ERROR, _("%s: unable to clear boot sector"), device_name);
+ if (MINIX_BLOCK_SIZE != lseek(DEV, MINIX_BLOCK_SIZE, SEEK_SET))
+ err(MKFS_EX_ERROR, _("%s: seek failed in write_tables"), device_name);
+
+ if (write_all(DEV, super_block_buffer, MINIX_BLOCK_SIZE))
+ err(MKFS_EX_ERROR, _("%s: unable to write super-block"), device_name);
+
+ if (write_all(DEV, inode_map, imaps * MINIX_BLOCK_SIZE))
+ err(MKFS_EX_ERROR, _("%s: unable to write inode map"), device_name);
+
+ if (write_all(DEV, zone_map, zmaps * MINIX_BLOCK_SIZE))
+ err(MKFS_EX_ERROR, _("%s: unable to write zone map"), device_name);
+
+ if (write_all(DEV, inode_buffer, buffsz))
+ err(MKFS_EX_ERROR, _("%s: unable to write inodes"), device_name);
+}
+
+static void write_block(int blk, char * buffer) {
+ if (blk*MINIX_BLOCK_SIZE != lseek(DEV, blk*MINIX_BLOCK_SIZE, SEEK_SET))
+ errx(MKFS_EX_ERROR, _("%s: seek failed in write_block"), device_name);
+
+ if (write_all(DEV, buffer, MINIX_BLOCK_SIZE))
+ errx(MKFS_EX_ERROR, _("%s: write failed in write_block"), device_name);
+}
+
+static int get_free_block(void) {
+ unsigned int blk;
+ unsigned int zones = get_nzones();
+ unsigned int first_zone = get_first_zone();
+
+ if (used_good_blocks+1 >= MAX_GOOD_BLOCKS)
+ errx(MKFS_EX_ERROR, _("%s: too many bad blocks"), device_name);
+ if (used_good_blocks)
+ blk = good_blocks_table[used_good_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"), device_name);
+ good_blocks_table[used_good_blocks] = blk;
+ used_good_blocks++;
+ return blk;
+}
+
+static void mark_good_blocks(void) {
+ int blk;
+
+ for (blk=0 ; blk < used_good_blocks ; blk++)
+ mark_zone(good_blocks_table[blk]);
+}
+
+static 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(void)
+{
+ 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 (!badblocks)
+ return;
+ mark_inode(MINIX_BAD_INO);
+ inode->i_nlinks = 1;
+ inode->i_time = time(NULL);
+ inode->i_mode = S_IFREG + 0000;
+ inode->i_size = badblocks*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();
+ 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();
+ memset(dind_block,0,MINIX_BLOCK_SIZE);
+ for (i=0 ; i<512 ; i++) {
+ write_block(ind,(char *) ind_block);
+ dind_block[i] = ind = get_free_block();
+ memset(ind_block,0,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"), device_name);
+end_bad:
+ if (ind)
+ write_block(ind, (char *) ind_block);
+ if (dind)
+ write_block(dind, (char *) dind_block);
+}
+
+static void make_bad_inode_v2_v3 (void)
+{
+ 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 (!badblocks)
+ return;
+ mark_inode (MINIX_BAD_INO);
+ inode->i_nlinks = 1;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = time (NULL);
+ inode->i_mode = S_IFREG + 0000;
+ inode->i_size = badblocks * 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 ();
+ 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 ();
+ memset (dind_block, 0, MINIX_BLOCK_SIZE);
+ for (i = 0; i < 256; i++) {
+ write_block (ind, (char *) ind_block);
+ dind_block[i] = ind = get_free_block ();
+ memset (ind_block, 0, 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"), device_name);
+ end_bad:
+ if (ind)
+ write_block (ind, (char *) ind_block);
+ if (dind)
+ write_block (dind, (char *) dind_block);
+}
+
+static void make_bad_inode(void)
+{
+ if (fs_version < 2)
+ return make_bad_inode_v1();
+ return make_bad_inode_v2_v3();
+}
+
+static void make_root_inode_v1(void) {
+ struct minix_inode * inode = &Inode[MINIX_ROOT_INO];
+
+ mark_inode(MINIX_ROOT_INO);
+ inode->i_zone[0] = get_free_block();
+ inode->i_nlinks = 2;
+ inode->i_time = time(NULL);
+ if (badblocks)
+ inode->i_size = 3*dirsize;
+ else {
+ root_block[2*dirsize] = '\0';
+ root_block[2*dirsize+1] = '\0';
+ inode->i_size = 2*dirsize;
+ }
+ inode->i_mode = S_IFDIR + 0755;
+ inode->i_uid = getuid();
+ if (inode->i_uid)
+ inode->i_gid = getgid();
+ write_block(inode->i_zone[0],root_block);
+}
+
+static void make_root_inode_v2_v3 (void) {
+ struct minix2_inode *inode = &Inode2[MINIX_ROOT_INO];
+
+ mark_inode (MINIX_ROOT_INO);
+ inode->i_zone[0] = get_free_block ();
+ inode->i_nlinks = 2;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = time (NULL);
+
+ if (badblocks)
+ inode->i_size = 3 * dirsize;
+ else {
+ root_block[2 * dirsize] = '\0';
+ inode->i_size = 2 * dirsize;
+ }
+
+ inode->i_mode = S_IFDIR + 0755;
+ inode->i_uid = getuid();
+ if (inode->i_uid)
+ inode->i_gid = getgid();
+ write_block (inode->i_zone[0], root_block);
+}
+
+static void make_root_inode(void)
+{
+ if (fs_version < 2)
+ return make_root_inode_v1();
+ return make_root_inode_v2_v3();
+}
+
+static void super_set_nzones(void)
+{
+ switch (fs_version) {
+ case 3:
+ Super3.s_zones = BLOCKS;
+ break;
+ case 2:
+ Super.s_zones = BLOCKS;
+ break;
+ default: /* v1 */
+ Super.s_nzones = 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(unsigned long inodes)
+{
+ switch (fs_version) {
+ case 3:
+ Super3.s_imap_blocks = UPPER(inodes + 1, BITS_PER_BLOCK);
+ Super3.s_zmap_blocks = UPPER(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(BLOCKS - (1+get_nimaps()+inode_blocks()),
+ BITS_PER_BLOCK+1);
+ Super.s_firstdatazone = first_zone_data();
+ break;
+ }
+}
+
+static void super_set_magic(void)
+{
+ switch (fs_version) {
+ case 3:
+ Super3.s_magic = magic;
+ break;
+ default:
+ Super.s_magic = magic;
+ break;
+ }
+}
+
+static void setup_tables(void) {
+ unsigned long inodes, zmaps, imaps, zones, i;
+
+ super_block_buffer = calloc(1, MINIX_BLOCK_SIZE);
+ if (!super_block_buffer)
+ err(MKFS_EX_ERROR, _("%s: unable to alloc buffer for superblock"),
+ device_name);
+
+ memset(boot_block_buffer,0,512);
+ super_set_magic();
+
+ 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();
+ zones = get_nzones();
+
+ /* some magic nrs: 1 inode / 3 blocks */
+ if ( req_nr_inodes == 0 )
+ inodes = BLOCKS/3;
+ else
+ inodes = req_nr_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 {
+ Super.s_ninodes = inodes;
+ if (inodes > MINIX_MAX_INODES)
+ inodes = MINIX_MAX_INODES;
+ }
+
+ super_set_map_blocks(inodes);
+ imaps = get_nimaps();
+ zmaps = get_nzmaps();
+
+ inode_map = malloc(imaps * MINIX_BLOCK_SIZE);
+ zone_map = malloc(zmaps * MINIX_BLOCK_SIZE);
+ if (!inode_map || !zone_map)
+ err(MKFS_EX_ERROR, _("%s: unable to allocate buffers for maps"),
+ device_name);
+ 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 = malloc(get_inode_buffer_size());
+ if (!inode_buffer)
+ err(MKFS_EX_ERROR, _("%s: unable to allocate buffer for inodes"),
+ device_name);
+ memset(inode_buffer,0, get_inode_buffer_size());
+ printf(_("%lu inodes\n"), inodes);
+ printf(_("%lu blocks\n"), zones);
+ printf(_("Firstdatazone=%ld (%ld)\n"), get_first_zone(), first_zone_data());
+ printf(_("Zonesize=%d\n"),MINIX_BLOCK_SIZE<<get_zone_size());
+ printf(_("Maxsize=%ld\n\n"),get_max_size());
+}
+
+/*
+ * Perform a test of a block; return the number of
+ * blocks readable/writeable.
+ */
+static long do_check(char * buffer, int try, unsigned int current_block) {
+ long got;
+
+ /* Seek to the correct loc. */
+ if (lseek(DEV, current_block * MINIX_BLOCK_SIZE, SEEK_SET) !=
+ current_block * MINIX_BLOCK_SIZE )
+ err(MKFS_EX_ERROR, _("%s: seek failed during testing of blocks"),
+ device_name);
+
+ /* Try the read */
+ got = read(DEV, 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(void) {
+ int 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(DEV,currently_testing*MINIX_BLOCK_SIZE,SEEK_SET) !=
+ currently_testing*MINIX_BLOCK_SIZE)
+ errx(MKFS_EX_ERROR, _("%s: seek failed in check_blocks"),
+ device_name);
+ try = TEST_BUFFER_BLOCKS;
+ if (currently_testing + try > zones)
+ try = zones-currently_testing;
+ got = do_check(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"), device_name);
+ mark_zone(currently_testing);
+ badblocks++;
+ currently_testing++;
+ }
+ if (badblocks > 1)
+ printf(_("%d bad blocks\n"), badblocks);
+ else if (badblocks == 1)
+ printf(_("one bad block\n"));
+}
+
+static void get_list_blocks(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"),
+ device_name);
+
+ while (!feof(listfile)) {
+ if (fscanf(listfile,"%ld\n", &blockno) != 1) {
+ printf(_("badblock number input error on line %d\n"), badblocks + 1);
+ errx(MKFS_EX_ERROR, _("%s: cannot read badblocks file"),
+ device_name);
+ }
+ mark_zone(blockno);
+ badblocks++;
+ }
+ fclose(listfile);
+
+ if(badblocks > 1)
+ printf(_("%d bad blocks\n"), badblocks);
+ else if (badblocks == 1)
+ printf(_("one bad block\n"));
+}
+
+int main(int argc, char ** argv) {
+ int i;
+ char * tmp;
+ struct stat statbuf;
+ char * listfile = NULL;
+ char * p;
+
+ if (argc && *argv)
+ program_name = *argv;
+ if ((p = strrchr(program_name, '/')) != NULL)
+ program_name = p+1;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ if (argc == 2 &&
+ (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version"))) {
+ printf(_("%s (%s)\n"), program_name, PACKAGE_STRING);
+ exit(0);
+ }
+
+ if (INODE_SIZE * MINIX_INODES_PER_BLOCK != MINIX_BLOCK_SIZE)
+ errx(MKFS_EX_ERROR, _("%s: bad inode size"), device_name);
+ if (INODE2_SIZE * MINIX2_INODES_PER_BLOCK != MINIX_BLOCK_SIZE)
+ errx(MKFS_EX_ERROR, _("%s: bad inode size"), device_name);
+
+ opterr = 0;
+ while ((i = getopt(argc, argv, "ci:l:n:v123")) != -1)
+ switch (i) {
+ case 'c':
+ check=1; break;
+ case 'i':
+ req_nr_inodes = (unsigned long) atol(optarg);
+ break;
+ case 'l':
+ listfile = optarg; break;
+ case 'n':
+ i = strtoul(optarg,&tmp,0);
+ if (*tmp)
+ usage();
+ if (i == 14)
+ magic = MINIX_SUPER_MAGIC;
+ else if (i == 30)
+ magic = MINIX_SUPER_MAGIC2;
+ else
+ usage();
+ namelen = i;
+ dirsize = i+2;
+ break;
+ case '1':
+ fs_version = 1;
+ break;
+ case '2':
+ case 'v': /* kept for backwards compatiblitly */
+ fs_version = 2;
+ break;
+ case '3':
+ fs_version = 3;
+ namelen = 60;
+ dirsize = 64;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc > 0 && !device_name) {
+ device_name = argv[0];
+ argc--;
+ argv++;
+ }
+ if (argc > 0) {
+ BLOCKS = strtol(argv[0],&tmp,0);
+ if (*tmp) {
+ printf(_("strtol error: number of blocks not specified"));
+ usage();
+ }
+ }
+
+ if (!device_name) {
+ usage();
+ }
+ check_mount(); /* is it already mounted? */
+ tmp = root_block;
+ if (fs_version == 3) {
+ *(uint32_t *)tmp = 1;
+ strcpy(tmp+4,".");
+ tmp += dirsize;
+ *(uint32_t *)tmp = 1;
+ strcpy(tmp+4,"..");
+ tmp += dirsize;
+ *(uint32_t *)tmp = 2;
+ strcpy(tmp+4, ".badblocks");
+ } else {
+ *(uint16_t *)tmp = 1;
+ strcpy(tmp+2,".");
+ tmp += dirsize;
+ *(uint16_t *)tmp = 1;
+ strcpy(tmp+2,"..");
+ tmp += dirsize;
+ *(uint16_t *)tmp = 2;
+ strcpy(tmp+2, ".badblocks");
+ }
+ if (stat(device_name, &statbuf) < 0)
+ err(MKFS_EX_ERROR, _("stat failed %s"), device_name);
+ if (S_ISBLK(statbuf.st_mode))
+ DEV = open(device_name,O_RDWR | O_EXCL);
+ else
+ DEV = open(device_name,O_RDWR);
+
+ if (DEV<0)
+ err(MKFS_EX_ERROR, _("cannot open %s"), device_name);
+
+ if (S_ISBLK(statbuf.st_mode)) {
+ int sectorsize;
+
+ if (blkdev_get_sector_size(DEV, &sectorsize) == -1)
+ sectorsize = DEFAULT_SECTOR_SIZE; /* kernel < 2.3.3 */
+
+ if (blkdev_is_misaligned(DEV))
+ warnx(_("%s: device is misaligned"), device_name);
+
+ if (MINIX_BLOCK_SIZE < sectorsize)
+ errx(MKFS_EX_ERROR, _("block size smaller than physical "
+ "sector size of %s"), device_name);
+ if (!BLOCKS) {
+ if (blkdev_get_size(DEV, &BLOCKS) == -1)
+ errx(MKFS_EX_ERROR, _("cannot determine size of %s"),
+ device_name);
+ BLOCKS /= MINIX_BLOCK_SIZE;
+ }
+ } else if (!S_ISBLK(statbuf.st_mode)) {
+ if (!BLOCKS)
+ BLOCKS = statbuf.st_size / MINIX_BLOCK_SIZE;
+ check=0;
+ } else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
+ errx(MKFS_EX_ERROR, _("will not try to make filesystem on '%s'"), device_name);
+ if (BLOCKS < 10)
+ errx(MKFS_EX_ERROR, _("%s: number of blocks too small"), device_name);
+
+ if (fs_version == 3)
+ magic = MINIX3_SUPER_MAGIC;
+ else if (fs_version == 2) {
+ if (namelen == 14)
+ magic = MINIX2_SUPER_MAGIC;
+ else
+ magic = MINIX2_SUPER_MAGIC2;
+ } else /* fs_version == 1 */
+ if (BLOCKS > MINIX_MAX_INODES)
+ BLOCKS = MINIX_MAX_INODES;
+ setup_tables();
+ if (check)
+ check_blocks();
+ else if (listfile)
+ get_list_blocks(listfile);
+
+ make_root_inode();
+ make_bad_inode();
+
+ mark_good_blocks();
+ write_tables();
+ close(DEV);
+
+ return 0;
+}
diff --git a/disk-utils/mkswap.8 b/disk-utils/mkswap.8
new file mode 100644
index 0000000..c653171
--- /dev/null
+++ b/disk-utils/mkswap.8
@@ -0,0 +1,148 @@
+.\" Copyright 1998 Andries E. Brouwer (aeb@cwi.nl)
+.\"
+.\" May be distributed under the GNU General Public License
+.\" Rewritten for 2.1.117, aeb, 981010.
+.\"
+.TH MKSWAP 8 "March 2009" "util-linux" "System Administration"
+.SH NAME
+mkswap \- set up a Linux swap area
+.SH SYNOPSIS
+.B mkswap
+.RI [ 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 -- 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, ...) and on a whole disk (e.g. /dev/sda).
+
+.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 and on
+a whole disk (e.g. /dev/sda).
+.TP
+.BR \-L , " \-\-label " \fIlabel\fR
+Specify a \fIlabel\fR for the device, to allow
+.B swapon
+by label.
+.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 NOTES
+The maximum useful size of a swap area depends on the architecture and
+the kernel version.
+It is roughly 2GiB on i386, PPC, m68k and ARM, 1GiB on sparc, 512MiB on mips,
+128GiB on alpha, and 3TiB on sparc64. For kernels after 2.3.3 (May 1999) there is no
+such limitation.
+
+Note that before version 2.1.117 the kernel allocated one byte for each page,
+while it now allocates two bytes, so that taking into use a swap area of 2 GiB
+might require 2 MiB of kernel memory.
+
+Presently, Linux allows 32 swap areas (this was 8 before Linux 2.4.10 (Sep 2001)).
+The areas in use can be seen in the file
+.I /proc/swaps
+(since 2.1.25 (Sep 1997)).
+
+.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 --
+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=1024 count=65536
+.RE
+.fi
+
+Note that a swap file must not contain any holes (so, using
+.BR cp (1)
+to create the file is not acceptable).
+
+
+.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
+ftp://ftp.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..34fe687
--- /dev/null
+++ b/disk-utils/mkswap.c
@@ -0,0 +1,672 @@
+/*
+ * mkswap.c - set up a linux swap device
+ *
+ * (C) 1991 Linus Torvalds. This file may be redistributed as per
+ * the Linux copyright.
+ */
+
+/*
+ * 20.12.91 - time began. Got VM working yesterday by doing this by hand.
+ *
+ * Usage: mkswap [-c] [-vN] [-f] device [size-in-blocks]
+ *
+ * -c for readability checking. (Use it unless you are SURE!)
+ * -vN for swap areas version N. (Only N=0,1 known today.)
+ * -f for forcing swap creation even if it would smash partition table.
+ *
+ * The device may be a block device or an image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ *
+ * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the
+ * size-in-blocks parameter optional added Wed Feb 8 10:33:43 1995.
+ *
+ * Version 1 swap area code (for kernel 2.1.117), aeb, 981010.
+ *
+ * Sparc fixes, jj@ultra.linux.cz (Jakub Jelinek), 981201 - mangled by aeb.
+ * V1_MAX_PAGES fixes, jj, 990325.
+ * sparc64 fixes, jj, 000219.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <mntent.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <getopt.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 "wholedisk.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
+
+static char *device_name = NULL;
+static int DEV = -1;
+static unsigned long long PAGES = 0;
+static unsigned long badpages = 0;
+static int check = 0;
+
+#define SELINUX_SWAPFILE_TYPE "swapfile_t"
+
+#ifdef __sparc__
+# ifdef __arch64__
+# define is_sparc64() 1
+# define is_be64() 1
+# else /* sparc32 */
+static int
+is_sparc64(void)
+{
+ struct utsname un;
+ static int sparc64 = -1;
+
+ if (sparc64 != -1)
+ return sparc64;
+ sparc64 = 0;
+
+ if (uname(&un) < 0)
+ return 0;
+ if (! strcmp(un.machine, "sparc64")) {
+ sparc64 = 1;
+ return 1;
+ }
+ if (strcmp(un.machine, "sparc"))
+ return 0; /* Should not happen */
+
+#ifdef HAVE_PERSONALITY
+ {
+ extern int personality(unsigned long);
+ int oldpers;
+#define PERS_LINUX 0x00000000
+#define PERS_LINUX_32BIT 0x00800000
+#define PERS_LINUX32 0x00000008
+
+ oldpers = personality(PERS_LINUX_32BIT);
+ if (oldpers != -1) {
+ if (personality(PERS_LINUX) != -1) {
+ uname(&un);
+ if (! strcmp(un.machine, "sparc64")) {
+ sparc64 = 1;
+ oldpers = PERS_LINUX32;
+ }
+ }
+ personality(oldpers);
+ }
+ }
+#endif
+
+ return sparc64;
+}
+# define is_be64() is_sparc64()
+# endif /* sparc32 */
+#else /* !sparc */
+# define is_be64() 0
+#endif
+
+/*
+ * The definition of the union swap_header uses the kernel constant PAGE_SIZE.
+ * Unfortunately, on some architectures this depends on the hardware model, and
+ * can only be found at run time -- we use getpagesize(), so that we do not
+ * need separate binaries e.g. for sun4, sun4c/d/m and sun4u.
+ *
+ * Even more unfortunately, getpagesize() does not always return the right
+ * information. For example, libc4, libc5 and glibc 2.0 do not use the system
+ * call but invent a value themselves (EXEC_PAGESIZE or NBPG * CLSIZE or NBPC),
+ * and thus it may happen that e.g. on a sparc kernel PAGE_SIZE=4096 and
+ * getpagesize() returns 8192.
+ *
+ * What to do? Let us allow the user to specify the pagesize explicitly.
+ *
+ */
+static unsigned int user_pagesize;
+static unsigned int pagesize;
+static unsigned long *signature_page = NULL;
+
+static void
+init_signature_page(void)
+{
+
+ unsigned int kernel_pagesize = pagesize = getpagesize();
+
+ if (user_pagesize) {
+ if (!is_power_of_2(user_pagesize) ||
+ user_pagesize < sizeof(struct swap_header_v1_2) + 10)
+ errx(EXIT_FAILURE,
+ _("Bad user-specified page size %u"),
+ user_pagesize);
+ pagesize = user_pagesize;
+ }
+
+ if (user_pagesize && user_pagesize != kernel_pagesize)
+ warnx(_("Using user-specified page size %d, "
+ "instead of the system value %d"),
+ pagesize, kernel_pagesize);
+
+ signature_page = (unsigned long *) xcalloc(1, pagesize);
+}
+
+static void
+write_signature(char *sig)
+{
+ char *sp = (char *) signature_page;
+
+ strncpy(sp + pagesize - 10, sig, 10);
+}
+
+static void
+write_uuid_and_label(unsigned char *uuid, char *volume_name)
+{
+ struct swap_header_v1_2 *h;
+
+ /* Sanity check */
+ if (sizeof(struct swap_header_v1) !=
+ sizeof(struct swap_header_v1_2)) {
+ warnx(_("Bad swap header size, no label written."));
+ return;
+ }
+
+ h = (struct swap_header_v1_2 *) signature_page;
+ if (uuid)
+ memcpy(h->uuid, uuid, sizeof(h->uuid));
+ if (volume_name) {
+ xstrncpy(h->volume_name, volume_name, sizeof(h->volume_name));
+ if (strlen(volume_name) > strlen(h->volume_name))
+ warnx(_("Label was truncated."));
+ }
+ if (uuid || volume_name) {
+ if (volume_name)
+ printf("LABEL=%s, ", h->volume_name);
+ else
+ printf(_("no label, "));
+#ifdef HAVE_LIBUUID
+ if (uuid) {
+ char uuid_string[37];
+ uuid_unparse(uuid, uuid_string);
+ printf("UUID=%s\n", uuid_string);
+ } else
+#endif
+ printf(_("no uuid\n"));
+ }
+}
+
+/*
+ * Find out what the maximum amount of swap space is that the kernel will
+ * handle. This wouldn't matter if the kernel just used as much of the
+ * swap space as it can handle, but until 2.3.4 it would return an error
+ * to swapon() if the swapspace was too large.
+ */
+/* Before 2.2.0pre9 */
+#define V1_OLD_MAX_PAGES ((0x7fffffff / pagesize) - 1)
+/* Since 2.2.0pre9, before 2.3.4:
+ error if nr of pages >= SWP_OFFSET(SWP_ENTRY(0,~0UL))
+ with variations on
+ #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8))
+ #define SWP_OFFSET(entry) ((entry) >> 8)
+ on the various architectures. Below the result - yuk.
+
+ Machine pagesize SWP_ENTRY SWP_OFFSET bound+1 oldbound+2
+ i386 2^12 o<<8 e>>8 1<<24 1<<19
+ mips 2^12 o<<15 e>>15 1<<17 1<<19
+ alpha 2^13 o<<40 e>>40 1<<24 1<<18
+ m68k 2^12 o<<12 e>>12 1<<20 1<<19
+ sparc 2^{12,13} (o&0x3ffff)<<9 (e>>9)&0x3ffff 1<<18 1<<{19,18}
+ sparc64 2^13 o<<13 e>>13 1<<51 1<<18
+ ppc 2^12 o<<8 e>>8 1<<24 1<<19
+ armo 2^{13,14,15} o<<8 e>>8 1<<24 1<<{18,17,16}
+ armv 2^12 o<<9 e>>9 1<<23 1<<19
+
+ assuming that longs have 64 bits on alpha and sparc64 and 32 bits elsewhere.
+
+ The bad part is that we need to know this since the kernel will
+ refuse a swap space if it is too large.
+*/
+/* patch from jj - why does this differ from the above? */
+/* 32bit kernels have a second limitation of 2GB, sparc64 is limited by
+ the size of virtual address space allocation for vmalloc */
+#if defined(__alpha__)
+#define V1_MAX_PAGES ((1 << 24) - 1)
+#elif defined(__mips__)
+#define V1_MAX_PAGES ((1 << 17) - 1)
+#elif defined(__sparc__)
+#define V1_MAX_PAGES (is_sparc64() ? ((3 << 29) - 1) : ((1 << 18) - 1))
+#elif defined(__ia64__)
+/*
+ * The actual size will depend on the amount of virtual address space
+ * available to vmalloc the swap map.
+ */
+#define V1_MAX_PAGES ((1UL << 54) - 1)
+#else
+#define V1_MAX_PAGES V1_OLD_MAX_PAGES
+#endif
+/* man page now says:
+The maximum useful size of a swap area now depends on the architecture.
+It is roughly 2GB on i386, PPC, m68k, ARM, 1GB on sparc, 512MB on mips,
+128GB on alpha and 3TB on sparc64.
+*/
+
+#define MAX_BADPAGES ((pagesize-1024-128*sizeof(int)-10)/sizeof(int))
+#define MIN_GOODPAGES 10
+
+static void __attribute__ ((__noreturn__)) usage(FILE *out)
+{
+ fprintf(out,
+ _("\nUsage:\n"
+ " %s [options] device [size]\n"),
+ program_invocation_short_name);
+
+ 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"
+ " -V, --version output version information and exit\n"
+ " -h, --help display this help and exit\n\n"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static void
+page_bad(int page)
+{
+ struct swap_header_v1_2 *p = (struct swap_header_v1_2 *) signature_page;
+
+ if (badpages == MAX_BADPAGES)
+ errx(EXIT_FAILURE, _("too many bad pages"));
+ p->badpages[badpages] = page;
+ badpages++;
+}
+
+static void
+check_blocks(void)
+{
+ unsigned int current_page;
+ int do_seek = 1;
+ char *buffer;
+
+ buffer = xmalloc(pagesize);
+ current_page = 0;
+ while (current_page < PAGES) {
+ if (do_seek && lseek(DEV,current_page*pagesize,SEEK_SET) !=
+ current_page*pagesize)
+ errx(EXIT_FAILURE, _("seek failed in check_blocks"));
+ if ((do_seek = (pagesize != read(DEV, buffer, pagesize))))
+ page_bad(current_page);
+ current_page++;
+ }
+ if (badpages == 1)
+ printf(_("one bad page\n"));
+ else if (badpages > 1)
+ printf(_("%lu bad pages\n"), badpages);
+ free(buffer);
+}
+
+/* return size in pages */
+static unsigned long long
+get_size(const char *file)
+{
+ int fd;
+ unsigned long long size;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ perror(file);
+ exit(EXIT_FAILURE);
+ }
+ if (blkdev_get_size(fd, &size) == 0)
+ size /= pagesize;
+
+ close(fd);
+ return size;
+}
+
+#ifdef HAVE_LIBBLKID
+static blkid_probe
+new_prober(int fd)
+{
+ blkid_probe pr = blkid_new_probe();
+ if (!pr)
+ errx(EXIT_FAILURE, _("unable to alloc new libblkid probe"));
+ if (blkid_probe_set_device(pr, fd, 0, 0))
+ errx(EXIT_FAILURE, _("unable to assign device to libblkid probe"));
+ return pr;
+}
+#endif
+
+static void
+wipe_device(int fd, const char *devname, int force, int is_blkdevice)
+{
+ char *type = NULL;
+ int whole = 0;
+ int zap = 1;
+#ifdef HAVE_LIBBLKID
+ blkid_probe pr = NULL;
+#endif
+ if (!force) {
+ if (lseek(fd, 0, SEEK_SET) != 0)
+ errx(EXIT_FAILURE, _("unable to rewind swap-device"));
+
+ if (is_blkdevice && is_whole_disk_fd(fd, devname)) {
+ /* don't zap bootbits on whole disk -- we know nothing
+ * about bootloaders on the device */
+ whole = 1;
+ zap = 0;
+ } else {
+#ifdef HAVE_LIBBLKID
+ pr = new_prober(fd);
+ blkid_probe_enable_partitions(pr, 1);
+ blkid_probe_enable_superblocks(pr, 0);
+
+ if (blkid_do_fullprobe(pr) == 0 &&
+ blkid_probe_lookup_value(pr, "PTTYPE",
+ (const char **) &type, NULL) == 0 &&
+ type) {
+ type = xstrdup(type);
+ zap = 0;
+ }
+#else
+ /* don't zap if compiled without libblkid */
+ zap = 0;
+#endif
+ }
+ }
+
+ if (zap) {
+ /*
+ * Wipe boodbits
+ */
+ char buf[1024];
+
+ if (lseek(fd, 0, SEEK_SET) != 0)
+ errx(EXIT_FAILURE, _("unable to rewind swap-device"));
+
+ memset(buf, 0, sizeof(buf));
+ if (write_all(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(fd);
+
+ blkid_probe_enable_superblocks(pr, 1);
+ blkid_probe_enable_partitions(pr, 0);
+ blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC);
+
+ while (blkid_do_probe(pr) == 0)
+ blkid_do_wipe(pr, 0);
+#endif
+ } else {
+ warnx(_("%s: warning: don't erase bootbits sectors"),
+ devname);
+ if (type)
+ fprintf(stderr, _(" (%s partition table detected). "), type);
+ else if (whole)
+ fprintf(stderr, _(" on whole disk. "));
+ else
+ fprintf(stderr, _(" (compiled without libblkid). "));
+ fprintf(stderr, "Use -f to force.\n");
+ }
+#ifdef HAVE_LIBBLKID
+ blkid_free_probe(pr);
+#endif
+}
+
+int
+main(int argc, char **argv) {
+ struct stat statbuf;
+ struct swap_header_v1_2 *hdr;
+ int c;
+ unsigned long long maxpages;
+ unsigned long long goodpages;
+ unsigned long long sz;
+ off_t offset;
+ int force = 0;
+ int version = 1;
+ char *block_count = 0;
+ char *opt_label = NULL;
+ unsigned char *uuid = NULL;
+#ifdef HAVE_LIBUUID
+ const char *opt_uuid = NULL;
+ uuid_t uuid_dat;
+#endif
+ static const struct option longopts[] = {
+ { "check", no_argument, 0, 'c' },
+ { "force", no_argument, 0, 'f' },
+ { "pagesize", required_argument, 0, 'p' },
+ { "label", required_argument, 0, 'L' },
+ { "swapversion", required_argument, 0, 'v' },
+ { "uuid", required_argument, 0, 'U' },
+ { "version", no_argument, 0, 'V' },
+ { "help", no_argument, 0, 'h' },
+ { NULL, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while((c = getopt_long(argc, argv, "cfp:L:v:U:Vh", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'c':
+ check=1;
+ break;
+ case 'f':
+ force=1;
+ break;
+ case 'p':
+ user_pagesize = strtou32_or_err(optarg, _("parse page size failed"));
+ break;
+ case 'L':
+ opt_label = optarg;
+ break;
+ case 'v':
+ version = strtos32_or_err(optarg, _("parse version number failed"));
+ break;
+ case 'U':
+#ifdef HAVE_LIBUUID
+ opt_uuid = optarg;
+#else
+ warnx(_("warning: ignore -U (UUIDs are unsupported by %s)"),
+ program_invocation_short_name);
+#endif
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ exit(EXIT_SUCCESS);
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+ }
+ if (optind < argc)
+ device_name = argv[optind++];
+ if (optind < argc)
+ block_count = argv[optind++];
+ if (optind != argc) {
+ warnx(("only one device as argument is currently supported."));
+ usage(stderr);
+ }
+
+ if (version != 1)
+ errx(EXIT_FAILURE,
+ _("does not support swapspace version %d."), version);
+
+#ifdef HAVE_LIBUUID
+ if(opt_uuid) {
+ if (uuid_parse(opt_uuid, uuid_dat) != 0)
+ errx(EXIT_FAILURE, _("error: UUID parsing failed"));
+ } else
+ uuid_generate(uuid_dat);
+ uuid = uuid_dat;
+#endif
+
+ init_signature_page(); /* get pagesize */
+
+ if (!device_name) {
+ warnx(_("error: Nowhere to set up swap on?"));
+ usage(stderr);
+ }
+ if (block_count) {
+ /* this silly user specified the number of blocks explicitly */
+ uint64_t blks = strtou64_or_err(block_count,
+ _("invalid block count argument"));
+ PAGES = blks / (pagesize / 1024);
+ }
+ sz = get_size(device_name);
+ if (!PAGES)
+ PAGES = sz;
+ else if (PAGES > sz && !force) {
+ errx(EXIT_FAILURE,
+ _("error: "
+ "size %llu KiB is larger than device size %llu KiB"),
+ PAGES*(pagesize/1024), sz*(pagesize/1024));
+ }
+
+ if (PAGES < MIN_GOODPAGES) {
+ warnx(_("error: swap area needs to be at least %ld KiB"),
+ (long)(MIN_GOODPAGES * pagesize/1024));
+ usage(stderr);
+ }
+
+#ifdef __linux__
+ if (get_linux_version() >= KERNEL_VERSION(2,3,4))
+ maxpages = UINT_MAX + 1ULL;
+ else if (get_linux_version() >= KERNEL_VERSION(2,2,1))
+ maxpages = V1_MAX_PAGES;
+ else
+#endif
+ maxpages = V1_OLD_MAX_PAGES;
+
+ if (PAGES > maxpages) {
+ PAGES = maxpages;
+ warnx(_("warning: truncating swap area to %llu KiB"),
+ PAGES * pagesize / 1024);
+ }
+
+ if (is_mounted(device_name))
+ errx(EXIT_FAILURE, _("error: "
+ "%s is mounted; will not make swapspace."),
+ device_name);
+
+ if (stat(device_name, &statbuf) < 0) {
+ perror(device_name);
+ exit(EXIT_FAILURE);
+ }
+ if (S_ISBLK(statbuf.st_mode))
+ DEV = open(device_name, O_RDWR | O_EXCL);
+ else
+ DEV = open(device_name, O_RDWR);
+
+ if (DEV < 0) {
+ perror(device_name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!S_ISBLK(statbuf.st_mode))
+ check=0;
+ else if (blkdev_is_misaligned(DEV))
+ warnx(_("warning: %s is misaligned"), device_name);
+
+ if (check)
+ check_blocks();
+
+ wipe_device(DEV, device_name, force, S_ISBLK(statbuf.st_mode));
+
+ hdr = (struct swap_header_v1_2 *) signature_page;
+ hdr->version = 1;
+ hdr->last_page = PAGES - 1;
+ hdr->nr_badpages = badpages;
+
+ if (badpages > PAGES - MIN_GOODPAGES)
+ errx(EXIT_FAILURE, _("Unable to set up swap-space: unreadable"));
+
+ goodpages = PAGES - badpages - 1;
+ printf(_("Setting up swapspace version 1, size = %llu KiB\n"),
+ goodpages * pagesize / 1024);
+
+ write_signature("SWAPSPACE2");
+ write_uuid_and_label(uuid, opt_label);
+
+ offset = 1024;
+ if (lseek(DEV, offset, SEEK_SET) != offset)
+ errx(EXIT_FAILURE, _("unable to rewind swap-device"));
+ if (write_all(DEV, (char *) signature_page + offset,
+ pagesize - offset) == -1)
+ err(EXIT_FAILURE,
+ _("%s: unable to write signature page"),
+ device_name);
+
+ /*
+ * A subsequent swapon() will fail if the signature
+ * is not actually on disk. (This is a kernel bug.)
+ */
+#ifdef HAVE_FSYNC
+ if (fsync(DEV))
+ errx(EXIT_FAILURE, _("fsync failed"));
+#endif
+
+#ifdef HAVE_LIBSELINUX
+ if (S_ISREG(statbuf.st_mode) && is_selinux_enabled() > 0) {
+ security_context_t context_string;
+ security_context_t oldcontext;
+ context_t newcontext;
+
+ if (fgetfilecon(DEV, &oldcontext) < 0) {
+ if (errno != ENODATA)
+ err(EXIT_FAILURE,
+ _("%s: unable to obtain selinux file label"),
+ device_name);
+ if (matchpathcon(device_name, statbuf.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(DEV, context_string))
+ err(EXIT_FAILURE, _("unable to relabel %s to %s"),
+ device_name, context_string);
+ }
+ context_free(newcontext);
+ freecon(oldcontext);
+ }
+#endif
+ return EXIT_SUCCESS;
+}
diff --git a/disk-utils/partx.8 b/disk-utils/partx.8
new file mode 100644
index 0000000..be1608d
--- /dev/null
+++ b/disk-utils/partx.8
@@ -0,0 +1,173 @@
+.\" partx.8 --
+.\" 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" "June 2012" "util-linux" "System Administration"
+.SH NAME
+partx \- tell the Linux kernel about the presence and numbering of
+on-disk partitions
+.SH SYNOPSIS
+partx [\-a|\-d|\-s] [\-t TYPE] [\-n M:N] [\-] disk
+.br
+partx [\-a|\-d|\-s] [\-t TYPE] partition [disk]
+.SH DESCRIPTION
+Given a device or disk-image,
+.B partx
+tries to parse the partition table and list its contents. It
+optionally adds or removes partitions.
+.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 "-". For example:
+
+.RS 7
+.TP
+partx \-\-show \- /dev/sda3
+.RE
+.PP
+This will see sda3 as a whole-disk rather than a partition.
+.PP
+The
+.B partx is not an fdisk program
+\-\- 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
+.IP "\fB\-a\fR, \fB\-\-add\fP"
+Add the specified partitions, or read the disk and add all partitions.
+.IP "\fB\-b\fR, \fB\-\-bytes\fP"
+Print the SIZE column in bytes rather than in human-readable format.
+.IP "\fB\-d\fR, \fB\-\-delete\fP"
+Delete the specified partitions or all partitions.
+.IP "\fB\-g\fR, \fB\-\-noheadings\fP"
+Do not print a header line.
+.IP "\fB\-h\fR, \fB\-\-help\fP"
+Print a help text and exit.
+.IP "\fB\-l\fR, \fB\-\-list\fP"
+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.
+.IP "\fB\-o\fR, \fB\-\-output \fIlist\fP"
+Define the output columns to use for
+.B \-\-show
+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
+.BR \-\-add ,
+.B \-\-delete
+or
+.B \-\-list
+options.
+.IP "\fB\-P\fR, \fB\-\-pairs\fP"
+Output using key="value" format.
+.IP "\fB\-n\fR, \fB\-\-nr \fIM:N\fP"
+Specify the range of partitions. For backward compatibility also the
+format
+.I M-N
+is supported. The range may contain negative numbers, for example
+.BI \-\-nr \ :\-1
+means the last partition, and
+.BI \-\-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\fR
+.IR 3 ).
+.TP
+.I M:
+Specifies lower limit only (e.g. \fB\-\-nr\fR
+.IR 2: ).
+.TP
+.I :N
+Specifies upper limit only (e.g. \fB\-\-nr\fR
+.IR :4 ).
+.TP
+.IR M:N \ or
+.TQ
+.I M-N
+Specifies lower and upper limits (e.g. \fB--nr\fR
+.IR 2:4 ).
+.RE
+.IP "\fB\-r\fR, \fB\-\-raw\fP"
+Use the raw output format.
+.IP "\fB\-s\fR, \fB\-\-show\fP"
+List the partitions. All numbers (except SIZE) are in 512-byte
+sectors. The output columns can be rearranged with the
+.B \-\-output
+option.
+.IP "\fB\-t\fR, \fB\-\-type \fItype\fP"
+Specify the partition table type
+.IR aix ,
+.IR bsd ,
+.IR dos ,
+.IR gpt ,
+.IR mac ,
+.IR minix ,
+.IR sgi ,
+.IR solaris_x86 ,
+.IR sun ,
+.I ultrix
+or
+.IR unixware .
+.IP "\fB\-v\fR, \fB\-\-verbose\fP"
+Verbose mode.
+.SH EXAMPLES
+.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 SEE ALSO
+.BR addpart (8),
+.BR delpart (8),
+.BR fdisk (8),
+.BR parted (8),
+.BR partprobe (8)
+.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 AVAILABILITY
+The partx command is part of the util-linux package and is available from
+.UR ftp://\:ftp.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..9764478
--- /dev/null
+++ b/disk-utils/partx.c
@@ -0,0 +1,891 @@
+/*
+ * 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 "c.h"
+#include "pathnames.h"
+#include "nls.h"
+#include "tt.h"
+#include "blkdev.h"
+#include "strutils.h"
+#include "xalloc.h"
+#include "partx.h"
+#include "sysfs.h"
+#include "loopdev.h"
+#include "at.h"
+#include "closestream.h"
+#include "optutils.h"
+
+/* this is the default upper limit, could be modified by --nr */
+#define SLICES_MAX 256
+
+/* 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_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; /* TT_FL_* */
+ const char *help;
+};
+
+/* columns descriptions */
+struct colinfo infos[] = {
+ [COL_PARTNO] = { "NR", 0.25, TT_FL_RIGHT, N_("partition number") },
+ [COL_START] = { "START", 0.30, TT_FL_RIGHT, N_("start of the partition in sectors") },
+ [COL_END] = { "END", 0.30, TT_FL_RIGHT, N_("end of the partition in sectors") },
+ [COL_SECTORS] = { "SECTORS", 0.30, TT_FL_RIGHT, N_("number of sectors") },
+ [COL_SIZE] = { "SIZE", 0.30, TT_FL_RIGHT, N_("human readable size") },
+ [COL_NAME] = { "NAME", 0.30, TT_FL_TRUNC, N_("partition name") },
+ [COL_UUID] = { "UUID", 36, 0, N_("partition UUID")},
+ [COL_SCHEME] = { "SCHEME", 0.1, TT_FL_TRUNC, N_("partition table type (dos, gpt, ...)")},
+ [COL_FLAGS] = { "FLAGS", 0.1, TT_FL_TRUNC, N_("partition flags")},
+ [COL_TYPE] = { "TYPE", 1, TT_FL_RIGHT, N_("partition type hex or uuid")},
+};
+
+#define NCOLS ARRAY_SIZE(infos)
+
+/* array with IDs of enabled columns */
+static int columns[NCOLS], 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 setup loop device"), fname);
+
+ loopdev = 1;
+}
+
+static inline int get_column_id(int num)
+{
+ assert(ARRAY_SIZE(columns) == NCOLS);
+ assert(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 sysfs_cxt cxt;
+ int rc;
+
+ if (sysfs_init(&cxt, devno, NULL))
+ goto err;
+
+ rc = sysfs_read_int(&cxt, "partition", &partno);
+ sysfs_deinit(&cxt);
+
+ if (rc == 0)
+ return partno;
+ }
+
+ sz = strlen(partition);
+ p = partition + sz - 1;
+
+ if (!isdigit((unsigned int) *p))
+ goto err;
+
+ while (isdigit((unsigned int) *(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)
+ continue;
+#endif
+ if (strncmp(parent, d->d_name, strlen(parent)))
+ continue;
+ snprintf(path, sizeof(path), "%s/partition", d->d_name);
+
+ fd = open_at(dirfd(dir), dirname, 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 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);
+
+ 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++) {
+ rc = partx_del_partition(fd, i);
+ if (rc == 0) {
+ if (verbose)
+ printf(_("%s: partition #%d removed\n"), device, i);
+ continue;
+ } else if (errno == ENXIO) {
+ if (verbose)
+ printf(_("%s: partition #%d already 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 = 0, errfirst = 0, errlast = 0;
+
+ assert(fd >= 0);
+ assert(device);
+ assert(ls);
+
+ 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 int list_parts(blkid_partlist ls, int lower, int upper)
+{
+ int i, nparts;
+
+ assert(ls);
+
+ 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(_("#%2d: %9ju-%9ju (%9ju sectors, %6ju MB)\n"),
+ n, start, start + size -1,
+ size, (size << 9) / 1000000);
+ }
+ return 0;
+}
+
+static void add_tt_line(struct tt *tt, blkid_partition par)
+{
+ struct tt_line *line;
+ int i;
+
+ assert(tt);
+ assert(par);
+
+ line = tt_add_line(tt, NULL);
+ if (!line) {
+ warn(_("failed to add line to output"));
+ return;
+ }
+
+ for (i = 0; i < ncolumns; i++) {
+ char *str = NULL;
+ int rc = 0;
+
+ switch (get_column_id(i)) {
+ case COL_PARTNO:
+ rc = xasprintf(&str, "%d",
+ blkid_partition_get_partno(par));
+ break;
+ case COL_START:
+ rc = xasprintf(&str, "%ju",
+ blkid_partition_get_start(par));
+ break;
+ case COL_END:
+ rc = xasprintf(&str, "%ju",
+ blkid_partition_get_start(par) +
+ blkid_partition_get_size(par) - 1);
+ break;
+ case COL_SECTORS:
+ rc = xasprintf(&str, "%ju",
+ blkid_partition_get_size(par));
+ break;
+ case COL_SIZE:
+ if (partx_flags & FL_BYTES)
+ rc = 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:
+ str = (char *) blkid_partition_get_name(par);
+ if (str)
+ str = xstrdup(str);
+ break;
+ case COL_UUID:
+ str = (char *) blkid_partition_get_uuid(par);
+ if (str)
+ str = xstrdup(str);
+ break;
+ case COL_TYPE:
+ str = (char *) blkid_partition_get_type_string(par);
+ if (str)
+ str = xstrdup(str);
+ else
+ rc = xasprintf(&str, "0x%x",
+ blkid_partition_get_type(par));
+ break;
+ case COL_FLAGS:
+ rc = xasprintf(&str, "0x%llx", blkid_partition_get_flags(par));
+ break;
+ case COL_SCHEME:
+ {
+ blkid_parttable tab = blkid_partition_get_table(par);
+ if (tab) {
+ str = (char *) blkid_parttable_get_type(tab);
+ if (str)
+ str = xstrdup(str);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (rc || str)
+ tt_line_set_data(line, i, str);
+ }
+}
+
+static int show_parts(blkid_partlist ls, int tt_flags, int lower, int upper)
+{
+ int i, rc = -1;
+ struct tt *tt;
+ int nparts;
+
+ assert(ls);
+
+ nparts = blkid_partlist_numof_partitions(ls);
+ if (!nparts)
+ return 0;
+
+ tt = tt_new_table(tt_flags);
+ if (!tt) {
+ warn(_("failed to initialize output table"));
+ return -1;
+ }
+
+ for (i = 0; i < ncolumns; i++) {
+ struct colinfo *col = get_column_info(i);
+
+ if (!tt_define_column(tt, col->name, col->whint, col->flags)) {
+ warnx(_("failed to initialize output column"));
+ 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;
+
+ add_tt_line(tt, par);
+ }
+
+ rc = 0;
+ tt_print_table(tt);
+done:
+ tt_free_table(tt);
+ 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(FILE *out)
+{
+ size_t i;
+
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %s [-a|-d|-s] [--nr <n:m> | <partition>] <disk>\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -a, --add add specified partitions or all of them\n"
+ " -d, --delete delete specified partitions or all of them\n"
+ " -l, --list list partitions (DEPRECATED)\n"
+ " -s, --show list partitions\n\n"
+
+ " -b, --bytes print SIZE in bytes rather than in human readable format\n"
+ " -g, --noheadings don't print headings for --show\n"
+ " -n, --nr <n:m> specify the range of partitions (e.g. --nr 2:4)\n"
+ " -o, --output <type> define which output columns to use\n"
+ " -P, --pairs use key=\"value\" output format\n"
+ " -r, --raw use raw output format\n"
+ " -t, --type <type> specify the partition type (dos, bsd, solaris, etc.)\n"
+ " -v, --verbose verbose mode\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+
+ fputs(_("\nAvailable columns (for --show, --raw or --pairs):\n"), out);
+
+ for (i = 0; i < NCOLS; i++)
+ fprintf(out, " %10s %s\n", infos[i].name, _(infos[i].help));
+
+ fprintf(out, USAGE_MAN_TAIL("partx(8)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int fd, c, what = ACT_NONE, lower = 0, upper = 0, rc = 0;
+ int tt_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;
+
+ 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' },
+ { "type", required_argument, NULL, 't' },
+ { "nr", required_argument, NULL, 'n' },
+ { "output", required_argument, NULL, 'o' },
+ { "pairs", no_argument, NULL, 'P' },
+ { "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 in ASCII order */
+ { 'P','a','d','l','r','s' },
+ { 0 }
+ };
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c = getopt_long(argc, argv,
+ "abdglrsvn:t:o:PhV", 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':
+ tt_flags |= TT_FL_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 'P':
+ tt_flags |= TT_FL_EXPORT;
+ what = ACT_SHOW;
+ break;
+ case 'r':
+ tt_flags |= TT_FL_RAW;
+ what = ACT_SHOW;
+ break;
+ case 's':
+ what = ACT_SHOW;
+ break;
+ case 't':
+ type = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'h':
+ usage(stdout);
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case '?':
+ default:
+ usage(stderr);
+ }
+ }
+
+ 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]);
+ }
+ } 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 failed %s"), 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
+ usage(stderr);
+
+ 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
+ ls = get_partlist(pr, wholedisk, type);
+
+ if (ls) {
+ int n = blkid_partlist_numof_partitions(ls);
+
+ if (lower < 0)
+ lower = n + lower + 1;
+ if (upper < 0)
+ upper = n + upper + 1;
+ if (lower > upper) {
+ warnx(_("specified range <%d:%d> "
+ "does not make sense"), lower, upper);
+ rc = -1, what = ACT_NONE;
+ }
+
+ switch (what) {
+ case ACT_SHOW:
+ rc = show_parts(ls, tt_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_NONE:
+ break;
+ default:
+ abort();
+ }
+ }
+ blkid_free_probe(pr);
+ }
+
+ if (loopdev)
+ loopcxt_deinit(&lc);
+
+ close(fd);
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/disk-utils/partx.h b/disk-utils/partx.h
new file mode 100644
index 0000000..254cd85
--- /dev/null
+++ b/disk-utils/partx.h
@@ -0,0 +1,75 @@
+#ifndef UTIL_LINUX_PARTX_H
+#define UTIL_LINUX_PARTX_H
+
+#include <sys/ioctl.h>
+#include <linux/blkpg.h>
+
+#ifndef BLKPG_ADD_PARTITION
+# define BLKPG_ADD_PARTITION 1
+#endif
+
+#ifndef BLKPG_DEL_PARTITION
+# define BLKPG_DEL_PARTITION 2
+#endif
+
+#ifndef BLKPG_RESIZE_PARTITION
+# define BLKPG_RESIZE_PARTITION 3 /* since Linux 3.6 */
+#endif
+
+static inline int partx_del_partition(int fd, unsigned int partno)
+{
+ struct blkpg_ioctl_arg a;
+ struct blkpg_partition p;
+
+ p.pno = partno;
+ p.start = 0;
+ p.length = 0;
+ p.devname[0] = 0;
+ p.volname[0] = 0;
+ a.op = BLKPG_DEL_PARTITION;
+ a.flags = 0;
+ a.datalen = sizeof(p);
+ a.data = &p;
+
+ return ioctl(fd, BLKPG, &a);
+}
+
+static inline int partx_add_partition(int fd, int partno,
+ uint64_t start, uint64_t size)
+{
+ struct blkpg_ioctl_arg a;
+ struct blkpg_partition p;
+
+ p.pno = partno;
+ p.start = start << 9;
+ p.length = size << 9;
+ p.devname[0] = 0;
+ p.volname[0] = 0;
+ a.op = BLKPG_ADD_PARTITION;
+ a.flags = 0;
+ a.datalen = sizeof(p);
+ a.data = &p;
+
+ return ioctl(fd, BLKPG, &a);
+}
+
+static inline int partx_resize_partition(int fd, int partno,
+ uint64_t start, uint64_t size)
+{
+ struct blkpg_ioctl_arg a;
+ struct blkpg_partition p;
+
+ p.pno = partno;
+ p.start = start << 9;
+ p.length = size << 9;
+ p.devname[0] = 0;
+ p.volname[0] = 0;
+ a.op = BLKPG_RESIZE_PARTITION;
+ a.flags = 0;
+ a.datalen = sizeof(p);
+ a.data = &p;
+
+ return ioctl(fd, BLKPG, &a);
+}
+
+#endif /* UTIL_LINUX_PARTX_H */
diff --git a/disk-utils/raw.8 b/disk-utils/raw.8
new file mode 100644
index 0000000..0e47843
--- /dev/null
+++ b/disk-utils/raw.8
@@ -0,0 +1,92 @@
+.\" -*- nroff -*-
+.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
+.B -q
+Set query mode.
+.B raw
+will query an existing binding instead of setting a new one.
+.TP
+.B -a
+With
+.B -q
+, specify that all bound raw devices should be queried.
+.TP
+.B -h
+Provide a usage summary.
+.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 AUTHOR
+Stephen Tweedie (sct@redhat.com)
+.SH AVAILABILITY
+The raw command is part of the util-linux package and is available from
+ftp://ftp.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..eb58e96
--- /dev/null
+++ b/disk-utils/raw.c
@@ -0,0 +1,270 @@
+/*
+ * 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 <sys/sysmacros.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
+
+int do_query;
+int do_query_all;
+
+int master_fd;
+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(int err)
+{
+ FILE *out = err == EXIT_SUCCESS ? stdout : stderr;
+
+ 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_OPTIONS, out);
+ fputs(_(" -q, --query set query mode\n"), out);
+ fputs(_(" -a, --all query all raw devices\n"), out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("raw(8)"));
+ exit(err);
+}
+
+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, 0, 'q'},
+ {"all", no_argument, 0, 'a'},
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {NULL, no_argument, 0, '0'},
+ };
+
+ setlocale(LC_MESSAGES, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ 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':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(EXIT_SUCCESS);
+ default:
+ usage(EXIT_FAILURE);
+ }
+
+ /*
+ * Check for, and open, the master raw device, /dev/raw
+ */
+ open_raw_ctl();
+
+ if (do_query_all) {
+ if (optind < argc)
+ usage(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)
+ usage(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)
+ usage(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:
+ usage(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))
+ 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))
+ 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..c009cc3
--- /dev/null
+++ b/disk-utils/resizepart.8
@@ -0,0 +1,38 @@
+.\" resizepart.8 --
+.\" Copyright 2012 Vivek Goyal <vgoyal@redhat.com>
+.\" Copyright 2012 Red Hat, Inc.
+.\" May be distributed under the GNU General Public License
+.TH RESIZEPART 8 "February 2012" "util-linux" "System Administration"
+.SH NAME
+resizepart \-
+simple wrapper around the "resize partition" ioctl
+.SH SYNOPSIS
+.B resizepart
+.I device partition length
+.SH DESCRIPTION
+.B resizepart
+is a program that informs the Linux kernel of new partition size.
+
+This command doesn't manipulate partitions on hard drive.
+
+.SH PARAMETERS
+.TP
+.I device
+Specify the disk device.
+.TP
+.I partition
+Specify the partition number.
+.TP
+.I length
+Specify the 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
+ftp://ftp.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..5517c4f
--- /dev/null
+++ b/disk-utils/resizepart.c
@@ -0,0 +1,106 @@
+#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"
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s <disk device> <partition number> <length>\n"),
+ program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("resizepart(8)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static int get_partition_start(int fd, int partno, uint64_t *start)
+{
+ struct stat st;
+ struct sysfs_cxt disk = UL_SYSFSCXT_EMPTY,
+ part = UL_SYSFSCXT_EMPTY;
+ dev_t devno = 0;
+ int rc = -1;
+
+ /*
+ * wholedisk
+ */
+ if (fstat(fd, &st) || !S_ISBLK(st.st_mode))
+ goto done;
+ devno = st.st_rdev;
+ if (sysfs_init(&disk, devno, NULL))
+ goto done;
+ /*
+ * partition
+ */
+ devno = sysfs_partno_to_devno(&disk, partno);
+ if (!devno)
+ goto done;
+ if (sysfs_init(&part, devno, &disk))
+ goto done;
+ if (sysfs_read_u64(&part, "start", start))
+ goto done;
+
+ rc = 0;
+done:
+ sysfs_deinit(&part);
+ sysfs_deinit(&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, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {NULL, no_argument, 0, '0'},
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ while ((c = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
+ switch (c) {
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ if (argc != 4)
+ usage(stderr);
+
+ 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"));
+
+ return 0;
+}
diff --git a/disk-utils/swaplabel.8 b/disk-utils/swaplabel.8
new file mode 100644
index 0000000..0d47456
--- /dev/null
+++ b/disk-utils/swaplabel.8
@@ -0,0 +1,67 @@
+.\" 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
+.IR 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 to change 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) .
+.PP
+.SH AUTHOR
+.B swaplabel
+was written by Jason Borden <jborden@bluehost.com> and Karel Zak <kzak@redhat.com>.
+.SH AVAILABILITY
+The swaplabel command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.SH SEE ALSO
+.BR mkswap (8),
+.BR swapon (8),
+.BR uuidgen (1)
diff --git a/disk-utils/swaplabel.c b/disk-utils/swaplabel.c
new file mode 100644
index 0000000..3e3e78b
--- /dev/null
+++ b/disk-utils/swaplabel.c
@@ -0,0 +1,228 @@
+/*
+ * 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 <blkid.h>
+#include <getopt.h>
+
+#ifdef HAVE_LIBUUID
+# include <uuid.h>
+#endif
+
+#include "c.h"
+#include "closestream.h"
+#include "all-io.h"
+#include "swapheader.h"
+#include "strutils.h"
+#include "nls.h"
+
+#define SWAP_UUID_OFFSET (offsetof(struct swap_header_v1_2, uuid))
+#define SWAP_LABEL_OFFSET (offsetof(struct swap_header_v1_2, volume_name))
+
+/*
+ * Returns new libblkid prober. This function call exit() on error.
+ */
+static blkid_probe get_swap_prober(const char *devname)
+{
+ blkid_probe pr;
+ int rc;
+ const char *version = NULL;
+ char *swap_filter[] = { "swap", NULL };
+
+ pr = blkid_new_probe_from_filename(devname);
+ if (!pr) {
+ warn(_("%s: unable to probe device"), devname);
+ return NULL;
+ }
+
+ blkid_probe_enable_superblocks(pr, TRUE);
+ blkid_probe_set_superblocks_flags(pr,
+ BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
+ BLKID_SUBLKS_VERSION);
+
+ blkid_probe_filter_superblocks_type(pr, BLKID_FLTR_ONLYIN, swap_filter);
+
+ rc = blkid_do_safeprobe(pr);
+ if (rc == -1)
+ warn(_("%s: unable to probe device"), devname);
+ else if (rc == -2)
+ warnx(_("%s: ambivalent probing result, use wipefs(8)"), devname);
+ else if (rc == 1)
+ warnx(_("%s: not a valid swap partition"), devname);
+
+ if (rc == 0) {
+ /* supported is SWAPSPACE2 only */
+ if (blkid_probe_lookup_value(pr, "VERSION", &version, NULL) == 0
+ && version
+ && strcmp(version, "2"))
+ warnx(_("%s: unsupported swap version '%s'"),
+ devname, version);
+ else
+ return pr;
+ }
+
+ blkid_free_probe(pr);
+ return NULL;
+}
+
+/* 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 */
+static int change_info(const char *devname, const char *label, const char *uuid)
+{
+ 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;
+ }
+ }
+
+ close(fd);
+ return 0;
+err:
+ if (fd >= 0)
+ close(fd);
+ return -1;
+}
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s [options] <device>\n"),
+ program_invocation_short_name);
+ 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);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("swaplabel(8)"));
+ exit(out == stderr ? EXIT_FAILURE : 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", 0, 0, 'h' },
+ { "version", 0, 0, 'V' },
+ { "label", 1, 0, 'L' },
+ { "uuid", 1, 0, 'U' },
+ { NULL, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c = getopt_long(argc, argv, "hVL:U:", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'h':
+ usage(stdout);
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'L':
+ label = optarg;
+ break;
+ case 'U':
+#ifdef HAVE_LIBUUID
+ uuid = optarg;
+#else
+ warnx(_("ignore -U (UUIDs are unsupported)"));
+#endif
+ break;
+ default:
+ usage(stderr);
+ break;
+ }
+ }
+
+ if (optind == argc)
+ usage(stderr);
+
+ 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;
+}
+