diff options
Diffstat (limited to 'sutils')
-rw-r--r-- | sutils/ChangeLog | 375 | ||||
-rw-r--r-- | sutils/MAKEDEV.sh | 181 | ||||
-rw-r--r-- | sutils/Makefile | 44 | ||||
-rw-r--r-- | sutils/clookup.c | 168 | ||||
-rwxr-xr-x | sutils/e2os.sh | 153 | ||||
-rw-r--r-- | sutils/fsck.c | 568 | ||||
-rw-r--r-- | sutils/fstab.c | 956 | ||||
-rw-r--r-- | sutils/fstab.h | 178 | ||||
-rw-r--r-- | sutils/halt.c | 39 | ||||
-rw-r--r-- | sutils/losetup.sh | 66 | ||||
-rw-r--r-- | sutils/reboot.c | 39 | ||||
-rw-r--r-- | sutils/swapoff.c | 2 | ||||
-rw-r--r-- | sutils/swapon.c | 165 | ||||
-rw-r--r-- | sutils/update.c | 54 |
14 files changed, 2988 insertions, 0 deletions
diff --git a/sutils/ChangeLog b/sutils/ChangeLog new file mode 100644 index 00000000..11c34ba9 --- /dev/null +++ b/sutils/ChangeLog @@ -0,0 +1,375 @@ +1999-12-03 Roland McGrath <roland@baalperazim.frob.com> + + * MAKEDEV.sh (mkdev: full): New device, using /hurd/null --full. + (mkdev: std): Add full to the standard list. + +1999-11-19 Roland McGrath <roland@baalperazim.frob.com> + + * MAKEDEV.sh (mkdev: time): Use --no-cache flag. + (mkdev: mem): New storeio device. + (mkdev: std): Add mem to standard list. + + * Makefile (progs, scripts): New variables. + (targets, special-targets, SRCS, OBJS): Define in terms of those. + (libshouldbeinlibc dep rule): Replaced with static pattern rule + with $(progs) as target list, giving %.o dep as well. + +1999-11-16 Roland McGrath <roland@baalperazim.frob.com> + + * swapon.c (swaponoff): Check for a null default pager port. + +1999-10-01 Roland McGrath <roland@baalperazim.frob.com> + + * fstab.c (fstypes_get): Check strdup return for ENOMEM failure. + (fstypes_find_program): New function, #if 0'd out for now. + (fstab_argp, fstab_argp_create, options, parse_opt): New variables + and functions. + * fstab.h: Misc comment fixes. + (struct fstab_argp_params): New type. + (fstab_argp, fstab_argp_create): Declare them. + * fsck.c (options): Remove --fstab/-F, --search-fmts/-S, + --exclude-root/-R, --exclude/-X, --fstype/-t, --all/-A; + all of these are now factored out into fstab_argp. + (main): Remove parsing code for options now in fstab_argp. + Instead, use fstab_argp as an argp_child and use fstab_argp_create + to process the option-parsing results into a struct fstab * to process. + + * fstab.c (fstab_read): Undo change of 1999-09-17. + It should not be necessary at all, given 1999-05-23 change, and: + (fstab_find_mount): Do not consider name "ignore" to match any mount. + +1999-10-11 Roland McGrath <roland@baalperazim.frob.com> + + * MAKEDEV.sh (fd): Pass --directory to /hurd/magic translator. + +1999-09-17 Thomas Bushnell, BSG <tb@mit.edu> + + * fstab.c (fstab_read): Ignore filesystems with types of ignore, + nfs, or swap. + +1999-09-14 Thomas Bushnell, BSG <tb@mit.edu> + + * MAKEDEV.sh (mkdev): pty master and slave nodes should have mode + 666, not 640. Reported by Marcus Brinkmann + <Marcus.Brinkmann@ruhr-uni-bochum.de>. + +1999-07-10 Roland McGrath <roland@baalperazim.frob.com> + + * clookup.c: Add #include <sys/mman.h>. + + * Makefile (special-targets): Add losetup. + (SCRIPTS): Add losetup.sh. + * losetup.sh: New file. + +1999-07-03 Thomas Bushnell, BSG <tb@mit.edu> + + * clookup.c (file_name_lookup_carefully): Use munmap instead of + vm_deallocate. + +1999-06-11 Roland McGrath <roland@baalperazim.frob.com> + + * fstab.c (fstypes_get): Don't free PROGRAM twice on ENOENT failure. + + * fsck.c (options): Add -t/--fstype option. + (main): Parse that option, and use it to constrain fstab list. + + * fsck.c (FSCK_F_DRYRUN): New macro. + (options, main): Add -N/--dry-run option to set it. + (fs_start_fsck): If FSCK_F_DRYRUN set, just print out and return zero. + * fsck.c (fscks_start_fsck): If FSCK_F_DRYRUN is set, don't try to + make read-only, just print a message that we would. + + * fsck.c (options, main): Change --fstab from -t to -F. + +1999-06-10 Roland McGrath <roland@baalperazim.frob.com> + + * fsck.c (options): Add -A/--all and -R/--exclude-root options for + compatibility with Linux, and --exclude=PATTERN as general form. + (main): Parse them. Constrain fstab list by exclude patterns. + + * fsck.c (fsck): Don't check for "noauto" mntopt. No other system + skips a filesystem because of it. + (fsck): Always start at pass 1, not 0. In every other system, + "pass 0" always means to skip that filesystem entirely. + +1999-05-23 Roland McGrath <roland@baalperazim.frob.com> + + * MAKEDEV.sh (ECHO, EXEC): Do not export. + (DEVDIR): Initialize to `pwd` before argument parsing. + (_CWD): Don't set this. + (lose): New function. Use it for all miscellaneous fatal errors. + (mkdev): Disallow directory names. Always use ${DEVDIR} for name to + embed in translator settings. + Rewrite syntax checking for disk device names. + + * fstab.c, fstab.h: Add numerous `const' qualifiers. + + * fstab.c (fstab_find_mount): Don't count "none" or "-" as matching + any other entry. + + * fsck.c (struct fsck): Don't use bitfield. + +1999-05-15 Roland McGrath <roland@baalperazim.frob.com> + + * swapon.c: Support -a (reading fstab). + +1999-05-13 Roland McGrath <roland@baalperazim.frob.com> + + * MAKEDEV.sh (case 'time'): Make /dev/time mode 644, not 666. + +Thu Feb 18 02:13:47 1999 Thomas Bushnell, BSG <tb@mit.edu> + + * MAKEDEV.sh (mkdev): put `time' in single-quotes; it's a reserved + word in bash 2.02 and this protects it. Reported by OKUJI + Yoshinori <okuji@kuicr.kyoto-u.ac.jp>. + +Mon Feb 1 16:27:15 1999 Thomas Bushnell, BSG <tb@mit.edu> + + * MAKEDEV.sh (PATH): Add /usr/bin out of deference to users + lacking the /usr symlink. Requested by Marcus Brinkmann + <Marcus.Brinkmann@ruhr-uni-bochum.de>. + +1998-11-29 Roland McGrath <roland@baalperazim.frob.com> + + * fsck.c (main): Add braces to silence gcc warning. + + * fstab.c: Rename fsys_remount -> fsys_update in extern decl. + +1998-11-28 Roland McGrath <roland@baalperazim.frob.com> + + * clookup.c (file_name_lookup_carefully): Change __getuids to geteuids. + Use hurd_file_name_lookup instead of __hurd_file_name_lookup. + +1998-10-24 Roland McGrath <roland@baalperazim.frob.com> + + * clookup.c (file_name_lookup_carefully): Use getdport instead of + __getdport, getumask () instead of _hurd_umask. + +1998-10-20 Roland McGrath <roland@baalperazim.frob.com> + + * fstab.c (fstab_add_mntent): Add braces to silence gcc warning. + (fs_readonly): Likewise. + +1998-07-19 Roland McGrath <roland@baalperazim.frob.com> + + * swapon.c (main): Fix return type to int, and use return. + +1997-10-28 Miles Bader <miles@gnu.ai.mit.edu> + + * MAKEDEV.sh (mkdev): Support 256 ptys. + +1997-08-20 Miles Bader <miles@gnu.ai.mit.edu> + + * fstab.c (_fs_check_mounted): file_get_translator_cntl can also + return ENXIO for an inactive translator. + +1997-08-19 Miles Bader <miles@gnu.ai.mit.edu> + + * MAKEDEV.sh (mkdev): Handle devices of the form "cd*". + +1997-07-22 Miles Bader <miles@gnu.ai.mit.edu> + + * MAKEDEV.sh (time): Use /hurd/storeio instead of /hurd/devport. + +1997-07-09 Miles Bader <miles@gnu.ai.mit.edu> + + * Makefile (targets): Add swapon & swapoff. + (SRCS): Add swapon.c & swapoff.c. + Add dependencies on libstore. + (HURDLIBS): Add libstore. + +1997-06-25 Miles Bader <miles@gnu.ai.mit.edu> + + * MAKEDEV.sh (mkdev): Add missing `ln' to command for stderr. + +1997-06-24 Miles Bader <miles@gnu.ai.mit.edu> + + * MAKEDEV.sh: Use bash fancy variables instead of various programs. + Try to get the correct devdir even if it's not the cwd. + Add new options --verbose/-v and --dry-run/-n. + (cmd): New function. + (st, mkdir): Use cmd to execute any real commands. + +1997-06-23 Miles Bader <miles@gnu.ai.mit.edu> + + * MAKEDEV.sh: Add the --devdir/-D option. + Use the basename of $I instead of $I for various things. + Correctly put derived names in the same directory as the arg. + +1997-06-21 Miles Bader <miles@gnu.ai.mit.edu> + + * e2os.sh: Use conv=notrunc when writing the superblock back to + the filesystem device, in case it's a file and not a real device. + + * e2os.sh: If $OD & $AWK don't exist, try to get them from + /usr/bin, so that this script works under linux too. + +Fri Feb 28 21:27:20 1997 Miles Bader <miles@gnu.ai.mit.edu> + + * fsck.c (fsck_cleanup): Correctly remove from fscks chain. + +Sun Feb 23 02:57:46 1997 Miles Bader <miles@gnu.ai.mit.edu> + + * MAKEDEV.sh (mkdev): Make I local. + +Wed Feb 19 23:08:04 1997 Miles Bader <miles@gnu.ai.mit.edu> + + * halt.c, reboot.c, fsck.c (argp_program_version): Make const. + +Wed Feb 5 11:50:19 1997 Miles Bader <miles@gnu.ai.mit.edu> + + * fstab.c (fstypes_get): Improve error behavior a bit. + + * fsck.c (fsck): In non-automatic mode, print a warning if a + filesystem is of a type that can't be fscked. + +Tue Feb 4 17:09:39 1997 Miles Bader <miles@gnu.ai.mit.edu> + + * fstab.c (fs_set_mntent): Avoid null deferences. + (fstypes_get): Fill in FSTYPE in the case where we added a new type. + +Thu Sep 26 15:36:55 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * Makefile (targets, special-targets): Add MAKEDEV. + (SCRIPTS): Add MAKEDEV.sh. + (DIST_FILES): Variable removed. + (all): Target removed. + ($(prefix)/dev/MAKEDEV): Get rid of dependencies. + Implement with a symbolic link to /sbin/MAKEDEV. + (%: %.sh): Variable removed. + +Tue Sep 24 14:46:11 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * MAKEDEV.sh: Fix argument parsing. + (zero): Use `storeio -Tzero' instead of `null -z'. + +Thu Sep 19 17:48:59 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * fstab.c (fs_remount): Use fsys_update instead of fsys_remount. + +Wed Sep 18 19:12:19 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * MAKEDEV.sh: Use a shell function `mkdev' for recursion, rather + than calling this script recursively. + +Thu Sep 12 18:53:42 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * fsck.c (fs_start_fsck): Print an error for filesystem types we + don't know how to fsck, instead of dying with an assertion failure. + +Thu Sep 19 16:58:18 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu> + + * Makefile (install): Depend on installation directory directly; + don't make installed files depend on it. + +Tue Sep 17 12:36:09 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu> + + * Makefile ($(prefix)/dev/MAKEDEV): Depend on $(prefix)/dev. + ($(prefix)/dev): New target. + +Thu Sep 12 16:38:11 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu> + + * Makefile (HURDLIBS): New variable. + +Sun Sep 8 13:57:34 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * MAKEDEV.sh: New file. + * Makefile (DIST_FILES): New variable. + (install): Depend on $(prefix)/dev/MAKEDEV. + (all): Depend on MAKEDEV. + ($(prefix)/dev/MAKEDEV): New rule. + +Thu Sep 5 11:40:00 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu> + + * Makefile: Use $(top_srcdir)/sh-version.sed to make executables + from .sh files. + + * fsck.c: Include <version.h>. + (argp_program_version): Define with STANDARD_HURD_VERSION. + * halt.c: Likewise. + * reboot.c: Likewise. + +Tue Aug 27 12:06:55 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * reboot.c, halt.c (main): Add argument parsing. + (argp_program_version): New variable. + <argp.h>, <hurd.h>: New includes. + * Makefile (fsck): Remove dependency on libshouldbeinlibc.a. + (reboot halt fsck): Add dependency on libshouldbeinlibc.a. + +Mon Aug 19 15:17:38 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * fsck.c (doc): Supply a useful value. + +Wed Aug 14 13:32:12 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * e2os.sh: Add `;' before last commands inside { } pairs. + (OS_FREEBSD, OS_LITES): New variables. + Add symbolic names for freebsd & lites. + +Mon Aug 12 10:51:24 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * e2os.sh: New file. + * Makefile (targets): Add e2os. + (SRCS): Add $(SCRIPTS). + (special-targets, SCRIPTS, LCLHDRS): New variables. + +Thu Aug 1 16:29:31 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu> + + * clookup.c (file_name_lookup_carefully/lookup): When appending + TAIL to RETRY_NAME, use strcpy instead of strcat. + +Sat Jul 6 19:55:22 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * fsck.c: (argp_program_version): New variable. + +Wed Jul 3 14:22:03 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * fstab.c (fstab_add_fs): Don't SEGV if COPY is 0. + +Thu Jun 27 00:01:04 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * fsck.c (fsck): Fsck pass 0 if not in automatic mode. Respect + `noauto' option in automatic mode. + (main): Set FSCK_F_AUTO flag if in automatic mode. + (FSCK_F_AUTO): New macro. + (fs_start_fsck): When deciding to use a flags arg, mask flags + against an explicit list of valid ones. + +Tue Jun 25 18:39:44 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * fsck.c (main, options): Add --writable/-w option. + (FSCK_F_WRITABLE): New macro. + (struct fsck): Rename WAS_READONLY field to MAKE_WRITABLE. + (fscks_start_fsck): Change to set make_writable field instead of + was_readonly. + (fsck_cleanup): Change RESTORE_WRITABLE to MAKE_WRITABLE. + (fscks_wait): Likewise. + +Thu Jun 20 14:08:12 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * fsck.c (fsck): Wait for fscks to finish. + * fstab.c (fstypes_create): Copy SEARCH_FMTS contents into NEW. + (fs_set_mntent): Don't keep old fsys fields if the mnt_dir is changed. + (fstab_add_mntent): Initialize fields in FS with non-zero values. + (_fs_check_mounted): The root is always mounted. + +Wed Jun 19 10:44:16 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * fstab.c (fs_set_readonly, fs_remount): If fsys_set_options + returns EINVAL, return EOPNOTSUPP instead. + (fs_set_readonly): Use fsys_set_readonly. + (fs_remount): Use fsys_remount. + (fs_readonly): Use fsys_get_readonly. + (_fs_check_mounted): Use file_name_lookup_carefully. + * clookup.c: New file. + * fsck.c (_debug): New variable. + (debug, fs_debug): New macros. + (fs_start_fsck, fscks_start_fsck, fsck_cleanup, fscks_wait, fsck, main): + Add debugging noise. + (main): Use FSTAB_PATH instead of _PATH_MNTTAB. + (args_doc): Fix. + +Tue Jun 18 22:56:11 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * fsck.c (options): Rename `--max-parallel' to `--parallel'. diff --git a/sutils/MAKEDEV.sh b/sutils/MAKEDEV.sh new file mode 100644 index 00000000..511deed2 --- /dev/null +++ b/sutils/MAKEDEV.sh @@ -0,0 +1,181 @@ +#!/bin/sh +# +# Make standard devices +# + +PATH=/bin:/usr/bin + +ECHO=: # Change to "echo" to echo commands. +EXEC="" # Change to ":" to suppress command execution. +DEVDIR=`pwd` # Reset below by -D/--devdir command line option. + +while :; do + case "$1" in + --help|"-?") + echo "\ +Usage: $0 [OPTION...] DEVNAME... +Make filesystem nodes for accessing standard system devices + + -D, --devdir=DIR Use DIR when a device node name must be + embedded in a translator; default is the cwd + -n, --dry-run Don't actually execute any commands + -v, --verbose Show what commands are executed to make the devices + -?, --help Give this help list + --usage Give a short usage message + -V, --version Print program version" + exit 0;; + --devdir) DEVDIR="$2"; shift 2;; + --devdir=*) DEVDIR="`echo "$1" | sed 's/^--devdir=//'`"; shift 1;; + -D) DEVDIR="$2"; shift 2;; + -D*) DEVDIR="`echo "$1" | sed 's/^-D//'`"; shift 1;; + --verbose|-v) ECHO=echo; shift;; + --dry-run|-n) EXEC=:; shift;; + -nv|-vn) ECHO=echo; EXEC=:; shift;; + --usage) + echo "Usage: $0 [-V?] [-D DIR] [--help] [--usage] [--version] [--devdir=DIR] DEVNAME..." + exit 0;; + --version|-V) + echo "STANDARD_HURD_VERSION_MAKEDEV_"; exit 0;; + -*) + echo 1>&2 "$0: unrecognized option \`$1'" + echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information"; + exit 1;; + *) + break;; + esac +done + +case "$#" in 0) + echo 1>&2 "Usage: $0 [OPTION...] DEVNAME..." + echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information" + exit 1;; +esac + +function cmd { + eval $ECHO "$@" + eval $EXEC "$@" +} + +function st { + local NODE="$1" + local OWNER="$2" + local PERM="$3" + shift 3 + if cmd settrans -cg "$NODE"; then + cmd chown "$OWNER" "$NODE" + cmd chmod "$PERM" "$NODE" + cmd settrans "$NODE" "$@" + fi +} + +function lose { + local line + for line; do + echo 1>&2 "$0: $line" + done + exit 1 +} + +function mkdev { + local I + for I; do + case "$I" in + /* | */*) + lose "Device names cannot contain directories" \ + "Change to target directory and run $0 from there." + ;; + + std) + mkdev console tty null zero full fd time mem + ;; + console|tty[0-9][0-9a-f]|tty[0-9a-f]|com[0-9]) + st $I root 600 /hurd/term ${DEVDIR}/$I device $I;; + null) + st $I root 666 /hurd/null;; + full) + st $I root 666 /hurd/null --full;; + zero) + st $I root 666 /hurd/storeio -Tzero;; + tty) + st $I root 666 /hurd/magic tty;; + fd) + st $I root 666 /hurd/magic --directory fd + cmd ln -f -s fd/0 stdin + cmd ln -f -s fd/1 stdout + cmd ln -f -s fd/2 stderr + ;; + 'time') + st $I root 644 /hurd/storeio --no-cache time ;; + mem) + st $I root 660 /hurd/storeio --no-cache mem ;; + + # ptys + [pt]ty[pqrstuvwxyzPQRST]?) + # Make one pty, both the master and slave halves. + local id="${I:3}" + st pty$id root 666 /hurd/term ${DEVDIR}/pty$id \ + pty-master ${DEVDIR}/tty$id + st tty$id root 666 /hurd/term ${DEVDIR}/tty$id \ + pty-slave ${DEVDIR}/pty$id + ;; + [pt]ty[pqrstuvwxyzPQRST]) + # Make a bunch of ptys. + mkdev ${I}0 ${I}1 ${I}2 ${I}3 ${I}4 ${I}5 ${I}6 ${I}7 + mkdev ${I}8 ${I}9 ${I}a ${I}b ${I}c ${I}d ${I}e ${I}f + ;; + + fd*|mt*) + st $I root 640 /hurd/storeio $I + ;; + + [hrsc]d*) + local n="${I#?d}" + local major="${n%%[!0-9]*}" + if [ -z "$major" ]; then + lose "$I: Invalid device name: must supply a device number" + fi + local minor="${n##$major}" + case "$minor" in + '') ;; # Whole disk + [a-z]) ;; # BSD partition syntax, no slice syntax + s[1-9]*) # Slice syntax. + local slicestuff="${minor#s}" + local slice="${slicestuff%%[!0-9]*}" + local rest="${slicestuff##$slice}" + case "$slice" in + [1-9] | [1-9][0-9]) ;; + *) + lose "$I: Invalid slice number \`$slice'" + ;; + esac + case "$rest" in + '') ;; # Whole slice + [a-z]) ;; # BSD partition after slice + *) + lose "$I: Invalid partition \`$rest'" + ;; + esac + ;; + *) + lose "$I: Invalid slice or partition syntax" + ;; + esac + + # The device name passed all syntax checks, so finally use it! + st $I root 640 /hurd/storeio $I + ;; + + # Linux compatibility + loop*) + # In Linux an inactive "/dev/loopN" device acts like /dev/null. + # The `losetup' script changes the translator to "activate" the device. + st $I root 640 /hurd/null + ;; + *) + lose "$I: Unknown device name" + ;; + esac + done +} + +mkdev "$@" diff --git a/sutils/Makefile b/sutils/Makefile new file mode 100644 index 00000000..5b5c2110 --- /dev/null +++ b/sutils/Makefile @@ -0,0 +1,44 @@ +# Makefile for sutils +# +# Copyright (C) 1996,97,99 Free Software Foundation, Inc. +# +# 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +dir := sutils +makemode := utilities + +progs = reboot halt fsck swapon swapoff +scripts = e2os MAKEDEV losetup +targets = $(special-targets) $(progs) +special-targets = $(scripts) +installationdir = $(sbindir) +SRCS = $(progs:=.c) $(scripts:=.sh) +LCLHDRS = fstab.h + +OBJS = $(progs:=.c) +HURDLIBS = store shouldbeinlibc + +include ../Makeconf + +fsck: fstab.o clookup.o +swapon swapoff: ../libstore/libstore.a +$(progs): %: %.o ../libshouldbeinlibc/libshouldbeinlibc.a + +install: $(prefix)/dev $(prefix)/dev/MAKEDEV +$(prefix)/dev/MAKEDEV: + ln -s ../sbin/MAKEDEV $@ +$(prefix)/dev: + @$(MKINSTALLDIRS) $@ diff --git a/sutils/clookup.c b/sutils/clookup.c new file mode 100644 index 00000000..d0f69d9b --- /dev/null +++ b/sutils/clookup.c @@ -0,0 +1,168 @@ +/* Careful filename lookup + + Copyright (C) 1996, 1998, 1999 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This file is part of the GNU Hurd. + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <hurd.h> +#include <hurd/lookup.h> +#include <hurd/id.h> +#include <hurd/fsys.h> + + +/* This function is like file_name_lookup, but tries hard to avoid starting + any passive translators. If a node with an unstarted passive translator + is encountered, ENXIO is returned in ERRNO; other errors are as for + file_name_lookup. Note that checking for an active translator currently + requires fetching the control port, which is a priveleged operation. */ +file_t +file_name_lookup_carefully (const char *name, int flags, mode_t mode) +{ + error_t err; + file_t node; + uid_t *uids; /* Authentication of the current process. */ + gid_t *gids; + size_t num_uids, num_gids; + + /* Do the actual directory lookup. We only do the first pathname element + of NAME, appending the rest to any RETRY_NAME returned. We then make + sure the result node doesn't have a passive translator with no active + translator started (but we make an exception for symlinks) -- if it + does, we just return ENXIO. */ + error_t lookup (file_t dir, char *name, int flags, mode_t mode, + retry_type *retry, string_t retry_name, + mach_port_t *node) + { + error_t err; + char *head, *tail; + char *slash = index (name, '/'); + + if (slash) + { + *stpncpy (head = alloca (slash - name + 1), name, slash - name) = 0; + tail = slash + 1; + } + else + { + head = name; + tail = 0; + } + + err = dir_lookup (dir, head, flags | O_NOTRANS, mode, + retry, retry_name, node); + if (err) + return err; + + if (*node != MACH_PORT_NULL + && (!(flags & O_NOTRANS) || tail || *retry_name)) + /* The dir_lookup has returned a node to use for the next stage of + the lookup. Unless it's the last element of the path and FLAGS + has O_NOTRANS set (in which case we just return what we got as + is), we have to simulate the above lookup being done without + O_NOTRANS. Do this being careful not to start any translators. */ + { + char _ptrans[1024], *ptrans = _ptrans; + size_t ptrans_len = sizeof _ptrans; + + err = file_get_translator (*node, &ptrans, &ptrans_len); + if (! err) + /* Has a passive translator, see if there's an active one too. */ + { + fsys_t fsys; /* Active translator control port. */ + + if (ptrans != _ptrans) + /* Deallocate out-of-line memory from file_get_translator. */ + munmap (ptrans, ptrans_len); + + err = file_get_translator_cntl (*node, &fsys); + if (! err) + /* There is! Get its root node to use as the actual file. */ + { + file_t unauth_dir; /* DIR unauthenticated. */ + err = io_restrict_auth (dir, &unauth_dir, 0, 0, 0, 0); + if (! err) + { + file_t old_node = *node; + err = fsys_getroot (fsys, + unauth_dir, MACH_MSG_TYPE_MOVE_SEND, + uids, num_uids, gids, num_gids, + flags & ~O_NOTRANS, retry, + retry_name, node); + if (! err) + mach_port_deallocate (mach_task_self (), old_node); + } + mach_port_deallocate (mach_task_self (), fsys); + } + } + else if (err == EINVAL) + /* No passive translator. */ + err = 0; + + if (!err && tail) + /* Append TAIL to RETRY_NAME. */ + { + size_t rtn_len = strlen (retry_name); + if (rtn_len + 1 + strlen (tail) + 1 > sizeof (string_t)) + err = ENAMETOOLONG; /* Argh. Lovely string_t. */ + else + { + if (rtn_len > 0 && retry_name[rtn_len - 1] != '/') + retry_name[rtn_len++] = '/'; + strcpy (retry_name + rtn_len, tail); + } + } + + if (err) + mach_port_deallocate (mach_task_self (), *node); + } + + return err; + } + + /* Fetch uids for use with fsys_getroot. */ + num_uids = geteuids (0, 0); + if (num_uids < 0) + return errno; + uids = alloca (num_uids * sizeof (uid_t)); + num_uids = geteuids (num_uids, uids); + if (num_uids < 0) + return errno; + + /* ... and gids. */ + num_gids = getgroups (0, 0); + if (num_gids < 0) + return errno; + gids = alloca (num_gids * sizeof (gid_t)); + num_gids = getgroups (num_gids, gids); + if (num_gids < 0) + return errno; + + /* Look things up ... */ + err = hurd_file_name_lookup (&_hurd_ports_use, &getdport, lookup, + name, flags, mode & ~getumask (), + &node); + + return err ? (__hurd_fail (err), MACH_PORT_NULL) : node; +} diff --git a/sutils/e2os.sh b/sutils/e2os.sh new file mode 100755 index 00000000..10fe5087 --- /dev/null +++ b/sutils/e2os.sh @@ -0,0 +1,153 @@ +#!/bin/sh +# Set/get the `creator_os' field of an ext2fs partition +# +# Copyright (C) 1996, 1997 Free Software Foundation, Inc. +# +# Written by Miles Bader <miles@gnu.ai.mit.edu> +# +# 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +USAGE="Usage: $0 DEVICE [OS]" + +DD=${DD-/bin/dd} +OD=${OD-/bin/od} +SED=${SED-/bin/sed} +AWK=${AWK-/bin/gawk} + +# Hack to allow this script to work well under linux too. +test ! -x "$OD" -a -x /usr/bin/od && OD=/usr/bin/od +test ! -x "$AWK" -a -x /usr/bin/gawk && AWK=/usr/bin/gawk + +while :; do + case "$1" in + --help|"-?") + echo "$USAGE" + echo "Get or set the creator_os parameter of an ext2fs partition." + echo "" + echo " -?, --help Give this help list" + echo " --usage Give a short usage message" + echo " -V, --version Print program version" + exit 0;; + --usage) + echo "Usage: $0 [-V?] [--help] [--usage] [--version] DEVICE [OS]" + exit 0;; + --version|-V) + echo "STANDARD_HURD_VERSION_e2os_"; exit 0;; + -*) + echo 1>&2 "$0: unrecognized option \`$1'" + echo 1>&2 "Try \`$0 --help' for more information"; + exit 1;; + *) + break;; + esac +done + +case "$#" in 1 | 2) ;; # ok + *) echo 1>&2 "$USAGE" + echo 1>&2 "Try \`--help' for more information"; + exit 1;; +esac + +DEVICE="$1"; shift +OS="$1" + +# Superblock fields (format is "BYTE_OFFS SIZE") +SB_MAGIC="56 2" +SB_OS="72 4" +# Ext2fs magic numbers +MAGIC_EXT2=ef53 +MAGIC_EXT2_OLD=ef53 +# Symbolic names for os types we know about +OS_LINUX=0 +OS_HURD=1 +OS_MASIX=2 +OS_FREEBSD=3 +OS_LITES=4 + +# Superblock +SB=/tmp/,e2os-sb.$$ + +# We have to store error output in a file so that we can filter it (for all +# unix's stressing of pipelines, /bin/sh sure works hard to prevent you using +# them). +ERRS=/tmp/,e2os-errs.$$ + +trap "/bin/rm -f $SB $ERRS" 0 + +# Read the superblock +$DD 2>"$ERRS" if="$DEVICE" of="$SB" bs=1k skip=1 count=1 \ +|| { $SED 1>&2 "s;^$DD:;$0:;" "$ERRS"; exit 2; } + +# Extract a word of SZ bytes from byte offset POS in the superblock +# Optional arg FMT is what format to use (x = hex, d = decimal) +function sbget +{ + local pos="$1" sz="$2" fmt="${3-d}" + pos=$(($pos / $sz)) + $DD 2>/dev/null if="$SB" bs="$sz" skip="$pos" count=1 \ + | $OD -An -t"$fmt$sz" \ + | $SED 's;^[ 0]*\(.\);\1;' +} + +# Set a word of SZ bytes at byte offset POS in the superblock to VAL +function sbset +{ + local pos="$1" sz="$2" val="$3" + pos=$(($pos / $sz)) + echo "$val" \ + | $AWK '{ n=$1+0; printf ("%c%c%c%c", n, n/256, n/(2^16), n/(2^24)); }' \ + | $DD 2>/dev/null of="$SB" bs="$sz" seek="$pos" count=1 conv=notrunc +} + +# Check the magic number +magic="`sbget $SB_MAGIC x`" +case "$magic" in + $MAGIC_EXT2) ;; # ok + $MAGIC_EXT2_OLD) echo "$0: $DEVICE: Old-format ext2 filesystem"; exit 3;; + *) echo "$0: $DEVICE: Not an ext2 filesystem (magic = 0x$magic)"; exit 4;; +esac + +if test "$OS"; then + # Set the os field + case "$OS" in + linux) OS=$OS_LINUX;; + hurd) OS=$OS_HURD;; + masix) OS=$OS_MASIX;; + freebsd) OS=$OS_FREEBSD;; + lites) OS=$OS_LITES;; + "*[!0-9]*") + echo 1>&2 "$0: $OS: Unknown ext2 creator_os value"; exit 5;; + esac + + # Frob the superlock + sbset $SB_OS "$OS" + + # Write the superblock + $DD 2>"$ERRS" if="$SB" of="$DEVICE" bs=1k seek=1 count=1 conv=notrunc \ + || { $SED 1>&2 "s;^$DD:;$0:;" "$ERRS"; exit 6; } +else + # Print the os field. + OS="`sbget $SB_OS`" + case "$OS" in + "") exit 2;; + $OS_LINUX) OS=linux;; + $OS_HURD) OS=hurd;; + $OS_MASIX) OS=masix;; + $OS_FREEBSD) OS=freebsd;; + $OS_LITES) OS=lites;; + esac + echo "$OS" +fi diff --git a/sutils/fsck.c b/sutils/fsck.c new file mode 100644 index 00000000..424e3f17 --- /dev/null +++ b/sutils/fsck.c @@ -0,0 +1,568 @@ +/* Hurd-aware fsck wrapper + + Copyright (C) 1996, 97, 98, 99 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.org> + + This file is part of the GNU Hurd. + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* This wrapper runs other file-system specific fsck programs. They are + expected to accept at least the following options: + + -p Terse automatic mode + -y Automatically answer yes to all questions + -n Automatically answer no to all questions + -f Check even if clean + -s Only print diagostic messages + + They should also return exit-status codes as following: + + 0 Filesystem was clean + 1,2 Filesystem fixed (and is now clean) + 4,8 Filesystem was broken, but couldn't be fixed + ... Anything else is assumed be some horrible error + + The exit-status from this wrapper is the greatest status returned from any + individual fsck. + + Although it knows something about the hurd, this fsck still uses + /etc/fstab, and is generally not very integrated. That will have to wait + until the appropiate mechanisms for doing so are decided. */ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <error.h> +#include <argp.h> +#include <argz.h> +#include <assert.h> +#include <version.h> + +#include "fstab.h" + +const char *argp_program_version = STANDARD_HURD_VERSION (fsck); + + +/* for debugging */ +static int _debug = 0; +#define debug(fmt, args...) \ + do { if (_debug) { \ + fprintf (stderr, "[%s: ", __FUNCTION__); \ + fprintf (stderr, fmt , ##args); \ + fprintf (stderr, "]\n"); } } while (0) +#define fs_debug(fs, fmt, args...) \ + debug ("%s: " fmt, (fs)->mntent.mnt_dir , ##args) + +#define FSCK_SEARCH_FMTS "/sbin/fsck.%s" + +/* Exit codes we return. */ +#define FSCK_EX_OK 0 /* No errors */ +#define FSCK_EX_FIXED 1 /* File system errors corrected */ +#define FSCK_EX_BROKEN 4 /* File system errors left uncorrected */ +#define FSCK_EX_QUIT 12 /* Got SIGQUIT */ +#define FSCK_EX_SIGNAL 20 /* Signalled (not SIGQUIT) */ +#define FSCK_EX_ERROR 50 +#define FSCK_EX_EXEC 99 /* Exec failed */ +/* Everything else is some sort of fsck problem. */ + +/* Things we know about what child fsck's might return. */ +#define FSCK_EX_IS_FIXED(st) ({ int _st = (st); _st >= 1 || _st <= 2; }) +#define FSCK_EX_IS_BROKEN(st) ({ int _st = (st); _st >= 4 || _st <= 8; }) + +/* Common fsck flags. */ +#define FSCK_F_PREEN 0x1 +#define FSCK_F_YES 0x2 +#define FSCK_F_NO 0x4 +#define FSCK_F_FORCE 0x8 +#define FSCK_F_SILENT 0x10 + +/* The following are only used internally. */ +#define FSCK_F_VERBOSE 0x100 +#define FSCK_F_WRITABLE 0x200 /* Make writable after fscking. */ +#define FSCK_F_AUTO 0x400 /* Do all filesystems in fstab. */ +#define FSCK_F_DRYRUN 0x800 /* Don't actually do anything. */ + +static int got_sigquit = 0, got_sigint = 0; + +static void sigquit () +{ + got_sigquit = 1; +} + +static void sigint () +{ + got_sigint = 1; +} + +struct fsck +{ + struct fs *fs; /* Filesystem being fscked. */ + int pid; /* Pid for process. */ + int make_writable; /* Make writable after fscking if possible. */ + struct fsck *next, **self; +}; + +struct fscks +{ + struct fsck *running; /* Fsck processes now running. */ + int free_slots; /* Number of fsck processes we can start. */ + int flags; +}; + +/* Starts FS's fsck program on FS's device, returning the pid of the process. + If an error is encountered, prints an error message and returns 0. + Filesystems that need not be fscked at all also return 0 (but don't print + an error message). */ +static pid_t +fs_start_fsck (struct fs *fs, int flags) +{ + pid_t pid; + char flags_buf[10]; + char *argv[4], **argp = argv; + struct fstype *type; + error_t err = fs_type (fs, &type); + + assert_perror (err); /* Should already have been checked for. */ + assert (type->program); + + *argp++ = type->program; + + if (flags & (FSCK_F_PREEN|FSCK_F_YES|FSCK_F_NO|FSCK_F_FORCE|FSCK_F_SILENT)) + { + char *p = flags_buf; + *argp++ = flags_buf; + *p++ = '-'; + if (flags & FSCK_F_PREEN) *p++ = 'p'; + if (flags & FSCK_F_YES) *p++ = 'y'; + if (flags & FSCK_F_NO) *p++ = 'n'; + if (flags & FSCK_F_FORCE) *p++ = 'f'; + if (flags & FSCK_F_SILENT) *p++ = 's'; + *p = '\0'; + } + + *argp++ = fs->mntent.mnt_fsname; + *argp = 0; + + if (flags & FSCK_F_DRYRUN) + { + char *argz; + size_t argz_len; + argz_create (argv, &argz, &argz_len); + argz_stringify (argz, argz_len, ' '); + puts (argz); + free (argz); + return 0; + } + + pid = fork (); + if (pid < 0) + { + error (0, errno, "fork"); + return 0; + } + + if (pid == 0) + /* Child. */ + { + execv (type->program, argv); + exit (FSCK_EX_EXEC); /* Exec failed. */ + } + + if ((flags & FSCK_F_VERBOSE) || _debug) + { + char *argz; + size_t argz_len; + argz_create (argv, &argz, &argz_len); + argz_stringify (argz, argz_len, ' '); + fs_debug (fs, "Spawned pid %d: %s", pid, argz); + if (flags & FSCK_F_VERBOSE) + puts (argz); + free (argz); + } + + return pid; +} + +/* Start a fsck process for FS running, and add an entry for it to FSCKS. + This also ensures that if FS is currently mounted, it will be made + readonly first. If the fsck is successfully started, 0 is returned, + otherwise FSCK_EX_ERROR. */ +static int +fscks_start_fsck (struct fscks *fscks, struct fs *fs) +{ + error_t err; + int mounted, make_writable; + struct fsck *fsck; + + if (got_sigint) + /* We got SIGINT, so we pretend that all fscks got a signal without even + attempting to run them. */ + { + fs_debug (fs, "Forcing signal"); + return FSCK_EX_SIGNAL; + } + +#define CK(err, fmt, args...) \ + do { if (err) { error (0, err, fmt , ##args); return FSCK_EX_ERROR; } } while (0) + + fs_debug (fs, "Checking mounted state"); + err = fs_mounted (fs, &mounted); + CK (err, "%s: Cannot check mounted state", fs->mntent.mnt_dir); + + if (mounted) + { + int readonly; + + fs_debug (fs, "Checking readonly state"); + err = fs_readonly (fs, &readonly); + CK (err, "%s: Cannot check readonly state", fs->mntent.mnt_dir); + + if (fscks->flags & FSCK_F_DRYRUN) + { + if (! readonly) + { + printf ("%s: writable filesystem %s would be made read-only\n", + program_invocation_name, fs->mntent.mnt_dir); + readonly = 1; + } + } + + if (! readonly) + { + fs_debug (fs, "Making readonly"); + err = fs_set_readonly (fs, 1); + CK (err, "%s: Cannot make readonly", fs->mntent.mnt_dir); + } + + make_writable = !readonly + || ((fscks->flags & FSCK_F_WRITABLE) && hasmntopt (&fs->mntent, "rw")); + if (make_writable) + { + fs_debug (fs, "Will make writable after fscking if possible"); + make_writable = 1; + } + } + else + make_writable = 0; + +#undef CK + + /* Ok, any mounted filesystem is safely readonly. */ + + fsck = malloc (sizeof (struct fsck)); + if (! fsck) + { + error (0, ENOMEM, "malloc"); + return FSCK_EX_ERROR; + } + + fsck->fs = fs; + fsck->make_writable = make_writable; + fsck->next = fscks->running; + if (fsck->next) + fsck->next->self = &fsck->next; + fsck->self = &fscks->running; + fsck->pid = fs_start_fsck (fs, fscks->flags); + fscks->running = fsck; + + if (fsck->pid) + fscks->free_slots--; + + return 0; +} + +/* Cleanup after fscking with FSCK. If REMOUNT is true, ask the filesystem + to remount itself (to incorporate changes made by the fsck program). If + MAKE_WRITABLE is true, then if the filesystem should be made writable, do + so (after remounting if applicable). */ +static void +fsck_cleanup (struct fsck *fsck, int remount, int make_writable) +{ + error_t err = 0; + struct fs *fs = fsck->fs; + + /* Remove from chain. */ + *fsck->self = fsck->next; + if (fsck->next) + fsck->next->self = fsck->self; + + fs_debug (fs, "Cleaning up after fsck (remount = %d, make_writable = %d)", + remount, make_writable); + + if (fs->mounted > 0) + /* It's currently mounted; if the fsck modified the device, tell the + running filesystem to remount it. Also we may make it writable. */ + { + if (remount) + { + fs_debug (fs, "Remounting"); + err = fs_remount (fs); + if (err) + error (0, err, "%s: Cannot remount", fs->mntent.mnt_dir); + } + if (!err && make_writable && fsck->make_writable) + { + fs_debug (fs, "Making writable"); + err = fs_set_readonly (fs, 0); + if (err) + error (0, err, "%s: Cannot make writable", fs->mntent.mnt_dir); + } + } + + free (fsck); +} + +/* Wait for some fsck process to exit, cleaning up after it, and return its + exit-status. */ +static int +fscks_wait (struct fscks *fscks) +{ + pid_t pid; + int wstatus, status; + struct fsck *fsck, *next; + + /* Cleanup fscks that didn't even start. */ + for (fsck = fscks->running; fsck; fsck = next) + { + next = fsck->next; + if (fsck->pid == 0) + { + fs_debug (fsck->fs, "Pruning failed fsck"); + fsck_cleanup (fsck, 0, 1); + } + } + + debug ("Waiting..."); + + do + pid = wait (&wstatus); + while (pid < 0 && errno == EINTR); + + if (pid > 0) + { + if (WIFEXITED (wstatus)) + status = WEXITSTATUS (wstatus); + else if (WIFSIGNALED (wstatus)) + status = FSCK_EX_SIGNAL; + else + status = FSCK_EX_ERROR; + + for (fsck = fscks->running; fsck; fsck = fsck->next) + if (fsck->pid == pid) + { + int remount = (status != 0); + int make_writable = (status == 0 || FSCK_EX_IS_FIXED (status)); + fs_debug (fsck->fs, "Fsck finished (status = %d)", status); + fsck_cleanup (fsck, remount, make_writable); + fscks->free_slots++; + break; + } + if (! fsck) + error (0, 0, "%d: Unknown process exited", pid); + } + else if (errno == ECHILD) + /* There are apparently no child processes left, and we weren't told of + their demise. This can't happen. */ + { + while (fscks->running) + { + error (0, 0, "%s: Fsck process disappeared!", + fscks->running->fs->mntent.mnt_fsname); + /* Be pessimistic -- remount the filesystem, but leave it + readonly. */ + fsck_cleanup (fscks->running, 1, 0); + fscks->free_slots++; + } + status = FSCK_EX_ERROR; + } + else + status = FSCK_EX_ERROR; /* What happened? */ + + return status; +} + +/* Fsck all the filesystems in FSTAB, with the flags in FLAGS, doing at most + MAX_PARALLEL parallel fscks. The greatest exit code returned by any one + fsck is returned. */ +static int +fsck (struct fstab *fstab, int flags, int max_parallel) +{ + int pass; + struct fs *fs; + int autom = (flags & FSCK_F_AUTO); + int summary_status = 0; + struct fscks fscks = { running: 0, flags: flags }; + + void merge_status (int status) + { + if (status > summary_status) + summary_status = status; + } + + /* Do in pass order; pass 0 is never run, it is reserved for "off". */ + for (pass = 1; pass > 0; pass = fstab_next_pass (fstab, pass)) + /* Submit all filesystems in the given pass, up to MAX_PARALLEL at a + time. There should currently be no fscks running. */ + { + debug ("Pass %d", pass); + + fscks.free_slots = max_parallel; + + /* Try and fsck every filesystem in this pass. */ + for (fs = fstab->entries; fs; fs = fs->next) + if (fs->mntent.mnt_passno == pass) + /* FS is applicable for this pass. */ + { + struct fstype *type; + error_t err = fs_type (fs, &type); + + if (err) + { + error (0, err, "%s: Cannot find fsck program (type %s)", + fs->mntent.mnt_dir, fs->mntent.mnt_type); + merge_status (FSCK_EX_ERROR); + } + else if (type->program) + /* This is a fsckable filesystem. */ + { + fs_debug (fs, "Fsckable; free_slots = %d", fscks.free_slots); + while (fscks.free_slots == 0) + /* No room; wait for another fsck to finish. */ + merge_status (fscks_wait (&fscks)); + merge_status (fscks_start_fsck (&fscks, fs)); + } + else if (autom) + fs_debug (fs, "Not fsckable"); + else + error (0, 0, "%s: %s: Not a fsckable filesystem type", + fs->mntent.mnt_dir, fs->mntent.mnt_type); + } + + /* Now wait for them all to finish. */ + while (fscks.running) + merge_status (fscks_wait (&fscks)); + } + + return summary_status; +} + +static const struct argp_option options[] = +{ + {"preen", 'p', 0, 0, "Terse automatic mode", 1}, + {"yes", 'y', 0, 0, "Automatically answer yes to all questions"}, + {"no", 'n', 0, 0, "Automatically answer no to all questions"}, + {"parallel", 'l', "NUM", 0, "Limit the number of parallel checks to NUM"}, + {"verbose", 'v', 0, 0, "Print informational messages"}, + {"writable", 'w', 0, 0, + "Make RW filesystems writable after fscking, if possible"}, + {"debug", 'D', 0, OPTION_HIDDEN }, + {"force", 'f', 0, 0, "Check even if clean"}, + + {"dry-run", 'N', 0, 0, "Don't check, just show what would be done"}, + {0, 0, 0, 0, "In --preen mode, the following also apply:", 2}, + {"silent", 's', 0, 0, "Print only diagnostic messages"}, + {"quiet", 'q', 0, OPTION_ALIAS | OPTION_HIDDEN }, + {0, 0} +}; +static const char doc[] = "Filesystem consistency check and repair"; +static const char args_doc[] = "[ DEVICE|FSYS... ]"; + + +int +main (int argc, char **argv) +{ + struct fstab *check; + int status; /* exit status */ + int flags = 0; + int max_parallel = -1; /* -1 => use default */ + + error_t parse_opt (int key, char *arg, struct argp_state *state) + { + struct fstab_argp_params *params = state->input; + switch (key) + { + case ARGP_KEY_INIT: + state->child_inputs[0] = params; /* pass down to fstab_argp parser */ + break; + case 'p': flags |= FSCK_F_PREEN; break; + case 'y': flags |= FSCK_F_YES; break; + case 'n': flags |= FSCK_F_NO; break; + case 'f': flags |= FSCK_F_FORCE; break; + case 's': flags |= FSCK_F_SILENT; break; + case 'v': flags |= FSCK_F_VERBOSE; break; + case 'w': flags |= FSCK_F_WRITABLE; break; + case 'N': flags |= FSCK_F_DRYRUN; break; + case 'D': _debug = 1; break; + case 'l': + max_parallel = atoi (arg); + if (max_parallel < 1) + argp_error (state, "%s: Invalid value for --max-parallel", arg); + break; + case ARGP_KEY_NO_ARGS: + if (flags & FSCK_F_PREEN) + params->do_all = 1; + else if (!params->do_all) + { + argp_usage (state); + return EINVAL; + } + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + static const struct argp_child kids[] = + { { &fstab_argp, 0, + "Filesystem selection (default is all in " _PATH_MNTTAB "):", 2 }, + { 0 } }; + struct argp argp = { options, parse_opt, args_doc, doc, kids }; + struct fstab_argp_params fstab_params; + + argp_parse (&argp, argc, argv, 0, 0, &fstab_params); + + check = fstab_argp_create (&fstab_params, + FSCK_SEARCH_FMTS, sizeof FSCK_SEARCH_FMTS); + if (fstab_params.do_all) + flags |= FSCK_F_AUTO; + + if (max_parallel <= 0) + { + if (flags & FSCK_F_PREEN) + max_parallel = 100; /* In preen mode, do lots in parallel. */ + else + max_parallel = 1; /* Do one at a time to keep output rational. */ + } + + /* If the user send a SIGQUIT (usually ^\), then do all checks, but + regardless of their outcome, return a status that will cause the + automatic reboot to stop after fscking is complete. */ + signal (SIGQUIT, sigquit); + + /* Let currently running fscks complete (each such program can handle + signals as it sees fit), and cause not-yet-run fscks to act as if they + got a signal. */ + signal (SIGINT, sigint); + + debug ("Fscking..."); + status = fsck (check, flags, max_parallel); + if (got_sigquit && status < FSCK_EX_QUIT) + status = FSCK_EX_QUIT; + + exit (status); +} diff --git a/sutils/fstab.c b/sutils/fstab.c new file mode 100644 index 00000000..4574d41d --- /dev/null +++ b/sutils/fstab.c @@ -0,0 +1,956 @@ +/* Fstab filesystem frobbing + + Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This file is part of the GNU Hurd. + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <error.h> +#include <argz.h> +#include <argp.h> +#include <fnmatch.h> + +#include <hurd/fsys.h> + +#include "fstab.h" + +extern error_t fsys_set_readonly (fsys_t fsys, int readonly); +extern error_t fsys_get_readonly (fsys_t fsys, int *readonly); +extern error_t fsys_update (fsys_t fsys); + +extern file_t file_name_lookup_carefully (const char *file, + int flags, mode_t mode); + +/* Return a new fstab in FSTAB. */ +error_t +fstab_create (struct fstypes *types, struct fstab **fstab) +{ + struct fstab *new = malloc (sizeof (struct fstab)); + if (new) + { + new->entries = 0; + new->types = types; + *fstab = new; + return 0; + } + else + return ENOMEM; +} + +/* Free FSTAB and all of its entries. */ +void +fstab_free (struct fstab *fstab) +{ + while (fstab->entries) + fs_free (fstab->entries); + free (fstab); +} + +/* Return a new fstypes structure in TYPES. SEARCH_FMTS is copied. */ +error_t +fstypes_create (const char *search_fmts, size_t search_fmts_len, + struct fstypes **types) +{ + struct fstypes *new = malloc (sizeof (struct fstypes)); + if (new) + { + new->entries = 0; + new->program_search_fmts = malloc (search_fmts_len); + new->program_search_fmts_len = search_fmts_len; + if (! new->program_search_fmts) + { + free (types); + return ENOMEM; + } + bcopy (search_fmts, new->program_search_fmts, search_fmts_len); + *types = new; + return 0; + } + else + return ENOMEM; +} + +/* Return an fstype entry in TYPES called NAME, in FSTYPE. If there is no + existing entry, an attempt to find a fsck program with the given type, + using the alternatives in the FSCK_SEARCH_FMTS field in TYPES. If + one is found, it is added to TYPES, otherwise an new entry is created + with a NULL PROGRAM field. */ +error_t +fstypes_get (struct fstypes *types, const char *name, struct fstype **fstype) +{ + char *fmts, *fmt; + size_t fmts_len; + struct fstype *type; + char *program = 0; + + for (type = types->entries; type; type = type->next) + if (strcasecmp (type->name, name) == 0) + { + *fstype = type; + return 0; + } + + /* No existing entry, make a new one. */ + + fmts = types->program_search_fmts; + fmts_len = types->program_search_fmts_len; + + for (fmt = fmts; fmt; fmt = argz_next (fmts, fmts_len, fmt)) + { + int fd; + + asprintf (&program, fmt, name); + fd = open (program, O_EXEC); + if (fd < 0) + { + free (program); /* Failed. */ + if (errno != ENOENT && errno != EACCES) + /* The program's there but something went wrong; fail. */ + return errno; + } + else + /* We can open for exec, but check the stat info too (e.g. root can + open everything). */ + { + struct stat stat; + int rv = fstat (fd, &stat); + + close (fd); + + if (rv < 0) + { + free (program); + return errno; + } + + if (stat.st_mode & S_IXUSR) + /* Yup execute bit is set. This must be a program... */ + break; + + free (program); + } + + program = 0; + } + + type = malloc (sizeof (struct fstype)); + if (! type) + { + free (program); + return ENOMEM; + } + + type->name = strdup (name); + if (type->name == 0) + { + free (type); + return ENOMEM; + } + type->program = program; + type->next = types->entries; + types->entries = type; + + *fstype = type; + + return 0; +} + +#if 0 +/* XXX nice idea, but not that useful since scanf's %s always eats all + non-ws, and it seems a bit overkill to convert it to a .+ regexp match */ +error_t +fstypes_find_program (struct fstypes *types, const char *program, + struct fstype **fstype) +{ + char *fmts, *fmt; + size_t fmts_len; + struct fstype *type; + char *typename; + + /* First see if a known type matches this program. */ + for (type = types->entries; type; type = type->next) + if (type->program && !strcmp (type->program, program)) + { + *fstype = type; + return 0; + } + + /* No existing entry, see if we can make a new one. */ + + typename = alloca (strlen (program) + 1); + + fmts = types->program_search_fmts; + fmts_len = types->program_search_fmts_len; + for (fmt = fmts; fmt; fmt = argz_next (fmts, fmts_len, fmt)) + /* XXX this only works for trailing %s */ + if (sscanf (program, fmt, typename) == 1) + { + /* This format matches the program and yields the type name. + Create a new entry for this type. */ + + type = malloc (sizeof (struct fstype)); + if (! type) + return ENOMEM; + type->name = strdup (typename); + if (type->name == 0) + { + free (type); + return ENOMEM; + } + type->program = strdup (program); + if (type->program == 0) + { + free (type->name); + free (type); + return ENOMEM; + } + type->next = types->entries; + types->entries = type; + + *fstype = type; + return 0; + } + + /* We could find no program search format that could have yielded this + program name. */ + *fstype = 0; + return 0; +} +#endif + +/* Copy MNTENT into FS, copying component strings as well. */ +error_t +fs_set_mntent (struct fs *fs, const struct mntent *mntent) +{ + char *end; + size_t needed = 0; + + if (fs->storage) + free (fs->storage); + + /* Allocate space for all string mntent fields in FS. */ +#define COUNT(field) if (mntent->field) needed += strlen (mntent->field) + 1; + COUNT (mnt_fsname); + COUNT (mnt_dir); + COUNT (mnt_type); + COUNT (mnt_opts); +#undef COUNT + + fs->storage = malloc (needed); + if (! fs->storage) + return ENOMEM; + + if (!fs->mntent.mnt_dir || !mntent->mnt_dir + || strcmp (fs->mntent.mnt_dir, mntent->mnt_dir) != 0) + { + fs->mounted = fs->readonly = -1; + if (fs->fsys != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), fs->fsys); + fs->fsys = MACH_PORT_NULL; + } + + /* Copy MNTENT into FS; string-valued fields will be fixed up next. */ + fs->mntent = *mntent; + + /* Copy each mntent field from MNTENT into FS's version. */ + end = fs->storage; +#define STORE(field) \ + fs->mntent.field = end; end = stpcpy (end, mntent->field) + 1 + STORE (mnt_fsname); + STORE (mnt_dir); + STORE (mnt_type); + STORE (mnt_opts); +#undef STORE + + if (fs->type + && (!mntent->mnt_type + || strcasecmp (fs->type->name, mntent->mnt_type) != 0)) + fs->type = 0; /* Type is different. */ + + return 0; +} + +/* Returns an fstype for FS in TYPE, trying to fill in FS's type field if + necessary. */ +error_t +fs_type (struct fs *fs, struct fstype **type) +{ + error_t err = 0; + if (! fs->type) + err = fstypes_get (fs->fstab->types, fs->mntent.mnt_type, &fs->type); + if (! err) + *type = fs->type; + return err; +} + +/* Looks to see if FS is currently mounted, being very careful to avoid + mounting anything that's not already, and fills in the fsys & mounted + fields in FS. */ +static error_t +_fs_check_mounted (struct fs *fs) +{ + error_t err = 0; + + if (fs->mounted < 0) + /* The mounted field in FS is -1 if we're not sure. */ + { + if (fs->fsys != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), fs->fsys); + + if (strcmp (fs->mntent.mnt_dir, "/") == 0) + /* The root is always mounted. Get its control port. */ + { + file_t root = getcrdir (); + if (root == MACH_PORT_NULL) + err = errno; + else + { + err = file_getcontrol (root, &fs->fsys); + mach_port_deallocate (mach_task_self (), root); + } + } + else + { + file_t mount_point = + file_name_lookup_carefully (fs->mntent.mnt_dir, O_NOTRANS, 0); + + if (mount_point != MACH_PORT_NULL) + /* The node exists. Is it the root of an active translator? + [Note that it could be a different translator than the one in + the mntent, but oh well, nothing we can do about that.] */ + { + err = file_get_translator_cntl (mount_point, &fs->fsys); + if (err == EINVAL || err == EOPNOTSUPP || err == ENXIO) + /* Either the mount point doesn't exist, or wasn't mounted. */ + { + fs->fsys = MACH_PORT_NULL; + err = 0; + } + } + else if (errno == ENXIO) + /* Ran into an inactive passive translator. FS can't be mounted. */ + { + fs->fsys = MACH_PORT_NULL; + err = 0; + } + } + + if (! err) + fs->mounted = (fs->fsys != MACH_PORT_NULL); + } + + return err; +} + +/* Looks to see if FS is currently mounted, being very careful to avoid + mounting anything that's not already, and returns the control port for the + mounted filesystem (MACH_PORT_NULL if not mounted). */ +error_t +fs_fsys (struct fs *fs, fsys_t *fsys) +{ + error_t err = _fs_check_mounted (fs); + if (!err && fsys) + *fsys = fs->fsys; + return err; +} + +/* Looks to see if FS is currently mounted, being very careful to avoid + mounting anything that's not already, and returns the boolean MOUNTED. */ +error_t +fs_mounted (struct fs *fs, int *mounted) +{ + error_t err = _fs_check_mounted (fs); + if (!err && mounted) + *mounted = fs->mounted; + return err; +} + +/* Looks to see if FS is currently mounted readonly, being very careful to + avoid mounting anything that's not already, and returns the boolean + READONLY. If FS isn't mounted at all, READONLY is set to 1 (it's not + going to write anything!). */ +error_t +fs_readonly (struct fs *fs, int *readonly) +{ + error_t err = 0; + + if (fs->readonly < 0) + /* Unknown. */ + { + fsys_t fsys; + + err = fs_fsys (fs, &fsys); + if (! err) + { + if (fsys == MACH_PORT_NULL) + fs->readonly = 1; + else + err = fsys_get_readonly (fsys, &fs->readonly); + } + } + + if (!err && readonly) + *readonly = fs->readonly; + + return err; +} + +/* If FS is currently mounted writable, try to make it readonly. XXX If FS + is not mounted at all, then nothing is done. */ +error_t +fs_set_readonly (struct fs *fs, int readonly) +{ + int currently_readonly; + error_t err = fs_readonly (fs, ¤tly_readonly); + + readonly = !!readonly; + + if (!err && readonly != currently_readonly) + /* We have to try and change the readonly state. */ + { + fsys_t fsys; + err = fs_fsys (fs, &fsys); + if (!err && fsys != MACH_PORT_NULL) /* XXX What to do if not mounted? */ + err = fsys_set_readonly (fsys, readonly); + if (! err) + fs->readonly = readonly; + } + + return err; +} + +/* If FS is currently mounted tell it to remount the device. XXX If FS is + not mounted at all, then nothing is done. */ +error_t +fs_remount (struct fs *fs) +{ + fsys_t fsys; + error_t err = fs_fsys (fs, &fsys); + if (!err && fsys != MACH_PORT_NULL) /* XXX What to do if not mounted? */ + err = fsys_update (fsys); + return err; +} + +/* Returns the FS entry in FSTAB with the device field NAME (there can only + be one such entry). */ +inline struct fs * +fstab_find_device (const struct fstab *fstab, const char *name) +{ + struct fs *fs; + for (fs = fstab->entries; fs; fs = fs->next) + if (strcmp (fs->mntent.mnt_fsname, name) == 0) + return fs; + return 0; +} + +/* Returns the FS entry in FSTAB with the mount point NAME (there can only + be one such entry). */ +inline struct fs * +fstab_find_mount (const struct fstab *fstab, const char *name) +{ + struct fs *fs; + + /* Don't count "none" or "-" as matching any other mount point. + It is canonical to use "none" for swap partitions, and multiple + such do not in fact conflict with each other. Likewise, the + special device name "ignore" is used for things that should not + be processed automatically. */ + if (!strcmp (name, "-") + || !strcmp (name, "none") + || !strcmp (name, "ignore")) + return 0; + + for (fs = fstab->entries; fs; fs = fs->next) + if (strcmp (fs->mntent.mnt_dir, name) == 0) + return fs; + return 0; +} + +/* Returns the FS entry in FSTAB with the device or mount point NAME (there + can only be one such entry). */ +inline struct fs * +fstab_find (const struct fstab *fstab, const char *name) +{ + return fstab_find_device (fstab, name) ?: fstab_find_mount (fstab, name); +} + +/* Cons FS onto the beginning of FSTAB's entry list. */ +static void +_fstab_add (struct fstab *fstab, struct fs *fs) +{ + fs->fstab = fstab; + fs->next = fstab->entries; + fs->self = &fstab->entries; + if (fstab->entries) + fstab->entries->self = &fs->next; + fstab->entries = fs; +} + +/* Destroy FS, removing it from its containing FSTAB. */ +void +fs_free (struct fs *fs) +{ + *fs->self = fs->next; /* unlink from chain */ + if (fs->storage) + free (fs->storage); + if (fs->fsys != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), fs->fsys); + free (fs); +} + +/* Add an entry for MNTENT to FSTAB, removing any existing entries that + conflict (in either the device or mount point). If RESULT is non-zero, the + new entry is returne in it. */ +error_t +fstab_add_mntent (struct fstab *const fstab, const struct mntent *mntent, + struct fs **result) +{ + int new = 0; /* True if we didn't overwrite an old entry. */ + error_t err = 0; + struct fs *fs = fstab_find_device (fstab, mntent->mnt_fsname); + struct fs *mounted_fs = fstab_find_mount (fstab, mntent->mnt_dir); + + if (! fs) + /* No old entry with the same device; see if there's one with the same + mount point. */ + { + fs = mounted_fs; + mounted_fs = 0; + } + + if (! fs) + /* No old entry, make a new one. */ + { + fs = malloc (sizeof (struct fs)); + if (fs) + { + bzero (fs, sizeof (struct fs)); + fs->mounted = fs->readonly = -1; + fs->fsys = MACH_PORT_NULL; + new = 1; + } + else + err = ENOMEM; + } + + if (! err) + /* Try and fill in FS's mntent. */ + err = fs_set_mntent (fs, mntent); + + if (new) + { + if (! err) + _fstab_add (fstab, fs); + else if (fs) + free (fs); + } + + if (!err && mounted_fs) + /* Get rid of the conflicting entry MOUNTED_FS. */ + fs_free (mounted_fs); + + if (!err && result) + *result = fs; + + return err; +} + +/* Copy the entry FS (which should belong to another fstab than DST) into + DST. If DST & SRC have different TYPES fields, EINVAL is returned. If + COPY is non-zero, the copy is returned in it. */ +error_t +fstab_add_fs (struct fstab *dst, const struct fs *fs, struct fs **copy) +{ + error_t err; + struct fs *new; + struct fstab *src = fs->fstab; + + if (dst->types != src->types) + return EINVAL; + + err = fstab_add_mntent (dst, &fs->mntent, &new); + if (err) + return err; + + new->type = fs->type; + + if (copy) + *copy = new; + + return 0; +} + +/* Merge SRC into DST, as if by calling fstab_add_fs on DST with every + entry in SRC, and then deallocating SRC. If DST & SRC have different + TYPES fields, EINVAL is returned. */ +error_t +fstab_merge (struct fstab *dst, struct fstab *src) +{ + struct fs *fs; + + if (dst->types != src->types) + return EINVAL; + + /* Remove entries in DST which conflict with those in SRC. */ + for (fs = src->entries; fs; fs = fs->next) + { + struct fs *old_fs; + + old_fs = fstab_find_device (dst, fs->mntent.mnt_fsname); + if (old_fs) + fs_free (old_fs); + old_fs = fstab_find_mount (dst, fs->mntent.mnt_dir); + if (old_fs) + fs_free (old_fs); + } + + /* Now that we know there are no conflicts, steal all SRC's entries and + cons them onto DST. */ + for (fs = src->entries; fs; fs = fs->next) + _fstab_add (dst, fs); + + /* Now all entries from SRC should be in DST, so just deallocate SRC. */ + free (src); + + return 0; +} + +/* Reads fstab-format entries into FSTAB from the file NAME. Any entries + duplicating one already in FS_LIST supersede the existing entry. */ +error_t +fstab_read (struct fstab *fstab, const char *name) +{ + error_t err; + /* Used to hold entries from the file, before merging with FSTAB at the + end. */ + struct fstab *contents; + FILE *stream = setmntent (name, "r"); + + if (! stream) + return errno; + + err = fstab_create (fstab->types, &contents); + if (! err) + { + while (!err && !feof (stream)) + { + struct mntent *mntent = getmntent (stream); + + if (! mntent) + err = errno; + else if (fstab_find_device (fstab, mntent->mnt_fsname)) + error (0, 0, "%s: Warning: duplicate entry for device %s (%s)", + name, mntent->mnt_fsname, mntent->mnt_dir); + else if (fstab_find_mount (fstab, mntent->mnt_dir)) + error (0, 0, "%s: Warning: duplicate entry for mount point %s (%s)", + name, mntent->mnt_dir, mntent->mnt_fsname); + else + err = fstab_add_mntent (fstab, mntent, 0); + } + + if (! err) + fstab_merge (fstab, contents); + else + fstab_free (contents); + } + + endmntent (stream); + + return err; +} + +/* Return the next pass number that applies to any filesystem in FSTAB that + is greater than PASS, or -1 if there isn't any. */ +int fstab_next_pass (const struct fstab *fstab, int pass) +{ + int next_pass = -1; + struct fs *fs; + for (fs = fstab->entries; fs; fs = fs->next) + if (fs->mntent.mnt_passno > pass) + if (next_pass < 0 || fs->mntent.mnt_passno < next_pass) + { + next_pass = fs->mntent.mnt_passno; + if (next_pass == pass + 1) + break; /* Only possible answer. */ + } + return next_pass; +} + + +static const struct argp_option options[] = +{ + {"all", 'a', 0, 0, "Do all filesystems in " _PATH_MNTTAB}, + {0, 'A', 0, OPTION_ALIAS }, + {"fstab", 'F', "FILE", 0, "File to use instead of " _PATH_MNTTAB}, + {"fstype", 't', "TYPE", 0, "Do only filesystems of given type(s)"}, + {"exclude-root",'R',0, 0, + "Exclude root (/) filesystem from " _PATH_MNTTAB " list"}, + {"exclude", 'X', "PATTERN", 0, "Exclude directories matching PATTERN"}, + + {"search-fmts",'S', "FMTS", 0, + "`:' separated list of formats to use for finding" + " filesystem-specific programs"}, + + {0, 0} +}; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + error_t err; + struct fstab_argp_params *params = state->input; + + switch (key) + { + case ARGP_KEY_INIT: + /* Initialize our parsing state. */ + if (! params) + return EINVAL; /* Need at least a way to return a result. */ + bzero (params, sizeof *params); + break; + + case 'A': + case 'a': + params->do_all = 1; + break; + + case 'F': + params->fstab_path = arg; + break; + + case 'S': + argz_create_sep (arg, ':', + ¶ms->program_search_fmts, + ¶ms->program_search_fmts_len); + break; + + case 'R': + arg = "/"; + /* FALLTHROUGH */ + case 'X': + err = argz_add (¶ms->exclude, ¶ms->exclude_len, arg); + if (err) + argp_failure (state, 100, ENOMEM, "%s", arg); + break; + case 't': + err = argz_add_sep (¶ms->types, ¶ms->types_len, arg, ','); + if (err) + argp_failure (state, 100, ENOMEM, "%s", arg); + break; + + case ARGP_KEY_ARG: + err = argz_add (¶ms->names, ¶ms->names_len, arg); + if (err) + argp_failure (state, 100, ENOMEM, "%s", arg); + break; + + case ARGP_KEY_END: + /* Check for bogus combinations of arguments. */ + if (params->names) + { + if (params->do_all) + argp_error (state, "filesystem arguments not allowed with --all"); + if (params->exclude) + argp_error (state, + "--exclude not allowed with filesystem arguments"); + if (params->types) + argp_error (state, + "--fstype not allowed with filesystem arguments"); + } + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +const struct argp fstab_argp = {options, parse_opt, 0, 0}; + +struct fstab * +fstab_argp_create (struct fstab_argp_params *params, + const char *default_search_fmts, + size_t default_search_fmts_len) +{ + error_t err; + struct fstab *fstab, *check; + struct fstypes *types; + + if (params->fstab_path == 0) + params->fstab_path = _PATH_MNTTAB; + if (params->program_search_fmts == 0) + { + params->program_search_fmts = (char *) default_search_fmts; + params->program_search_fmts_len = default_search_fmts_len; + } + + err = fstypes_create (params->program_search_fmts, + params->program_search_fmts_len, + &types); + if (err) + error (102, err, "fstypes_create"); + + err = fstab_create (types, &fstab); + if (err) + error (101, err, "fstab_create"); + + err = fstab_read (fstab, params->fstab_path); + if (err) + error (103, err, "%s", params->fstab_path); + + if (params->names) + { + /* Process specified filesystems; also look at /var/run/mtab. */ + const char *name; + + err = fstab_read (fstab, _PATH_MOUNTED); + if (err && err != ENOENT) + error (104, err, "%s", _PATH_MOUNTED); + + err = fstab_create (types, &check); + if (err) + error (105, err, "fstab_create"); + + for (name = params->names; name; name = argz_next (params->names, + params->names_len, + name)) + { + struct fs *fs = fstab_find (fstab, name); + if (! fs) + error (106, 0, "%s: Unknown device or filesystem", name); + fstab_add_fs (check, fs, 0); + } + + /* fstab_free (fstab); XXX */ + } + else + { + /* Process everything in /etc/fstab. */ + + if (params->exclude == 0 && params->types == 0) + check = fstab; + else + { + struct fs *fs; + const char *tn; + unsigned int nonexclude_types; + + err = fstab_create (types, &check); + if (err) + error (105, err, "fstab_create"); + + /* For each excluded type (i.e. `-t notype'), clobber the + fstype entry's program with an empty string to mark it. */ + nonexclude_types = 0; + for (tn = params->types; tn; + tn = argz_next (params->types, params->types_len, tn)) + { + if (!strncasecmp (tn, "no", 2)) + { + struct fstype *type; + err = fstypes_get (types, &tn[2], &type); + if (err) + error (106, err, "fstypes_get"); + free (type->program); + type->program = strdup (""); + } + else + ++nonexclude_types; + } + + if (nonexclude_types != 0) + { + const char *tn; + struct fstypes *wanttypes; + + /* We will copy the types we want to include into a fresh + list in WANTTYPES. Since we specify no search formats, + `fstypes_get' applied to WANTTYPES can only create + elements with a null `program' field. */ + err = fstypes_create (0, 0, &wanttypes); + if (err) + error (102, err, "fstypes_create"); + + for (tn = params->types; tn; + tn = argz_next (params->types, params->types_len, tn)) + if (strncasecmp (tn, "no", 2)) + { + struct fstype *type; + err = fstypes_get (types, tn, &type); + if (err) + error (106, err, "fstypes_get"); + if (type->program == 0) + error (0, 0, + "requested filesystem type `%s' unknown", tn); + else + { + struct fstype *newtype = malloc (sizeof *newtype); + newtype->name = strdup (type->name); + newtype->program = strdup (type->program); + newtype->next = wanttypes->entries; + wanttypes->entries = newtype; + } + } + + /* fstypes_free (types); */ + types = wanttypes; + } + + for (fs = fstab->entries; fs; fs = fs->next) + { + const char *ptn; + struct fstype *type; + + err = fs_type (fs, &type); + if (err || nonexclude_types) + { + err = fstypes_get (types, fs->mntent.mnt_type, &type); + if (err) + error (106, err, "fstypes_get"); + if (params->types != 0) + continue; + } + if (nonexclude_types && type->program == 0) + continue; /* Freshly created, was not in WANTTYPES. */ + if (type->program != 0 && type->program[0] == '\0') + continue; /* This type is marked as excluded. */ + + for (ptn = params->exclude; ptn; + ptn = argz_next (params->exclude, params->exclude_len, ptn)) + if (fnmatch (ptn, fs->mntent.mnt_dir, 0) == 0) + break; + if (ptn) /* An exclude pattern matched. */ + continue; + + err = fstab_add_fs (check, fs, 0); + if (err) + error (107, err, "fstab_add_fs"); + } + + /* fstab_free (fstab); XXX */ + } + } + + return check; +} diff --git a/sutils/fstab.h b/sutils/fstab.h new file mode 100644 index 00000000..ddd1656c --- /dev/null +++ b/sutils/fstab.h @@ -0,0 +1,178 @@ +/* Fstab filesystem frobbing + + Copyright (C) 1996, 1999 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This file is part of the GNU Hurd. + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef __FSTAB_H__ +#define __FSTAB_H__ + +#include <mntent.h> +#include <hurd.h> + +struct fs +{ + struct fstab *fstab; /* Containing fstab. */ + struct mntent mntent; /* Mount entry from fstab file. */ + char *storage; /* Storage for strings in MNTENT. */ + struct fstype *type; /* Only set if fs_type called. */ + int readonly, mounted; /* Only set if fs_{readonly,mounted} called. + 0 or 1 if known; -1 if unknown. */ + fsys_t fsys; /* Only set if fs_fsys called. */ + struct fs *next, **self; +}; + +struct fstab +{ + struct fs *entries; + struct fstypes *types; +}; + +struct fstype +{ + char *name; /* Malloced. */ + char *program; /* Malloced. */ + struct fstype *next; +}; + +struct fstypes +{ + struct fstype *entries; + + /* How to search for programs. A '\0' separated list of strings. Each + should be a printf-style format string, which will be passed the + filesystem type. The first such expansion that results in an executable + file will be chosen as the fsck program for that type. */ + char *program_search_fmts; + size_t program_search_fmts_len; /* Length of PROGRAM_SEARCH_FMTS. */ +}; + +/* Return a new fstab in FSTAB. */ +error_t fstab_create (struct fstypes *types, struct fstab **fstab); + +/* Free FSTAB and all of its entries. */ +void fstab_free (struct fstab *fstab); + +/* Return a new fstypes structure in TYPES. SEARCH_FMTS is copied. */ +error_t fstypes_create (const char *search_fmts, size_t search_fmts_len, + struct fstypes **types); + +/* Return an fstype entry in TYPES called NAME, in FSTYPE. If there is no + existing entry, an attempt to find a program with the given type, + using the alternatives in the PROGRAM_SEARCH_FMTS field in TYPES. If + one is found, it is added to TYPES, otherwise a new entry is created + with a null PROGRAM field. */ +error_t fstypes_get (struct fstypes *types, + const char *name, struct fstype **fstype); + +/* Copy MNTENT into FS, copying component strings as well. */ +error_t fs_set_mntent (struct fs *fs, const struct mntent *mntent); + +/* Returns an fstype for FS in TYPE, trying to fill in FS's type field if + necessary. */ +error_t fs_type (struct fs *fs, struct fstype **type); + +/* Looks to see if FS is currently mounted, being very careful to avoid + mounting anything that's not already, and returns the control port for the + mounted filesystem (MACH_PORT_NULL if not mounted). */ +error_t fs_fsys (struct fs *fs, fsys_t *fsys); + +/* Looks to see if FS is currently mounted, being very careful to avoid + mounting anything that's not already, and returns the boolean MOUNTED. */ +error_t fs_mounted (struct fs *fs, int *mounted); + +/* Looks to see if FS is currently mounted readonly, being very careful to + avoid mounting anything that's not already, and returns the boolean + READONLY. If FS isn't mounted at all, READONLY is set to 1 (it's not + going to write anything!). */ +error_t fs_readonly (struct fs *fs, int *readonly); + +/* If FS is currently mounted writable, try to make it readonly. XXX If FS + is not mounted at all, then nothing is done. */ +error_t fs_set_readonly (struct fs *fs, int readonly); + +/* If FS is currently mounted tell it to remount the device. XXX If FS is + not mounted at all, then nothing is done. */ +error_t fs_remount (struct fs *fs); + +/* Destroy FS, removing it from its containing FSTAB. */ +void fs_free (struct fs *fs); + +/* Returns the FS entry in FSTAB with the device field NAME (there can only + be one such entry). */ +struct fs *fstab_find_device (const struct fstab *fstab, const char *name); + +/* Returns the FS entry in FSTAB with the mount point NAME (there can only + be one such entry). */ +struct fs *fstab_find_mount (const struct fstab *fstab, const char *name); + +/* Returns the FS entry in FSTAB with the device or mount point NAME (there + can only be one such entry). */ +struct fs *fstab_find (const struct fstab *fstab, const char *name); + +/* Add an entry for MNTENT to FSTAB, removing any existing entries that + conflict (in either the device or mount point). If RESULT is non-zero, the + new entry is returned in it. */ +error_t fstab_add_mntent (struct fstab *fstab, const struct mntent *mntent, + struct fs **result); + +/* Copy the entry FS (which should belong to another fstab than DST) into + DST. If DST & SRC have different TYPES fields, EINVAL is returned. If + COPY is non-zero, the copy is returned in it. */ +error_t fstab_add_fs (struct fstab *dst, const struct fs *fs, + struct fs **copy); + +/* Merge SRC into DST, as if by calling fstab_add_fs on DST with every + entry in SRC, and then deallocating SRC. If DST & SRC have different + TYPES fields, EINVAL is returned. */ +error_t fstab_merge (struct fstab *dst, struct fstab *src); + +/* Reads fstab-format entries into FSTAB from the file NAME. Any entries + duplicating one already in FS_LIST supersede the existing entry. */ +error_t fstab_read (struct fstab *fstab, const char *name); + +/* Return the next pass number that applies to any filesystem in FSTAB that + is greater than PASS, or -1 if there isn't any. */ +int fstab_next_pass (const struct fstab *fstab, int pass); + + +struct argp; +extern const struct argp fstab_argp; +struct fstab_argp_params +{ + char *fstab_path; + char *program_search_fmts; + size_t program_search_fmts_len; + + int do_all; + char *types; + size_t types_len; + char *exclude; + size_t exclude_len; + + char *names; + size_t names_len; +}; + +struct fstab *fstab_argp_create (struct fstab_argp_params *params, + const char *default_search_fmts, + size_t default_search_fmts_len); + + +#endif /* __FSTAB_H__ */ diff --git a/sutils/halt.c b/sutils/halt.c new file mode 100644 index 00000000..01f06b4f --- /dev/null +++ b/sutils/halt.c @@ -0,0 +1,39 @@ +/* Halt the system + Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc. + Written by Michael I. Bushnell. + + This file is part of the GNU Hurd. + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include <sys/reboot.h> +#include <unistd.h> +#include <stdio.h> +#include <argp.h> +#include <hurd.h> +#include <version.h> + +const char *argp_program_version = STANDARD_HURD_VERSION (halt); + +int +main (int argc, char *argv[]) +{ + struct argp argp = {0, 0, 0, "Halt the system"}; + argp_parse (&argp, argc, argv, 0, 0, 0); + reboot (RB_HALT); + perror ("reboot"); + return 1; +} diff --git a/sutils/losetup.sh b/sutils/losetup.sh new file mode 100644 index 00000000..85734571 --- /dev/null +++ b/sutils/losetup.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# +# This script is roughly compatible with the Linux `losetup' utility. +# The Hurd's `storeio' translator provides the equivalent functionality +# (and a whole lot more), and of course works on any old file you want +# to translate, not just magical "/dev/loopN" device files. +# + +PATH=/bin + +usage() { + echo >&2 ... + exit 1 +} + +offset=0 +while [ $# -gt 0 ]; do + case "$arg" in + -d) + [ $# -eq 2 ] || usage + exec settrans -g -- "$2" /hurd/null + ;; + -e) + echo >&2 "$0: encryption not supported" + exit 3 + ;; + -o) + [ $# -gt 1 ] || usage + offset="$1" + shift + ;; + --) + shift + break + ;; + -*) + usage + ;; + *) + break + ;; + esac +done + +[ $# -eq 2 ] || usage +device="$1" +file="$2" + +# If the device name is "/dev/loopN", then create it if necessary. (?) +create= +case "$device" in +'/dev/loop[0-9]*') ;; # smarty pants +/dev/loop[0-9]*) create=--create ;; +esac + +type='-Tfile ' +if [ "$offset" != 0 ]; then + blksz=`storeinfo -B -- "$file"` + if [ $[ $offset % $blksz ] -ne 0 ]; then + echo >&2 "$0: offset $offset is not a multiple of device block size $blksz" + exit 1 + fi + type="-Tremap $[ $offset / $blksz ]+:file:" +fi + +exec settrans $create -gap -- "${device}" /hurd/storeio ${type}"${file}" diff --git a/sutils/reboot.c b/sutils/reboot.c new file mode 100644 index 00000000..715dc90c --- /dev/null +++ b/sutils/reboot.c @@ -0,0 +1,39 @@ +/* Reboot the system + Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc. + Written by Michael I. Bushnell. + + This file is part of the GNU Hurd. + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include <sys/reboot.h> +#include <unistd.h> +#include <stdio.h> +#include <argp.h> +#include <hurd.h> +#include <version.h> + +const char *argp_program_version = STANDARD_HURD_VERSION (reboot); + +int +main (int argc, char *argv[]) +{ + struct argp argp = {0, 0, 0, "Reboot the system"}; + argp_parse (&argp, argc, argv, 0, 0, 0); + reboot (0); + perror ("reboot"); + return 1; +} diff --git a/sutils/swapoff.c b/sutils/swapoff.c new file mode 100644 index 00000000..42521257 --- /dev/null +++ b/sutils/swapoff.c @@ -0,0 +1,2 @@ +#define SWAPOFF +#include "swapon.c" diff --git a/sutils/swapon.c b/sutils/swapon.c new file mode 100644 index 00000000..fb264bc6 --- /dev/null +++ b/sutils/swapon.c @@ -0,0 +1,165 @@ +/* Add/remove paging devices + + Copyright (C) 1997,98,99 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.org> + This file is part of the GNU Hurd. + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd 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., + 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <hurd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <argp.h> +#include <error.h> +#include <hurd/store.h> +#include <version.h> +#include <mach/default_pager.h> +#include <mntent.h> + +#ifdef SWAPOFF +const char *argp_program_version = STANDARD_HURD_VERSION (swapoff); +#else +const char *argp_program_version = STANDARD_HURD_VERSION (swapon); +#endif + +static struct argp_option options[] = +{ + {"standard", 'a', 0, 0, + "Use all devices marked as `swap' in " _PATH_MNTTAB}, + {0, 0} +}; +static char *args_doc = "DEVICE..."; + +#ifdef SWAPOFF +static char *doc = "Stop paging on DEVICE..."; +#else +static char *doc = "Start paging onto DEVICE..."; +#endif + +static int +swaponoff (const char *file, int add) +{ + error_t err; + struct store *store; + static mach_port_t def_pager = MACH_PORT_NULL; + static mach_port_t dev_master = MACH_PORT_NULL; + + err = store_open (file, 0, 0, &store); + if (err) + { + error (0, err, "%s", file); + return err; + } + + if (store->class != &store_device_class) + { + error (0, 0, "%s: Can't get device", file); + return err; + } + if (! (store->flags & STORE_ENFORCED)) + { + error (0, 0, "%s: Can only page to the entire device", file); + return err; + } + + if (def_pager == MACH_PORT_NULL) + { + mach_port_t host; + + err = get_privileged_ports (&host, &dev_master); + if (err) + error (12, err, "Cannot get host port"); + + err = vm_set_default_memory_manager (host, &def_pager); + mach_port_deallocate (mach_task_self (), host); + if (err) + error (13, err, "Cannot get default pager port"); + if (def_pager == MACH_PORT_NULL) + error (14, 0, "No default pager (memory manager) is running!"); + } + + { + char pname[sizeof "/dev/" + strlen (store->name) + 1]; + snprintf (pname, sizeof pname, "/dev/%s", store->name); + err = default_pager_paging_file (def_pager, dev_master, pname, add); + if (err) + error (0, err, "%s", file); + } + + store_free (store); + + return err; +} + +static int do_all; + +int +main(int argc, char *argv[]) +{ + /* Parse our options... */ + error_t parse_opt (int key, char *arg, struct argp_state *state) + { + switch (key) + { + case 'a': + do_all = 1; + break; + + case ARGP_KEY_ARG: +#ifdef SWAPOFF +#define ONOFF 0 +#else +#define ONOFF 1 +#endif + swaponoff (arg, ONOFF); + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + struct argp argp = {options, parse_opt, args_doc, doc}; + + argp_parse (&argp, argc, argv, 0, 0, 0); + + if (do_all) + { + struct mntent *me; + FILE *f; + + f = setmntent (_PATH_MNTTAB, "r"); + if (f == NULL) + error (1, errno, "Cannot read %s", _PATH_MNTTAB); + else + { + int done = 0, err = 0; + while ((me = getmntent (f)) != NULL) + if (!strcmp (me->mnt_type, MNTTYPE_SWAP)) + { + done = 1; + + err |= swaponoff (me->mnt_fsname, ONOFF); + } + if (done == 0) + error (2, 0, "No swap partitions found in %s", _PATH_MNTTAB); + else if (err) + return 1; + } + } + + return 0; +} diff --git a/sutils/update.c b/sutils/update.c new file mode 100644 index 00000000..0342824f --- /dev/null +++ b/sutils/update.c @@ -0,0 +1,54 @@ +/* Periodically call sync. + Copyright (C) 1994 Free Software Foundation, Inc. + Written by Michael I. Bushnell. + + This file is part of the GNU Hurd. + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (int argc, char **argv) +{ + int interval; + + switch (argc) + { + case 1: + interval = 30; + break; + case 2: + interval = atoi (argv[1]); + break; + default: + fprintf (stderr, "Usage: %s [SECONDS]\n", argv[0]); + exit (1); + } + + if (daemon (0, 0)) + { + perror ("daemon"); + exit (1); + } + + for (;;) + { + sync (); + sleep (interval); + } +} |