diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-11-02 20:15:39 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-11-02 20:15:39 +0400 |
commit | b13154de3eca5ba28fbb4854d916cd0be5febeed (patch) | |
tree | 30f2e9e89ab71a2df837076ac68c3ba770230294 /disk-utils | |
download | util-linux-upstream.tar.gz |
Imported Upstream version 2.22upstream/2.22upstream
Diffstat (limited to 'disk-utils')
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)¶m) < 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, §orsize) == -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; +} + |