diff options
Diffstat (limited to 'sbin')
207 files changed, 47134 insertions, 0 deletions
diff --git a/sbin/Makefile b/sbin/Makefile new file mode 100644 index 0000000..919fba6 --- /dev/null +++ b/sbin/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.5 (Berkeley) 3/31/94 + +SUBDIR= XNSrouted badsect clri disklabel dmesg dump dumpfs dumplfs fastboot \ + fsck fsdb icheck ifconfig init mknod mount mount_cd9660 mount_fdesc \ + mount_kernfs mount_lfs mount_nfs mount_null mount_portal \ + mount_procfs mount_umap mount_union mountd ncheck newfs newlfs nfsd \ + nfsiod nologin ping quotacheck reboot restore route routed savecore \ + scsiformat shutdown slattach startslip swapon tunefs umount + +.include <bsd.subdir.mk> diff --git a/sbin/Makefile.inc b/sbin/Makefile.inc new file mode 100644 index 0000000..79c318e --- /dev/null +++ b/sbin/Makefile.inc @@ -0,0 +1,3 @@ +# @(#)Makefile.inc 8.1 (Berkeley) 6/8/93 + +BINDIR?= /sbin diff --git a/sbin/badsect/Makefile b/sbin/badsect/Makefile new file mode 100644 index 0000000..ff9f4f1 --- /dev/null +++ b/sbin/badsect/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= badsect +MAN8= badsect.0 + +.include <bsd.prog.mk> diff --git a/sbin/badsect/badsect.8 b/sbin/badsect/badsect.8 new file mode 100644 index 0000000..840011d --- /dev/null +++ b/sbin/badsect/badsect.8 @@ -0,0 +1,132 @@ +.\" Copyright (c) 1985, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)badsect.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt BADSECT 8 +.Os BSD 4 +.Sh NAME +.Nm badsect +.Nd create files to contain bad sectors +.Sh SYNOPSIS +.Nm /etc/badsect +.Ar bbdir sector ... +.Sh DESCRIPTION +.Nm Badsect +makes a file to contain a bad sector. Normally, bad sectors +are made inaccessible by the standard formatter, which provides +a forwarding table for bad sectors to the driver; see +.Xr bad144 8 +for details. +If a driver supports the bad blocking standard it is much preferable to +use that method to isolate bad blocks, since the bad block forwarding +makes the pack appear perfect, and such packs can then be copied with +.Xr dd 1 . +The technique used by this program is also less general than +bad block forwarding, as +.Nm badsect +can't make amends for +bad blocks in the i-list of file systems or in swap areas. +.Pp +On some disks, +adding a sector which is suddenly bad to the bad sector table +currently requires the running of the standard +.Tn DEC +formatter. +Thus to deal with a newly bad block +or on disks where the drivers +do not support the bad-blocking standard +.Nm badsect +may be used to good effect. +.Pp +.Nm Badsect +is used on a quiet file system in the following way: +First mount the file system, and change to its root directory. +Make a directory +.Li BAD +there. Run +.Nm badsect +giving as argument the +.Ar BAD +directory followed by +all the bad sectors you wish to add. +(The sector numbers must be relative to the beginning of +the file system, but this is not hard as the system reports +relative sector numbers in its console error messages.) +Then change back to the root directory, unmount the file system +and run +.Xr fsck 8 +on the file system. The bad sectors should show up in two files +or in the bad sector files and the free list. Have +.Xr fsck +remove files containing the offending bad sectors, but +.Em do not +have it remove the +.Pa BAD/ Ns Em nnnnn +files. +This will leave the bad sectors in only the +.Li BAD +files. +.Pp +.Nm Badsect +works by giving the specified sector numbers in a +.Xr mknod 2 +system call, +creating an illegal file whose first block address is the block containing +bad sector and whose name is the bad sector number. +When it is discovered by +.Xr fsck +it will ask +.Dq Li "HOLD BAD BLOCK ?" +A positive response will cause +.Xr fsck +to convert the inode to a regular file containing the bad block. +.Sh SEE ALSO +.Xr bad144 8 , +.Xr fsck 8 , +.Xr format 8 +.Sh DIAGNOSTICS +.Nm Badsect +refuses to attach a block that +resides in a critical area or is out of range of the file system. +A warning is issued if the block is already in use. +.Sh BUGS +If more than one sector which comprise a file system fragment are bad, +you should specify only one of them to +.Nm badsect , +as the blocks in the bad sector files actually cover all the sectors in a +file system fragment. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.1 . diff --git a/sbin/badsect/badsect.c b/sbin/badsect/badsect.c new file mode 100644 index 0000000..fa978b1 --- /dev/null +++ b/sbin/badsect/badsect.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 1981, 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1981, 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)badsect.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +/* + * badsect + * + * Badsect takes a list of file-system relative sector numbers + * and makes files containing the blocks of which these sectors are a part. + * It can be used to contain sectors which have problems if these sectors + * are not part of the bad file for the pack (see bad144). For instance, + * this program can be used if the driver for the file system in question + * does not support bad block forwarding. + */ +#include <sys/param.h> +#include <sys/dir.h> +#include <sys/stat.h> + +#include <ufs/ffs/fs.h> +#include <ufs/ufs/dinode.h> + +#include <fcntl.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +union { + struct fs fs; + char fsx[SBSIZE]; +} ufs; +#define sblock ufs.fs +union { + struct cg cg; + char cgx[MAXBSIZE]; +} ucg; +#define acg ucg.cg +struct fs *fs; +int fso, fsi; +int errs; +long dev_bsize = 1; + +char buf[MAXBSIZE]; + +void rdfs __P((daddr_t, int, char *)); +int chkuse __P((daddr_t, int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + daddr_t number; + struct stat stbuf, devstat; + register struct direct *dp; + DIR *dirp; + char name[BUFSIZ]; + + if (argc < 3) { + fprintf(stderr, "usage: badsect bbdir blkno [ blkno ]\n"); + exit(1); + } + if (chdir(argv[1]) < 0 || stat(".", &stbuf) < 0) { + perror(argv[1]); + exit(2); + } + strcpy(name, _PATH_DEV); + if ((dirp = opendir(name)) == NULL) { + perror(name); + exit(3); + } + while ((dp = readdir(dirp)) != NULL) { + strcpy(&name[5], dp->d_name); + if (stat(name, &devstat) < 0) { + perror(name); + exit(4); + } + if (stbuf.st_dev == devstat.st_rdev && + (devstat.st_mode & IFMT) == IFBLK) + break; + } + closedir(dirp); + if (dp == NULL) { + printf("Cannot find dev 0%o corresponding to %s\n", + stbuf.st_rdev, argv[1]); + exit(5); + } + if ((fsi = open(name, 0)) < 0) { + perror(name); + exit(6); + } + fs = &sblock; + rdfs(SBOFF, SBSIZE, (char *)fs); + dev_bsize = fs->fs_fsize / fsbtodb(fs, 1); + for (argc -= 2, argv += 2; argc > 0; argc--, argv++) { + number = atoi(*argv); + if (chkuse(number, 1)) + continue; + if (mknod(*argv, IFMT|0600, dbtofsb(fs, number)) < 0) { + perror(*argv); + errs++; + } + } + printf("Don't forget to run ``fsck %s''\n", name); + exit(errs); +} + +int +chkuse(blkno, cnt) + daddr_t blkno; + int cnt; +{ + int cg; + daddr_t fsbn, bn; + + fsbn = dbtofsb(fs, blkno); + if ((unsigned)(fsbn+cnt) > fs->fs_size) { + printf("block %d out of range of file system\n", blkno); + return (1); + } + cg = dtog(fs, fsbn); + if (fsbn < cgdmin(fs, cg)) { + if (cg == 0 || (fsbn+cnt) > cgsblock(fs, cg)) { + printf("block %d in non-data area: cannot attach\n", + blkno); + return (1); + } + } else { + if ((fsbn+cnt) > cgbase(fs, cg+1)) { + printf("block %d in non-data area: cannot attach\n", + blkno); + return (1); + } + } + rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)sblock.fs_cgsize, + (char *)&acg); + if (!cg_chkmagic(&acg)) { + fprintf(stderr, "cg %d: bad magic number\n", cg); + errs++; + return (1); + } + bn = dtogd(fs, fsbn); + if (isclr(cg_blksfree(&acg), bn)) + printf("Warning: sector %d is in use\n", blkno); + return (0); +} + +/* + * read a block from the file system + */ +void +rdfs(bno, size, bf) + daddr_t bno; + int size; + char *bf; +{ + int n; + + if (lseek(fsi, (off_t)bno * dev_bsize, SEEK_SET) < 0) { + printf("seek error: %ld\n", bno); + perror("rdfs"); + exit(1); + } + n = read(fsi, bf, size); + if (n != size) { + printf("read error: %ld\n", bno); + perror("rdfs"); + exit(1); + } +} diff --git a/sbin/clri/Makefile b/sbin/clri/Makefile new file mode 100644 index 0000000..deb3cbb --- /dev/null +++ b/sbin/clri/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= clri +MAN8= clri.0 + +.include <bsd.prog.mk> diff --git a/sbin/clri/clri.8 b/sbin/clri/clri.8 new file mode 100644 index 0000000..df313a7 --- /dev/null +++ b/sbin/clri/clri.8 @@ -0,0 +1,79 @@ +.\" Copyright (c) 1980, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)clri.8 8.2 (Berkeley) 4/19/94 +.\" +.Dd April 19, 1994 +.Dt CLRI 8 +.Os BSD 4 +.Sh NAME +.Nm clri +.Nd clear an inode +.Sh SYNOPSIS +.Nm clri +.Ar special_device inode_number ... +.Sh DESCRIPTION +.Bf -symbolic +.Nm Clri +is obsoleted for normal file system repair work by +.Xr fsck 8 . +.Ef +.Pp +.Nm Clri +zeros out the inodes with the specified inode number(s) +on the filesystem residing on the given +.Ar special_device . +The +.Xr fsck 8 +utility is usually run after +.Nm clri +to reclaim the zero'ed inode(s) and the +blocks previously claimed by those inode(s). +Both read and write permission are required on the specified +.Ar special_device . +.Pp +The primary purpose of this routine +is to remove a file which +for some reason is not being properly handled by +.Xr fsck 8 . +Once removed, +it is anticipated that +.Xr fsck 8 +will be able to clean up the resulting mess. +.Sh "SEE ALSO" +.Xr fsck 8 , +.Xr fsdb 8 , +.Xr icheck 8 , +.Xr ncheck 8 +.Sh BUGS +If the file is open, the work of +.Nm clri +will be lost when the inode is written back to disk from the inode cache. diff --git a/sbin/clri/clri.c b/sbin/clri/clri.c new file mode 100644 index 0000000..c2bdccc --- /dev/null +++ b/sbin/clri/clri.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rich $alz of BBN Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)clri.c 8.2 (Berkeley) 9/23/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct fs *sbp; + register struct dinode *ip; + register int fd; + struct dinode ibuf[MAXBSIZE / sizeof (struct dinode)]; + long generation, bsize; + off_t offset; + int inonum; + char *fs, sblock[SBSIZE]; + + if (argc < 3) { + (void)fprintf(stderr, "usage: clri filesystem inode ...\n"); + exit(1); + } + + fs = *++argv; + + /* get the superblock. */ + if ((fd = open(fs, O_RDWR, 0)) < 0) + err(1, "%s", fs); + if (lseek(fd, (off_t)(SBLOCK * DEV_BSIZE), SEEK_SET) < 0) + err(1, "%s", fs); + if (read(fd, sblock, sizeof(sblock)) != sizeof(sblock)) { + (void)fprintf(stderr, + "clri: %s: can't read the superblock.\n", fs); + exit(1); + } + + sbp = (struct fs *)sblock; + if (sbp->fs_magic != FS_MAGIC) { + (void)fprintf(stderr, + "clri: %s: superblock magic number 0x%x, not 0x%x.\n", + fs, sbp->fs_magic, FS_MAGIC); + exit(1); + } + bsize = sbp->fs_bsize; + + /* remaining arguments are inode numbers. */ + while (*++argv) { + /* get the inode number. */ + if ((inonum = atoi(*argv)) <= 0) { + (void)fprintf(stderr, + "clri: %s is not a valid inode number.\n", *argv); + exit(1); + } + (void)printf("clearing %d\n", inonum); + + /* read in the appropriate block. */ + offset = ino_to_fsba(sbp, inonum); /* inode to fs blk */ + offset = fsbtodb(sbp, offset); /* fs blk disk blk */ + offset *= DEV_BSIZE; /* disk blk to bytes */ + + /* seek and read the block */ + if (lseek(fd, offset, SEEK_SET) < 0) + err(1, "%s", fs); + if (read(fd, ibuf, bsize) != bsize) + err(1, "%s", fs); + + /* get the inode within the block. */ + ip = &ibuf[ino_to_fsbo(sbp, inonum)]; + + /* clear the inode, and bump the generation count. */ + generation = ip->di_gen + 1; + memset(ip, 0, sizeof(*ip)); + ip->di_gen = generation; + + /* backup and write the block */ + if (lseek(fd, (off_t)-bsize, SEEK_CUR) < 0) + err(1, "%s", fs); + if (write(fd, ibuf, bsize) != bsize) + err(1, "%s", fs); + (void)fsync(fd); + } + (void)close(fd); + exit(0); +} diff --git a/sbin/disklabel/Makefile b/sbin/disklabel/Makefile new file mode 100644 index 0000000..3f4749c --- /dev/null +++ b/sbin/disklabel/Makefile @@ -0,0 +1,14 @@ +# @(#)Makefile 8.2 (Berkeley) 3/17/94 + +PROG= disklabel +SRCS= disklabel.c dkcksum.c +MAN8= disklabel.0 +CLEANFILES=disklabel.5.0 + +all: ${PROG} disklabel.5.0 ${MAN8} + +beforeinstall: + install -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} disklabel.5.0 \ + ${DESTDIR}${MANDIR}5/disklabel.0 + +.include <bsd.prog.mk> diff --git a/sbin/disklabel/disklabel.5.5 b/sbin/disklabel/disklabel.5.5 new file mode 100644 index 0000000..fb6f6cd --- /dev/null +++ b/sbin/disklabel/disklabel.5.5 @@ -0,0 +1,384 @@ +.\" Copyright (c) 1987, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Symmetric Computer Systems. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)disklabel.5.5 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt DISKLABEL 5 +.Os +.Sh NAME +.Nm disklabel +.Nd disk pack label +.Sh SYNOPSIS +.Fd #include <sys/disklabel.h> +.Sh DESCRIPTION +Each disk or disk pack on a system may contain a disk label +which provides detailed information +about the geometry of the disk and the partitions into which the disk +is divided. +It should be initialized when the disk is formatted, +and may be changed later with the +.Xr disklabel 8 +program. +This information is used by the system disk driver and by the bootstrap +program to determine how to program the drive +and where to find the filesystems on the disk partitions. +Additional information is used by the filesystem in order +to use the disk most efficiently and to locate important filesystem information. +The description of each partition contains an identifier for the partition +type (standard filesystem, swap area, etc.). +The filesystem updates the in-core copy of the label if it contains +incomplete information about the filesystem. +.Pp +The label is located in sector number +.Dv LABELSECTOR +of the drive, usually sector 0 where it may be found +without any information about the disk geometry. +It is at an offset +.Dv LABELOFFSET +from the beginning of the sector, to allow room for the initial bootstrap. +The disk sector containing the label is normally made read-only +so that it is not accidentally overwritten by pack-to-pack copies +or swap operations; +the +.Dv DIOCWLABEL +.Xr ioctl 2 , +which is done as needed by the +.Xr disklabel +program. +.Pp +A copy of the in-core label for a disk can be obtained with the +.Dv DIOCGDINFO +.Xr ioctl ; +this works with a file descriptor for a block or character (``raw'') device +for any partition of the disk. +The in-core copy of the label is set by the +.Dv DIOCSDINFO +.Xr ioctl . +The offset of a partition cannot generally be changed while it is open, +nor can it be made smaller while it is open. +One exception is that any change is allowed if no label was found +on the disk, and the driver was able to construct only a skeletal label +without partition information. +Finally, the +.Dv DIOCWDINFO +.Xr ioctl +operation sets the in-core label and then updates the on-disk label; +there must be an existing label on the disk for this operation to succeed. +Thus, the initial label for a disk or disk pack must be installed +by writing to the raw disk. +All of these operations are normally done using +.Xr disklabel . +.Pp +The format of the disk label, as specified in +.Aw Pa sys/disklabel.h , +is +.Bd -literal +/* +* Disk description table, see disktab(5) +*/ +#define DISKTAB "/etc/disktab" + +/* +* Each disk has a label which includes information about the hardware +* disk geometry, filesystem partitions, and drive specific information. +* The label is in block 0 or 1, possibly offset from the beginning +* to leave room for a bootstrap, etc. +*/ + +#ifndef LABELSECTOR +#define LABELSECTOR 0 /* sector containing label */ +#endif + +#ifndef LABELOFFSET +#define LABELOFFSET 64 /* offset of label in sector */ +#endif + +#define DISKMAGIC ((u_long) 0x82564557) /* The disk magic number */ +#ifndef MAXPARTITIONS +#define MAXPARTITIONS 8 +#endif + +#ifndef LOCORE +struct disklabel { + u_long d_magic; /* the magic number */ + short d_type; /* drive type */ + short d_subtype; /* controller/d_type specific */ + char d_typename[16]; /* type name, e.g. "eagle" */ + /* + * d_packname contains the pack identifier and is returned when + * the disklabel is read off the disk or in-core copy. + * d_boot0 and d_boot1 are the (optional) names of the + * primary (block 0) and secondary (block 1-15) bootstraps + * as found in /usr/mdec. These are returned when using + * getdiskbyname(3) + to retrieve the values from /etc/disktab. + */ +#if defined(KERNEL) || defined(STANDALONE) + char d_packname[16]; /* pack identifier */ +#else + union { + char un_d_packname[16]; /* pack identifier */ + struct { + char *un_d_boot0; /* primary bootstrap name */ + char *un_d_boot1; /* secondary bootstrap name */ + } un_b; + } d_un; + +#define d_packname d_un.un_d_packname +#define d_boot0 d_un.un_b.un_d_boot0 +#define d_boot1 d_un.un_b.un_d_boot1 +#endif /* ! KERNEL or STANDALONE */ + + /* disk geometry: */ + u_long d_secsize; /* # of bytes per sector */ + u_long d_nsectors; /* # of data sectors per track */ + u_long d_ntracks; /* # of tracks per cylinder */ + u_long d_ncylinders; /* # of data cylinders per unit */ + u_long d_secpercyl; /* # of data sectors per cylinder */ + u_long d_secperunit; /* # of data sectors per unit */ + /* + * Spares (bad sector replacements) below + * are not counted in d_nsectors or d_secpercyl. + * Spare sectors are assumed to be physical sectors + * which occupy space at the end of each track and/or cylinder. + */ + u_short d_sparespertrack; /* # of spare sectors per track */ + u_short d_sparespercyl; /* # of spare sectors per cylinder */ + /* + * Alternate cylinders include maintenance, replacement, + * configuration description areas, etc. + */ + u_long d_acylinders; /* # of alt. cylinders per unit */ + + /* hardware characteristics: */ + /* + * d_interleave, d_trackskew and d_cylskew describe perturbations + * in the media format used to compensate for a slow controller. + * Interleave is physical sector interleave, set up by the formatter + * or controller when formatting. When interleaving is in use, + * logically adjacent sectors are not physically contiguous, + * but instead are separated by some number of sectors. + * It is specified as the ratio of physical sectors traversed + * per logical sector. Thus an interleave of 1:1 implies contiguous + * layout, while 2:1 implies that logical sector 0 is separated + * by one sector from logical sector 1. + * d_trackskew is the offset of sector 0 on track N + * relative to sector 0 on track N-1 on the same cylinder. + * Finally, d_cylskew is the offset of sector 0 on cylinder N + * relative to sector 0 on cylinder N-1. + */ + u_short d_rpm; /* rotational speed */ + u_short d_interleave; /* hardware sector interleave */ + u_short d_trackskew; /* sector 0 skew, per track */ + u_short d_cylskew; /* sector 0 skew, per cylinder */ + u_long d_headswitch; /* head switch time, usec */ + u_long d_trkseek; /* track-to-track seek, usec */ + u_long d_flags; /* generic flags */ +#define NDDATA 5 + u_long d_drivedata[NDDATA]; /* drive-type specific information */ +#define NSPARE 5 + u_long d_spare[NSPARE]; /* reserved for future use */ + u_long d_magic2; /* the magic number (again) */ + u_short d_checksum; /* xor of data incl. partitions */ + + /* filesystem and partition information: */ + u_short d_npartitions; /* number of partitions in following */ + u_long d_bbsize; /* size of boot area at sn0, bytes */ + u_long d_sbsize; /* max size of fs superblock, bytes */ + struct partition { /* the partition table */ + u_long p_size; /* number of sectors in partition */ + u_long p_offset; /* starting sector */ + u_long p_fsize; /* filesystem basic fragment size */ + u_char p_fstype; /* filesystem type, see below */ + u_char p_frag; /* filesystem fragments per block */ + union { + u_short cpg; /* UFS: FS cylinders per group */ + u_short sgs; /* LFS: FS segment shift */ + } __partition_u1; +#define p_cpg __partition_u1.cpg +#define p_sgs __partition_u1.sgs + u_short p_cpg; /* filesystem cylinders per group */ + } d_partitions[MAXPARTITIONS]; /* actually may be more */ +}; + +/* d_type values: */ +#define DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */ +#define DTYPE_MSCP 2 /* MSCP */ +#define DTYPE_DEC 3 /* other DEC (rk, rl) */ +#define DTYPE_SCSI 4 /* SCSI */ +#define DTYPE_ESDI 5 /* ESDI interface */ +#define DTYPE_ST506 6 /* ST506 etc. */ +#define DTYPE_HPIB 7 /* CS/80 on HP-IB */ +#define DTYPE_HPFL 8 /* HP Fiber-link */ +#define DTYPE_FLOPPY 10 /* floppy */ + +#ifdef DKTYPENAMES +static char *dktypenames[] = { + "unknown", + "SMD", + "MSCP", + "old DEC", + "SCSI", + "ESDI", + "ST506", + "HP-IB", + "HP-FL", + "type 9", + "floppy", + 0 +}; +#define DKMAXTYPES (sizeof(dktypenames) / sizeof(dktypenames[0]) - 1) +#endif + +/* +* Filesystem type and version. +* Used to interpret other filesystem-specific +* per-partition information. +*/ +#define FS_UNUSED 0 /* unused */ +#define FS_SWAP 1 /* swap */ +#define FS_V6 2 /* Sixth Edition */ +#define FS_V7 3 /* Seventh Edition */ +#define FS_SYSV 4 /* System V */ +#define FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */ +#define FS_V8 6 /* Eighth Edition, 4K blocks */ +#define FS_BSDFFS 7 /* 4.2BSD fast file system */ +#define FS_MSDOS 8 /* MSDOS file system */ +#define FS_BSDLFS 9 /* 4.4BSD log-structured file system */ +#define FS_OTHER 10 /* in use, but unknown/unsupported */ +#define FS_HPFS 11 /* OS/2 high-performance file system */ +#define FS_ISO9660 12 /* ISO 9660, normally CD-ROM */ +#define FS_BOOT 13 /* partition contains bootstrap */ + +#ifdef DKTYPENAMES +static char *fstypenames[] = { + "unused", + "swap", + "Version 6", + "Version 7", + "System V", + "4.1BSD", + "Eighth Edition", + "4.2BSD", + "MSDOS", + "4.4LFS", + "unknown", + "HPFS", + "ISO9660", + "boot", + 0 +}; +#define FSMAXTYPES (sizeof(fstypenames) / sizeof(fstypenames[0]) - 1) +#endif + +/* +* flags shared by various drives: +*/ +#define D_REMOVABLE 0x01 /* removable media */ +#define D_ECC 0x02 /* supports ECC */ +#define D_BADSECT 0x04 /* supports bad sector forw. */ +#define D_RAMDISK 0x08 /* disk emulator */ +#define D_CHAIN 0x10 /* can do back-back transfers */ + +/* +* Drive data for SMD. +*/ + +#define d_smdflags d_drivedata[0] +#define D_SSE 0x1 /* supports skip sectoring */ +#define d_mindist d_drivedata[1] +#define d_maxdist d_drivedata[2] +#define d_sdist d_drivedata[3] + +/* +* Drive data for ST506. +*/ +#define d_precompcyl d_drivedata[0] +#define d_gap3 d_drivedata[1] /* used only when formatting */ + +/* + * Drive data for SCSI. + */ +#define d_blind d_drivedata[0] + +#ifndef LOCORE +/* +* Structure used to perform a format +* or other raw operation, returning data +* and/or register values. +* Register identification and format +* are device- and driver-dependent. +*/ +struct format_op { + char *df_buf; + int df_count; /* value-result */ + daddr_t df_startblk; + int df_reg[8]; /* result */ +}; + +/* +* Structure used internally to retrieve +* information about a partition on a disk. +*/ +struct partinfo { + struct disklabel *disklab; + struct partition *part; +}; + +/* +* Disk-specific ioctls. +*/ + /* get and set disklabel; DIOCGPART used internally */ +#define DIOCGDINFO _IOR('d', 101, struct disklabel) /* get */ +#define DIOCSDINFO _IOW('d', 102, struct disklabel) /* set */ +#define DIOCWDINFO _IOW('d', 103, struct disklabel) /* set, update disk */ +#define DIOCGPART _IOW('d', 104, struct partinfo) /* get partition */ + +/* do format operation, read or write */ +#define DIOCRFORMAT _IOWR('d', 105, struct format_op) +#define DIOCWFORMAT _IOWR('d', 106, struct format_op) + +#define DIOCSSTEP _IOW('d', 107, int) /* set step rate */ +#define DIOCSRETRIES _IOW('d', 108, int) /* set # of retries */ +#define DIOCWLABEL _IOW('d', 109, int) /* write en/disable label */ + +#define DIOCSBAD _IOW('d', 110, struct dkbad) /* set kernel dkbad */ + +#endif LOCORE +.Ed +.Sh SEE ALSO +.Xr disktab 5 , +.Xr disklabel 8 +.Sh HISTORY diff --git a/sbin/disklabel/disklabel.8 b/sbin/disklabel/disklabel.8 new file mode 100644 index 0000000..bf14e0f --- /dev/null +++ b/sbin/disklabel/disklabel.8 @@ -0,0 +1,343 @@ +.\" Copyright (c) 1987, 1988, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Symmetric Computer Systems. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)disklabel.8 8.2 (Berkeley) 4/19/94 +.\" +.Dd "April 19, 1994" +.Dt DISKLABEL 8 +.Os BSD 4.2 +.Sh NAME +.Nm disklabel +.Nd read and write disk pack label +.Sh SYNOPSIS +.Nm disklabel +.Op Fl r +.Ar disk +.Nm disklabel +.Fl w +.Op Fl r +.Ar disk Ar disktype +.Oo Ar packid Oc +.Nm disklabel +.Fl e +.Op Fl r +.Ar disk +.Nm disklabel +.Fl R +.Op Fl r +.Ar disk Ar protofile +.Nm disklabel +.Op Fl NW +.Ar disk +.sp +.Nm disklabel +.Fl B +.Oo +.Fl b Ar boot1 +.Op Fl s Ar boot2 +.Oc +.Ar disk +.Oo Ar disktype Oc +.Nm disklabel +.Fl w +.Fl B +.Oo +.Fl b Ar boot1 +.Op Fl s Ar boot2 +.Oc +.Ar disk Ar disktype +.Oo Ar packid Oc +.Nm disklabel +.Fl R +.Fl B +.Oo +.Fl b Ar boot1 +.Op Fl s Ar boot2 +.Oc +.Ar disk Ar protofile +.Oo Ar disktype Oc +.Sh DESCRIPTION +.Nm Disklabel +can be used to install, examine or modify the label on a disk drive or pack. +When writing the label, it can be used +to change the drive identification, +the disk partitions on the drive, +or to replace a damaged label. +On some systems, +.Nm disklabel +can be used to install bootstrap code as well. +There are several forms of the command that read (display), install or edit +the label on a disk. +Each form has an additional option, +.Fl r , +which causes the label to be read from or written to the disk directly, +rather than going through the system's in-core copy of the label. +This option may allow a label to be installed on a disk +without kernel support for a label, such as when labels are first installed +on a system; it must be used when first installing a label on a disk. +The specific effect of +.Fl r +is described under each command. +The read and install forms also support the +.Fl B +option to install bootstrap code. +These variants are described later. +.Pp +The first form of the command (read) is used to examine the label on the named +disk drive (e.g. sd0 or /dev/rsd0c). +It will display all of the parameters associated with the drive +and its partition layout. +Unless the +.Fl r +flag is given, +the kernel's in-core copy of the label is displayed; +if the disk has no label, or the partition types on the disk are incorrect, +the kernel may have constructed or modified the label. +If the +.Fl r +flag is given, the label from the raw disk will be displayed rather +than the in-core label. +.Pp +The second form of the command, with the +.Fl w +flag, is used to write a standard label on the designated drive. +The required arguments to +.Nm disklabel +are the drive to be labelled (e.g. sd0), and +the drive type as described in the +.Xr disktab 5 +file. +The drive parameters and partitions are taken from that file. +If different disks of the same physical type are to have different +partitions, it will be necessary to have separate disktab entries +describing each, or to edit the label after installation as described below. +The optional argument is a pack identification string, +up to 16 characters long. +The pack id must be quoted if it contains blanks. +If the +.Fl r +flag is given, the disk sectors containing the label and bootstrap +will be written directly. +A side-effect of this is that any existing bootstrap code will be overwritten +and the disk rendered unbootable. +If +.Fl r +is not specified, +the existing label will be updated via the in-core copy and any bootstrap +code will be unaffected. +If the disk does not already have a label, the +.Fl r +flag must be used. +In either case, the kernel's in-core label is replaced. +.Pp +An existing disk label may be edited by using the +.Fl e +flag. +The label is read from the in-core kernel copy, +or directly from the disk if the +.Fl r +flag is also given. +The label is formatted and then supplied to an editor for changes. +If no editor is specified in an +.Ev EDITOR +environment variable, +.Xr vi 1 +is used. +When the editor terminates, the formatted label is reread +and used to rewrite the disk label. +Existing bootstrap code is unchanged regardless of whether +.Fl r +was specified. +.Pp +With the +.Fl R +flag, +.Nm disklabel +is capable of restoring a disk label that was formatted +in a prior operation and saved in an ascii file. +The prototype file used to create the label should be in the same format +as that produced when reading or editing a label. +Comments are delimited by +.Ar \&# +and newline. +As with +.Fl w , +any existing bootstrap code will be clobbered if +.Fl r +is specified and will be unaffected otherwise. +.Pp +The +.Fl NW +flags for +.Nm disklabel +explicitly disallow and +allow, respectively, writing of the pack label area on the selected disk. +.Pp +The final three forms of +.Nm disklabel +are used to install boostrap code on machines where the bootstrap is part +of the label. +The bootstrap code is comprised of one or two boot programs depending on +the machine. +The +.Fl B +option is used to denote that bootstrap code is to be installed. +The +.Fl r +flag is implied by +.Fl B +and never needs to be specified. +The name of the boot program(s) to be installed can be selected in a +variety of ways. +First, the names can be specified explicitly via the +.Fl b +and +.Fl s +flags. +On machines with only a single level of boot program, +.Fl b +is the name of that program. +For machines with a two-level bootstrap, +.Fl b +indicates the primary boot program and +.Fl s +the secondary boot program. +If the names are not explicitly given, standard boot programs will be used. +The boot programs are located in +.Pa /usr/mdec . +The names of the programs are taken from the ``b0'' and ``b1'' parameters +of the +.Xr disktab 5 +entry for the disk if +.Ar disktype +was given and its disktab entry exists and includes those parameters. +Otherwise, boot program names are derived from the name of the disk. +These names are of the form +.Pa basename Ns boot +for the primary (or only) bootstrap, and +.Pf boot Pa basename +for the secondary bootstrap; +for example, +.Pa /usr/mdec/sdboot +and +.Pa /usr/mdec/bootsd +if the disk device is +.Em sd0 . +.Pp +The first of the three boot-installation forms is used to install +bootstrap code without changing the existing label. +It is essentially a read command with respect to the disk label +itself and all options are related to the specification of the boot +program as described previously. +The final two forms are analogous to the basic write and restore versions +except that they will install bootstrap code in addition to a new label. +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /etc/disktab +.It Pa /usr/mdec/ Ns Em xx Ns boot +.It Pa /usr/mdec/boot Ns Em xx +.El +.Sh EXAMPLES +.Dl disklabel sd0 +.Pp +Display the in-core label for sd0 as obtained via +.Pa /dev/rsd0c . +.Pp +.Dl disklabel -w -r /dev/rsd0c sd2212 foo +.Pp +Create a label for sd0 based on information for ``sd2212'' found in +.Pa /etc/disktab . +Any existing bootstrap code will be clobbered. +.Pp +.Dl disklabel -e -r sd0 +.Pp +Read the on-disk label for sd0, edit it and reinstall in-core as well +as on-disk. +Existing bootstrap code is unaffected. +.Pp +.Dl disklabel -R sd0 mylabel +.Pp +Restore the on-disk and in-core label for sd0 from information in +.Pa mylabel . +Existing bootstrap code is unaffected. +.Pp +.Dl disklabel -B sd0 +.Pp +Install a new bootstrap on sd0. +The boot code comes from +.Pa /usr/mdec/sdboot +and possibly +.Pa /usr/mdec/bootsd . +On-disk and in-core labels are unchanged. +.Pp +.Dl disklabel -w -B /dev/rsd0c -b newboot sd2212 +.Pp +Install a new label and bootstrap. +The label is derived from disktab information for ``sd2212'' and +installed both in-core and on-disk. +The bootstrap code comes from the file +.Pa /usr/mdec/newboot . +.Sh SEE ALSO +.Xr disktab 5 , +.Xr disklabel 5 +.Sh DIAGNOSTICS +The kernel device drivers will not allow the size of a disk partition +to be decreased or the offset of a partition to be changed while it is open. +Some device drivers create a label containing only a single large partition +if a disk is unlabeled; thus, the label must be written to the ``a'' +partition of the disk while it is open. +This sometimes requires the desired label to be set in two steps, +the first one creating at least one other partition, +and the second setting the label on the new partition +while shrinking the ``a'' partition. +.Pp +On some machines the bootstrap code may not fit entirely in the area +allocated for it by some filesystems. +As a result, it may not be possible to have filesystems on some partitions +of a ``bootable'' disk. +When installing bootstrap code, +.Nm disklabel +checks for these cases. +If the installed boot code would overlap a partition of type FS_UNUSED +it is marked as type FS_BOOT. +The +.Xr newfs 8 +utility will disallow creation of filesystems on FS_BOOT partitions. +Conversely, if a partition has a type other than FS_UNUSED or FS_BOOT, +.Nm disklabel +will not install bootstrap code that overlaps it. +.Sh BUGS +When a disk name is given without a full pathname, +the constructed device name uses the ``a'' partition on the tahoe, +the ``c'' partition on all others. diff --git a/sbin/disklabel/disklabel.c b/sbin/disklabel/disklabel.c new file mode 100644 index 0000000..4a29340 --- /dev/null +++ b/sbin/disklabel/disklabel.c @@ -0,0 +1,1314 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Symmetric Computer Systems. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)disklabel.c 8.2 (Berkeley) 1/7/94"; +/* from static char sccsid[] = "@(#)disklabel.c 1.2 (Symmetric) 11/28/85"; */ +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/signal.h> +#include <sys/errno.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#define DKTYPENAMES +#include <sys/disklabel.h> +#include <ufs/ffs/fs.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include "pathnames.h" + +/* + * Disklabel: read and write disklabels. + * The label is usually placed on one of the first sectors of the disk. + * Many machines also place a bootstrap in the same area, + * in which case the label is embedded in the bootstrap. + * The bootstrap source must leave space at the proper offset + * for the label on such machines. + */ + +#ifdef tahoe +#define RAWPARTITION 'a' +#else +#define RAWPARTITION 'c' +#endif + +#ifndef BBSIZE +#define BBSIZE 8192 /* size of boot area, with label */ +#endif + +#ifdef tahoe +#define NUMBOOT 0 +#else +#if defined(hp300) || defined(hp800) +#define NUMBOOT 1 +#else +#define NUMBOOT 2 +#endif +#endif + +#define DEFEDITOR _PATH_VI +#define streq(a,b) (strcmp(a,b) == 0) + +char *dkname; +char *specname; +char tmpfil[] = _PATH_TMP; + +extern int errno; +char namebuf[BBSIZE], *np = namebuf; +struct disklabel lab; +struct disklabel *readlabel(), *makebootarea(); +char bootarea[BBSIZE]; + +#if NUMBOOT > 0 +int installboot; /* non-zero if we should install a boot program */ +char *bootbuf; /* pointer to buffer with remainder of boot prog */ +int bootsize; /* size of remaining boot program */ +char *xxboot; /* primary boot */ +char *bootxx; /* secondary boot */ +char boot0[MAXPATHLEN]; +char boot1[MAXPATHLEN]; +#endif + +enum { + UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE, WRITEBOOT +} op = UNSPEC; + +int rflag; + +#ifdef DEBUG +int debug; +#define OPTIONS "BNRWb:ders:w" +#else +#define OPTIONS "BNRWb:ers:w" +#endif + + +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + register struct disklabel *lp; + FILE *t; + int ch, f, flag, error = 0; + char *name = 0; + + while ((ch = getopt(argc, argv, OPTIONS)) != EOF) + switch (ch) { +#if NUMBOOT > 0 + case 'B': + ++installboot; + break; + case 'b': + xxboot = optarg; + break; +#if NUMBOOT > 1 + case 's': + bootxx = optarg; + break; +#endif +#endif + case 'N': + if (op != UNSPEC) + usage(); + op = NOWRITE; + break; + case 'R': + if (op != UNSPEC) + usage(); + op = RESTORE; + break; + case 'W': + if (op != UNSPEC) + usage(); + op = WRITEABLE; + break; + case 'e': + if (op != UNSPEC) + usage(); + op = EDIT; + break; + case 'r': + ++rflag; + break; + case 'w': + if (op != UNSPEC) + usage(); + op = WRITE; + break; +#ifdef DEBUG + case 'd': + debug++; + break; +#endif + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; +#if NUMBOOT > 0 + if (installboot) { + rflag++; + if (op == UNSPEC) + op = WRITEBOOT; + } else { + if (op == UNSPEC) + op = READ; + xxboot = bootxx = 0; + } +#else + if (op == UNSPEC) + op = READ; +#endif + if (argc < 1) + usage(); + + dkname = argv[0]; + if (dkname[0] != '/') { + (void)sprintf(np, "%sr%s%c", _PATH_DEV, dkname, RAWPARTITION); + specname = np; + np += strlen(specname) + 1; + } else + specname = dkname; + f = open(specname, op == READ ? O_RDONLY : O_RDWR); + if (f < 0 && errno == ENOENT && dkname[0] != '/') { + (void)sprintf(specname, "%sr%s", _PATH_DEV, dkname); + np = namebuf + strlen(specname) + 1; + f = open(specname, op == READ ? O_RDONLY : O_RDWR); + } + if (f < 0) + Perror(specname); + + switch(op) { + + case EDIT: + if (argc != 1) + usage(); + lp = readlabel(f); + error = edit(lp, f); + break; + + case NOWRITE: + flag = 0; + if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0) + Perror("ioctl DIOCWLABEL"); + break; + + case READ: + if (argc != 1) + usage(); + lp = readlabel(f); + display(stdout, lp); + error = checklabel(lp); + break; + + case RESTORE: +#if NUMBOOT > 0 + if (installboot && argc == 3) { + makelabel(argv[2], 0, &lab); + argc--; + } +#endif + if (argc != 2) + usage(); + lp = makebootarea(bootarea, &lab, f); + if (!(t = fopen(argv[1], "r"))) + Perror(argv[1]); + if (getasciilabel(t, lp)) + error = writelabel(f, bootarea, lp); + break; + + case WRITE: + if (argc == 3) { + name = argv[2]; + argc--; + } + if (argc != 2) + usage(); + makelabel(argv[1], name, &lab); + lp = makebootarea(bootarea, &lab, f); + *lp = lab; + if (checklabel(lp) == 0) + error = writelabel(f, bootarea, lp); + break; + + case WRITEABLE: + flag = 1; + if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0) + Perror("ioctl DIOCWLABEL"); + break; + +#if NUMBOOT > 0 + case WRITEBOOT: + { + struct disklabel tlab; + + lp = readlabel(f); + tlab = *lp; + if (argc == 2) + makelabel(argv[1], 0, &lab); + lp = makebootarea(bootarea, &lab, f); + *lp = tlab; + if (checklabel(lp) == 0) + error = writelabel(f, bootarea, lp); + break; + } +#endif + } + exit(error); +} + +/* + * Construct a prototype disklabel from /etc/disktab. As a side + * effect, set the names of the primary and secondary boot files + * if specified. + */ +makelabel(type, name, lp) + char *type, *name; + register struct disklabel *lp; +{ + register struct disklabel *dp; + char *strcpy(); + + dp = getdiskbyname(type); + if (dp == NULL) { + fprintf(stderr, "%s: unknown disk type\n", type); + exit(1); + } + *lp = *dp; +#if NUMBOOT > 0 + /* + * Set bootstrap name(s). + * 1. If set from command line, use those, + * 2. otherwise, check if disktab specifies them (b0 or b1), + * 3. otherwise, makebootarea() will choose ones based on the name + * of the disk special file. E.g. /dev/ra0 -> raboot, bootra + */ + if (!xxboot && lp->d_boot0) { + if (*lp->d_boot0 != '/') + (void)sprintf(boot0, "%s/%s", + _PATH_BOOTDIR, lp->d_boot0); + else + (void)strcpy(boot0, lp->d_boot0); + xxboot = boot0; + } +#if NUMBOOT > 1 + if (!bootxx && lp->d_boot1) { + if (*lp->d_boot1 != '/') + (void)sprintf(boot1, "%s/%s", + _PATH_BOOTDIR, lp->d_boot1); + else + (void)strcpy(boot1, lp->d_boot1); + bootxx = boot1; + } +#endif +#endif + /* d_packname is union d_boot[01], so zero */ + bzero(lp->d_packname, sizeof(lp->d_packname)); + if (name) + (void)strncpy(lp->d_packname, name, sizeof(lp->d_packname)); +} + +writelabel(f, boot, lp) + int f; + char *boot; + register struct disklabel *lp; +{ + register int i; + int flag; + + setbootflag(lp); + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_checksum = 0; + lp->d_checksum = dkcksum(lp); + if (rflag) { + /* + * First set the kernel disk label, + * then write a label to the raw disk. + * If the SDINFO ioctl fails because it is unimplemented, + * keep going; otherwise, the kernel consistency checks + * may prevent us from changing the current (in-core) + * label. + */ + if (ioctl(f, DIOCSDINFO, lp) < 0 && + errno != ENODEV && errno != ENOTTY) { + l_perror("ioctl DIOCSDINFO"); + return (1); + } + (void)lseek(f, (off_t)0, SEEK_SET); + /* + * write enable label sector before write (if necessary), + * disable after writing. + */ + flag = 1; + if (ioctl(f, DIOCWLABEL, &flag) < 0) + perror("ioctl DIOCWLABEL"); + if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) { + perror("write"); + return (1); + } +#if NUMBOOT > 0 + /* + * Output the remainder of the disklabel + */ + if (bootbuf && write(f, bootbuf, bootsize) != bootsize) { + perror("write"); + return(1); + } +#endif + flag = 0; + (void) ioctl(f, DIOCWLABEL, &flag); + } else if (ioctl(f, DIOCWDINFO, lp) < 0) { + l_perror("ioctl DIOCWDINFO"); + return (1); + } +#ifdef vax + if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) { + daddr_t alt; + + alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors; + for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) { + (void)lseek(f, (off_t)((alt + i) * lp->d_secsize), + SEEK_SET); + if (write(f, boot, lp->d_secsize) < lp->d_secsize) { + int oerrno = errno; + fprintf(stderr, "alternate label %d ", i/2); + errno = oerrno; + perror("write"); + } + } + } +#endif + return (0); +} + +l_perror(s) + char *s; +{ + int saverrno = errno; + + fprintf(stderr, "disklabel: %s: ", s); + + switch (saverrno) { + + case ESRCH: + fprintf(stderr, "No disk label on disk;\n"); + fprintf(stderr, + "use \"disklabel -r\" to install initial label\n"); + break; + + case EINVAL: + fprintf(stderr, "Label magic number or checksum is wrong!\n"); + fprintf(stderr, "(disklabel or kernel is out of date?)\n"); + break; + + case EBUSY: + fprintf(stderr, "Open partition would move or shrink\n"); + break; + + case EXDEV: + fprintf(stderr, + "Labeled partition or 'a' partition must start at beginning of disk\n"); + break; + + default: + errno = saverrno; + perror((char *)NULL); + break; + } +} + +/* + * Fetch disklabel for disk. + * Use ioctl to get label unless -r flag is given. + */ +struct disklabel * +readlabel(f) + int f; +{ + register struct disklabel *lp; + + if (rflag) { + if (read(f, bootarea, BBSIZE) < BBSIZE) + Perror(specname); + for (lp = (struct disklabel *)bootarea; + lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp)); + lp = (struct disklabel *)((char *)lp + 16)) + if (lp->d_magic == DISKMAGIC && + lp->d_magic2 == DISKMAGIC) + break; + if (lp > (struct disklabel *)(bootarea+BBSIZE-sizeof(*lp)) || + lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC || + dkcksum(lp) != 0) { + fprintf(stderr, + "Bad pack magic number (label is damaged, or pack is unlabeled)\n"); + /* lp = (struct disklabel *)(bootarea + LABELOFFSET); */ + exit (1); + } + } else { + lp = &lab; + if (ioctl(f, DIOCGDINFO, lp) < 0) + Perror("ioctl DIOCGDINFO"); + } + return (lp); +} + +/* + * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot'' + * Returns a pointer to the disklabel portion of the bootarea. + */ +struct disklabel * +makebootarea(boot, dp, f) + char *boot; + register struct disklabel *dp; + int f; +{ + struct disklabel *lp; + register char *p; + int b; +#if NUMBOOT > 0 + char *dkbasename; + struct stat sb; +#endif + + /* XXX */ + if (dp->d_secsize == 0) { + dp->d_secsize = DEV_BSIZE; + dp->d_bbsize = BBSIZE; + } + lp = (struct disklabel *) + (boot + (LABELSECTOR * dp->d_secsize) + LABELOFFSET); + bzero((char *)lp, sizeof *lp); +#if NUMBOOT > 0 + /* + * If we are not installing a boot program but we are installing a + * label on disk then we must read the current bootarea so we don't + * clobber the existing boot. + */ + if (!installboot) { + if (rflag) { + if (read(f, boot, BBSIZE) < BBSIZE) + Perror(specname); + bzero((char *)lp, sizeof *lp); + } + return (lp); + } + /* + * We are installing a boot program. Determine the name(s) and + * read them into the appropriate places in the boot area. + */ + if (!xxboot || !bootxx) { + dkbasename = np; + if ((p = rindex(dkname, '/')) == NULL) + p = dkname; + else + p++; + while (*p && !isdigit(*p)) + *np++ = *p++; + *np++ = '\0'; + + if (!xxboot) { + (void)sprintf(np, "%s/%sboot", + _PATH_BOOTDIR, dkbasename); + if (access(np, F_OK) < 0 && dkbasename[0] == 'r') + dkbasename++; + xxboot = np; + (void)sprintf(xxboot, "%s/%sboot", + _PATH_BOOTDIR, dkbasename); + np += strlen(xxboot) + 1; + } +#if NUMBOOT > 1 + if (!bootxx) { + (void)sprintf(np, "%s/boot%s", + _PATH_BOOTDIR, dkbasename); + if (access(np, F_OK) < 0 && dkbasename[0] == 'r') + dkbasename++; + bootxx = np; + (void)sprintf(bootxx, "%s/boot%s", + _PATH_BOOTDIR, dkbasename); + np += strlen(bootxx) + 1; + } +#endif + } +#ifdef DEBUG + if (debug) + fprintf(stderr, "bootstraps: xxboot = %s, bootxx = %s\n", + xxboot, bootxx ? bootxx : "NONE"); +#endif + + /* + * Strange rules: + * 1. One-piece bootstrap (hp300/hp800) + * up to d_bbsize bytes of ``xxboot'' go in bootarea, the rest + * is remembered and written later following the bootarea. + * 2. Two-piece bootstraps (vax/i386?/mips?) + * up to d_secsize bytes of ``xxboot'' go in first d_secsize + * bytes of bootarea, remaining d_bbsize-d_secsize filled + * from ``bootxx''. + */ + b = open(xxboot, O_RDONLY); + if (b < 0) + Perror(xxboot); +#if NUMBOOT > 1 + if (read(b, boot, (int)dp->d_secsize) < 0) + Perror(xxboot); + (void)close(b); + b = open(bootxx, O_RDONLY); + if (b < 0) + Perror(bootxx); + if (read(b, &boot[dp->d_secsize], (int)(dp->d_bbsize-dp->d_secsize)) < 0) + Perror(bootxx); +#else + if (read(b, boot, (int)dp->d_bbsize) < 0) + Perror(xxboot); + (void)fstat(b, &sb); + bootsize = (int)sb.st_size - dp->d_bbsize; + if (bootsize > 0) { + /* XXX assume d_secsize is a power of two */ + bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1); + bootbuf = (char *)malloc((size_t)bootsize); + if (bootbuf == 0) + Perror(xxboot); + if (read(b, bootbuf, bootsize) < 0) { + free(bootbuf); + Perror(xxboot); + } + } +#endif + (void)close(b); +#endif + /* + * Make sure no part of the bootstrap is written in the area + * reserved for the label. + */ + for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++) + if (*p) { + fprintf(stderr, + "Bootstrap doesn't leave room for disk label\n"); + exit(2); + } + return (lp); +} + +display(f, lp) + FILE *f; + register struct disklabel *lp; +{ + register int i, j; + register struct partition *pp; + + fprintf(f, "# %s:\n", specname); + if ((unsigned) lp->d_type < DKMAXTYPES) + fprintf(f, "type: %s\n", dktypenames[lp->d_type]); + else + fprintf(f, "type: %d\n", lp->d_type); + fprintf(f, "disk: %.*s\n", sizeof(lp->d_typename), lp->d_typename); + fprintf(f, "label: %.*s\n", sizeof(lp->d_packname), lp->d_packname); + fprintf(f, "flags:"); + if (lp->d_flags & D_REMOVABLE) + fprintf(f, " removeable"); + if (lp->d_flags & D_ECC) + fprintf(f, " ecc"); + if (lp->d_flags & D_BADSECT) + fprintf(f, " badsect"); + fprintf(f, "\n"); + fprintf(f, "bytes/sector: %d\n", lp->d_secsize); + fprintf(f, "sectors/track: %d\n", lp->d_nsectors); + fprintf(f, "tracks/cylinder: %d\n", lp->d_ntracks); + fprintf(f, "sectors/cylinder: %d\n", lp->d_secpercyl); + fprintf(f, "cylinders: %d\n", lp->d_ncylinders); + fprintf(f, "rpm: %d\n", lp->d_rpm); + fprintf(f, "interleave: %d\n", lp->d_interleave); + fprintf(f, "trackskew: %d\n", lp->d_trackskew); + fprintf(f, "cylinderskew: %d\n", lp->d_cylskew); + fprintf(f, "headswitch: %d\t\t# milliseconds\n", lp->d_headswitch); + fprintf(f, "track-to-track seek: %d\t# milliseconds\n", lp->d_trkseek); + fprintf(f, "drivedata: "); + for (i = NDDATA - 1; i >= 0; i--) + if (lp->d_drivedata[i]) + break; + if (i < 0) + i = 0; + for (j = 0; j <= i; j++) + fprintf(f, "%d ", lp->d_drivedata[j]); + fprintf(f, "\n\n%d partitions:\n", lp->d_npartitions); + fprintf(f, + "# size offset fstype [fsize bsize cpg]\n"); + pp = lp->d_partitions; + for (i = 0; i < lp->d_npartitions; i++, pp++) { + if (pp->p_size) { + fprintf(f, " %c: %8d %8d ", 'a' + i, + pp->p_size, pp->p_offset); + if ((unsigned) pp->p_fstype < FSMAXTYPES) + fprintf(f, "%8.8s", fstypenames[pp->p_fstype]); + else + fprintf(f, "%8d", pp->p_fstype); + switch (pp->p_fstype) { + + case FS_UNUSED: /* XXX */ + fprintf(f, " %5d %5d %5.5s ", + pp->p_fsize, pp->p_fsize * pp->p_frag, ""); + break; + + case FS_BSDFFS: + fprintf(f, " %5d %5d %5d ", + pp->p_fsize, pp->p_fsize * pp->p_frag, + pp->p_cpg); + break; + + default: + fprintf(f, "%20.20s", ""); + break; + } + fprintf(f, "\t# (Cyl. %4d", + pp->p_offset / lp->d_secpercyl); + if (pp->p_offset % lp->d_secpercyl) + putc('*', f); + else + putc(' ', f); + fprintf(f, "- %d", + (pp->p_offset + + pp->p_size + lp->d_secpercyl - 1) / + lp->d_secpercyl - 1); + if (pp->p_size % lp->d_secpercyl) + putc('*', f); + fprintf(f, ")\n"); + } + } + fflush(f); +} + +edit(lp, f) + struct disklabel *lp; + int f; +{ + register int c; + struct disklabel label; + FILE *fd; + char *mktemp(); + + (void) mktemp(tmpfil); + fd = fopen(tmpfil, "w"); + if (fd == NULL) { + fprintf(stderr, "%s: Can't create\n", tmpfil); + return (1); + } + (void)fchmod(fileno(fd), 0600); + display(fd, lp); + fclose(fd); + for (;;) { + if (!editit()) + break; + fd = fopen(tmpfil, "r"); + if (fd == NULL) { + fprintf(stderr, "%s: Can't reopen for reading\n", + tmpfil); + break; + } + bzero((char *)&label, sizeof(label)); + if (getasciilabel(fd, &label)) { + *lp = label; + if (writelabel(f, bootarea, lp) == 0) { + (void) unlink(tmpfil); + return (0); + } + } + printf("re-edit the label? [y]: "); fflush(stdout); + c = getchar(); + if (c != EOF && c != (int)'\n') + while (getchar() != (int)'\n') + ; + if (c == (int)'n') + break; + } + (void) unlink(tmpfil); + return (1); +} + +editit() +{ + register int pid, xpid; + int stat, omask; + extern char *getenv(); + + omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); + while ((pid = fork()) < 0) { + extern int errno; + + if (errno == EPROCLIM) { + fprintf(stderr, "You have too many processes\n"); + return(0); + } + if (errno != EAGAIN) { + perror("fork"); + return(0); + } + sleep(1); + } + if (pid == 0) { + register char *ed; + + sigsetmask(omask); + setgid(getgid()); + setuid(getuid()); + if ((ed = getenv("EDITOR")) == (char *)0) + ed = DEFEDITOR; + execlp(ed, ed, tmpfil, 0); + perror(ed); + exit(1); + } + while ((xpid = wait(&stat)) >= 0) + if (xpid == pid) + break; + sigsetmask(omask); + return(!stat); +} + +char * +skip(cp) + register char *cp; +{ + + while (*cp != '\0' && isspace(*cp)) + cp++; + if (*cp == '\0' || *cp == '#') + return ((char *)NULL); + return (cp); +} + +char * +word(cp) + register char *cp; +{ + register char c; + + while (*cp != '\0' && !isspace(*cp) && *cp != '#') + cp++; + if ((c = *cp) != '\0') { + *cp++ = '\0'; + if (c != '#') + return (skip(cp)); + } + return ((char *)NULL); +} + +/* + * Read an ascii label in from fd f, + * in the same format as that put out by display(), + * and fill in lp. + */ +getasciilabel(f, lp) + FILE *f; + register struct disklabel *lp; +{ + register char **cpp, *cp; + register struct partition *pp; + char *tp, *s, line[BUFSIZ]; + int v, lineno = 0, errors = 0; + + lp->d_bbsize = BBSIZE; /* XXX */ + lp->d_sbsize = SBSIZE; /* XXX */ + while (fgets(line, sizeof(line) - 1, f)) { + lineno++; + if (cp = index(line,'\n')) + *cp = '\0'; + cp = skip(line); + if (cp == NULL) + continue; + tp = index(cp, ':'); + if (tp == NULL) { + fprintf(stderr, "line %d: syntax error\n", lineno); + errors++; + continue; + } + *tp++ = '\0', tp = skip(tp); + if (streq(cp, "type")) { + if (tp == NULL) + tp = "unknown"; + cpp = dktypenames; + for (; cpp < &dktypenames[DKMAXTYPES]; cpp++) + if ((s = *cpp) && streq(s, tp)) { + lp->d_type = cpp - dktypenames; + goto next; + } + v = atoi(tp); + if ((unsigned)v >= DKMAXTYPES) + fprintf(stderr, "line %d:%s %d\n", lineno, + "Warning, unknown disk type", v); + lp->d_type = v; + continue; + } + if (streq(cp, "flags")) { + for (v = 0; (cp = tp) && *cp != '\0';) { + tp = word(cp); + if (streq(cp, "removeable")) + v |= D_REMOVABLE; + else if (streq(cp, "ecc")) + v |= D_ECC; + else if (streq(cp, "badsect")) + v |= D_BADSECT; + else { + fprintf(stderr, + "line %d: %s: bad flag\n", + lineno, cp); + errors++; + } + } + lp->d_flags = v; + continue; + } + if (streq(cp, "drivedata")) { + register int i; + + for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) { + lp->d_drivedata[i++] = atoi(cp); + tp = word(cp); + } + continue; + } + if (sscanf(cp, "%d partitions", &v) == 1) { + if (v == 0 || (unsigned)v > MAXPARTITIONS) { + fprintf(stderr, + "line %d: bad # of partitions\n", lineno); + lp->d_npartitions = MAXPARTITIONS; + errors++; + } else + lp->d_npartitions = v; + continue; + } + if (tp == NULL) + tp = ""; + if (streq(cp, "disk")) { + strncpy(lp->d_typename, tp, sizeof (lp->d_typename)); + continue; + } + if (streq(cp, "label")) { + strncpy(lp->d_packname, tp, sizeof (lp->d_packname)); + continue; + } + if (streq(cp, "bytes/sector")) { + v = atoi(tp); + if (v <= 0 || (v % 512) != 0) { + fprintf(stderr, + "line %d: %s: bad sector size\n", + lineno, tp); + errors++; + } else + lp->d_secsize = v; + continue; + } + if (streq(cp, "sectors/track")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_nsectors = v; + continue; + } + if (streq(cp, "sectors/cylinder")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_secpercyl = v; + continue; + } + if (streq(cp, "tracks/cylinder")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_ntracks = v; + continue; + } + if (streq(cp, "cylinders")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_ncylinders = v; + continue; + } + if (streq(cp, "rpm")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_rpm = v; + continue; + } + if (streq(cp, "interleave")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_interleave = v; + continue; + } + if (streq(cp, "trackskew")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_trackskew = v; + continue; + } + if (streq(cp, "cylinderskew")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_cylskew = v; + continue; + } + if (streq(cp, "headswitch")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_headswitch = v; + continue; + } + if (streq(cp, "track-to-track seek")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_trkseek = v; + continue; + } + if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') { + unsigned part = *cp - 'a'; + + if (part > lp->d_npartitions) { + fprintf(stderr, + "line %d: bad partition name\n", lineno); + errors++; + continue; + } + pp = &lp->d_partitions[part]; +#define NXTNUM(n) { \ + cp = tp, tp = word(cp); \ + if (tp == NULL) \ + tp = cp; \ + (n) = atoi(cp); \ + } + + NXTNUM(v); + if (v < 0) { + fprintf(stderr, + "line %d: %s: bad partition size\n", + lineno, cp); + errors++; + } else + pp->p_size = v; + NXTNUM(v); + if (v < 0) { + fprintf(stderr, + "line %d: %s: bad partition offset\n", + lineno, cp); + errors++; + } else + pp->p_offset = v; + cp = tp, tp = word(cp); + cpp = fstypenames; + for (; cpp < &fstypenames[FSMAXTYPES]; cpp++) + if ((s = *cpp) && streq(s, cp)) { + pp->p_fstype = cpp - fstypenames; + goto gottype; + } + if (isdigit(*cp)) + v = atoi(cp); + else + v = FSMAXTYPES; + if ((unsigned)v >= FSMAXTYPES) { + fprintf(stderr, "line %d: %s %s\n", lineno, + "Warning, unknown filesystem type", cp); + v = FS_UNUSED; + } + pp->p_fstype = v; + gottype: + + switch (pp->p_fstype) { + + case FS_UNUSED: /* XXX */ + NXTNUM(pp->p_fsize); + if (pp->p_fsize == 0) + break; + NXTNUM(v); + pp->p_frag = v / pp->p_fsize; + break; + + case FS_BSDFFS: + NXTNUM(pp->p_fsize); + if (pp->p_fsize == 0) + break; + NXTNUM(v); + pp->p_frag = v / pp->p_fsize; + NXTNUM(pp->p_cpg); + break; + + default: + break; + } + continue; + } + fprintf(stderr, "line %d: %s: Unknown disklabel field\n", + lineno, cp); + errors++; + next: + ; + } + errors += checklabel(lp); + return (errors == 0); +} + +/* + * Check disklabel for errors and fill in + * derived fields according to supplied values. + */ +checklabel(lp) + register struct disklabel *lp; +{ + register struct partition *pp; + int i, errors = 0; + char part; + + if (lp->d_secsize == 0) { + fprintf(stderr, "sector size %d\n", lp->d_secsize); + return (1); + } + if (lp->d_nsectors == 0) { + fprintf(stderr, "sectors/track %d\n", lp->d_nsectors); + return (1); + } + if (lp->d_ntracks == 0) { + fprintf(stderr, "tracks/cylinder %d\n", lp->d_ntracks); + return (1); + } + if (lp->d_ncylinders == 0) { + fprintf(stderr, "cylinders/unit %d\n", lp->d_ncylinders); + errors++; + } + if (lp->d_rpm == 0) + Warning("revolutions/minute %d", lp->d_rpm); + if (lp->d_secpercyl == 0) + lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; + if (lp->d_secperunit == 0) + lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; + if (lp->d_bbsize == 0) { + fprintf(stderr, "boot block size %d\n", lp->d_bbsize); + errors++; + } else if (lp->d_bbsize % lp->d_secsize) + Warning("boot block size %% sector-size != 0"); + if (lp->d_sbsize == 0) { + fprintf(stderr, "super block size %d\n", lp->d_sbsize); + errors++; + } else if (lp->d_sbsize % lp->d_secsize) + Warning("super block size %% sector-size != 0"); + if (lp->d_npartitions > MAXPARTITIONS) + Warning("number of partitions (%d) > MAXPARTITIONS (%d)", + lp->d_npartitions, MAXPARTITIONS); + for (i = 0; i < lp->d_npartitions; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size == 0 && pp->p_offset != 0) + Warning("partition %c: size 0, but offset %d", + part, pp->p_offset); +#ifdef notdef + if (pp->p_size % lp->d_secpercyl) + Warning("partition %c: size %% cylinder-size != 0", + part); + if (pp->p_offset % lp->d_secpercyl) + Warning("partition %c: offset %% cylinder-size != 0", + part); +#endif + if (pp->p_offset > lp->d_secperunit) { + fprintf(stderr, + "partition %c: offset past end of unit\n", part); + errors++; + } + if (pp->p_offset + pp->p_size > lp->d_secperunit) { + fprintf(stderr, + "partition %c: partition extends past end of unit\n", + part); + errors++; + } + } + for (; i < MAXPARTITIONS; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size || pp->p_offset) + Warning("unused partition %c: size %d offset %d", + 'a' + i, pp->p_size, pp->p_offset); + } + return (errors); +} + +/* + * If we are installing a boot program that doesn't fit in d_bbsize + * we need to mark those partitions that the boot overflows into. + * This allows newfs to prevent creation of a filesystem where it might + * clobber bootstrap code. + */ +setbootflag(lp) + register struct disklabel *lp; +{ + register struct partition *pp; + int i, errors = 0; + char part; + u_long boffset; + + if (bootbuf == 0) + return; + boffset = bootsize / lp->d_secsize; + for (i = 0; i < lp->d_npartitions; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size == 0) + continue; + if (boffset <= pp->p_offset) { + if (pp->p_fstype == FS_BOOT) + pp->p_fstype = FS_UNUSED; + } else if (pp->p_fstype != FS_BOOT) { + if (pp->p_fstype != FS_UNUSED) { + fprintf(stderr, + "boot overlaps used partition %c\n", + part); + errors++; + } else { + pp->p_fstype = FS_BOOT; + Warning("boot overlaps partition %c, %s", + part, "marked as FS_BOOT"); + } + } + } + if (errors) { + fprintf(stderr, "Cannot install boot program\n"); + exit(4); + } +} + +/*VARARGS1*/ +Warning(fmt, a1, a2, a3, a4, a5) + char *fmt; +{ + + fprintf(stderr, "Warning, "); + fprintf(stderr, fmt, a1, a2, a3, a4, a5); + fprintf(stderr, "\n"); +} + +Perror(str) + char *str; +{ + fputs("disklabel: ", stderr); perror(str); + exit(4); +} + +usage() +{ +#if NUMBOOT > 0 + fprintf(stderr, +"%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n", +"usage: disklabel [-r] disk", + "(to read label)", +"or disklabel -w [-r] disk type [ packid ]", + "(to write label with existing boot program)", +"or disklabel -e [-r] disk", + "(to edit label)", +"or disklabel -R [-r] disk protofile", + "(to restore label with existing boot program)", +#if NUMBOOT > 1 +"or disklabel -B [ -b boot1 [ -s boot2 ] ] disk [ type ]", + "(to install boot program with existing label)", +"or disklabel -w -B [ -b boot1 [ -s boot2 ] ] disk type [ packid ]", + "(to write label and boot program)", +"or disklabel -R -B [ -b boot1 [ -s boot2 ] ] disk protofile [ type ]", + "(to restore label and boot program)", +#else +"or disklabel -B [ -b bootprog ] disk [ type ]", + "(to install boot program with existing on-disk label)", +"or disklabel -w -B [ -b bootprog ] disk type [ packid ]", + "(to write label and install boot program)", +"or disklabel -R -B [ -b bootprog ] disk protofile [ type ]", + "(to restore label and install boot program)", +#endif +"or disklabel [-NW] disk", + "(to write disable/enable label)"); +#else + fprintf(stderr, "%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n", +"usage: disklabel [-r] disk", "(to read label)", +"or disklabel -w [-r] disk type [ packid ]", "(to write label)", +"or disklabel -e [-r] disk", "(to edit label)", +"or disklabel -R [-r] disk protofile", "(to restore label)", +"or disklabel [-NW] disk", "(to write disable/enable label)"); +#endif + exit(1); +} diff --git a/sbin/disklabel/dkcksum.c b/sbin/disklabel/dkcksum.c new file mode 100644 index 0000000..f7a1e49 --- /dev/null +++ b/sbin/disklabel/dkcksum.c @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dkcksum.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/disklabel.h> + +u_short +dkcksum(lp) + register struct disklabel *lp; +{ + register u_short *start, *end; + register u_short sum = 0; + + start = (u_short *)lp; + end = (u_short *)&lp->d_partitions[lp->d_npartitions]; + while (start < end) + sum ^= *start++; + return (sum); +} diff --git a/sbin/disklabel/pathnames.h b/sbin/disklabel/pathnames.h new file mode 100644 index 0000000..def3297 --- /dev/null +++ b/sbin/disklabel/pathnames.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + */ + +#include <paths.h> + +#define _PATH_BOOTDIR "/usr/mdec" +#undef _PATH_TMP +#define _PATH_TMP "/tmp/EdDk.aXXXXXX" diff --git a/sbin/dmesg/Makefile b/sbin/dmesg/Makefile new file mode 100644 index 0000000..fc5edce --- /dev/null +++ b/sbin/dmesg/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= dmesg +MAN8= dmesg.0 +BINGRP= kmem +BINMODE=2555 +LDADD= -lkvm +DPADD= ${LIBKVM} + +.include <bsd.prog.mk> diff --git a/sbin/dmesg/dmesg.8 b/sbin/dmesg/dmesg.8 new file mode 100644 index 0000000..7dd0acf --- /dev/null +++ b/sbin/dmesg/dmesg.8 @@ -0,0 +1,63 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dmesg.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt DMESG 8 +.Os BSD 4 +.Sh NAME +.Nm dmesg +.Nd "display the system message buffer" +.Sh SYNOPSIS +.Nm dmesg +.Op Fl M Ar core +.Op Fl N Ar system +.Sh DESCRIPTION +.Nm Dmesg +displays the contents of the system message buffer. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl M +Extract values associated with the name list from the specified core +instead of the default ``/dev/kmem''. +.It Fl N +Extract the name list from the specified system instead of the default +``/vmunix''. +.El +.Sh SEE ALSO +.Xr syslogd 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.0 . diff --git a/sbin/dmesg/dmesg.c b/sbin/dmesg/dmesg.c new file mode 100644 index 0000000..d789f5d --- /dev/null +++ b/sbin/dmesg/dmesg.c @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)dmesg.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/cdefs.h> +#include <sys/msgbuf.h> + +#include <fcntl.h> +#include <kvm.h> +#include <limits.h> +#include <nlist.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> +#include <vis.h> + +struct nlist nl[] = { +#define X_MSGBUF 0 + { "_msgbufp" }, + { NULL }, +}; + +void usage __P((void)); + +#define KREAD(addr, var) \ + kvm_read(kd, addr, &var, sizeof(var)) != sizeof(var) + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register int ch, newl, skip; + register char *p, *ep; + struct msgbuf *bufp, cur; + char *memf, *nlistf; + kvm_t *kd; + char buf[5]; + + memf = nlistf = NULL; + while ((ch = getopt(argc, argv, "M:N:")) != EOF) + switch(ch) { + case 'M': + memf = optarg; + break; + case 'N': + nlistf = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * Discard setgid privileges if not the running kernel so that bad + * guys can't print interesting stuff from kernel memory. + */ + if (memf != NULL || nlistf != NULL) + setgid(getgid()); + + /* Read in kernel message buffer, do sanity checks. */ + if ((kd = kvm_open(nlistf, memf, NULL, O_RDONLY, "dmesg")) == NULL) + exit (1); + if (kvm_nlist(kd, nl) == -1) + errx(1, "kvm_nlist: %s", kvm_geterr(kd)); + if (nl[X_MSGBUF].n_type == 0) + errx(1, "%s: msgbufp not found", nlistf ? nlistf : "namelist"); + if (KREAD(nl[X_MSGBUF].n_value, bufp) || KREAD((long)bufp, cur)) + errx(1, "kvm_read: %s", kvm_geterr(kd)); + kvm_close(kd); + if (cur.msg_magic != MSG_MAGIC) + errx(1, "magic number incorrect"); + if (cur.msg_bufx >= MSG_BSIZE) + cur.msg_bufx = 0; + + /* + * The message buffer is circular; start at the read pointer, and + * go to the write pointer - 1. + */ + p = cur.msg_bufc + cur.msg_bufx; + ep = cur.msg_bufc + cur.msg_bufx - 1; + for (newl = skip = 0; p != ep; ++p) { + if (p == cur.msg_bufc + MSG_BSIZE) + p = cur.msg_bufc; + ch = *p; + /* Skip "\n<.*>" syslog sequences. */ + if (skip) { + if (ch == '>') + newl = skip = 0; + continue; + } + if (newl && ch == '<') { + skip = 1; + continue; + } + if (ch == '\0') + continue; + newl = ch == '\n'; + (void)vis(buf, ch, 0, 0); + if (buf[1] == 0) + (void)putchar(buf[0]); + else + (void)printf("%s", buf); + } + if (!newl) + (void)putchar('\n'); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: dmesg [-M core] [-N system]\n"); + exit(1); +} diff --git a/sbin/dump/Makefile b/sbin/dump/Makefile new file mode 100644 index 0000000..78ab5d8 --- /dev/null +++ b/sbin/dump/Makefile @@ -0,0 +1,25 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +# dump.h header file +# itime.c reads /etc/dumpdates +# main.c driver +# optr.c operator interface +# dumprmt.c handles remote tape via rmt(8) +# tape.c handles the mag tape and opening/closing +# traverse.c traverses the file system +# unctime.c undo ctime +# +# DEBUG use local directory to find ddate and dumpdates +# TDEBUG trace out the process forking + +PROG= dump +LINKS= ${BINDIR}/dump ${BINDIR}/rdump +CFLAGS+=-DRDUMP +SRCS= itime.c main.c optr.c dumprmt.c tape.c traverse.c unctime.c +BINOWN= root +BINGRP= tty +BINMODE=6555 +MAN8= dump.0 +MLINKS+=dump.8 rdump.8 + +.include <bsd.prog.mk> diff --git a/sbin/dump/dump.8 b/sbin/dump/dump.8 new file mode 100644 index 0000000..2cd9335 --- /dev/null +++ b/sbin/dump/dump.8 @@ -0,0 +1,335 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dump.8 8.1 (Berkeley) 6/16/93 +.\" +.Dd June 16, 1993 +.Dt DUMP 8 +.Os BSD 4 +.Sh NAME +.Nm dump +.Nd filesystem backup +.Sh SYNOPSIS +.Nm dump +.Op Cm 0123456789BbhfusTdWn Op Ar argument ... +.Op Ar filesystem +.Sh DESCRIPTION +.Nm Dump +examines files +on a filesystem +and determines which files +need to be backed up. These files +are copied to the given disk, tape or other +storage medium for safe keeping (see the +.Cm f +option below for doing remote backups). +A dump that is larger than the output medium is broken into +multiple volumes. +On most media the size is determined by writing until an +end-of-media indication is returned. +On media that cannot reliably return an end-of-media indication +(such as some cartridge tape drives) +each volume is of a fixed size; +the actual size is determined by the tape size and density and/or +block count options below. +By default, the same output file name is used for each volume +after prompting the operator to change media. +.Pp +The following options are supported by +.Nm dump: +.Bl -tag -width 4n +.It Cm 0\-9 +Dump levels. +A level 0, full backup, +guarantees the entire file system is copied +(but see also the +.Cm h +option below). +A level number above 0, +incremental backup, +tells dump to +copy all files new or modified since the +last dump of the same or lower level. The default +level is 9. +.It Cm B Ar records +The number of dump records per volume. +This option overrides the calculation of tape size +based on length and density. +.It Cm b Ar blocksize +The number of kilobytes per dump record. +.It Cm h Ar level +Honor the user +.Dq nodump +flag +.Dp Dv UF_NODUMP +only for dumps at or above the given +.Ar level . +The default honor level is 1, +so that incremental backups omit such files +but full backups retain them. +.It Cm f Ar file +Write the backup to +.Ar file ; +.Ar file +may be a special device file +like +.Pa /dev/rmt12 +(a tape drive), +.Pa /dev/rsd1c +(a disk drive), +an ordinary file, +or +.Ql Fl +(the standard output). +Multiple file names may be given as a single argument separated by commas. +Each file will be used for one dump volume in the order listed; +if the dump requires more volumes than the number of names given, +the last file name will used for all remaining volumes after prompting +for media changes. +If the name of the file is of the form +.Dq host:file , +or +.Dq user@host:file , +.Nm dump +writes to the named file on the remote host using +.Xr rmt 8 . +.It Cm d Ar density +Set tape density to +.Ar density . +The default is 1600BPI. +.It Cm n +Whenever +.Nm dump +requires operator attention, +notify all operators in the group +.Dq operator +by means similar to a +.Xr wall 1 . +.It Cm s Ar feet +Attempt to calculate the amount of tape needed +at a particular density. +If this amount is exceeded, +.Nm dump +prompts for a new tape. +It is recommended to be a bit conservative on this option. +The default tape length is 2300 feet. +.It Cm u +Update the file +.Pa /etc/dumpdates +after a successful dump. +The format of +.Pa /etc/dumpdates +is readable by people, consisting of one +free format record per line: +filesystem name, +increment level +and +.Xr ctime 3 +format dump date. +There may be only one entry per filesystem at each level. +The file +.Pa /etc/dumpdates +may be edited to change any of the fields, +if necessary. +.It Cm T Ar date +Use the specified date as the starting time for the dump +instead of the time determined from looking in +.Pa /etc/dumpdates . +The format of date is the same as that of +.Xr ctime 3 . +This option is useful for automated dump scripts that wish to +dump over a specific period of time. +The +.Cm T +option is mutually exclusive from the +.Cm u +option. +.It Cm W +.Nm Dump +tells the operator what file systems need to be dumped. +This information is gleaned from the files +.Pa /etc/dumpdates +and +.Pa /etc/fstab . +The +.Cm W +option causes +.Nm dump +to print out, for each file system in +.Pa /etc/dumpdates +the most recent dump date and level, +and highlights those file systems that should be dumped. +If the +.Cm W +option is set, all other options are ignored, and +.Nm dump +exits immediately. +.It Cm w +Is like W, but prints only those filesystems which need to be dumped. +.El +.Pp +.Nm Dump +requires operator intervention on these conditions: +end of tape, +end of dump, +tape write error, +tape open error or +disk read error (if there are more than a threshold of 32). +In addition to alerting all operators implied by the +.Cm n +key, +.Nm dump +interacts with the operator on +.Em dump's +control terminal at times when +.Nm dump +can no longer proceed, +or if something is grossly wrong. +All questions +.Nm dump +poses +.Em must +be answered by typing +.Dq yes +or +.Dq no , +appropriately. +.Pp +Since making a dump involves a lot of time and effort for full dumps, +.Nm dump +checkpoints itself at the start of each tape volume. +If writing that volume fails for some reason, +.Nm dump +will, +with operator permission, +restart itself from the checkpoint +after the old tape has been rewound and removed, +and a new tape has been mounted. +.Pp +.Nm Dump +tells the operator what is going on at periodic intervals, +including usually low estimates of the number of blocks to write, +the number of tapes it will take, the time to completion, and +the time to the tape change. +The output is verbose, +so that others know that the terminal +controlling +.Nm dump +is busy, +and will be for some time. +.Pp +In the event of a catastrophic disk event, the time required +to restore all the necessary backup tapes or files to disk +can be kept to a minimum by staggering the incremental dumps. +An efficient method of staggering incremental dumps +to minimize the number of tapes follows: +.Bl -bullet -offset indent +.It +Always start with a level 0 backup, for example: +.Bd -literal -offset indent +/etc/dump 0uf /dev/nrst1 /usr/src +.Ed +.Pp +This should be done at set intervals, say once a month or once every two months, +and on a set of fresh tapes that is saved forever. +.It +After a level 0, dumps of active file +systems are taken on a daily basis, +using a modified Tower of Hanoi algorithm, +with this sequence of dump levels: +.Bd -literal -offset indent +3 2 5 4 7 6 9 8 9 9 ... +.Ed +.Pp +For the daily dumps, it should be possible to use a fixed number of tapes +for each day, used on a weekly basis. +Each week, a level 1 dump is taken, and +the daily Hanoi sequence repeats beginning with 3. +For weekly dumps, another fixed set of tapes per dumped file system is +used, also on a cyclical basis. +.El +.Pp +After several months or so, the daily and weekly tapes should get +rotated out of the dump cycle and fresh tapes brought in. +.Sh FILES +.Bl -tag -width /etc/dumpdates -compact +.It Pa /dev/rmt8 +default tape unit to dump to +.It Pa /etc/dumpdates +dump date records +.It Pa /etc/fstab +dump table: file systems and frequency +.It Pa /etc/group +to find group +.Em operator +.El +.Sh SEE ALSO +.Xr restore 8 , +.Xr rmt 8 , +.Xr dump 5 , +.Xr fstab 5 +.Sh DIAGNOSTICS +Many, and verbose. +.Pp +Dump exits with zero status on success. +Startup errors are indicated with an exit code of 1; +abnormal termination is indicated with an exit code of 3. +.Sh BUGS +.Pp +Fewer than 32 read errors on the filesystem are ignored. +Each reel requires a new process, so parent processes for +reels already written just hang around until the entire tape +is written. +.Pp +.Nm Dump +with the +.Cm W +or +.Cm w +options does not report filesystems that have never been recorded +in +.Pa /etc/dumpdates , +even if listed in +.Pa /etc/fstab . +.Pp +It would be nice if +.Nm dump +knew about the dump sequence, +kept track of the tapes scribbled on, +told the operator which tape to mount when, +and provided more assistance +for the operator running +.Xr restore . +.Sh HISTORY +A +.Nm dump +command appeared in Version 6 AT&T UNIX. diff --git a/sbin/dump/dump.h b/sbin/dump/dump.h new file mode 100644 index 0000000..9060b00 --- /dev/null +++ b/sbin/dump/dump.h @@ -0,0 +1,212 @@ +/*- + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)dump.h 8.1 (Berkeley) 6/5/93 + */ + +#define MAXINOPB (MAXBSIZE / sizeof(struct dinode)) +#define MAXNINDIR (MAXBSIZE / sizeof(daddr_t)) + +/* + * Dump maps used to describe what is to be dumped. + */ +int mapsize; /* size of the state maps */ +char *usedinomap; /* map of allocated inodes */ +char *dumpdirmap; /* map of directories to be dumped */ +char *dumpinomap; /* map of files to be dumped */ +/* + * Map manipulation macros. + */ +#define SETINO(ino, map) \ + map[(u_int)((ino) - 1) / NBBY] |= 1 << ((u_int)((ino) - 1) % NBBY) +#define CLRINO(ino, map) \ + map[(u_int)((ino) - 1) / NBBY] &= ~(1 << ((u_int)((ino) - 1) % NBBY)) +#define TSTINO(ino, map) \ + (map[(u_int)((ino) - 1) / NBBY] & (1 << ((u_int)((ino) - 1) % NBBY))) + +/* + * All calculations done in 0.1" units! + */ +char *disk; /* name of the disk file */ +char *tape; /* name of the tape file */ +char *dumpdates; /* name of the file containing dump date information*/ +char *temp; /* name of the file for doing rewrite of dumpdates */ +char lastlevel; /* dump level of previous dump */ +char level; /* dump level of this dump */ +int uflag; /* update flag */ +int diskfd; /* disk file descriptor */ +int tapefd; /* tape file descriptor */ +int pipeout; /* true => output to standard output */ +ino_t curino; /* current inumber; used globally */ +int newtape; /* new tape flag */ +int density; /* density in 0.1" units */ +long tapesize; /* estimated tape size, blocks */ +long tsize; /* tape size in 0.1" units */ +long asize; /* number of 0.1" units written on current tape */ +int etapes; /* estimated number of tapes */ +int nonodump; /* if set, do not honor UF_NODUMP user flags */ + +int notify; /* notify operator flag */ +int blockswritten; /* number of blocks written on current tape */ +int tapeno; /* current tape number */ +time_t tstart_writing; /* when started writing the first tape block */ +struct fs *sblock; /* the file system super block */ +char sblock_buf[MAXBSIZE]; +long dev_bsize; /* block size of underlying disk device */ +int dev_bshift; /* log2(dev_bsize) */ +int tp_bshift; /* log2(TP_BSIZE) */ + +#ifndef __P +#include <sys/cdefs.h> +#endif + +/* operator interface functions */ +void broadcast __P((char *message)); +void lastdump __P((int arg)); /* int should be char */ +void msg __P((const char *fmt, ...)); +void msgtail __P((const char *fmt, ...)); +int query __P((char *question)); +void quit __P((const char *fmt, ...)); +void set_operators __P((void)); +void timeest __P((void)); +time_t unctime __P((char *str)); + +/* mapping rouintes */ +struct dinode; +long blockest __P((struct dinode *dp)); +int mapfiles __P((ino_t maxino, long *tapesize)); +int mapdirs __P((ino_t maxino, long *tapesize)); + +/* file dumping routines */ +void blksout __P((daddr_t *blkp, int frags, ino_t ino)); +void bread __P((daddr_t blkno, char *buf, int size)); +void dumpino __P((struct dinode *dp, ino_t ino)); +void dumpmap __P((char *map, int type, ino_t ino)); +void writeheader __P((ino_t ino)); + +/* tape writing routines */ +int alloctape __P((void)); +void close_rewind __P((void)); +void dumpblock __P((daddr_t blkno, int size)); +void startnewtape __P((int top)); +void trewind __P((void)); +void writerec __P((char *dp, int isspcl)); + +__dead void Exit __P((int status)); +void dumpabort __P((int signo)); +void getfstab __P((void)); + +char *rawname __P((char *cp)); +struct dinode *getino __P((ino_t inum)); + +/* rdump routines */ +#ifdef RDUMP +void rmtclose __P((void)); +int rmthost __P((char *host)); +int rmtopen __P((char *tape, int mode)); +int rmtwrite __P((char *buf, int count)); +#endif /* RDUMP */ + +void interrupt __P((int signo)); /* in case operator bangs on console */ + +/* + * Exit status codes + */ +#define X_FINOK 0 /* normal exit */ +#define X_REWRITE 2 /* restart writing from the check point */ +#define X_ABORT 3 /* abort dump; don't attempt checkpointing */ + +#define OPGRENT "operator" /* group entry to notify */ +#define DIALUP "ttyd" /* prefix for dialups */ + +struct fstab *fstabsearch __P((char *key)); /* search fs_file and fs_spec */ + +#ifndef NAME_MAX +#define NAME_MAX 255 +#endif + +/* + * The contents of the file _PATH_DUMPDATES is maintained both on + * a linked list, and then (eventually) arrayified. + */ +struct dumpdates { + char dd_name[NAME_MAX+3]; + char dd_level; + time_t dd_ddate; +}; +struct dumptime { + struct dumpdates dt_value; + struct dumptime *dt_next; +}; +struct dumptime *dthead; /* head of the list version */ +int nddates; /* number of records (might be zero) */ +int ddates_in; /* we have read the increment file */ +struct dumpdates **ddatev; /* the arrayfied version */ +void initdumptimes __P((void)); +void getdumptime __P((void)); +void putdumptime __P((void)); +#define ITITERATE(i, ddp) \ + for (ddp = ddatev[i = 0]; i < nddates; ddp = ddatev[++i]) + +void sig __P((int signo)); + +/* + * Compatibility with old systems. + */ +#ifdef COMPAT +#include <sys/file.h> +extern char *index(), *rindex(), *strdup(); +extern char *ctime(); +extern int read(), write(); +extern int errno; +#endif + +#ifndef _PATH_UTMP +#define _PATH_UTMP "/etc/utmp" +#endif +#ifndef _PATH_FSTAB +#define _PATH_FSTAB "/etc/fstab" +#endif + +#ifdef sunos +extern char *calloc(); +extern char *malloc(); +extern long atol(); +extern char *strcpy(); +extern char *strncpy(); +extern char *strcat(); +extern time_t time(); +extern void endgrent(); +extern __dead void exit(); +extern off_t lseek(); +extern const char *strerror(); +#endif diff --git a/sbin/dump/dumprmt.c b/sbin/dump/dumprmt.c new file mode 100644 index 0000000..22acbfd --- /dev/null +++ b/sbin/dump/dumprmt.c @@ -0,0 +1,380 @@ +/*- + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dumprmt.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mtio.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/time.h> +#ifdef sunos +#include <sys/vnode.h> + +#include <ufs/inode.h> +#else +#include <ufs/ufs/dinode.h> +#endif + +#include <netinet/in.h> +#include <netinet/tcp.h> + +#include <protocols/dumprestore.h> + +#include <ctype.h> +#include <netdb.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#ifdef __STDC__ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include "pathnames.h" +#include "dump.h" + +#define TS_CLOSED 0 +#define TS_OPEN 1 + +static int rmtstate = TS_CLOSED; +static int rmtape; +static char *rmtpeer; + +static int okname __P((char *)); +static int rmtcall __P((char *, char *)); +static void rmtconnaborted __P((/* int, int */)); +static int rmtgetb __P((void)); +static void rmtgetconn __P((void)); +static void rmtgets __P((char *, int)); +static int rmtreply __P((char *)); + +extern int ntrec; /* blocking factor on tape */ + +int +rmthost(host) + char *host; +{ + + rmtpeer = malloc(strlen(host) + 1); + if (rmtpeer) + strcpy(rmtpeer, host); + else + rmtpeer = host; + signal(SIGPIPE, rmtconnaborted); + rmtgetconn(); + if (rmtape < 0) + return (0); + return (1); +} + +static void +rmtconnaborted() +{ + + (void) fprintf(stderr, "rdump: Lost connection to remote host.\n"); + exit(1); +} + +void +rmtgetconn() +{ + register char *cp; + static struct servent *sp = NULL; + static struct passwd *pwd = NULL; +#ifdef notdef + static int on = 1; +#endif + char *tuser; + int size; + int maxseg; + + if (sp == NULL) { + sp = getservbyname("shell", "tcp"); + if (sp == NULL) { + (void) fprintf(stderr, + "rdump: shell/tcp: unknown service\n"); + exit(1); + } + pwd = getpwuid(getuid()); + if (pwd == NULL) { + (void) fprintf(stderr, "rdump: who are you?\n"); + exit(1); + } + } + if ((cp = index(rmtpeer, '@')) != NULL) { + tuser = rmtpeer; + *cp = '\0'; + if (!okname(tuser)) + exit(1); + rmtpeer = ++cp; + } else + tuser = pwd->pw_name; + rmtape = rcmd(&rmtpeer, (u_short)sp->s_port, pwd->pw_name, tuser, + _PATH_RMT, (int *)0); + size = ntrec * TP_BSIZE; + if (size > 60 * 1024) /* XXX */ + size = 60 * 1024; + /* Leave some space for rmt request/response protocol */ + size += 2 * 1024; + while (size > TP_BSIZE && + setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0) + size -= TP_BSIZE; + (void)setsockopt(rmtape, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)); + maxseg = 1024; + if (setsockopt(rmtape, IPPROTO_TCP, TCP_MAXSEG, + &maxseg, sizeof (maxseg)) < 0) + perror("TCP_MAXSEG setsockopt"); + +#ifdef notdef + if (setsockopt(rmtape, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0) + perror("TCP_NODELAY setsockopt"); +#endif +} + +static int +okname(cp0) + char *cp0; +{ + register char *cp; + register int c; + + for (cp = cp0; *cp; cp++) { + c = *cp; + if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) { + (void) fprintf(stderr, "rdump: invalid user name %s\n", + cp0); + return (0); + } + } + return (1); +} + +int +rmtopen(tape, mode) + char *tape; + int mode; +{ + char buf[256]; + + (void)sprintf(buf, "O%s\n%d\n", tape, mode); + rmtstate = TS_OPEN; + return (rmtcall(tape, buf)); +} + +void +rmtclose() +{ + + if (rmtstate != TS_OPEN) + return; + rmtcall("close", "C\n"); + rmtstate = TS_CLOSED; +} + +int +rmtread(buf, count) + char *buf; + int count; +{ + char line[30]; + int n, i, cc; + extern errno; + + (void)sprintf(line, "R%d\n", count); + n = rmtcall("read", line); + if (n < 0) { + errno = n; + return (-1); + } + for (i = 0; i < n; i += cc) { + cc = read(rmtape, buf+i, n - i); + if (cc <= 0) { + rmtconnaborted(); + } + } + return (n); +} + +int +rmtwrite(buf, count) + char *buf; + int count; +{ + char line[30]; + + (void)sprintf(line, "W%d\n", count); + write(rmtape, line, strlen(line)); + write(rmtape, buf, count); + return (rmtreply("write")); +} + +void +rmtwrite0(count) + int count; +{ + char line[30]; + + (void)sprintf(line, "W%d\n", count); + write(rmtape, line, strlen(line)); +} + +void +rmtwrite1(buf, count) + char *buf; + int count; +{ + + write(rmtape, buf, count); +} + +int +rmtwrite2() +{ + + return (rmtreply("write")); +} + +int +rmtseek(offset, pos) + int offset, pos; +{ + char line[80]; + + (void)sprintf(line, "L%d\n%d\n", offset, pos); + return (rmtcall("seek", line)); +} + +struct mtget mts; + +struct mtget * +rmtstatus() +{ + register int i; + register char *cp; + + if (rmtstate != TS_OPEN) + return (NULL); + rmtcall("status", "S\n"); + for (i = 0, cp = (char *)&mts; i < sizeof(mts); i++) + *cp++ = rmtgetb(); + return (&mts); +} + +int +rmtioctl(cmd, count) + int cmd, count; +{ + char buf[256]; + + if (count < 0) + return (-1); + (void)sprintf(buf, "I%d\n%d\n", cmd, count); + return (rmtcall("ioctl", buf)); +} + +static int +rmtcall(cmd, buf) + char *cmd, *buf; +{ + + if (write(rmtape, buf, strlen(buf)) != strlen(buf)) + rmtconnaborted(); + return (rmtreply(cmd)); +} + +static int +rmtreply(cmd) + char *cmd; +{ + register char *cp; + char code[30], emsg[BUFSIZ]; + + rmtgets(code, sizeof (code)); + if (*code == 'E' || *code == 'F') { + rmtgets(emsg, sizeof (emsg)); + msg("%s: %s", cmd, emsg); + if (*code == 'F') { + rmtstate = TS_CLOSED; + return (-1); + } + return (-1); + } + if (*code != 'A') { + /* Kill trailing newline */ + cp = code + strlen(code); + if (cp > code && *--cp == '\n') + *cp = '\0'; + + msg("Protocol to remote tape server botched (code \"%s\").\n", + code); + rmtconnaborted(); + } + return (atoi(code + 1)); +} + +int +rmtgetb() +{ + char c; + + if (read(rmtape, &c, 1) != 1) + rmtconnaborted(); + return (c); +} + +/* Get a line (guaranteed to have a trailing newline). */ +void +rmtgets(line, len) + char *line; + int len; +{ + register char *cp = line; + + while (len > 1) { + *cp = rmtgetb(); + if (*cp == '\n') { + cp[1] = '\0'; + return; + } + cp++; + len--; + } + *cp = '\0'; + msg("Protocol to remote tape server botched.\n"); + msg("(rmtgets got \"%s\").\n", line); + rmtconnaborted(); +} diff --git a/sbin/dump/itime.c b/sbin/dump/itime.c new file mode 100644 index 0000000..94656ac --- /dev/null +++ b/sbin/dump/itime.c @@ -0,0 +1,271 @@ +/*- + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)itime.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#ifdef sunos +#include <sys/vnode.h> + +#include <ufs/fsdir.h> +#include <ufs/inode.h> +#include <ufs/fs.h> +#else +#include <ufs/ufs/dinode.h> +#endif + +#include <protocols/dumprestore.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#ifdef __STDC__ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include "dump.h" + +struct dumpdates **ddatev = 0; +int nddates = 0; +int ddates_in = 0; +struct dumptime *dthead = 0; + +static void dumprecout __P((FILE *, struct dumpdates *)); +static int getrecord __P((FILE *, struct dumpdates *)); +static int makedumpdate __P((struct dumpdates *, char *)); +static void readdumptimes __P((FILE *)); + +void +initdumptimes() +{ + FILE *df; + + if ((df = fopen(dumpdates, "r")) == NULL) { + if (errno != ENOENT) { + quit("cannot read %s: %s\n", dumpdates, + strerror(errno)); + /* NOTREACHED */ + } + /* + * Dumpdates does not exist, make an empty one. + */ + msg("WARNING: no file `%s', making an empty one\n", dumpdates); + if ((df = fopen(dumpdates, "w")) == NULL) { + quit("cannot create %s: %s\n", dumpdates, + strerror(errno)); + /* NOTREACHED */ + } + (void) fclose(df); + if ((df = fopen(dumpdates, "r")) == NULL) { + quit("cannot read %s even after creating it: %s\n", + dumpdates, strerror(errno)); + /* NOTREACHED */ + } + } + (void) flock(fileno(df), LOCK_SH); + readdumptimes(df); + (void) fclose(df); +} + +static void +readdumptimes(df) + FILE *df; +{ + register int i; + register struct dumptime *dtwalk; + + for (;;) { + dtwalk = (struct dumptime *)calloc(1, sizeof (struct dumptime)); + if (getrecord(df, &(dtwalk->dt_value)) < 0) + break; + nddates++; + dtwalk->dt_next = dthead; + dthead = dtwalk; + } + + ddates_in = 1; + /* + * arrayify the list, leaving enough room for the additional + * record that we may have to add to the ddate structure + */ + ddatev = (struct dumpdates **) + calloc((unsigned) (nddates + 1), sizeof (struct dumpdates *)); + dtwalk = dthead; + for (i = nddates - 1; i >= 0; i--, dtwalk = dtwalk->dt_next) + ddatev[i] = &dtwalk->dt_value; +} + +void +getdumptime() +{ + register struct dumpdates *ddp; + register int i; + char *fname; + + fname = disk; +#ifdef FDEBUG + msg("Looking for name %s in dumpdates = %s for level = %c\n", + fname, dumpdates, level); +#endif + spcl.c_ddate = 0; + lastlevel = '0'; + + initdumptimes(); + /* + * Go find the entry with the same name for a lower increment + * and older date + */ + ITITERATE(i, ddp) { + if (strncmp(fname, ddp->dd_name, sizeof (ddp->dd_name)) != 0) + continue; + if (ddp->dd_level >= level) + continue; + if (ddp->dd_ddate <= spcl.c_ddate) + continue; + spcl.c_ddate = ddp->dd_ddate; + lastlevel = ddp->dd_level; + } +} + +void +putdumptime() +{ + FILE *df; + register struct dumpdates *dtwalk; + register int i; + int fd; + char *fname; + + if(uflag == 0) + return; + if ((df = fopen(dumpdates, "r+")) == NULL) + quit("cannot rewrite %s: %s\n", dumpdates, strerror(errno)); + fd = fileno(df); + (void) flock(fd, LOCK_EX); + fname = disk; + free((char *)ddatev); + ddatev = 0; + nddates = 0; + dthead = 0; + ddates_in = 0; + readdumptimes(df); + if (fseek(df, 0L, 0) < 0) + quit("fseek: %s\n", strerror(errno)); + spcl.c_ddate = 0; + ITITERATE(i, dtwalk) { + if (strncmp(fname, dtwalk->dd_name, + sizeof (dtwalk->dd_name)) != 0) + continue; + if (dtwalk->dd_level != level) + continue; + goto found; + } + /* + * construct the new upper bound; + * Enough room has been allocated. + */ + dtwalk = ddatev[nddates] = + (struct dumpdates *)calloc(1, sizeof (struct dumpdates)); + nddates += 1; + found: + (void) strncpy(dtwalk->dd_name, fname, sizeof (dtwalk->dd_name)); + dtwalk->dd_level = level; + dtwalk->dd_ddate = spcl.c_date; + + ITITERATE(i, dtwalk) { + dumprecout(df, dtwalk); + } + if (fflush(df)) + quit("%s: %s\n", dumpdates, strerror(errno)); + if (ftruncate(fd, ftell(df))) + quit("ftruncate (%s): %s\n", dumpdates, strerror(errno)); + (void) fclose(df); + msg("level %c dump on %s", level, + spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date)); +} + +static void +dumprecout(file, what) + FILE *file; + struct dumpdates *what; +{ + + if (fprintf(file, DUMPOUTFMT, + what->dd_name, + what->dd_level, + ctime(&what->dd_ddate)) < 0) + quit("%s: %s\n", dumpdates, strerror(errno)); +} + +int recno; + +static int +getrecord(df, ddatep) + FILE *df; + struct dumpdates *ddatep; +{ + char tbuf[BUFSIZ]; + + recno = 0; + if ( (fgets(tbuf, sizeof (tbuf), df)) != tbuf) + return(-1); + recno++; + if (makedumpdate(ddatep, tbuf) < 0) + msg("Unknown intermediate format in %s, line %d\n", + dumpdates, recno); + +#ifdef FDEBUG + msg("getrecord: %s %c %s", ddatep->dd_name, ddatep->dd_level, + ddatep->dd_ddate == 0 ? "the epoch\n" : ctime(&ddatep->dd_ddate)); +#endif + return(0); +} + +static int +makedumpdate(ddp, tbuf) + struct dumpdates *ddp; + char *tbuf; +{ + char un_buf[128]; + + (void) sscanf(tbuf, DUMPINFMT, ddp->dd_name, &ddp->dd_level, un_buf); + ddp->dd_ddate = unctime(un_buf); + if (ddp->dd_ddate < 0) + return(-1); + return(0); +} diff --git a/sbin/dump/main.c b/sbin/dump/main.c new file mode 100644 index 0000000..40d668e --- /dev/null +++ b/sbin/dump/main.c @@ -0,0 +1,560 @@ +/*- + * Copyright (c) 1980, 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 4/15/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#ifdef sunos +#include <sys/vnode.h> + +#include <ufs/inode.h> +#include <ufs/fs.h> +#else +#include <ufs/ffs/fs.h> +#include <ufs/ufs/dinode.h> +#endif + +#include <protocols/dumprestore.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <fstab.h> +#include <signal.h> +#include <stdio.h> +#ifdef __STDC__ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include "dump.h" +#include "pathnames.h" + +#ifndef SBOFF +#define SBOFF (SBLOCK * DEV_BSIZE) +#endif + +int notify = 0; /* notify operator flag */ +int blockswritten = 0; /* number of blocks written on current tape */ +int tapeno = 0; /* current tape number */ +int density = 0; /* density in bytes/0.1" */ +int ntrec = NTREC; /* # tape blocks in each tape record */ +int cartridge = 0; /* Assume non-cartridge tape */ +long dev_bsize = 1; /* recalculated below */ +long blocksperfile; /* output blocks per file */ +char *host = NULL; /* remote host (if any) */ + +static long numarg __P((int, char *, long, long, int *, char ***)); +static __dead void missingarg __P((int, char *)); + +int +main(argc, argv) + int argc; + char **argv; +{ + register ino_t ino; + register int dirty; + register struct dinode *dp; + register struct fstab *dt; + register char *map; + register char *cp; + int i, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1; + ino_t maxino; + + spcl.c_date = 0; + (void)time((time_t *)&spcl.c_date); + + tsize = 0; /* Default later, based on 'c' option for cart tapes */ + tape = _PATH_DEFTAPE; + dumpdates = _PATH_DUMPDATES; + temp = _PATH_DTMP; + if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0) + quit("TP_BSIZE must be a multiple of DEV_BSIZE\n"); + level = '0'; + if (argc == 1) { + (void) fprintf(stderr, "Must specify a key.\n"); + Exit(X_ABORT); + } + argv++; + argc -= 2; + for (cp = *argv++; cp != NULL && *cp != '\0'; cp++) { + switch (*cp) { + case '-': + break; + + case 'w': + lastdump('w'); /* tell us only what has to be done */ + exit(0); + + case 'W': /* what to do */ + lastdump('W'); /* tell us state of what is done */ + exit(0); /* do nothing else */ + + case 'f': /* output file */ + if (argc < 1) + missingarg('f', "output file"); + tape = *argv++; + argc--; + break; + + case 'd': /* density, in bits per inch */ + density = numarg('d', "density", + 10L, 327670L, &argc, &argv) / 10; + if (density >= 625 && !bflag) + ntrec = HIGHDENSITYTREC; + break; + + case 's': /* tape size, feet */ + tsize = numarg('s', "size", + 1L, 0L, &argc, &argv) * 12 * 10; + break; + + case 'T': /* time of last dump */ + if (argc < 1) + missingarg('T', "time of last dump"); + spcl.c_ddate = unctime(*argv); + if (spcl.c_ddate < 0) { + (void)fprintf(stderr, "bad time \"%s\"\n", + *argv); + exit(X_ABORT); + } + Tflag = 1; + lastlevel = '?'; + argc--; + argv++; + break; + + case 'b': /* blocks per tape write */ + ntrec = numarg('b', "number of blocks per write", + 1L, 1000L, &argc, &argv); + break; + + case 'B': /* blocks per output file */ + blocksperfile = numarg('B', "number of blocks per file", + 1L, 0L, &argc, &argv); + break; + + case 'c': /* Tape is cart. not 9-track */ + cartridge = 1; + break; + + /* dump level */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + level = *cp; + break; + + case 'u': /* update /etc/dumpdates */ + uflag = 1; + break; + + case 'n': /* notify operators */ + notify = 1; + break; + + case 'h': + honorlevel = numarg('h', "honor level", + 0L, 10L, &argc, &argv); + break; + + default: + (void)fprintf(stderr, "bad key '%c'\n", *cp); + exit(X_ABORT); + } + } + if (argc < 1) { + (void)fprintf(stderr, "Must specify disk or filesystem\n"); + exit(X_ABORT); + } + disk = *argv++; + argc--; + if (argc >= 1) { + (void)fprintf(stderr, "Unknown arguments to dump:"); + while (argc--) + (void)fprintf(stderr, " %s", *argv++); + (void)fprintf(stderr, "\n"); + exit(X_ABORT); + } + if (Tflag && uflag) { + (void)fprintf(stderr, + "You cannot use the T and u flags together.\n"); + exit(X_ABORT); + } + if (strcmp(tape, "-") == 0) { + pipeout++; + tape = "standard output"; + } + + if (blocksperfile) + blocksperfile = blocksperfile / ntrec * ntrec; /* round down */ + else { + /* + * Determine how to default tape size and density + * + * density tape size + * 9-track 1600 bpi (160 bytes/.1") 2300 ft. + * 9-track 6250 bpi (625 bytes/.1") 2300 ft. + * cartridge 8000 bpi (100 bytes/.1") 1700 ft. + * (450*4 - slop) + */ + if (density == 0) + density = cartridge ? 100 : 160; + if (tsize == 0) + tsize = cartridge ? 1700L*120L : 2300L*120L; + } + + if (index(tape, ':')) { + host = tape; + tape = index(host, ':'); + *tape++ = '\0'; +#ifdef RDUMP + if (rmthost(host) == 0) + exit(X_ABORT); +#else + (void)fprintf(stderr, "remote dump not enabled\n"); + exit(X_ABORT); +#endif + } + (void)setuid(getuid()); /* rmthost() is the only reason to be setuid */ + + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + signal(SIGHUP, sig); + if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) + signal(SIGTRAP, sig); + if (signal(SIGFPE, SIG_IGN) != SIG_IGN) + signal(SIGFPE, sig); + if (signal(SIGBUS, SIG_IGN) != SIG_IGN) + signal(SIGBUS, sig); + if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) + signal(SIGSEGV, sig); + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, sig); + if (signal(SIGINT, interrupt) == SIG_IGN) + signal(SIGINT, SIG_IGN); + + set_operators(); /* /etc/group snarfed */ + getfstab(); /* /etc/fstab snarfed */ + /* + * disk can be either the full special file name, + * the suffix of the special file name, + * the special name missing the leading '/', + * the file system name with or without the leading '/'. + */ + dt = fstabsearch(disk); + if (dt != NULL) { + disk = rawname(dt->fs_spec); + (void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN); + (void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN); + } else { + (void)strncpy(spcl.c_dev, disk, NAMELEN); + (void)strncpy(spcl.c_filesys, "an unlisted file system", + NAMELEN); + } + (void)strcpy(spcl.c_label, "none"); + (void)gethostname(spcl.c_host, NAMELEN); + spcl.c_level = level - '0'; + spcl.c_type = TS_TAPE; + if (!Tflag) + getdumptime(); /* /etc/dumpdates snarfed */ + + msg("Date of this level %c dump: %s", level, + spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date)); + msg("Date of last level %c dump: %s", lastlevel, + spcl.c_ddate == 0 ? "the epoch\n" : ctime(&spcl.c_ddate)); + msg("Dumping %s ", disk); + if (dt != NULL) + msgtail("(%s) ", dt->fs_file); + if (host) + msgtail("to %s on host %s\n", tape, host); + else + msgtail("to %s\n", tape); + + if ((diskfd = open(disk, O_RDONLY)) < 0) { + msg("Cannot open %s\n", disk); + exit(X_ABORT); + } + sync(); + sblock = (struct fs *)sblock_buf; + bread(SBOFF, (char *) sblock, SBSIZE); + if (sblock->fs_magic != FS_MAGIC) + quit("bad sblock magic number\n"); + dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1); + dev_bshift = ffs(dev_bsize) - 1; + if (dev_bsize != (1 << dev_bshift)) + quit("dev_bsize (%d) is not a power of 2", dev_bsize); + tp_bshift = ffs(TP_BSIZE) - 1; + if (TP_BSIZE != (1 << tp_bshift)) + quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE); +#ifdef FS_44INODEFMT + if (sblock->fs_inodefmt >= FS_44INODEFMT) + spcl.c_flags |= DR_NEWINODEFMT; +#endif + maxino = sblock->fs_ipg * sblock->fs_ncg; + mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE); + usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); + dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char)); + dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); + tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); + + nonodump = spcl.c_level < honorlevel; + + msg("mapping (Pass I) [regular files]\n"); + anydirskipped = mapfiles(maxino, &tapesize); + + msg("mapping (Pass II) [directories]\n"); + while (anydirskipped) { + anydirskipped = mapdirs(maxino, &tapesize); + } + + if (pipeout) { + tapesize += 10; /* 10 trailer blocks */ + msg("estimated %ld tape blocks.\n", tapesize); + } else { + double fetapes; + + if (blocksperfile) + fetapes = (double) tapesize / blocksperfile; + else if (cartridge) { + /* Estimate number of tapes, assuming streaming stops at + the end of each block written, and not in mid-block. + Assume no erroneous blocks; this can be compensated + for with an artificially low tape size. */ + fetapes = + ( tapesize /* blocks */ + * TP_BSIZE /* bytes/block */ + * (1.0/density) /* 0.1" / byte */ + + + tapesize /* blocks */ + * (1.0/ntrec) /* streaming-stops per block */ + * 15.48 /* 0.1" / streaming-stop */ + ) * (1.0 / tsize ); /* tape / 0.1" */ + } else { + /* Estimate number of tapes, for old fashioned 9-track + tape */ + int tenthsperirg = (density == 625) ? 3 : 7; + fetapes = + ( tapesize /* blocks */ + * TP_BSIZE /* bytes / block */ + * (1.0/density) /* 0.1" / byte */ + + + tapesize /* blocks */ + * (1.0/ntrec) /* IRG's / block */ + * tenthsperirg /* 0.1" / IRG */ + ) * (1.0 / tsize ); /* tape / 0.1" */ + } + etapes = fetapes; /* truncating assignment */ + etapes++; + /* count the dumped inodes map on each additional tape */ + tapesize += (etapes - 1) * + (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); + tapesize += etapes + 10; /* headers + 10 trailer blks */ + msg("estimated %ld tape blocks on %3.2f tape(s).\n", + tapesize, fetapes); + } + + /* + * Allocate tape buffer. + */ + if (!alloctape()) + quit("can't allocate tape buffers - try a smaller blocking factor.\n"); + + startnewtape(1); + (void)time((time_t *)&(tstart_writing)); + dumpmap(usedinomap, TS_CLRI, maxino - 1); + + msg("dumping (Pass III) [directories]\n"); + dirty = 0; /* XXX just to get gcc to shut up */ + for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { + if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ + dirty = *map++; + else + dirty >>= 1; + if ((dirty & 1) == 0) + continue; + /* + * Skip directory inodes deleted and maybe reallocated + */ + dp = getino(ino); + if ((dp->di_mode & IFMT) != IFDIR) + continue; + (void)dumpino(dp, ino); + } + + msg("dumping (Pass IV) [regular files]\n"); + for (map = dumpinomap, ino = 1; ino < maxino; ino++) { + int mode; + + if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ + dirty = *map++; + else + dirty >>= 1; + if ((dirty & 1) == 0) + continue; + /* + * Skip inodes deleted and reallocated as directories. + */ + dp = getino(ino); + mode = dp->di_mode & IFMT; + if (mode == IFDIR) + continue; + (void)dumpino(dp, ino); + } + + spcl.c_type = TS_END; + for (i = 0; i < ntrec; i++) + writeheader(maxino - 1); + if (pipeout) + msg("DUMP: %ld tape blocks\n",spcl.c_tapea); + else + msg("DUMP: %ld tape blocks on %d volumes(s)\n", + spcl.c_tapea, spcl.c_volume); + putdumptime(); + trewind(); + broadcast("DUMP IS DONE!\7\7\n"); + msg("DUMP IS DONE\n"); + Exit(X_FINOK); + /* NOTREACHED */ +} + +/* + * Pick up a numeric argument. It must be nonnegative and in the given + * range (except that a vmax of 0 means unlimited). + */ +static long +numarg(letter, meaning, vmin, vmax, pargc, pargv) + int letter; + char *meaning; + long vmin, vmax; + int *pargc; + char ***pargv; +{ + register char *p; + long val; + char *str; + + if (--*pargc < 0) + missingarg(letter, meaning); + str = *(*pargv)++; + for (p = str; *p; p++) + if (!isdigit(*p)) + goto bad; + val = atol(str); + if (val < vmin || (vmax && val > vmax)) + goto bad; + return (val); + +bad: + (void)fprintf(stderr, "bad '%c' (%s) value \"%s\"\n", + letter, meaning, str); + exit(X_ABORT); +} + +static __dead void +missingarg(letter, meaning) + int letter; + char *meaning; +{ + + (void)fprintf(stderr, "The '%c' flag (%s) requires an argument\n", + letter, meaning); + exit(X_ABORT); +} + +void +sig(signo) + int signo; +{ + switch(signo) { + case SIGALRM: + case SIGBUS: + case SIGFPE: + case SIGHUP: + case SIGTERM: + case SIGTRAP: + if (pipeout) + quit("Signal on pipe: cannot recover\n"); + msg("Rewriting attempted as response to unknown signal.\n"); + (void)fflush(stderr); + (void)fflush(stdout); + close_rewind(); + exit(X_REWRITE); + /* NOTREACHED */ + case SIGSEGV: + msg("SIGSEGV: ABORTING!\n"); + (void)signal(SIGSEGV, SIG_DFL); + (void)kill(0, SIGSEGV); + /* NOTREACHED */ + } +} + +char * +rawname(cp) + char *cp; +{ + static char rawbuf[MAXPATHLEN]; + char *dp = rindex(cp, '/'); + + if (dp == NULL) + return (NULL); + *dp = '\0'; + (void)strcpy(rawbuf, cp); + *dp = '/'; + (void)strcat(rawbuf, "/r"); + (void)strcat(rawbuf, dp + 1); + return (rawbuf); +} + +#ifdef sunos +const char * +strerror(errnum) + int errnum; +{ + extern int sys_nerr; + extern const char *const sys_errlist[]; + + if (errnum < sys_nerr) + return (sys_errlist[errnum]); + else + return ("bogus errno in strerror"); +} +#endif diff --git a/sbin/dump/optr.c b/sbin/dump/optr.c new file mode 100644 index 0000000..6db0908 --- /dev/null +++ b/sbin/dump/optr.c @@ -0,0 +1,538 @@ +/*- + * Copyright (c) 1980, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)optr.c 8.2 (Berkeley) 1/6/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/wait.h> +#include <sys/time.h> + +#include <errno.h> +#include <fstab.h> +#include <grp.h> +#include <signal.h> +#include <stdio.h> +#ifdef __STDC__ +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#endif +#include <tzfile.h> +#ifdef __STDC__ +#include <unistd.h> +#endif +#include <utmp.h> +#ifndef __STDC__ +#include <varargs.h> +#endif + +#include "dump.h" +#include "pathnames.h" + +void alarmcatch __P((/* int, int */)); +int datesort __P((const void *, const void *)); +static void sendmes __P((char *, char *)); + +/* + * Query the operator; This previously-fascist piece of code + * no longer requires an exact response. + * It is intended to protect dump aborting by inquisitive + * people banging on the console terminal to see what is + * happening which might cause dump to croak, destroying + * a large number of hours of work. + * + * Every 2 minutes we reprint the message, alerting others + * that dump needs attention. + */ +static int timeout; +static char *attnmessage; /* attention message */ + +int +query(question) + char *question; +{ + char replybuffer[64]; + int back, errcount; + FILE *mytty; + + if ((mytty = fopen(_PATH_TTY, "r")) == NULL) + quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); + attnmessage = question; + timeout = 0; + alarmcatch(); + back = -1; + errcount = 0; + do { + if (fgets(replybuffer, 63, mytty) == NULL) { + clearerr(mytty); + if (++errcount > 30) /* XXX ugly */ + quit("excessive operator query failures\n"); + } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { + back = 1; + } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { + back = 0; + } else { + (void) fprintf(stderr, + " DUMP: \"Yes\" or \"No\"?\n"); + (void) fprintf(stderr, + " DUMP: %s: (\"yes\" or \"no\") ", question); + } + } while (back < 0); + + /* + * Turn off the alarm, and reset the signal to trap out.. + */ + (void) alarm(0); + if (signal(SIGALRM, sig) == SIG_IGN) + signal(SIGALRM, SIG_IGN); + (void) fclose(mytty); + return(back); +} + +char lastmsg[100]; + +/* + * Alert the console operator, and enable the alarm clock to + * sleep for 2 minutes in case nobody comes to satisfy dump + */ +void +alarmcatch() +{ + if (notify == 0) { + if (timeout == 0) + (void) fprintf(stderr, + " DUMP: %s: (\"yes\" or \"no\") ", + attnmessage); + else + msgtail("\7\7"); + } else { + if (timeout) { + msgtail("\n"); + broadcast(""); /* just print last msg */ + } + (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", + attnmessage); + } + signal(SIGALRM, alarmcatch); + (void) alarm(120); + timeout = 1; +} + +/* + * Here if an inquisitive operator interrupts the dump program + */ +void +interrupt(signo) + int signo; +{ + msg("Interrupt received.\n"); + if (query("Do you want to abort dump?")) + dumpabort(0); +} + +/* + * The following variables and routines manage alerting + * operators to the status of dump. + * This works much like wall(1) does. + */ +struct group *gp; + +/* + * Get the names from the group entry "operator" to notify. + */ +void +set_operators() +{ + if (!notify) /*not going to notify*/ + return; + gp = getgrnam(OPGRENT); + (void) endgrent(); + if (gp == NULL) { + msg("No group entry for %s.\n", OPGRENT); + notify = 0; + return; + } +} + +struct tm *localclock; + +/* + * We fork a child to do the actual broadcasting, so + * that the process control groups are not messed up + */ +void +broadcast(message) + char *message; +{ + time_t clock; + FILE *f_utmp; + struct utmp utmp; + char **np; + int pid, s; + + if (!notify || gp == NULL) + return; + + switch (pid = fork()) { + case -1: + return; + case 0: + break; + default: + while (wait(&s) != pid) + continue; + return; + } + + clock = time((time_t *)0); + localclock = localtime(&clock); + + if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) { + msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno)); + return; + } + + while (!feof(f_utmp)) { + if (fread((char *) &utmp, sizeof (struct utmp), 1, f_utmp) != 1) + break; + if (utmp.ut_name[0] == 0) + continue; + for (np = gp->gr_mem; *np; np++) { + if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0) + continue; + /* + * Do not send messages to operators on dialups + */ + if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0) + continue; +#ifdef DEBUG + msg("Message to %s at %s\n", *np, utmp.ut_line); +#endif + sendmes(utmp.ut_line, message); + } + } + (void) fclose(f_utmp); + Exit(0); /* the wait in this same routine will catch this */ + /* NOTREACHED */ +} + +static void +sendmes(tty, message) + char *tty, *message; +{ + char t[50], buf[BUFSIZ]; + register char *cp; + int lmsg = 1; + FILE *f_tty; + + (void) strcpy(t, _PATH_DEV); + (void) strcat(t, tty); + + if ((f_tty = fopen(t, "w")) != NULL) { + setbuf(f_tty, buf); + (void) fprintf(f_tty, + "\n\ +\7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\ +DUMP: NEEDS ATTENTION: ", + localclock->tm_hour, localclock->tm_min); + for (cp = lastmsg; ; cp++) { + if (*cp == '\0') { + if (lmsg) { + cp = message; + if (*cp == '\0') + break; + lmsg = 0; + } else + break; + } + if (*cp == '\n') + (void) putc('\r', f_tty); + (void) putc(*cp, f_tty); + } + (void) fclose(f_tty); + } +} + +/* + * print out an estimate of the amount of time left to do the dump + */ + +time_t tschedule = 0; + +void +timeest() +{ + time_t tnow, deltat; + + (void) time((time_t *) &tnow); + if (tnow >= tschedule) { + tschedule = tnow + 300; + if (blockswritten < 500) + return; + deltat = tstart_writing - tnow + + (1.0 * (tnow - tstart_writing)) + / blockswritten * tapesize; + msg("%3.2f%% done, finished in %d:%02d\n", + (blockswritten * 100.0) / tapesize, + deltat / 3600, (deltat % 3600) / 60); + } +} + +void +#if __STDC__ +msg(const char *fmt, ...) +#else +msg(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; + + (void) fprintf(stderr," DUMP: "); +#ifdef TDEBUG + (void) fprintf(stderr, "pid=%d ", getpid()); +#endif +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void) vfprintf(stderr, fmt, ap); + (void) fflush(stdout); + (void) fflush(stderr); + (void) vsprintf(lastmsg, fmt, ap); + va_end(ap); +} + +void +#if __STDC__ +msgtail(const char *fmt, ...) +#else +msgtail(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void) vfprintf(stderr, fmt, ap); + va_end(ap); +} + +void +#if __STDC__ +quit(const char *fmt, ...) +#else +quit(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; + + (void) fprintf(stderr," DUMP: "); +#ifdef TDEBUG + (void) fprintf(stderr, "pid=%d ", getpid()); +#endif +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + (void) fflush(stdout); + (void) fflush(stderr); + dumpabort(0); +} + +/* + * Tell the operator what has to be done; + * we don't actually do it + */ + +struct fstab * +allocfsent(fs) + register struct fstab *fs; +{ + register struct fstab *new; + + new = (struct fstab *)malloc(sizeof (*fs)); + if (new == NULL || + (new->fs_file = strdup(fs->fs_file)) == NULL || + (new->fs_type = strdup(fs->fs_type)) == NULL || + (new->fs_spec = strdup(fs->fs_spec)) == NULL) + quit("%s\n", strerror(errno)); + new->fs_passno = fs->fs_passno; + new->fs_freq = fs->fs_freq; + return (new); +} + +struct pfstab { + struct pfstab *pf_next; + struct fstab *pf_fstab; +}; + +static struct pfstab *table; + +void +getfstab() +{ + register struct fstab *fs; + register struct pfstab *pf; + + if (setfsent() == 0) { + msg("Can't open %s for dump table information: %s\n", + _PATH_FSTAB, strerror(errno)); + return; + } + while ((fs = getfsent()) != NULL) { + if (strcmp(fs->fs_type, FSTAB_RW) && + strcmp(fs->fs_type, FSTAB_RO) && + strcmp(fs->fs_type, FSTAB_RQ)) + continue; + fs = allocfsent(fs); + if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) + quit("%s\n", strerror(errno)); + pf->pf_fstab = fs; + pf->pf_next = table; + table = pf; + } + (void) endfsent(); +} + +/* + * Search in the fstab for a file name. + * This file name can be either the special or the path file name. + * + * The entries in the fstab are the BLOCK special names, not the + * character special names. + * The caller of fstabsearch assures that the character device + * is dumped (that is much faster) + * + * The file name can omit the leading '/'. + */ +struct fstab * +fstabsearch(key) + char *key; +{ + register struct pfstab *pf; + register struct fstab *fs; + char *rn; + + for (pf = table; pf != NULL; pf = pf->pf_next) { + fs = pf->pf_fstab; + if (strcmp(fs->fs_file, key) == 0 || + strcmp(fs->fs_spec, key) == 0) + return (fs); + rn = rawname(fs->fs_spec); + if (rn != NULL && strcmp(rn, key) == 0) + return (fs); + if (key[0] != '/') { + if (*fs->fs_spec == '/' && + strcmp(fs->fs_spec + 1, key) == 0) + return (fs); + if (*fs->fs_file == '/' && + strcmp(fs->fs_file + 1, key) == 0) + return (fs); + } + } + return (NULL); +} + +/* + * Tell the operator what to do + */ +void +lastdump(arg) + char arg; /* w ==> just what to do; W ==> most recent dumps */ +{ + register int i; + register struct fstab *dt; + register struct dumpdates *dtwalk; + char *lastname, *date; + int dumpme; + time_t tnow; + + (void) time(&tnow); + getfstab(); /* /etc/fstab input */ + initdumptimes(); /* /etc/dumpdates input */ + qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort); + + if (arg == 'w') + (void) printf("Dump these file systems:\n"); + else + (void) printf("Last dump(s) done (Dump '>' file systems):\n"); + lastname = "??"; + ITITERATE(i, dtwalk) { + if (strncmp(lastname, dtwalk->dd_name, + sizeof(dtwalk->dd_name)) == 0) + continue; + date = (char *)ctime(&dtwalk->dd_ddate); + date[16] = '\0'; /* blast away seconds and year */ + lastname = dtwalk->dd_name; + dt = fstabsearch(dtwalk->dd_name); + dumpme = (dt != NULL && + dt->fs_freq != 0 && + dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY)); + if (arg != 'w' || dumpme) + (void) printf( + "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", + dumpme && (arg != 'w') ? '>' : ' ', + dtwalk->dd_name, + dt ? dt->fs_file : "", + dtwalk->dd_level, + date); + } +} + +int +datesort(a1, a2) + const void *a1, *a2; +{ + struct dumpdates *d1 = *(struct dumpdates **)a1; + struct dumpdates *d2 = *(struct dumpdates **)a2; + int diff; + + diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); + if (diff == 0) + return (d2->dd_ddate - d1->dd_ddate); + return (diff); +} diff --git a/sbin/dump/pathnames.h b/sbin/dump/pathnames.h new file mode 100644 index 0000000..e4dff53 --- /dev/null +++ b/sbin/dump/pathnames.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + */ + +#include <paths.h> + +#define _PATH_DEFTAPE "/dev/rmt8" +#define _PATH_DTMP "/etc/dtmp" +#define _PATH_DUMPDATES "/etc/dumpdates" +#define _PATH_LOCK "/tmp/dumplockXXXXXX" +#define _PATH_RMT "rmt" diff --git a/sbin/dump/tape.c b/sbin/dump/tape.c new file mode 100644 index 0000000..f6a4e5b --- /dev/null +++ b/sbin/dump/tape.c @@ -0,0 +1,859 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)tape.c 8.2 (Berkeley) 3/17/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/wait.h> +#ifdef sunos +#include <sys/vnode.h> + +#include <ufs/fs.h> +#include <ufs/inode.h> +#else +#include <ufs/ffs/fs.h> +#include <ufs/ufs/dinode.h> +#endif + +#include <protocols/dumprestore.h> + +#include <errno.h> +#include <fcntl.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#ifdef __STDC__ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#else +int write(), read(); +#endif + +#include "dump.h" +#include "pathnames.h" + +int writesize; /* size of malloc()ed buffer for tape */ +long lastspclrec = -1; /* tape block number of last written header */ +int trecno = 0; /* next record to write in current block */ +extern long blocksperfile; /* number of blocks per output file */ +long blocksthisvol; /* number of blocks on current output file */ +extern int ntrec; /* blocking factor on tape */ +extern int cartridge; +extern char *host; +char *nexttape; + +static int atomic __P((int (*)(), int, char *, int)); +static void doslave __P((int, int)); +static void enslave __P((void)); +static void flushtape __P((void)); +static void killall __P((void)); +static void rollforward __P((void)); + +/* + * Concurrent dump mods (Caltech) - disk block reading and tape writing + * are exported to several slave processes. While one slave writes the + * tape, the others read disk blocks; they pass control of the tape in + * a ring via signals. The parent process traverses the filesystem and + * sends writeheader()'s and lists of daddr's to the slaves via pipes. + * The following structure defines the instruction packets sent to slaves. + */ +struct req { + daddr_t dblk; + int count; +}; +int reqsiz; + +#define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ +struct slave { + int tapea; /* header number at start of this chunk */ + int count; /* count to next header (used for TS_TAPE */ + /* after EOT) */ + int inode; /* inode that we are currently dealing with */ + int fd; /* FD for this slave */ + int pid; /* PID for this slave */ + int sent; /* 1 == we've sent this slave requests */ + int firstrec; /* record number of this block */ + char (*tblock)[TP_BSIZE]; /* buffer for data blocks */ + struct req *req; /* buffer for requests */ +} slaves[SLAVES+1]; +struct slave *slp; + +char (*nextblock)[TP_BSIZE]; + +int master; /* pid of master, for sending error signals */ +int tenths; /* length of tape used per block written */ +static int caught; /* have we caught the signal to proceed? */ +static int ready; /* have we reached the lock point without having */ + /* received the SIGUSR2 signal from the prev slave? */ +static jmp_buf jmpbuf; /* where to jump to if we are ready when the */ + /* SIGUSR2 arrives from the previous slave */ + +int +alloctape() +{ + int pgoff = getpagesize() - 1; + char *buf; + int i; + + writesize = ntrec * TP_BSIZE; + reqsiz = (ntrec + 1) * sizeof(struct req); + /* + * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode + * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require + * repositioning after stopping, i.e, streaming mode, where the gap is + * variable, 0.30" to 0.45". The gap is maximal when the tape stops. + */ + if (blocksperfile == 0) + tenths = writesize / density + + (cartridge ? 16 : density == 625 ? 5 : 8); + /* + * Allocate tape buffer contiguous with the array of instruction + * packets, so flushtape() can write them together with one write(). + * Align tape buffer on page boundary to speed up tape write(). + */ + for (i = 0; i <= SLAVES; i++) { + buf = (char *) + malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE)); + if (buf == NULL) + return(0); + slaves[i].tblock = (char (*)[TP_BSIZE]) + (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); + slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1; + } + slp = &slaves[0]; + slp->count = 1; + slp->tapea = 0; + slp->firstrec = 0; + nextblock = slp->tblock; + return(1); +} + +void +writerec(dp, isspcl) + char *dp; + int isspcl; +{ + + slp->req[trecno].dblk = (daddr_t)0; + slp->req[trecno].count = 1; + *(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp; + if (isspcl) + lastspclrec = spcl.c_tapea; + trecno++; + spcl.c_tapea++; + if (trecno >= ntrec) + flushtape(); +} + +void +dumpblock(blkno, size) + daddr_t blkno; + int size; +{ + int avail, tpblks, dblkno; + + dblkno = fsbtodb(sblock, blkno); + tpblks = size >> tp_bshift; + while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { + slp->req[trecno].dblk = dblkno; + slp->req[trecno].count = avail; + trecno += avail; + spcl.c_tapea += avail; + if (trecno >= ntrec) + flushtape(); + dblkno += avail << (tp_bshift - dev_bshift); + tpblks -= avail; + } +} + +int nogripe = 0; + +void +tperror(signo) + int signo; +{ + + if (pipeout) { + msg("write error on %s\n", tape); + quit("Cannot recover\n"); + /* NOTREACHED */ + } + msg("write error %d blocks into volume %d\n", blocksthisvol, tapeno); + broadcast("DUMP WRITE ERROR!\n"); + if (!query("Do you want to restart?")) + dumpabort(0); + msg("Closing this volume. Prepare to restart with new media;\n"); + msg("this dump volume will be rewritten.\n"); + killall(); + nogripe = 1; + close_rewind(); + Exit(X_REWRITE); +} + +void +sigpipe(signo) + int signo; +{ + + quit("Broken pipe\n"); +} + +static void +flushtape() +{ + int i, blks, got; + long lastfirstrec; + + int siz = (char *)nextblock - (char *)slp->req; + + slp->req[trecno].count = 0; /* Sentinel */ + + if (atomic(write, slp->fd, (char *)slp->req, siz) != siz) + quit("error writing command pipe: %s\n", strerror(errno)); + slp->sent = 1; /* we sent a request, read the response later */ + + lastfirstrec = slp->firstrec; + + if (++slp >= &slaves[SLAVES]) + slp = &slaves[0]; + + /* Read results back from next slave */ + if (slp->sent) { + if (atomic(read, slp->fd, (char *)&got, sizeof got) + != sizeof got) { + perror(" DUMP: error reading command pipe in master"); + dumpabort(0); + } + slp->sent = 0; + + /* Check for end of tape */ + if (got < writesize) { + msg("End of tape detected\n"); + + /* + * Drain the results, don't care what the values were. + * If we read them here then trewind won't... + */ + for (i = 0; i < SLAVES; i++) { + if (slaves[i].sent) { + if (atomic(read, slaves[i].fd, + (char *)&got, sizeof got) + != sizeof got) { + perror(" DUMP: error reading command pipe in master"); + dumpabort(0); + } + slaves[i].sent = 0; + } + } + + close_rewind(); + rollforward(); + return; + } + } + + blks = 0; + if (spcl.c_type != TS_END) { + for (i = 0; i < spcl.c_count; i++) + if (spcl.c_addr[i] != 0) + blks++; + } + slp->count = lastspclrec + blks + 1 - spcl.c_tapea; + slp->tapea = spcl.c_tapea; + slp->firstrec = lastfirstrec + ntrec; + slp->inode = curino; + nextblock = slp->tblock; + trecno = 0; + asize += tenths; + blockswritten += ntrec; + blocksthisvol += ntrec; + if (!pipeout && (blocksperfile ? + (blocksthisvol >= blocksperfile) : (asize > tsize))) { + close_rewind(); + startnewtape(0); + } + timeest(); +} + +void +trewind() +{ + int f; + int got; + + for (f = 0; f < SLAVES; f++) { + /* + * Drain the results, but unlike EOT we DO (or should) care + * what the return values were, since if we detect EOT after + * we think we've written the last blocks to the tape anyway, + * we have to replay those blocks with rollforward. + * + * fixme: punt for now. + */ + if (slaves[f].sent) { + if (atomic(read, slaves[f].fd, (char *)&got, sizeof got) + != sizeof got) { + perror(" DUMP: error reading command pipe in master"); + dumpabort(0); + } + slaves[f].sent = 0; + if (got != writesize) { + msg("EOT detected in last 2 tape records!\n"); + msg("Use a longer tape, decrease the size estimate\n"); + quit("or use no size estimate at all.\n"); + } + } + (void) close(slaves[f].fd); + } + while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */ + /* void */; + + if (pipeout) + return; + + msg("Closing %s\n", tape); + +#ifdef RDUMP + if (host) { + rmtclose(); + while (rmtopen(tape, 0) < 0) + sleep(10); + rmtclose(); + return; + } +#endif + (void) close(tapefd); + while ((f = open(tape, 0)) < 0) + sleep (10); + (void) close(f); +} + +void +close_rewind() +{ + trewind(); + if (nexttape) + return; + if (!nogripe) { + msg("Change Volumes: Mount volume #%d\n", tapeno+1); + broadcast("CHANGE DUMP VOLUMES!\7\7\n"); + } + while (!query("Is the new volume mounted and ready to go?")) + if (query("Do you want to abort?")) { + dumpabort(0); + /*NOTREACHED*/ + } +} + +void +rollforward() +{ + register struct req *p, *q, *prev; + register struct slave *tslp; + int i, size, savedtapea, got; + union u_spcl *ntb, *otb; + tslp = &slaves[SLAVES]; + ntb = (union u_spcl *)tslp->tblock[1]; + + /* + * Each of the N slaves should have requests that need to + * be replayed on the next tape. Use the extra slave buffers + * (slaves[SLAVES]) to construct request lists to be sent to + * each slave in turn. + */ + for (i = 0; i < SLAVES; i++) { + q = &tslp->req[1]; + otb = (union u_spcl *)slp->tblock; + + /* + * For each request in the current slave, copy it to tslp. + */ + + prev = NULL; + for (p = slp->req; p->count > 0; p += p->count) { + *q = *p; + if (p->dblk == 0) + *ntb++ = *otb++; /* copy the datablock also */ + prev = q; + q += q->count; + } + if (prev == NULL) + quit("rollforward: protocol botch"); + if (prev->dblk != 0) + prev->count -= 1; + else + ntb--; + q -= 1; + q->count = 0; + q = &tslp->req[0]; + if (i == 0) { + q->dblk = 0; + q->count = 1; + trecno = 0; + nextblock = tslp->tblock; + savedtapea = spcl.c_tapea; + spcl.c_tapea = slp->tapea; + startnewtape(0); + spcl.c_tapea = savedtapea; + lastspclrec = savedtapea - 1; + } + size = (char *)ntb - (char *)q; + if (atomic(write, slp->fd, (char *)q, size) != size) { + perror(" DUMP: error writing command pipe"); + dumpabort(0); + } + slp->sent = 1; + if (++slp >= &slaves[SLAVES]) + slp = &slaves[0]; + + q->count = 1; + + if (prev->dblk != 0) { + /* + * If the last one was a disk block, make the + * first of this one be the last bit of that disk + * block... + */ + q->dblk = prev->dblk + + prev->count * (TP_BSIZE / DEV_BSIZE); + ntb = (union u_spcl *)tslp->tblock; + } else { + /* + * It wasn't a disk block. Copy the data to its + * new location in the buffer. + */ + q->dblk = 0; + *((union u_spcl *)tslp->tblock) = *ntb; + ntb = (union u_spcl *)tslp->tblock[1]; + } + } + slp->req[0] = *q; + nextblock = slp->tblock; + if (q->dblk == 0) + nextblock++; + trecno = 1; + + /* + * Clear the first slaves' response. One hopes that it + * worked ok, otherwise the tape is much too short! + */ + if (slp->sent) { + if (atomic(read, slp->fd, (char *)&got, sizeof got) + != sizeof got) { + perror(" DUMP: error reading command pipe in master"); + dumpabort(0); + } + slp->sent = 0; + + if (got != writesize) { + quit("EOT detected at start of the tape!\n"); + } + } +} + +/* + * We implement taking and restoring checkpoints on the tape level. + * When each tape is opened, a new process is created by forking; this + * saves all of the necessary context in the parent. The child + * continues the dump; the parent waits around, saving the context. + * If the child returns X_REWRITE, then it had problems writing that tape; + * this causes the parent to fork again, duplicating the context, and + * everything continues as if nothing had happened. + */ +void +startnewtape(top) + int top; +{ + int parentpid; + int childpid; + int status; + int waitpid; + char *p; +#ifdef sunos + void (*interrupt_save)(); +#else + sig_t interrupt_save; +#endif + + interrupt_save = signal(SIGINT, SIG_IGN); + parentpid = getpid(); + +restore_check_point: + (void)signal(SIGINT, interrupt_save); + /* + * All signals are inherited... + */ + childpid = fork(); + if (childpid < 0) { + msg("Context save fork fails in parent %d\n", parentpid); + Exit(X_ABORT); + } + if (childpid != 0) { + /* + * PARENT: + * save the context by waiting + * until the child doing all of the work returns. + * don't catch the interrupt + */ + signal(SIGINT, SIG_IGN); +#ifdef TDEBUG + msg("Tape: %d; parent process: %d child process %d\n", + tapeno+1, parentpid, childpid); +#endif /* TDEBUG */ + while ((waitpid = wait(&status)) != childpid) + msg("Parent %d waiting for child %d has another child %d return\n", + parentpid, childpid, waitpid); + if (status & 0xFF) { + msg("Child %d returns LOB status %o\n", + childpid, status&0xFF); + } + status = (status >> 8) & 0xFF; +#ifdef TDEBUG + switch(status) { + case X_FINOK: + msg("Child %d finishes X_FINOK\n", childpid); + break; + case X_ABORT: + msg("Child %d finishes X_ABORT\n", childpid); + break; + case X_REWRITE: + msg("Child %d finishes X_REWRITE\n", childpid); + break; + default: + msg("Child %d finishes unknown %d\n", + childpid, status); + break; + } +#endif /* TDEBUG */ + switch(status) { + case X_FINOK: + Exit(X_FINOK); + case X_ABORT: + Exit(X_ABORT); + case X_REWRITE: + goto restore_check_point; + default: + msg("Bad return code from dump: %d\n", status); + Exit(X_ABORT); + } + /*NOTREACHED*/ + } else { /* we are the child; just continue */ +#ifdef TDEBUG + sleep(4); /* allow time for parent's message to get out */ + msg("Child on Tape %d has parent %d, my pid = %d\n", + tapeno+1, parentpid, getpid()); +#endif /* TDEBUG */ + /* + * If we have a name like "/dev/rmt0,/dev/rmt1", + * use the name before the comma first, and save + * the remaining names for subsequent volumes. + */ + tapeno++; /* current tape sequence */ + if (nexttape || index(tape, ',')) { + if (nexttape && *nexttape) + tape = nexttape; + if ((p = index(tape, ',')) != NULL) { + *p = '\0'; + nexttape = p + 1; + } else + nexttape = NULL; + msg("Dumping volume %d on %s\n", tapeno, tape); + } +#ifdef RDUMP + while ((tapefd = (host ? rmtopen(tape, 2) : + pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0) +#else + while ((tapefd = (pipeout ? 1 : + open(tape, O_WRONLY|O_CREAT, 0666))) < 0) +#endif + { + msg("Cannot open output \"%s\".\n", tape); + if (!query("Do you want to retry the open?")) + dumpabort(0); + } + + enslave(); /* Share open tape file descriptor with slaves */ + + asize = 0; + blocksthisvol = 0; + if (top) + newtape++; /* new tape signal */ + spcl.c_count = slp->count; + /* + * measure firstrec in TP_BSIZE units since restore doesn't + * know the correct ntrec value... + */ + spcl.c_firstrec = slp->firstrec; + spcl.c_volume++; + spcl.c_type = TS_TAPE; + spcl.c_flags |= DR_NEWHEADER; + writeheader((ino_t)slp->inode); + spcl.c_flags &=~ DR_NEWHEADER; + if (tapeno > 1) + msg("Volume %d begins with blocks from inode %d\n", + tapeno, slp->inode); + } +} + +void +dumpabort(signo) + int signo; +{ + + if (master != 0 && master != getpid()) + /* Signals master to call dumpabort */ + (void) kill(master, SIGTERM); + else { + killall(); + msg("The ENTIRE dump is aborted.\n"); + } +#ifdef RDUMP + rmtclose(); +#endif + Exit(X_ABORT); +} + +__dead void +Exit(status) + int status; +{ + +#ifdef TDEBUG + msg("pid = %d exits with status %d\n", getpid(), status); +#endif /* TDEBUG */ + exit(status); +} + +/* + * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. + */ +void +proceed(signo) + int signo; +{ + + if (ready) + longjmp(jmpbuf, 1); + caught++; +} + +void +enslave() +{ + int cmd[2]; + register int i, j; + + master = getpid(); + + signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ + signal(SIGPIPE, sigpipe); + signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ + signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */ + + for (i = 0; i < SLAVES; i++) { + if (i == slp - &slaves[0]) { + caught = 1; + } else { + caught = 0; + } + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 || + (slaves[i].pid = fork()) < 0) + quit("too many slaves, %d (recompile smaller): %s\n", + i, strerror(errno)); + + slaves[i].fd = cmd[1]; + slaves[i].sent = 0; + if (slaves[i].pid == 0) { /* Slave starts up here */ + for (j = 0; j <= i; j++) + (void) close(slaves[j].fd); + signal(SIGINT, SIG_IGN); /* Master handles this */ + doslave(cmd[0], i); + Exit(X_FINOK); + } + } + + for (i = 0; i < SLAVES; i++) + (void) atomic(write, slaves[i].fd, + (char *) &slaves[(i + 1) % SLAVES].pid, + sizeof slaves[0].pid); + + master = 0; +} + +void +killall() +{ + register int i; + + for (i = 0; i < SLAVES; i++) + if (slaves[i].pid > 0) + (void) kill(slaves[i].pid, SIGKILL); +} + +/* + * Synchronization - each process has a lockfile, and shares file + * descriptors to the following process's lockfile. When our write + * completes, we release our lock on the following process's lock- + * file, allowing the following process to lock it and proceed. We + * get the lock back for the next cycle by swapping descriptors. + */ +static void +doslave(cmd, slave_number) + register int cmd; + int slave_number; +{ + register int nread; + int nextslave, size, wrote, eot_count; + + /* + * Need our own seek pointer. + */ + (void) close(diskfd); + if ((diskfd = open(disk, O_RDONLY)) < 0) + quit("slave couldn't reopen disk: %s\n", strerror(errno)); + + /* + * Need the pid of the next slave in the loop... + */ + if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave)) + != sizeof nextslave) { + quit("master/slave protocol botched - didn't get pid of next slave.\n"); + } + + /* + * Get list of blocks to dump, read the blocks into tape buffer + */ + while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) { + register struct req *p = slp->req; + + for (trecno = 0; trecno < ntrec; + trecno += p->count, p += p->count) { + if (p->dblk) { + bread(p->dblk, slp->tblock[trecno], + p->count * TP_BSIZE); + } else { + if (p->count != 1 || atomic(read, cmd, + (char *)slp->tblock[trecno], + TP_BSIZE) != TP_BSIZE) + quit("master/slave protocol botched.\n"); + } + } + if (setjmp(jmpbuf) == 0) { + ready = 1; + if (!caught) + (void) pause(); + } + ready = 0; + caught = 0; + + /* Try to write the data... */ + eot_count = 0; + size = 0; + + while (eot_count < 10 && size < writesize) { +#ifdef RDUMP + if (host) + wrote = rmtwrite(slp->tblock[0]+size, + writesize-size); + else +#endif + wrote = write(tapefd, slp->tblock[0]+size, + writesize-size); +#ifdef WRITEDEBUG + printf("slave %d wrote %d\n", slave_number, wrote); +#endif + if (wrote < 0) + break; + if (wrote == 0) + eot_count++; + size += wrote; + } + +#ifdef WRITEDEBUG + if (size != writesize) + printf("slave %d only wrote %d out of %d bytes and gave up.\n", + slave_number, size, writesize); +#endif + + if (eot_count > 0) + size = 0; + + /* + * fixme: Pyramids running OSx return ENOSPC + * at EOT on 1/2 inch drives. + */ + if (size < 0) { + (void) kill(master, SIGUSR1); + for (;;) + (void) sigpause(0); + } else { + /* + * pass size of write back to master + * (for EOT handling) + */ + (void) atomic(write, cmd, (char *)&size, sizeof size); + } + + /* + * If partial write, don't want next slave to go. + * Also jolts him awake. + */ + (void) kill(nextslave, SIGUSR2); + } + if (nread != 0) + quit("error reading command pipe: %s\n", strerror(errno)); +} + +/* + * Since a read from a pipe may not return all we asked for, + * or a write may not write all we ask if we get a signal, + * loop until the count is satisfied (or error). + */ +static int +atomic(func, fd, buf, count) + int (*func)(), fd; + char *buf; + int count; +{ + int got, need = count; + + while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) + buf += got; + return (got < 0 ? got : count - need); +} diff --git a/sbin/dump/traverse.c b/sbin/dump/traverse.c new file mode 100644 index 0000000..ce70050 --- /dev/null +++ b/sbin/dump/traverse.c @@ -0,0 +1,613 @@ +/*- + * Copyright (c) 1980, 1988, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)traverse.c 8.2 (Berkeley) 9/23/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> +#ifdef sunos +#include <sys/vnode.h> + +#include <ufs/fs.h> +#include <ufs/fsdir.h> +#include <ufs/inode.h> +#else +#include <ufs/ffs/fs.h> +#include <ufs/ufs/dir.h> +#include <ufs/ufs/dinode.h> +#endif + +#include <protocols/dumprestore.h> + +#include <ctype.h> +#include <stdio.h> +#ifdef __STDC__ +#include <string.h> +#include <unistd.h> +#endif + +#include "dump.h" + +#define HASDUMPEDFILE 0x1 +#define HASSUBDIRS 0x2 + +#ifdef FS_44INODEFMT +typedef quad_t fsizeT; +#else +typedef long fsizeT; +#endif + +static int dirindir __P((ino_t ino, daddr_t blkno, int level, long *size)); +static void dmpindir __P((ino_t ino, daddr_t blk, int level, fsizeT *size)); +static int searchdir __P((ino_t ino, daddr_t blkno, long size, long filesize)); + +/* + * This is an estimation of the number of TP_BSIZE blocks in the file. + * It estimates the number of blocks in files with holes by assuming + * that all of the blocks accounted for by di_blocks are data blocks + * (when some of the blocks are usually used for indirect pointers); + * hence the estimate may be high. + */ +long +blockest(dp) + register struct dinode *dp; +{ + long blkest, sizeest; + + /* + * dp->di_size is the size of the file in bytes. + * dp->di_blocks stores the number of sectors actually in the file. + * If there are more sectors than the size would indicate, this just + * means that there are indirect blocks in the file or unused + * sectors in the last file block; we can safely ignore these + * (blkest = sizeest below). + * If the file is bigger than the number of sectors would indicate, + * then the file has holes in it. In this case we must use the + * block count to estimate the number of data blocks used, but + * we use the actual size for estimating the number of indirect + * dump blocks (sizeest vs. blkest in the indirect block + * calculation). + */ + blkest = howmany(dbtob(dp->di_blocks), TP_BSIZE); + sizeest = howmany(dp->di_size, TP_BSIZE); + if (blkest > sizeest) + blkest = sizeest; + if (dp->di_size > sblock->fs_bsize * NDADDR) { + /* calculate the number of indirect blocks on the dump tape */ + blkest += + howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE, + TP_NINDIR); + } + return (blkest + 1); +} + +/* Auxiliary macro to pick up files changed since previous dump. */ +#ifdef FS_44INODEFMT +#define CHANGEDSINCE(dp, t) \ + ((dp)->di_mtime.ts_sec >= (t) || (dp)->di_ctime.ts_sec >= (t)) +#else +#define CHANGEDSINCE(dp, t) \ + ((dp)->di_mtime >= (t) || (dp)->di_ctime >= (t)) +#endif + +/* The WANTTODUMP macro decides whether a file should be dumped. */ +#ifdef UF_NODUMP +#define WANTTODUMP(dp) \ + (CHANGEDSINCE(dp, spcl.c_ddate) && \ + (nonodump || ((dp)->di_flags & UF_NODUMP) != UF_NODUMP)) +#else +#define WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate) +#endif + +/* + * Dump pass 1. + * + * Walk the inode list for a filesystem to find all allocated inodes + * that have been modified since the previous dump time. Also, find all + * the directories in the filesystem. + */ +int +mapfiles(maxino, tapesize) + ino_t maxino; + long *tapesize; +{ + register int mode; + register ino_t ino; + register struct dinode *dp; + int anydirskipped = 0; + + for (ino = ROOTINO; ino < maxino; ino++) { + dp = getino(ino); + if ((mode = (dp->di_mode & IFMT)) == 0) + continue; + SETINO(ino, usedinomap); + if (mode == IFDIR) + SETINO(ino, dumpdirmap); + if (WANTTODUMP(dp)) { + SETINO(ino, dumpinomap); + if (mode != IFREG && mode != IFDIR && mode != IFLNK) + *tapesize += 1; + else + *tapesize += blockest(dp); + continue; + } + if (mode == IFDIR) + anydirskipped = 1; + } + /* + * Restore gets very upset if the root is not dumped, + * so ensure that it always is dumped. + */ + SETINO(ROOTINO, dumpinomap); + return (anydirskipped); +} + +/* + * Dump pass 2. + * + * Scan each directory on the filesystem to see if it has any modified + * files in it. If it does, and has not already been added to the dump + * list (because it was itself modified), then add it. If a directory + * has not been modified itself, contains no modified files and has no + * subdirectories, then it can be deleted from the dump list and from + * the list of directories. By deleting it from the list of directories, + * its parent may now qualify for the same treatment on this or a later + * pass using this algorithm. + */ +int +mapdirs(maxino, tapesize) + ino_t maxino; + long *tapesize; +{ + register struct dinode *dp; + register int i, isdir; + register char *map; + register ino_t ino; + long filesize; + int ret, change = 0; + + isdir = 0; /* XXX just to get gcc to shut up */ + for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { + if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ + isdir = *map++; + else + isdir >>= 1; + if ((isdir & 1) == 0 || TSTINO(ino, dumpinomap)) + continue; + dp = getino(ino); + filesize = dp->di_size; + for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) { + if (dp->di_db[i] != 0) + ret |= searchdir(ino, dp->di_db[i], + (long)dblksize(sblock, dp, i), + filesize); + if (ret & HASDUMPEDFILE) + filesize = 0; + else + filesize -= sblock->fs_bsize; + } + for (i = 0; filesize > 0 && i < NIADDR; i++) { + if (dp->di_ib[i] == 0) + continue; + ret |= dirindir(ino, dp->di_ib[i], i, &filesize); + } + if (ret & HASDUMPEDFILE) { + SETINO(ino, dumpinomap); + *tapesize += blockest(dp); + change = 1; + continue; + } + if ((ret & HASSUBDIRS) == 0) { + if (!TSTINO(ino, dumpinomap)) { + CLRINO(ino, dumpdirmap); + change = 1; + } + } + } + return (change); +} + +/* + * Read indirect blocks, and pass the data blocks to be searched + * as directories. Quit as soon as any entry is found that will + * require the directory to be dumped. + */ +static int +dirindir(ino, blkno, ind_level, filesize) + ino_t ino; + daddr_t blkno; + int ind_level; + long *filesize; +{ + int ret = 0; + register int i; + daddr_t idblk[MAXNINDIR]; + + bread(fsbtodb(sblock, blkno), (char *)idblk, (int)sblock->fs_bsize); + if (ind_level <= 0) { + for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { + blkno = idblk[i]; + if (blkno != 0) + ret |= searchdir(ino, blkno, sblock->fs_bsize, + *filesize); + if (ret & HASDUMPEDFILE) + *filesize = 0; + else + *filesize -= sblock->fs_bsize; + } + return (ret); + } + ind_level--; + for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { + blkno = idblk[i]; + if (blkno != 0) + ret |= dirindir(ino, blkno, ind_level, filesize); + } + return (ret); +} + +/* + * Scan a disk block containing directory information looking to see if + * any of the entries are on the dump list and to see if the directory + * contains any subdirectories. + */ +static int +searchdir(ino, blkno, size, filesize) + ino_t ino; + daddr_t blkno; + register long size; + long filesize; +{ + register struct direct *dp; + register long loc, ret = 0; + char dblk[MAXBSIZE]; + + bread(fsbtodb(sblock, blkno), dblk, (int)size); + if (filesize < size) + size = filesize; + for (loc = 0; loc < size; ) { + dp = (struct direct *)(dblk + loc); + if (dp->d_reclen == 0) { + msg("corrupted directory, inumber %d\n", ino); + break; + } + loc += dp->d_reclen; + if (dp->d_ino == 0) + continue; + if (dp->d_name[0] == '.') { + if (dp->d_name[1] == '\0') + continue; + if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') + continue; + } + if (TSTINO(dp->d_ino, dumpinomap)) { + ret |= HASDUMPEDFILE; + if (ret & HASSUBDIRS) + break; + } + if (TSTINO(dp->d_ino, dumpdirmap)) { + ret |= HASSUBDIRS; + if (ret & HASDUMPEDFILE) + break; + } + } + return (ret); +} + +/* + * Dump passes 3 and 4. + * + * Dump the contents of an inode to tape. + */ +void +dumpino(dp, ino) + register struct dinode *dp; + ino_t ino; +{ + int ind_level, cnt; + fsizeT size; + char buf[TP_BSIZE]; + + if (newtape) { + newtape = 0; + dumpmap(dumpinomap, TS_BITS, ino); + } + CLRINO(ino, dumpinomap); + spcl.c_dinode = *dp; + spcl.c_type = TS_INODE; + spcl.c_count = 0; + switch (dp->di_mode & S_IFMT) { + + case 0: + /* + * Freed inode. + */ + return; + + case S_IFLNK: + /* + * Check for short symbolic link. + */ +#ifdef FS_44INODEFMT + if (dp->di_size > 0 && + dp->di_size < sblock->fs_maxsymlinklen) { + spcl.c_addr[0] = 1; + spcl.c_count = 1; + writeheader(ino); + bcopy((caddr_t)dp->di_shortlink, buf, + (u_long)dp->di_size); + buf[dp->di_size] = '\0'; + writerec(buf, 0); + return; + } +#endif + /* fall through */ + + case S_IFDIR: + case S_IFREG: + if (dp->di_size > 0) + break; + /* fall through */ + + case S_IFIFO: + case S_IFSOCK: + case S_IFCHR: + case S_IFBLK: + writeheader(ino); + return; + + default: + msg("Warning: undefined file type 0%o\n", dp->di_mode & IFMT); + return; + } + if (dp->di_size > NDADDR * sblock->fs_bsize) + cnt = NDADDR * sblock->fs_frag; + else + cnt = howmany(dp->di_size, sblock->fs_fsize); + blksout(&dp->di_db[0], cnt, ino); + if ((size = dp->di_size - NDADDR * sblock->fs_bsize) <= 0) + return; + for (ind_level = 0; ind_level < NIADDR; ind_level++) { + dmpindir(ino, dp->di_ib[ind_level], ind_level, &size); + if (size <= 0) + return; + } +} + +/* + * Read indirect blocks, and pass the data blocks to be dumped. + */ +static void +dmpindir(ino, blk, ind_level, size) + ino_t ino; + daddr_t blk; + int ind_level; + fsizeT *size; +{ + int i, cnt; + daddr_t idblk[MAXNINDIR]; + + if (blk != 0) + bread(fsbtodb(sblock, blk), (char *)idblk, (int) sblock->fs_bsize); + else + bzero((char *)idblk, (int)sblock->fs_bsize); + if (ind_level <= 0) { + if (*size < NINDIR(sblock) * sblock->fs_bsize) + cnt = howmany(*size, sblock->fs_fsize); + else + cnt = NINDIR(sblock) * sblock->fs_frag; + *size -= NINDIR(sblock) * sblock->fs_bsize; + blksout(&idblk[0], cnt, ino); + return; + } + ind_level--; + for (i = 0; i < NINDIR(sblock); i++) { + dmpindir(ino, idblk[i], ind_level, size); + if (*size <= 0) + return; + } +} + +/* + * Collect up the data into tape record sized buffers and output them. + */ +void +blksout(blkp, frags, ino) + daddr_t *blkp; + int frags; + ino_t ino; +{ + register daddr_t *bp; + int i, j, count, blks, tbperdb; + + blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); + tbperdb = sblock->fs_bsize >> tp_bshift; + for (i = 0; i < blks; i += TP_NINDIR) { + if (i + TP_NINDIR > blks) + count = blks; + else + count = i + TP_NINDIR; + for (j = i; j < count; j++) + if (blkp[j / tbperdb] != 0) + spcl.c_addr[j - i] = 1; + else + spcl.c_addr[j - i] = 0; + spcl.c_count = count - i; + writeheader(ino); + bp = &blkp[i / tbperdb]; + for (j = i; j < count; j += tbperdb, bp++) + if (*bp != 0) + if (j + tbperdb <= count) + dumpblock(*bp, (int)sblock->fs_bsize); + else + dumpblock(*bp, (count - j) * TP_BSIZE); + spcl.c_type = TS_ADDR; + } +} + +/* + * Dump a map to the tape. + */ +void +dumpmap(map, type, ino) + char *map; + int type; + ino_t ino; +{ + register int i; + char *cp; + + spcl.c_type = type; + spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE); + writeheader(ino); + for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE) + writerec(cp, 0); +} + +/* + * Write a header record to the dump tape. + */ +void +writeheader(ino) + ino_t ino; +{ + register long sum, cnt, *lp; + + spcl.c_inumber = ino; + spcl.c_magic = NFS_MAGIC; + spcl.c_checksum = 0; + lp = (long *)&spcl; + sum = 0; + cnt = sizeof(union u_spcl) / (4 * sizeof(long)); + while (--cnt >= 0) { + sum += *lp++; + sum += *lp++; + sum += *lp++; + sum += *lp++; + } + spcl.c_checksum = CHECKSUM - sum; + writerec((char *)&spcl, 1); +} + +struct dinode * +getino(inum) + ino_t inum; +{ + static daddr_t minino, maxino; + static struct dinode inoblock[MAXINOPB]; + + curino = inum; + if (inum >= minino && inum < maxino) + return (&inoblock[inum - minino]); + bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), (char *)inoblock, + (int)sblock->fs_bsize); + minino = inum - (inum % INOPB(sblock)); + maxino = minino + INOPB(sblock); + return (&inoblock[inum - minino]); +} + +/* + * Read a chunk of data from the disk. + * Try to recover from hard errors by reading in sector sized pieces. + * Error recovery is attempted at most BREADEMAX times before seeking + * consent from the operator to continue. + */ +int breaderrors = 0; +#define BREADEMAX 32 + +void +bread(blkno, buf, size) + daddr_t blkno; + char *buf; + int size; +{ + int cnt, i; + extern int errno; + +loop: + if ((int)lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0) + msg("bread: lseek fails\n"); + if ((cnt = read(diskfd, buf, size)) == size) + return; + if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) { + /* + * Trying to read the final fragment. + * + * NB - dump only works in TP_BSIZE blocks, hence + * rounds `dev_bsize' fragments up to TP_BSIZE pieces. + * It should be smarter about not actually trying to + * read more than it can get, but for the time being + * we punt and scale back the read only when it gets + * us into trouble. (mkm 9/25/83) + */ + size -= dev_bsize; + goto loop; + } + if (cnt == -1) + msg("read error from %s: %s: [block %d]: count=%d\n", + disk, strerror(errno), blkno, size); + else + msg("short read error from %s: [block %d]: count=%d, got=%d\n", + disk, blkno, size, cnt); + if (++breaderrors > BREADEMAX) { + msg("More than %d block read errors from %d\n", + BREADEMAX, disk); + broadcast("DUMP IS AILING!\n"); + msg("This is an unrecoverable error.\n"); + if (!query("Do you want to attempt to continue?")){ + dumpabort(0); + /*NOTREACHED*/ + } else + breaderrors = 0; + } + /* + * Zero buffer, then try to read each sector of buffer separately. + */ + bzero(buf, size); + for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) { + if ((int)lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0) + msg("bread: lseek2 fails!\n"); + if ((cnt = read(diskfd, buf, (int)dev_bsize)) == dev_bsize) + continue; + if (cnt == -1) { + msg("read error from %s: %s: [sector %d]: count=%d\n", + disk, strerror(errno), blkno, dev_bsize); + continue; + } + msg("short read error from %s: [sector %d]: count=%d, got=%d\n", + disk, blkno, dev_bsize, cnt); + } +} diff --git a/sbin/dump/unctime.c b/sbin/dump/unctime.c new file mode 100644 index 0000000..7b3fd4e --- /dev/null +++ b/sbin/dump/unctime.c @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)unctime.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdio.h> +#include <time.h> +#ifdef __STDC__ +#include <stdlib.h> +#include <string.h> +#endif + +#ifndef __P +#include <sys/cdefs.h> +#endif + +/* + * Convert a ctime(3) format string into a system format date. + * Return the date thus calculated. + * + * Return -1 if the string is not in ctime format. + */ + +/* + * Offsets into the ctime string to various parts. + */ + +#define E_MONTH 4 +#define E_DAY 8 +#define E_HOUR 11 +#define E_MINUTE 14 +#define E_SECOND 17 +#define E_YEAR 20 + +static int dcmp __P((struct tm *, struct tm *)); +static time_t emitl __P((struct tm *)); +static int lookup __P((char *)); + + +time_t +unctime(str) + char *str; +{ + struct tm then; + char dbuf[26]; + + (void) strncpy(dbuf, str, sizeof(dbuf) - 1); + dbuf[sizeof(dbuf) - 1] = '\0'; + dbuf[E_MONTH+3] = '\0'; + if ((then.tm_mon = lookup(&dbuf[E_MONTH])) < 0) + return (-1); + then.tm_mday = atoi(&dbuf[E_DAY]); + then.tm_hour = atoi(&dbuf[E_HOUR]); + then.tm_min = atoi(&dbuf[E_MINUTE]); + then.tm_sec = atoi(&dbuf[E_SECOND]); + then.tm_year = atoi(&dbuf[E_YEAR]) - 1900; + return(emitl(&then)); +} + +static char months[] = + "JanFebMarAprMayJunJulAugSepOctNovDec"; + +static int +lookup(str) + char *str; +{ + register char *cp, *cp2; + + for (cp = months, cp2 = str; *cp != '\0'; cp += 3) + if (strncmp(cp, cp2, 3) == 0) + return((cp-months) / 3); + return(-1); +} +/* + * Routine to convert a localtime(3) format date back into + * a system format date. + * + * Use a binary search. + */ + +static time_t +emitl(dp) + struct tm *dp; +{ + time_t conv; + register int i, bit; + struct tm dcopy; + + dcopy = *dp; + dp = &dcopy; + conv = 0; + for (i = 30; i >= 0; i--) { + bit = 1 << i; + conv |= bit; + if (dcmp(localtime(&conv), dp) > 0) + conv &= ~bit; + } + return(conv); +} + +/* + * Compare two localtime dates, return result. + */ + +#define DECIDE(a) \ + if (dp->a > dp2->a) \ + return(1); \ + if (dp->a < dp2->a) \ + return(-1) + +static int +dcmp(dp, dp2) + register struct tm *dp, *dp2; +{ + + DECIDE(tm_year); + DECIDE(tm_mon); + DECIDE(tm_mday); + DECIDE(tm_hour); + DECIDE(tm_min); + DECIDE(tm_sec); + return(0); +} diff --git a/sbin/dumpfs/Makefile b/sbin/dumpfs/Makefile new file mode 100644 index 0000000..dd2a0c2 --- /dev/null +++ b/sbin/dumpfs/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= dumpfs +MAN8= dumpfs.0 + +.include <bsd.prog.mk> diff --git a/sbin/dumpfs/dumpfs.8 b/sbin/dumpfs/dumpfs.8 new file mode 100644 index 0000000..17263d0 --- /dev/null +++ b/sbin/dumpfs/dumpfs.8 @@ -0,0 +1,62 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dumpfs.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt DUMPFS 8 +.Os BSD 4.2 +.Sh NAME +.Nm dumpfs +.Nd dump file system information +.Sh SYNOPSIS +.Nm dumpfs +.Op Ar filesys No \&| Ar device +.Sh DESCRIPTION +.Nm Dumpfs +prints out the super block and cylinder group information +for the file system or special device specified. +The listing is very long and detailed. This +command is useful mostly for finding out certain file system +information such as the file system block size and minimum +free space percentage. +.Sh SEE ALSO +.Xr fs 5 , +.Xr disktab 5 , +.Xr disklabel 8 , +.Xr tunefs 8 , +.Xr newfs 8 , +.Xr fsck 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c new file mode 100644 index 0000000..d453fab --- /dev/null +++ b/sbin/dumpfs/dumpfs.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 1983, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)dumpfs.c 8.2 (Berkeley) 2/2/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> + +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <fstab.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +union { + struct fs fs; + char pad[MAXBSIZE]; +} fsun; +#define afs fsun.fs + +union { + struct cg cg; + char pad[MAXBSIZE]; +} cgun; +#define acg cgun.cg + +long dev_bsize = 1; + +int dumpfs __P((char *)); +int dumpcg __P((char *, int, int)); +void pbits __P((void *, int)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct fstab *fs; + int ch, eval; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch(ch) { + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + for (eval = 0; *argv; ++argv) + if ((fs = getfsfile(*argv)) == NULL) + eval |= dumpfs(*argv); + else + eval |= dumpfs(fs->fs_spec); + exit(eval); +} + +int +dumpfs(name) + char *name; +{ + int fd, c, i, j, k, size; + + if ((fd = open(name, O_RDONLY, 0)) < 0) + goto err; + if (lseek(fd, (off_t)SBOFF, SEEK_SET) == (off_t)-1) + goto err; + if (read(fd, &afs, SBSIZE) != SBSIZE) + goto err; + + if (afs.fs_postblformat == FS_42POSTBLFMT) + afs.fs_nrpos = 8; + dev_bsize = afs.fs_fsize / fsbtodb(&afs, 1); + printf("magic\t%x\ttime\t%s", afs.fs_magic, + ctime(&afs.fs_time)); + printf("cylgrp\t%s\tinodes\t%s\n", + afs.fs_postblformat == FS_42POSTBLFMT ? "static" : "dynamic", + afs.fs_inodefmt < FS_44INODEFMT ? "4.2/4.3BSD" : "4.4BSD"); + printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n", + afs.fs_cstotal.cs_nbfree, afs.fs_cstotal.cs_ndir, + afs.fs_cstotal.cs_nifree, afs.fs_cstotal.cs_nffree); + printf("ncg\t%d\tncyl\t%d\tsize\t%d\tblocks\t%d\n", + afs.fs_ncg, afs.fs_ncyl, afs.fs_size, afs.fs_dsize); + printf("bsize\t%d\tshift\t%d\tmask\t0x%08x\n", + afs.fs_bsize, afs.fs_bshift, afs.fs_bmask); + printf("fsize\t%d\tshift\t%d\tmask\t0x%08x\n", + afs.fs_fsize, afs.fs_fshift, afs.fs_fmask); + printf("frag\t%d\tshift\t%d\tfsbtodb\t%d\n", + afs.fs_frag, afs.fs_fragshift, afs.fs_fsbtodb); + printf("cpg\t%d\tbpg\t%d\tfpg\t%d\tipg\t%d\n", + afs.fs_cpg, afs.fs_fpg / afs.fs_frag, afs.fs_fpg, afs.fs_ipg); + printf("minfree\t%d%%\toptim\t%s\tmaxcontig %d\tmaxbpg\t%d\n", + afs.fs_minfree, afs.fs_optim == FS_OPTSPACE ? "space" : "time", + afs.fs_maxcontig, afs.fs_maxbpg); + printf("rotdelay %dms\theadswitch %dus\ttrackseek %dus\trps\t%d\n", + afs.fs_rotdelay, afs.fs_headswitch, afs.fs_trkseek, afs.fs_rps); + printf("ntrak\t%d\tnsect\t%d\tnpsect\t%d\tspc\t%d\n", + afs.fs_ntrak, afs.fs_nsect, afs.fs_npsect, afs.fs_spc); + printf("symlinklen %d\ttrackskew %d\tinterleave %d\tcontigsumsize %d\n", + afs.fs_maxsymlinklen, afs.fs_trackskew, afs.fs_interleave, + afs.fs_contigsumsize); + printf("nindir\t%d\tinopb\t%d\tnspf\t%d\n", + afs.fs_nindir, afs.fs_inopb, afs.fs_nspf); + printf("sblkno\t%d\tcblkno\t%d\tiblkno\t%d\tdblkno\t%d\n", + afs.fs_sblkno, afs.fs_cblkno, afs.fs_iblkno, afs.fs_dblkno); + printf("sbsize\t%d\tcgsize\t%d\tcgoffset %d\tcgmask\t0x%08x\n", + afs.fs_sbsize, afs.fs_cgsize, afs.fs_cgoffset, afs.fs_cgmask); + printf("csaddr\t%d\tcssize\t%d\tshift\t%d\tmask\t0x%08x\n", + afs.fs_csaddr, afs.fs_cssize, afs.fs_csshift, afs.fs_csmask); + printf("cgrotor\t%d\tfmod\t%d\tronly\t%d\n", + afs.fs_cgrotor, afs.fs_fmod, afs.fs_ronly); + if (afs.fs_cpc != 0) + printf("blocks available in each of %d rotational positions", + afs.fs_nrpos); + else + printf("insufficient space to maintain rotational tables\n"); + for (c = 0; c < afs.fs_cpc; c++) { + printf("\ncylinder number %d:", c); + for (i = 0; i < afs.fs_nrpos; i++) { + if (fs_postbl(&afs, c)[i] == -1) + continue; + printf("\n position %d:\t", i); + for (j = fs_postbl(&afs, c)[i], k = 1; ; + j += fs_rotbl(&afs)[j], k++) { + printf("%5d", j); + if (k % 12 == 0) + printf("\n\t\t"); + if (fs_rotbl(&afs)[j] == 0) + break; + } + } + } + printf("\ncs[].cs_(nbfree,ndir,nifree,nffree):\n\t"); + for (i = 0, j = 0; i < afs.fs_cssize; i += afs.fs_bsize, j++) { + size = afs.fs_cssize - i < afs.fs_bsize ? + afs.fs_cssize - i : afs.fs_bsize; + afs.fs_csp[j] = calloc(1, size); + if (lseek(fd, + (off_t)(fsbtodb(&afs, (afs.fs_csaddr + j * afs.fs_frag)) * + dev_bsize), SEEK_SET) == (off_t)-1) + goto err; + if (read(fd, afs.fs_csp[j], size) != size) + goto err; + } + for (i = 0; i < afs.fs_ncg; i++) { + struct csum *cs = &afs.fs_cs(&afs, i); + if (i && i % 4 == 0) + printf("\n\t"); + printf("(%d,%d,%d,%d) ", + cs->cs_nbfree, cs->cs_ndir, cs->cs_nifree, cs->cs_nffree); + } + printf("\n"); + if (afs.fs_ncyl % afs.fs_cpg) { + printf("cylinders in last group %d\n", + i = afs.fs_ncyl % afs.fs_cpg); + printf("blocks in last group %d\n", + i * afs.fs_spc / NSPB(&afs)); + } + printf("\n"); + for (i = 0; i < afs.fs_ncg; i++) + if (dumpcg(name, fd, i)) + goto err; + (void)close(fd); + return (0); + +err: if (fd != -1) + (void)close(fd); + (void)fprintf(stderr, "dumpfs: %s: %s\n", name, strerror(errno)); + return (1); +}; + +int +dumpcg(name, fd, c) + char *name; + int fd, c; +{ + off_t cur; + int i, j; + + printf("\ncg %d:\n", c); + if ((cur = lseek(fd, (off_t)(fsbtodb(&afs, cgtod(&afs, c)) * dev_bsize), + SEEK_SET)) == (off_t)-1) + return (1); + if (read(fd, &acg, afs.fs_bsize) != afs.fs_bsize) { + (void)fprintf(stderr, "dumpfs: %s: error reading cg\n", name); + return (1); + } + printf("magic\t%x\ttell\t%qx\ttime\t%s", + afs.fs_postblformat == FS_42POSTBLFMT ? + ((struct ocg *)&acg)->cg_magic : acg.cg_magic, + cur, ctime(&acg.cg_time)); + printf("cgx\t%d\tncyl\t%d\tniblk\t%d\tndblk\t%d\n", + acg.cg_cgx, acg.cg_ncyl, acg.cg_niblk, acg.cg_ndblk); + printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n", + acg.cg_cs.cs_nbfree, acg.cg_cs.cs_ndir, + acg.cg_cs.cs_nifree, acg.cg_cs.cs_nffree); + printf("rotor\t%d\tirotor\t%d\tfrotor\t%d\nfrsum", + acg.cg_rotor, acg.cg_irotor, acg.cg_frotor); + for (i = 1, j = 0; i < afs.fs_frag; i++) { + printf("\t%d", acg.cg_frsum[i]); + j += i * acg.cg_frsum[i]; + } + printf("\nsum of frsum: %d", j); + if (afs.fs_contigsumsize > 0) { + for (i = 1; i < afs.fs_contigsumsize; i++) { + if ((i - 1) % 8 == 0) + printf("\nclusters %d-%d:", i, + afs.fs_contigsumsize - 1 < i + 7 ? + afs.fs_contigsumsize - 1 : i + 7); + printf("\t%d", cg_clustersum(&acg)[i]); + } + printf("\nclusters size %d and over: %d\n", + afs.fs_contigsumsize, + cg_clustersum(&acg)[afs.fs_contigsumsize]); + printf("clusters free:\t"); + pbits(cg_clustersfree(&acg), acg.cg_nclusterblks); + } else + printf("\n"); + printf("iused:\t"); + pbits(cg_inosused(&acg), afs.fs_ipg); + printf("free:\t"); + pbits(cg_blksfree(&acg), afs.fs_fpg); + printf("b:\n"); + for (i = 0; i < afs.fs_cpg; i++) { + if (cg_blktot(&acg)[i] == 0) + continue; + printf(" c%d:\t(%d)\t", i, cg_blktot(&acg)[i]); + for (j = 0; j < afs.fs_nrpos; j++) { + if (afs.fs_cpc > 0 && + fs_postbl(&afs, i % afs.fs_cpc)[j] == -1) + continue; + printf(" %d", cg_blks(&afs, &acg, i)[j]); + } + printf("\n"); + } + return (0); +}; + +void +pbits(vp, max) + register void *vp; + int max; +{ + register int i; + register char *p; + int count, j; + + for (count = i = 0, p = vp; i < max; i++) + if (isset(p, i)) { + if (count) + printf(",%s", count % 6 ? " " : "\n\t"); + count++; + printf("%d", i); + j = i; + while ((i+1)<max && isset(p, i+1)) + i++; + if (i != j) + printf("-%d", i); + } + printf("\n"); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: dumpfs filesys | device\n"); + exit(1); +} diff --git a/sbin/dumplfs/Makefile b/sbin/dumplfs/Makefile new file mode 100644 index 0000000..2557492 --- /dev/null +++ b/sbin/dumplfs/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 6/18/93 + +PROG= dumplfs +CFLAGS+=-I/sys/ufs/lfs +SRCS= dumplfs.c lfs_cksum.c misc.c +.PATH: /sys/ufs/lfs +MAN8= dumplfs.0 + +.include <bsd.prog.mk> diff --git a/sbin/dumplfs/dumplfs.8 b/sbin/dumplfs/dumplfs.8 new file mode 100644 index 0000000..de0c680 --- /dev/null +++ b/sbin/dumplfs/dumplfs.8 @@ -0,0 +1,59 @@ +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dumplfs.8 8.1 (Berkeley) 6/18/93 +.\" +.Dd June 18, 1993 +.Dt DUMPLFS 8 +.Os BSD 4.4 +.Sh NAME +.Nm dumplfs +.Nd dump file system information +.Sh SYNOPSIS +.Nm dumplfs +.Op Ar filesys No \&| Ar device +.Sh DESCRIPTION +.Nm Dumplfs +prints out the file system layout information for the +LFS file system or special device specified. +The listing is very long and detailed. +This command is useful mostly for finding out certain file system +information such as the file system block size. +.Sh SEE ALSO +.Xr fs 5 , +.Xr disktab 5 , +.Xr disklabel 8 , +.Xr newlfs 8 , +.Sh HISTORY +The +.Nm dumplfs +command appeared in +.Bx 4.4 . diff --git a/sbin/dumplfs/dumplfs.c b/sbin/dumplfs/dumplfs.c new file mode 100644 index 0000000..943c6cd --- /dev/null +++ b/sbin/dumplfs/dumplfs.c @@ -0,0 +1,612 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)dumplfs.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/mount.h> +#include <sys/time.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/lfs/lfs.h> + +#include <fcntl.h> +#include <fstab.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "extern.h" + +static void addseg __P((char *)); +static void dump_cleaner_info __P((struct lfs *, void *)); +static void dump_dinode __P((struct dinode *)); +static void dump_ifile __P((int, struct lfs *, int)); +static int dump_ipage_ifile __P((int, IFILE *, int)); +static int dump_ipage_segusage __P((struct lfs *, int, IFILE *, int)); +static void dump_segment __P((int, int, daddr_t, struct lfs *, int)); +static int dump_sum __P((int, struct lfs *, SEGSUM *, int, daddr_t)); +static void dump_super __P((struct lfs *)); +static void usage __P((void)); + +typedef struct seglist SEGLIST; +struct seglist { + SEGLIST *next; + int num; +}; +SEGLIST *seglist; + +int daddr_shift; +char *special; + +/* Segment Usage formats */ +#define print_suheader \ + (void)printf("segnum\tflags\tnbytes\tninos\tnsums\tlastmod\n") + +#define print_suentry(i, sp) \ + (void)printf("%d\t%c%c%c\t%d\t%d\t%d\t%s", i, \ + (((sp)->su_flags & SEGUSE_ACTIVE) ? 'A' : ' '), \ + (((sp)->su_flags & SEGUSE_DIRTY) ? 'D' : 'C'), \ + (((sp)->su_flags & SEGUSE_SUPERBLOCK) ? 'S' : ' '), \ + (sp)->su_nbytes, (sp)->su_ninos, (sp)->su_nsums, \ + ctime((time_t *)&(sp)->su_lastmod)) + +/* Ifile formats */ +#define print_iheader \ + (void)printf("inum\tstatus\tversion\tdaddr\t\tfreeptr\n") +#define print_ientry(i, ip) \ + if (ip->if_daddr == LFS_UNUSED_DADDR) \ + (void)printf("%d\tFREE\t%d\t \t\t%d\n", \ + i, ip->if_version, ip->if_nextfree); \ + else \ + (void)printf("%d\tINUSE\t%d\t%8X \n", \ + i, ip->if_version, ip->if_daddr) +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct lfs lfs_sb1, lfs_sb2, *lfs_master; + daddr_t seg_addr; + int ch, do_allsb, do_ientries, fd, segnum; + + do_allsb = 0; + do_ientries = 0; + while ((ch = getopt(argc, argv, "ais:")) != EOF) + switch(ch) { + case 'a': /* Dump all superblocks */ + do_allsb = 1; + break; + case 'i': /* Dump ifile entries */ + do_ientries = 1; + break; + case 's': /* Dump out these segments */ + addseg(optarg); + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + special = argv[0]; + if ((fd = open(special, O_RDONLY, 0)) < 0) + err("%s: %s", special, strerror(errno)); + + /* Read the first superblock */ + get(fd, LFS_LABELPAD, &lfs_sb1, sizeof(struct lfs)); + daddr_shift = lfs_sb1.lfs_bshift - lfs_sb1.lfs_fsbtodb; + + /* + * Read the second superblock and figure out which check point is + * most up to date. + */ + get(fd, + lfs_sb1.lfs_sboffs[1] << daddr_shift, &lfs_sb2, sizeof(struct lfs)); + + lfs_master = &lfs_sb1; + if (lfs_sb1.lfs_tstamp < lfs_sb2.lfs_tstamp) + lfs_master = &lfs_sb2; + + (void)printf("Master Superblock:\n"); + dump_super(lfs_master); + + dump_ifile(fd, lfs_master, do_ientries); + + if (seglist != NULL) + for (; seglist != NULL; seglist = seglist->next) { + seg_addr = lfs_master->lfs_sboffs[0] + seglist->num * + (lfs_master->lfs_ssize << lfs_master->lfs_fsbtodb); + dump_segment(fd, + seglist->num, seg_addr, lfs_master, do_allsb); + } + else + for (segnum = 0, seg_addr = lfs_master->lfs_sboffs[0]; + segnum < lfs_master->lfs_nseg; segnum++, seg_addr += + lfs_master->lfs_ssize << lfs_master->lfs_fsbtodb) + dump_segment(fd, + segnum, seg_addr, lfs_master, do_allsb); + + (void)close(fd); + exit(0); +} + +/* + * We are reading all the blocks of an inode and dumping out the ifile table. + * This code could be tighter, but this is a first pass at getting the stuff + * printed out rather than making this code incredibly efficient. + */ +static void +dump_ifile(fd, lfsp, do_ientries) + int fd; + struct lfs *lfsp; + int do_ientries; +{ + IFILE *ipage; + struct dinode *dip, *dpage; + daddr_t addr, *addrp, *dindir, *iaddrp, *indir; + int block_limit, i, inum, j, nblocks, nsupb, psize; + + psize = lfsp->lfs_bsize; + addr = lfsp->lfs_idaddr; + + if (!(dpage = malloc(psize))) + err("%s", strerror(errno)); + get(fd, addr << daddr_shift, dpage, psize); + + for (dip = dpage + INOPB(lfsp) - 1; dip >= dpage; --dip) + if (dip->di_inumber == LFS_IFILE_INUM) + break; + + if (dip < dpage) + err("unable to locate ifile inode"); + + (void)printf("\nIFILE inode\n"); + dump_dinode(dip); + + (void)printf("\nIFILE contents\n"); + nblocks = dip->di_size >> lfsp->lfs_bshift; + block_limit = MIN(nblocks, NDADDR); + + /* Get the direct block */ + if ((ipage = malloc(psize)) == NULL) + err("%s", strerror(errno)); + for (inum = 0, addrp = dip->di_db, i = 0; i < block_limit; + i++, addrp++) { + get(fd, *addrp << daddr_shift, ipage, psize); + if (i < lfsp->lfs_cleansz) { + dump_cleaner_info(lfsp, ipage); + print_suheader; + continue; + } + + if (i < (lfsp->lfs_segtabsz + lfsp->lfs_cleansz)) { + inum = dump_ipage_segusage(lfsp, inum, ipage, + lfsp->lfs_sepb); + if (!inum) + if(!do_ientries) + goto e0; + else + print_iheader; + } else + inum = dump_ipage_ifile(inum, ipage, lfsp->lfs_ifpb); + + } + + if (nblocks <= NDADDR) + goto e0; + + /* Dump out blocks off of single indirect block */ + if (!(indir = malloc(psize))) + err("%s", strerror(errno)); + get(fd, dip->di_ib[0] << daddr_shift, indir, psize); + block_limit = MIN(i + lfsp->lfs_nindir, nblocks); + for (addrp = indir; i < block_limit; i++, addrp++) { + if (*addrp == LFS_UNUSED_DADDR) + break; + get(fd, *addrp << daddr_shift,ipage, psize); + if (i < lfsp->lfs_cleansz) { + dump_cleaner_info(lfsp, ipage); + continue; + } else + i -= lfsp->lfs_cleansz; + + if (i < lfsp->lfs_segtabsz) { + inum = dump_ipage_segusage(lfsp, inum, ipage, + lfsp->lfs_sepb); + if (!inum) + if(!do_ientries) + goto e1; + else + print_iheader; + } else + inum = dump_ipage_ifile(inum, ipage, lfsp->lfs_ifpb); + } + + if (nblocks <= lfsp->lfs_nindir * lfsp->lfs_ifpb) + goto e1; + + /* Get the double indirect block */ + if (!(dindir = malloc(psize))) + err("%s", strerror(errno)); + get(fd, dip->di_ib[1] << daddr_shift, dindir, psize); + for (iaddrp = dindir, j = 0; j < lfsp->lfs_nindir; j++, iaddrp++) { + if (*iaddrp == LFS_UNUSED_DADDR) + break; + get(fd, *iaddrp << daddr_shift, indir, psize); + block_limit = MIN(i + lfsp->lfs_nindir, nblocks); + for (addrp = indir; i < block_limit; i++, addrp++) { + if (*addrp == LFS_UNUSED_DADDR) + break; + get(fd, *addrp << daddr_shift, ipage, psize); + if (i < lfsp->lfs_cleansz) { + dump_cleaner_info(lfsp, ipage); + continue; + } else + i -= lfsp->lfs_cleansz; + + if (i < lfsp->lfs_segtabsz) { + inum = dump_ipage_segusage(lfsp, + inum, ipage, lfsp->lfs_sepb); + if (!inum) + if(!do_ientries) + goto e2; + else + print_iheader; + } else + inum = dump_ipage_ifile(inum, + ipage, lfsp->lfs_ifpb); + } + } +e2: free(dindir); +e1: free(indir); +e0: free(dpage); + free(ipage); +} + +static int +dump_ipage_ifile(i, pp, tot) + int i; + IFILE *pp; + int tot; +{ + IFILE *ip; + int cnt, max; + + max = i + tot; + + for (ip = pp, cnt = i; cnt < max; cnt++, ip++) + print_ientry(cnt, ip); + return (max); +} + +static int +dump_ipage_segusage(lfsp, i, pp, tot) + struct lfs *lfsp; + int i; + IFILE *pp; + int tot; +{ + SEGUSE *sp; + int cnt, max; + + max = i + tot; + for (sp = (SEGUSE *)pp, cnt = i; + cnt < lfsp->lfs_nseg && cnt < max; cnt++, sp++) + print_suentry(cnt, sp); + if (max >= lfsp->lfs_nseg) + return (0); + else + return (max); +} + +static void +dump_dinode(dip) + struct dinode *dip; +{ + int i; + + (void)printf("%s%d\t%s%d\t%s%d\t%s%d\t%s%d\n", + "mode ", dip->di_mode, + "nlink ", dip->di_nlink, + "uid ", dip->di_uid, + "gid ", dip->di_gid, + "size ", dip->di_size); + (void)printf("%s%s%s%s%s%s", + "atime ", ctime(&dip->di_atime.ts_sec), + "mtime ", ctime(&dip->di_mtime.ts_sec), + "ctime ", ctime(&dip->di_ctime.ts_sec)); + (void)printf("inum %d\n", dip->di_inumber); + (void)printf("Direct Addresses\n"); + for (i = 0; i < NDADDR; i++) { + (void)printf("\t0x%X", dip->di_db[i]); + if ((i % 6) == 5) + (void)printf("\n"); + } + for (i = 0; i < NIADDR; i++) + (void)printf("\t0x%X", dip->di_ib[i]); + (void)printf("\n"); +} + +static int +dump_sum(fd, lfsp, sp, segnum, addr) + struct lfs *lfsp; + SEGSUM *sp; + int fd, segnum; + daddr_t addr; +{ + FINFO *fp; + long *dp; + int i, j; + int ck; + int numblocks; + struct dinode *inop; + + if (sp->ss_sumsum != (ck = cksum(&sp->ss_datasum, + LFS_SUMMARY_SIZE - sizeof(sp->ss_sumsum)))) { + (void)printf("dumplfs: %s %d address 0x%lx\n", + "corrupt summary block; segment", segnum, addr); + return(0); + } + + (void)printf("Segment Summary Info at 0x%lx\n", addr); + (void)printf(" %s0x%X\t%s%d\t%s%d\n %s0x%X\t%s0x%X", + "next ", sp->ss_next, + "nfinfo ", sp->ss_nfinfo, + "ninos ", sp->ss_ninos, + "sumsum ", sp->ss_sumsum, + "datasum ", sp->ss_datasum ); + (void)printf("\tcreate %s", ctime((time_t *)&sp->ss_create)); + + numblocks = (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp); + + /* Dump out inode disk addresses */ + dp = (daddr_t *)sp; + dp += LFS_SUMMARY_SIZE / sizeof(daddr_t); + inop = malloc(1 << lfsp->lfs_bshift); + printf(" Inode addresses:"); + for (dp--, i = 0; i < sp->ss_ninos; dp--) { + printf("\t0x%X {", *dp); + get(fd, *dp << (lfsp->lfs_bshift - lfsp->lfs_fsbtodb), inop, + (1 << lfsp->lfs_bshift)); + for (j = 0; i < sp->ss_ninos && j < INOPB(lfsp); j++, i++) { + if (j > 0) + (void)printf(", "); + (void)printf("%d", inop[j].di_inumber); + } + (void)printf("}"); + if (((i/INOPB(lfsp)) % 4) == 3) + (void)printf("\n"); + } + free(inop); + + printf("\n"); + for (fp = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo; i++) { + numblocks += fp->fi_nblocks; + (void)printf(" FINFO for inode: %d version %d nblocks %d\n", + fp->fi_ino, fp->fi_version, fp->fi_nblocks); + dp = &(fp->fi_blocks[0]); + for (j = 0; j < fp->fi_nblocks; j++, dp++) { + (void)printf("\t%d", *dp); + if ((j % 8) == 7) + (void)printf("\n"); + } + if ((j % 8) != 0) + (void)printf("\n"); + fp = (FINFO *)dp; + } + return (numblocks); +} + +static void +dump_segment(fd, segnum, addr, lfsp, dump_sb) + int fd, segnum; + daddr_t addr; + struct lfs *lfsp; + int dump_sb; +{ + struct lfs lfs_sb, *sbp; + SEGSUM *sump; + char sumblock[LFS_SUMMARY_SIZE]; + int did_one, nblocks, sb; + off_t sum_offset, super_off; + + (void)printf("\nSEGMENT %d (Disk Address 0x%X)\n", + addr >> (lfsp->lfs_segshift - daddr_shift), addr); + sum_offset = (addr << (lfsp->lfs_bshift - lfsp->lfs_fsbtodb)); + + sb = 0; + did_one = 0; + do { + get(fd, sum_offset, sumblock, LFS_SUMMARY_SIZE); + sump = (SEGSUM *)sumblock; + if (sump->ss_sumsum != cksum (&sump->ss_datasum, + LFS_SUMMARY_SIZE - sizeof(sump->ss_sumsum))) { + sbp = (struct lfs *)sump; + if (sb = (sbp->lfs_magic == LFS_MAGIC)) { + super_off = sum_offset; + sum_offset += LFS_SBPAD; + } else if (did_one) + break; + else { + printf("Segment at 0x%X corrupt\n", addr); + break; + } + } else { + nblocks = dump_sum(fd, lfsp, sump, segnum, sum_offset >> + (lfsp->lfs_bshift - lfsp->lfs_fsbtodb)); + if (nblocks) + sum_offset += LFS_SUMMARY_SIZE + + (nblocks << lfsp->lfs_bshift); + else + sum_offset = 0; + did_one = 1; + } + } while (sum_offset); + + if (dump_sb && sb) { + get(fd, super_off, &lfs_sb, sizeof(struct lfs)); + dump_super(&lfs_sb); + } + return; +} + +static void +dump_super(lfsp) + struct lfs *lfsp; +{ + int i; + + (void)printf("%s0x%X\t%s0x%X\t%s%d\t%s%d\n", + "magic ", lfsp->lfs_magic, + "version ", lfsp->lfs_version, + "size ", lfsp->lfs_size, + "ssize ", lfsp->lfs_ssize); + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "dsize ", lfsp->lfs_dsize, + "bsize ", lfsp->lfs_bsize, + "fsize ", lfsp->lfs_fsize, + "frag ", lfsp->lfs_frag); + + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "minfree ", lfsp->lfs_minfree, + "inopb ", lfsp->lfs_inopb, + "ifpb ", lfsp->lfs_ifpb, + "nindir ", lfsp->lfs_nindir); + + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "nseg ", lfsp->lfs_nseg, + "nspf ", lfsp->lfs_nspf, + "cleansz ", lfsp->lfs_cleansz, + "segtabsz ", lfsp->lfs_segtabsz); + + (void)printf("%s0x%X\t%s%d\t%s0x%X\t%s%d\n", + "segmask ", lfsp->lfs_segmask, + "segshift ", lfsp->lfs_segshift, + "bmask ", lfsp->lfs_bmask, + "bshift ", lfsp->lfs_bshift); + + (void)printf("%s0x%X\t\t%s%d\t%s0x%X\t%s%d\n", + "ffmask ", lfsp->lfs_ffmask, + "ffshift ", lfsp->lfs_ffshift, + "fbmask ", lfsp->lfs_fbmask, + "fbshift ", lfsp->lfs_fbshift); + + (void)printf("%s%d\t%s%d\t%s0x%X\t%s0x%qx\n", + "sushift ", lfsp->lfs_sushift, + "fsbtodb ", lfsp->lfs_fsbtodb, + "cksum ", lfsp->lfs_cksum, + "maxfilesize ", lfsp->lfs_maxfilesize); + + (void)printf("Superblock disk addresses:\t"); + for (i = 0; i < LFS_MAXNUMSB; i++) { + (void)printf(" 0x%X", lfsp->lfs_sboffs[i]); + if ( i == (LFS_MAXNUMSB >> 1)) + (void)printf("\n\t\t\t\t"); + } + (void)printf("\n"); + + (void)printf("Checkpoint Info\n"); + (void)printf("%s%d\t%s0x%X\t%s%d\n", + "free ", lfsp->lfs_free, + "idaddr ", lfsp->lfs_idaddr, + "ifile ", lfsp->lfs_ifile); + (void)printf("%s%d\t%s%d\t%s%d\n", + "bfree ", lfsp->lfs_bfree, + "avail ", lfsp->lfs_avail, + "uinodes ", lfsp->lfs_uinodes); + (void)printf("%s%d\t%s0x%X\t%s0x%X\n%s0x%X\t%s0x%X\t", + "nfiles ", lfsp->lfs_nfiles, + "lastseg ", lfsp->lfs_lastseg, + "nextseg ", lfsp->lfs_nextseg, + "curseg ", lfsp->lfs_curseg, + "offset ", lfsp->lfs_offset); + (void)printf("tstamp %s", ctime((time_t *)&lfsp->lfs_tstamp)); + (void)printf("\nIn-Memory Information\n"); + (void)printf("%s%d\t%s0x%X\t%s%d%s%d\t%s%d\n", + "seglock ", lfsp->lfs_seglock, + "iocount ", lfsp->lfs_iocount, + "writer ", lfsp->lfs_writer, + "dirops ", lfsp->lfs_dirops, + "doifile ", lfsp->lfs_doifile); + (void)printf("%s%d\t%s%d\t%s0x%X\t%s%d\n", + "nactive ", lfsp->lfs_nactive, + "fmod ", lfsp->lfs_fmod, + "clean ", lfsp->lfs_clean, + "ronly ", lfsp->lfs_ronly); +} + +static void +addseg(arg) + char *arg; +{ + SEGLIST *p; + + if ((p = malloc(sizeof(SEGLIST))) == NULL) + err("%s", strerror(errno)); + p->next = seglist; + p->num = atoi(arg); + seglist = p; +} + +static void +dump_cleaner_info(lfsp, ipage) + struct lfs *lfsp; + void *ipage; +{ + CLEANERINFO *cip; + + cip = (CLEANERINFO *)ipage; + (void)printf("segments clean\t%d\tsegments dirty\t%d\n\n", + cip->clean, cip->dirty); +} + +static void +usage() +{ + (void)fprintf(stderr, "usage: dumplfs [-ai] [-s segnum] file\n"); + exit(1); +} diff --git a/sbin/dumplfs/extern.h b/sbin/dumplfs/extern.h new file mode 100644 index 0000000..82e87b2 --- /dev/null +++ b/sbin/dumplfs/extern.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/5/93 + */ + +void err __P((const char *, ...)); +void get __P((int, off_t, void *, size_t)); + +extern char *special; diff --git a/sbin/dumplfs/misc.c b/sbin/dumplfs/misc.c new file mode 100644 index 0000000..861d0fd --- /dev/null +++ b/sbin/dumplfs/misc.c @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "extern.h" + +void +get(fd, off, p, len) + int fd; + off_t off; + void *p; + size_t len; +{ + int rbytes; + + if (lseek(fd, off, SEEK_SET) < 0) + err("%s: %s", special, strerror(errno)); + if ((rbytes = read(fd, p, len)) < 0) + err("%s: %s", special, strerror(errno)); + if (rbytes != len) + err("%s: short read (%d, not %d)", special, rbytes, len); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "dumplfs: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/sbin/fastboot/Makefile b/sbin/fastboot/Makefile new file mode 100644 index 0000000..9504f79 --- /dev/null +++ b/sbin/fastboot/Makefile @@ -0,0 +1,12 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +MAN8= fastboot.0 +MLINKS= fastboot.8 fasthalt.8 + +beforeinstall: + install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + ${.CURDIR}/fastboot.sh ${DESTDIR}${BINDIR}/fastboot + install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + ${.CURDIR}/fasthalt.sh ${DESTDIR}${BINDIR}/fasthalt + +.include <bsd.prog.mk> diff --git a/sbin/fastboot/fastboot.8 b/sbin/fastboot/fastboot.8 new file mode 100644 index 0000000..2f6ac829 --- /dev/null +++ b/sbin/fastboot/fastboot.8 @@ -0,0 +1,69 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)fastboot.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt FASTBOOT 8 +.Os BSD 4.2 +.Sh NAME +.Nm fastboot , +.Nm fasthalt +.Nd "reboot/halt the system without checking the disks" +.Sh SYNOPSIS +.Nm fastboot +.Op Ar boot-options +.Nm fasthalt +.Op Ar halt-options +.Sh DESCRIPTION +.Nm Fastboot +and +.Nm fasthalt +are shell scripts which reboot and halt the system without +checking the file systems. This is done by creating a +file +.Pa /fastboot , +then invoking the +.Xr reboot +program. The system startup script, +.Pa /etc/rc , +looks for this file and, if present, skips the normal +invocation of +.Xr fsck 8 . +.Sh SEE ALSO +.Xr halt 8 , +.Xr reboot 8 , +.Xr rc 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/fastboot/fastboot.sh b/sbin/fastboot/fastboot.sh new file mode 100644 index 0000000..852ec4a --- /dev/null +++ b/sbin/fastboot/fastboot.sh @@ -0,0 +1,38 @@ +#!/bin/sh - +# +# Copyright (c) 1985, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)fastboot.sh 8.1 (Berkeley) 6/5/93 +# + +cp /dev/null /fastboot +/sbin/reboot $* diff --git a/sbin/fastboot/fasthalt.sh b/sbin/fastboot/fasthalt.sh new file mode 100644 index 0000000..c8e381f --- /dev/null +++ b/sbin/fastboot/fasthalt.sh @@ -0,0 +1,38 @@ +#!/bin/sh - +# +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)fasthalt.sh 8.1 (Berkeley) 6/5/93 +# + +cp /dev/null /fastboot +/sbin/halt $* diff --git a/sbin/fsck/Makefile b/sbin/fsck/Makefile new file mode 100644 index 0000000..224f6b2 --- /dev/null +++ b/sbin/fsck/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= fsck +MAN8= fsck.0 +SRCS= dir.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \ + pass5.c preen.c setup.c utilities.c ffs_subr.c ffs_tables.c +.PATH: ${.CURDIR}/../../sys/ufs/ffs + +.include <bsd.prog.mk> diff --git a/sbin/fsck/SMM.doc/0.t b/sbin/fsck/SMM.doc/0.t new file mode 100644 index 0000000..9de391e --- /dev/null +++ b/sbin/fsck/SMM.doc/0.t @@ -0,0 +1,150 @@ +.\" Copyright (c) 1986, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)0.t 8.1 (Berkeley) 6/8/93 +.\" +.if n .ND +.TL +Fsck \- The UNIX\(dg File System Check Program +.EH 'SMM:3-%''The \s-2UNIX\s+2 File System Check Program' +.OH 'The \s-2UNIX\s+2 File System Check Program''SMM:3-%' +.AU +Marshall Kirk McKusick +.AI +Computer Systems Research Group +Computer Science Division +Department of Electrical Engineering and Computer Science +University of California, Berkeley +Berkeley, CA 94720 +.AU +T. J. Kowalski +.AI +Bell Laboratories +Murray Hill, New Jersey 07974 +.AB +.FS +\(dgUNIX is a trademark of Bell Laboratories. +.FE +.FS +This work was done under grants from +the National Science Foundation under grant MCS80-05144, +and the Defense Advance Research Projects Agency (DoD) under +Arpa Order No. 4031 monitored by Naval Electronic System Command under +Contract No. N00039-82-C-0235. +.FE +This document reflects the use of +.I fsck +with the 4.2BSD and 4.3BSD file system organization. This +is a revision of the +original paper written by +T. J. Kowalski. +.PP +File System Check Program (\fIfsck\fR) +is an interactive file system check and repair program. +.I Fsck +uses the redundant structural information in the +UNIX file system to perform several consistency checks. +If an inconsistency is detected, it is reported +to the operator, who may elect to fix or ignore +each inconsistency. +These inconsistencies result from the permanent interruption +of the file system updates, which are performed every +time a file is modified. +Unless there has been a hardware failure, +.I fsck +is able to repair corrupted file systems +using procedures based upon the order in which UNIX honors +these file system update requests. +.PP +The purpose of this document is to describe the normal updating +of the file system, +to discuss the possible causes of file system corruption, +and to present the corrective actions implemented +by +.I fsck. +Both the program and the interaction between the +program and the operator are described. +.sp 2 +.LP +Revised July 16, 1985 +.AE +.LP +.bp +.ce +.B "TABLE OF CONTENTS" +.LP +.sp 1 +.nf +.B "1. Introduction" +.LP +.sp .5v +.nf +.B "2. Overview of the file system +2.1. Superblock +2.2. Summary Information +2.3. Cylinder groups +2.4. Fragments +2.5. Updates to the file system +.LP +.sp .5v +.nf +.B "3. Fixing corrupted file systems +3.1. Detecting and correcting corruption +3.2. Super block checking +3.3. Free block checking +3.4. Checking the inode state +3.5. Inode links +3.6. Inode data size +3.7. Checking the data associated with an inode +3.8. File system connectivity +.LP +.sp .5v +.nf +.B Acknowledgements +.LP +.sp .5v +.nf +.B References +.LP +.sp .5v +.nf +.B "4. Appendix A +4.1. Conventions +4.2. Initialization +4.3. Phase 1 - Check Blocks and Sizes +4.4. Phase 1b - Rescan for more Dups +4.5. Phase 2 - Check Pathnames +4.6. Phase 3 - Check Connectivity +4.7. Phase 4 - Check Reference Counts +4.8. Phase 5 - Check Cyl groups +4.9. Cleanup +.ds RH Introduction +.bp diff --git a/sbin/fsck/SMM.doc/1.t b/sbin/fsck/SMM.doc/1.t new file mode 100644 index 0000000..4d2f535 --- /dev/null +++ b/sbin/fsck/SMM.doc/1.t @@ -0,0 +1,83 @@ +.\" Copyright (c) 1982, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)1.t 8.1 (Berkeley) 6/5/93 +.\" +.ds RH Introduction +.NH +Introduction +.PP +This document reflects the use of +.I fsck +with the 4.2BSD and 4.3BSD file system organization. This +is a revision of the +original paper written by +T. J. Kowalski. +.PP +When a UNIX +operating system is brought up, a consistency +check of the file systems should always be performed. +This precautionary measure helps to insure +a reliable environment for file storage on disk. +If an inconsistency is discovered, +corrective action must be taken. +.I Fsck +runs in two modes. +Normally it is run non-interactively by the system after +a normal boot. +When running in this mode, +it will only make changes to the file system that are known +to always be correct. +If an unexpected inconsistency is found +.I fsck +will exit with a non-zero exit status, +leaving the system running single-user. +Typically the operator then runs +.I fsck +interactively. +When running in this mode, +each problem is listed followed by a suggested corrective action. +The operator must decide whether or not the suggested correction +should be made. +.PP +The purpose of this memo is to dispel the +mystique surrounding +file system inconsistencies. +It first describes the updating of the file system +(the calm before the storm) and +then describes file system corruption (the storm). +Finally, +the set of deterministic corrective actions +used by +.I fsck +(the Coast Guard +to the rescue) is presented. +.ds RH Overview of the File System diff --git a/sbin/fsck/SMM.doc/2.t b/sbin/fsck/SMM.doc/2.t new file mode 100644 index 0000000..7d00cea --- /dev/null +++ b/sbin/fsck/SMM.doc/2.t @@ -0,0 +1,265 @@ +.\" Copyright (c) 1982, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)2.t 8.1 (Berkeley) 6/5/93 +.\" +.ds RH Overview of the file system +.NH +Overview of the file system +.PP +The file system is discussed in detail in [Mckusick84]; +this section gives a brief overview. +.NH 2 +Superblock +.PP +A file system is described by its +.I "super-block" . +The super-block is built when the file system is created (\c +.I newfs (8)) +and never changes. +The super-block +contains the basic parameters of the file system, +such as the number of data blocks it contains +and a count of the maximum number of files. +Because the super-block contains critical data, +.I newfs +replicates it to protect against catastrophic loss. +The +.I "default super block" +always resides at a fixed offset from the beginning +of the file system's disk partition. +The +.I "redundant super blocks" +are not referenced unless a head crash +or other hard disk error causes the default super-block +to be unusable. +The redundant blocks are sprinkled throughout the disk partition. +.PP +Within the file system are files. +Certain files are distinguished as directories and contain collections +of pointers to files that may themselves be directories. +Every file has a descriptor associated with it called an +.I "inode". +The inode contains information describing ownership of the file, +time stamps indicating modification and access times for the file, +and an array of indices pointing to the data blocks for the file. +In this section, +we assume that the first 12 blocks +of the file are directly referenced by values stored +in the inode structure itself\(dg. +.FS +\(dgThe actual number may vary from system to system, but is usually in +the range 5-13. +.FE +The inode structure may also contain references to indirect blocks +containing further data block indices. +In a file system with a 4096 byte block size, a singly indirect +block contains 1024 further block addresses, +a doubly indirect block contains 1024 addresses of further single indirect +blocks, +and a triply indirect block contains 1024 addresses of further doubly indirect +blocks (the triple indirect block is never needed in practice). +.PP +In order to create files with up to +2\(ua32 bytes, +using only two levels of indirection, +the minimum size of a file system block is 4096 bytes. +The size of file system blocks can be any power of two +greater than or equal to 4096. +The block size of the file system is maintained in the super-block, +so it is possible for file systems of different block sizes +to be accessible simultaneously on the same system. +The block size must be decided when +.I newfs +creates the file system; +the block size cannot be subsequently +changed without rebuilding the file system. +.NH 2 +Summary information +.PP +Associated with the super block is non replicated +.I "summary information" . +The summary information changes +as the file system is modified. +The summary information contains +the number of blocks, fragments, inodes and directories in the file system. +.NH 2 +Cylinder groups +.PP +The file system partitions the disk into one or more areas called +.I "cylinder groups". +A cylinder group is comprised of one or more consecutive +cylinders on a disk. +Each cylinder group includes inode slots for files, a +.I "block map" +describing available blocks in the cylinder group, +and summary information describing the usage of data blocks +within the cylinder group. +A fixed number of inodes is allocated for each cylinder group +when the file system is created. +The current policy is to allocate one inode for each 2048 +bytes of disk space; +this is expected to be far more inodes than will ever be needed. +.PP +All the cylinder group bookkeeping information could be +placed at the beginning of each cylinder group. +However if this approach were used, +all the redundant information would be on the top platter. +A single hardware failure that destroyed the top platter +could cause the loss of all copies of the redundant super-blocks. +Thus the cylinder group bookkeeping information +begins at a floating offset from the beginning of the cylinder group. +The offset for +the +.I "i+1" st +cylinder group is about one track further +from the beginning of the cylinder group +than it was for the +.I "i" th +cylinder group. +In this way, +the redundant +information spirals down into the pack; +any single track, cylinder, +or platter can be lost without losing all copies of the super-blocks. +Except for the first cylinder group, +the space between the beginning of the cylinder group +and the beginning of the cylinder group information stores data. +.NH 2 +Fragments +.PP +To avoid waste in storing small files, +the file system space allocator divides a single +file system block into one or more +.I "fragments". +The fragmentation of the file system is specified +when the file system is created; +each file system block can be optionally broken into +2, 4, or 8 addressable fragments. +The lower bound on the size of these fragments is constrained +by the disk sector size; +typically 512 bytes is the lower bound on fragment size. +The block map associated with each cylinder group +records the space availability at the fragment level. +Aligned fragments are examined +to determine block availability. +.PP +On a file system with a block size of 4096 bytes +and a fragment size of 1024 bytes, +a file is represented by zero or more 4096 byte blocks of data, +and possibly a single fragmented block. +If a file system block must be fragmented to obtain +space for a small amount of data, +the remainder of the block is made available for allocation +to other files. +For example, +consider an 11000 byte file stored on +a 4096/1024 byte file system. +This file uses two full size blocks and a 3072 byte fragment. +If no fragments with at least 3072 bytes +are available when the file is created, +a full size block is split yielding the necessary 3072 byte +fragment and an unused 1024 byte fragment. +This remaining fragment can be allocated to another file, as needed. +.NH 2 +Updates to the file system +.PP +Every working day hundreds of files +are created, modified, and removed. +Every time a file is modified, +the operating system performs a +series of file system updates. +These updates, when written on disk, yield a consistent file system. +The file system stages +all modifications of critical information; +modification can +either be completed or cleanly backed out after a crash. +Knowing the information that is first written to the file system, +deterministic procedures can be developed to +repair a corrupted file system. +To understand this process, +the order that the update +requests were being honored must first be understood. +.PP +When a user program does an operation to change the file system, +such as a +.I write , +the data to be written is copied into an internal +.I "in-core" +buffer in the kernel. +Normally, the disk update is handled asynchronously; +the user process is allowed to proceed even though +the data has not yet been written to the disk. +The data, +along with the inode information reflecting the change, +is eventually written out to disk. +The real disk write may not happen until long after the +.I write +system call has returned. +Thus at any given time, the file system, +as it resides on the disk, +lags the state of the file system represented by the in-core information. +.PP +The disk information is updated to reflect the in-core information +when the buffer is required for another use, +when a +.I sync (2) +is done (at 30 second intervals) by +.I "/etc/update" "(8)," +or by manual operator intervention with the +.I sync (8) +command. +If the system is halted without writing out the in-core information, +the file system on the disk will be in an inconsistent state. +.PP +If all updates are done asynchronously, several serious +inconsistencies can arise. +One inconsistency is that a block may be claimed by two inodes. +Such an inconsistency can occur when the system is halted before +the pointer to the block in the old inode has been cleared +in the copy of the old inode on the disk, +and after the pointer to the block in the new inode has been written out +to the copy of the new inode on the disk. +Here, +there is no deterministic method for deciding +which inode should really claim the block. +A similar problem can arise with a multiply claimed inode. +.PP +The problem with asynchronous inode updates +can be avoided by doing all inode deallocations synchronously. +Consequently, +inodes and indirect blocks are written to the disk synchronously +(\fIi.e.\fP the process blocks until the information is +really written to disk) +when they are being deallocated. +Similarly inodes are kept consistent by synchronously +deleting, adding, or changing directory entries. +.ds RH Fixing corrupted file systems diff --git a/sbin/fsck/SMM.doc/3.t b/sbin/fsck/SMM.doc/3.t new file mode 100644 index 0000000..07b5431 --- /dev/null +++ b/sbin/fsck/SMM.doc/3.t @@ -0,0 +1,439 @@ +.\" Copyright (c) 1982, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)3.t 8.1 (Berkeley) 6/5/93 +.\" +.ds RH Fixing corrupted file systems +.NH +Fixing corrupted file systems +.PP +A file system +can become corrupted in several ways. +The most common of these ways are +improper shutdown procedures +and hardware failures. +.PP +File systems may become corrupted during an +.I "unclean halt" . +This happens when proper shutdown +procedures are not observed, +physically write-protecting a mounted file system, +or a mounted file system is taken off-line. +The most common operator procedural failure is forgetting to +.I sync +the system before halting the CPU. +.PP +File systems may become further corrupted if proper startup +procedures are not observed, e.g., +not checking a file system for inconsistencies, +and not repairing inconsistencies. +Allowing a corrupted file system to be used (and, thus, to be modified +further) can be disastrous. +.PP +Any piece of hardware can fail at any time. +Failures +can be as subtle as a bad block +on a disk pack, or as blatant as a non-functional disk-controller. +.NH 2 +Detecting and correcting corruption +.PP +Normally +.I fsck +is run non-interactively. +In this mode it will only fix +corruptions that are expected to occur from an unclean halt. +These actions are a proper subset of the actions that +.I fsck +will take when it is running interactively. +Throughout this paper we assume that +.I fsck +is being run interactively, +and all possible errors can be encountered. +When an inconsistency is discovered in this mode, +.I fsck +reports the inconsistency for the operator to +chose a corrective action. +.PP +A quiescent\(dd +.FS +\(dd I.e., unmounted and not being written on. +.FE +file system may be checked for structural integrity +by performing consistency checks on the +redundant data intrinsic to a file system. +The redundant data is either read from +the file system, +or computed from other known values. +The file system +.B must +be in a quiescent state when +.I fsck +is run, +since +.I fsck +is a multi-pass program. +.PP +In the following sections, +we discuss methods to discover inconsistencies +and possible corrective actions +for the cylinder group blocks, the inodes, the indirect blocks, and +the data blocks containing directory entries. +.NH 2 +Super-block checking +.PP +The most commonly corrupted item in a file system +is the summary information +associated with the super-block. +The summary information is prone to corruption +because it is modified with every change to the file +system's blocks or inodes, +and is usually corrupted +after an unclean halt. +.PP +The super-block is checked for inconsistencies +involving file-system size, number of inodes, +free-block count, and the free-inode count. +The file-system size must be larger than the +number of blocks used by the super-block +and the number of blocks used by the list of inodes. +The file-system size and layout information +are the most critical pieces of information for +.I fsck . +While there is no way to actually check these sizes, +since they are statically determined by +.I newfs , +.I fsck +can check that these sizes are within reasonable bounds. +All other file system checks require that these sizes be correct. +If +.I fsck +detects corruption in the static parameters of the default super-block, +.I fsck +requests the operator to specify the location of an alternate super-block. +.NH 2 +Free block checking +.PP +.I Fsck +checks that all the blocks +marked as free in the cylinder group block maps +are not claimed by any files. +When all the blocks have been initially accounted for, +.I fsck +checks that +the number of free blocks +plus the number of blocks claimed by the inodes +equals the total number of blocks in the file system. +.PP +If anything is wrong with the block allocation maps, +.I fsck +will rebuild them, +based on the list it has computed of allocated blocks. +.PP +The summary information associated with the super-block +counts the total number of free blocks within the file system. +.I Fsck +compares this count to the +number of free blocks it found within the file system. +If the two counts do not agree, then +.I fsck +replaces the incorrect count in the summary information +by the actual free-block count. +.PP +The summary information +counts the total number of free inodes within the file system. +.I Fsck +compares this count to the number +of free inodes it found within the file system. +If the two counts do not agree, then +.I fsck +replaces the incorrect count in the +summary information by the actual free-inode count. +.NH 2 +Checking the inode state +.PP +An individual inode is not as likely to be corrupted as +the allocation information. +However, because of the great number of active inodes, +a few of the inodes are usually corrupted. +.PP +The list of inodes in the file system +is checked sequentially starting with inode 2 +(inode 0 marks unused inodes; +inode 1 is saved for future generations) +and progressing through the last inode in the file system. +The state of each inode is checked for +inconsistencies involving format and type, +link count, +duplicate blocks, +bad blocks, +and inode size. +.PP +Each inode contains a mode word. +This mode word describes the type and state of the inode. +Inodes must be one of six types: +regular inode, directory inode, symbolic link inode, +special block inode, special character inode, or socket inode. +Inodes may be found in one of three allocation states: +unallocated, allocated, and neither unallocated nor allocated. +This last state suggests an incorrectly formated inode. +An inode can get in this state if +bad data is written into the inode list. +The only possible corrective action is for +.I fsck +is to clear the inode. +.NH 2 +Inode links +.PP +Each inode counts the +total number of directory entries +linked to the inode. +.I Fsck +verifies the link count of each inode +by starting at the root of the file system, +and descending through the directory structure. +The actual link count for each inode +is calculated during the descent. +.PP +If the stored link count is non-zero and the actual +link count is zero, +then no directory entry appears for the inode. +If this happens, +.I fsck +will place the disconnected file in the +.I lost+found +directory. +If the stored and actual link counts are non-zero and unequal, +a directory entry may have been added or removed without the inode being +updated. +If this happens, +.I fsck +replaces the incorrect stored link count by the actual link count. +.PP +Each inode contains a list, +or pointers to +lists (indirect blocks), +of all the blocks claimed by the inode. +Since indirect blocks are owned by an inode, +inconsistencies in indirect blocks directly +affect the inode that owns it. +.PP +.I Fsck +compares each block number claimed by an inode +against a list of already allocated blocks. +If another inode already claims a block number, +then the block number is added to a list of +.I "duplicate blocks" . +Otherwise, the list of allocated blocks +is updated to include the block number. +.PP +If there are any duplicate blocks, +.I fsck +will perform a partial second +pass over the inode list +to find the inode of the duplicated block. +The second pass is needed, +since without examining the files associated with +these inodes for correct content, +not enough information is available +to determine which inode is corrupted and should be cleared. +If this condition does arise +(only hardware failure will cause it), +then the inode with the earliest +modify time is usually incorrect, +and should be cleared. +If this happens, +.I fsck +prompts the operator to clear both inodes. +The operator must decide which one should be kept +and which one should be cleared. +.PP +.I Fsck +checks the range of each block number claimed by an inode. +If the block number is +lower than the first data block in the file system, +or greater than the last data block, +then the block number is a +.I "bad block number" . +Many bad blocks in an inode are usually caused by +an indirect block that was not written to the file system, +a condition which can only occur if there has been a hardware failure. +If an inode contains bad block numbers, +.I fsck +prompts the operator to clear it. +.NH 2 +Inode data size +.PP +Each inode contains a count of the number of data blocks +that it contains. +The number of actual data blocks +is the sum of the allocated data blocks +and the indirect blocks. +.I Fsck +computes the actual number of data blocks +and compares that block count against +the actual number of blocks the inode claims. +If an inode contains an incorrect count +.I fsck +prompts the operator to fix it. +.PP +Each inode contains a thirty-two bit size field. +The size is the number of data bytes +in the file associated with the inode. +The consistency of the byte size field is roughly checked +by computing from the size field the maximum number of blocks +that should be associated with the inode, +and comparing that expected block count against +the actual number of blocks the inode claims. +.NH 2 +Checking the data associated with an inode +.PP +An inode can directly or indirectly +reference three kinds of data blocks. +All referenced blocks must be the same kind. +The three types of data blocks are: +plain data blocks, symbolic link data blocks, and directory data blocks. +Plain data blocks +contain the information stored in a file; +symbolic link data blocks +contain the path name stored in a link. +Directory data blocks contain directory entries. +.I Fsck +can only check the validity of directory data blocks. +.PP +Each directory data block is checked for +several types of inconsistencies. +These inconsistencies include +directory inode numbers pointing to unallocated inodes, +directory inode numbers that are greater than +the number of inodes in the file system, +incorrect directory inode numbers for ``\fB.\fP'' and ``\fB..\fP'', +and directories that are not attached to the file system. +If the inode number in a directory data block +references an unallocated inode, +then +.I fsck +will remove that directory entry. +Again, +this condition can only arise when there has been a hardware failure. +.PP +If a directory entry inode number references +outside the inode list, then +.I fsck +will remove that directory entry. +This condition occurs if bad data is written into a directory data block. +.PP +The directory inode number entry for ``\fB.\fP'' +must be the first entry in the directory data block. +The inode number for ``\fB.\fP'' +must reference itself; +e.g., it must equal the inode number +for the directory data block. +The directory inode number entry +for ``\fB..\fP'' must be +the second entry in the directory data block. +Its value must equal the inode number for the +parent of the directory entry +(or the inode number of the directory +data block if the directory is the +root directory). +If the directory inode numbers are +incorrect, +.I fsck +will replace them with the correct values. +If there are multiple hard links to a directory, +the first one encountered is considered the real parent +to which ``\fB..\fP'' should point; +\fIfsck\fP recommends deletion for the subsequently discovered names. +.NH 2 +File system connectivity +.PP +.I Fsck +checks the general connectivity of the file system. +If directories are not linked into the file system, then +.I fsck +links the directory back into the file system in the +.I lost+found +directory. +This condition only occurs when there has been a hardware failure. +.ds RH "References" +.SH +\s+2Acknowledgements\s0 +.PP +I thank Bill Joy, Sam Leffler, Robert Elz and Dennis Ritchie +for their suggestions and help in implementing the new file system. +Thanks also to Robert Henry for his editorial input to +get this document together. +Finally we thank our sponsors, +the National Science Foundation under grant MCS80-05144, +and the Defense Advance Research Projects Agency (DoD) under +Arpa Order No. 4031 monitored by Naval Electronic System Command under +Contract No. N00039-82-C-0235. (Kirk McKusick, July 1983) +.PP +I would like to thank Larry A. Wehr for advice that lead +to the first version of +.I fsck +and Rick B. Brandt for adapting +.I fsck +to +UNIX/TS. (T. Kowalski, July 1979) +.sp 2 +.SH +\s+2References\s0 +.LP +.IP [Dolotta78] 20 +Dolotta, T. A., and Olsson, S. B. eds., +.I "UNIX User's Manual, Edition 1.1\^" , +January 1978. +.IP [Joy83] 20 +Joy, W., Cooper, E., Fabry, R., Leffler, S., McKusick, M., and Mosher, D. +4.2BSD System Manual, +.I "University of California at Berkeley" , +.I "Computer Systems Research Group Technical Report" +#4, 1982. +.IP [McKusick84] 20 +McKusick, M., Joy, W., Leffler, S., and Fabry, R. +A Fast File System for UNIX, +\fIACM Transactions on Computer Systems 2\fP, 3. +pp. 181-197, August 1984. +.IP [Ritchie78] 20 +Ritchie, D. M., and Thompson, K., +The UNIX Time-Sharing System, +.I "The Bell System Technical Journal" +.B 57 , +6 (July-August 1978, Part 2), pp. 1905-29. +.IP [Thompson78] 20 +Thompson, K., +UNIX Implementation, +.I "The Bell System Technical Journal\^" +.B 57 , +6 (July-August 1978, Part 2), pp. 1931-46. +.ds RH Appendix A \- Fsck Error Conditions +.bp diff --git a/sbin/fsck/SMM.doc/4.t b/sbin/fsck/SMM.doc/4.t new file mode 100644 index 0000000..5ea8179 --- /dev/null +++ b/sbin/fsck/SMM.doc/4.t @@ -0,0 +1,1424 @@ +.\" Copyright (c) 1982, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)4.t 8.1 (Berkeley) 6/5/93 +.\" +.ds RH Appendix A \- Fsck Error Conditions +.NH +Appendix A \- Fsck Error Conditions +.NH 2 +Conventions +.PP +.I Fsck +is +a multi-pass file system check program. +Each file system pass invokes a different Phase of the +.I fsck +program. +After the initial setup, +.I fsck +performs successive Phases over each file system, +checking blocks and sizes, +path-names, +connectivity, +reference counts, +and the map of free blocks, +(possibly rebuilding it), +and performs some cleanup. +.LP +Normally +.I fsck +is run non-interactively to +.I preen +the file systems after an unclean halt. +While preen'ing a file system, +it will only fix corruptions that are expected +to occur from an unclean halt. +These actions are a proper subset of the actions that +.I fsck +will take when it is running interactively. +Throughout this appendix many errors have several options +that the operator can take. +When an inconsistency is detected, +.I fsck +reports the error condition to the operator. +If a response is required, +.I fsck +prints a prompt message and +waits for a response. +When preen'ing most errors are fatal. +For those that are expected, +the response taken is noted. +This appendix explains the meaning of each error condition, +the possible responses, and the related error conditions. +.LP +The error conditions are organized by the +.I Phase +of the +.I fsck +program in which they can occur. +The error conditions that may occur +in more than one Phase +will be discussed in initialization. +.NH 2 +Initialization +.PP +Before a file system check can be performed, certain +tables have to be set up and certain files opened. +This section concerns itself with the opening of files and +the initialization of tables. +This section lists error conditions resulting from +command line options, +memory requests, +opening of files, +status of files, +file system size checks, +and creation of the scratch file. +All the initialization errors are fatal +when the file system is being preen'ed. +.sp +.LP +.B "\fIC\fP option?" +.br +\fIC\fP is not a legal option to +.I fsck ; +legal options are \-b, \-c, \-y, \-n, and \-p. +.I Fsck +terminates on this error condition. +See the +.I fsck (8) +manual entry for further detail. +.sp +.LP +.B "cannot alloc NNN bytes for blockmap" +.br +.B "cannot alloc NNN bytes for freemap" +.br +.B "cannot alloc NNN bytes for statemap" +.br +.B "cannot alloc NNN bytes for lncntp" +.br +.I Fsck 's +request for memory for its virtual +memory tables failed. +This should never happen. +.I Fsck +terminates on this error condition. +See a guru. +.sp +.LP +.B "Can't open checklist file: \fIF\fP" +.br +The file system checklist file +\fIF\fP (usually +.I /etc/fstab ) +can not be opened for reading. +.I Fsck +terminates on this error condition. +Check access modes of \fIF\fP. +.sp +.LP +.B "Can't stat root" +.br +.I Fsck 's +request for statistics about the root directory ``/'' failed. +This should never happen. +.I Fsck +terminates on this error condition. +See a guru. +.sp +.LP +.B "Can't stat \fIF\fP" +.br +.B "Can't make sense out of name \fIF\fP" +.br +.I Fsck 's +request for statistics about the file system \fIF\fP failed. +When running manually, +it ignores this file system +and continues checking the next file system given. +Check access modes of \fIF\fP. +.sp +.LP +.B "Can't open \fIF\fP" +.br +.I Fsck 's +request attempt to open the file system \fIF\fP failed. +When running manually, it ignores this file system +and continues checking the next file system given. +Check access modes of \fIF\fP. +.sp +.LP +.B "\fIF\fP: (NO WRITE)" +.br +Either the \-n flag was specified or +.I fsck 's +attempt to open the file system \fIF\fP for writing failed. +When running manually, +all the diagnostics are printed out, +but no modifications are attempted to fix them. +.sp +.LP +.B "file is not a block or character device; OK" +.br +You have given +.I fsck +a regular file name by mistake. +Check the type of the file specified. +.LP +Possible responses to the OK prompt are: +.IP YES +ignore this error condition. +.IP NO +ignore this file system and continues checking +the next file system given. +.sp +.LP +.B "UNDEFINED OPTIMIZATION IN SUPERBLOCK (SET TO DEFAULT)" +.br +The superblock optimization parameter is neither OPT_TIME +nor OPT_SPACE. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The superblock is set to request optimization to minimize +running time of the system. +(If optimization to minimize disk space utilization is +desired, it can be set using \fItunefs\fP(8).) +.IP NO +ignore this error condition. +.sp +.LP +.B "IMPOSSIBLE MINFREE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)" +.br +The superblock minimum space percentage is greater than 99% +or less then 0%. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The minfree parameter is set to 10%. +(If some other percentage is desired, +it can be set using \fItunefs\fP(8).) +.IP NO +ignore this error condition. +.sp +.LP +.B "IMPOSSIBLE INTERLEAVE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)" +.br +The file system interleave is less than or equal to zero. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The interleave parameter is set to 1. +.IP NO +ignore this error condition. +.sp +.LP +.B "IMPOSSIBLE NPSECT=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)" +.br +The number of physical sectors per track is less than the number +of usable sectors per track. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The npsect parameter is set to the number of usable sectors per track. +.IP NO +ignore this error condition. +.sp +.LP +One of the following messages will appear: +.br +.B "MAGIC NUMBER WRONG" +.br +.B "NCG OUT OF RANGE" +.br +.B "CPG OUT OF RANGE" +.br +.B "NCYL DOES NOT JIVE WITH NCG*CPG" +.br +.B "SIZE PREPOSTEROUSLY LARGE" +.br +.B "TRASHED VALUES IN SUPER BLOCK" +.br +and will be followed by the message: +.br +.B "\fIF\fP: BAD SUPER BLOCK: \fIB\fP" +.br +.B "USE -b OPTION TO FSCK TO SPECIFY LOCATION OF AN ALTERNATE" +.br +.B "SUPER-BLOCK TO SUPPLY NEEDED INFORMATION; SEE fsck(8)." +.br +The super block has been corrupted. +An alternative super block must be selected from among those +listed by +.I newfs +(8) when the file system was created. +For file systems with a blocksize less than 32K, +specifying \-b 32 is a good first choice. +.sp +.LP +.B "INTERNAL INCONSISTENCY: \fIM\fP" +.br +.I Fsck 's +has had an internal panic, whose message is specified as \fIM\fP. +This should never happen. +See a guru. +.sp +.LP +.B "CAN NOT SEEK: BLK \fIB\fP (CONTINUE)" +.br +.I Fsck 's +request for moving to a specified block number \fIB\fP in +the file system failed. +This should never happen. +See a guru. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +attempt to continue to run the file system check. +Often, +however the problem will persist. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If the block was part of the virtual memory buffer +cache, +.I fsck +will terminate with the message ``Fatal I/O error''. +.IP NO +terminate the program. +.sp +.LP +.B "CAN NOT READ: BLK \fIB\fP (CONTINUE)" +.br +.I Fsck 's +request for reading a specified block number \fIB\fP in +the file system failed. +This should never happen. +See a guru. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +attempt to continue to run the file system check. +It will retry the read and print out the message: +.br +.B "THE FOLLOWING SECTORS COULD NOT BE READ: \fIN\fP" +.br +where \fIN\fP indicates the sectors that could not be read. +If +.I fsck +ever tries to write back one of the blocks on which the read failed +it will print the message: +.br +.B "WRITING ZERO'ED BLOCK \fIN\fP TO DISK" +.br +where \fIN\fP indicates the sector that was written with zero's. +If the disk is experiencing hardware problems, the problem will persist. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If the block was part of the virtual memory buffer +cache, +.I fsck +will terminate with the message ``Fatal I/O error''. +.IP NO +terminate the program. +.sp +.LP +.B "CAN NOT WRITE: BLK \fIB\fP (CONTINUE)" +.br +.I Fsck 's +request for writing a specified block number \fIB\fP +in the file system failed. +The disk is write-protected; +check the write protect lock on the drive. +If that is not the problem, see a guru. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +attempt to continue to run the file system check. +The write operation will be retried with the failed blocks +indicated by the message: +.br +.B "THE FOLLOWING SECTORS COULD NOT BE WRITTEN: \fIN\fP" +.br +where \fIN\fP indicates the sectors that could not be written. +If the disk is experiencing hardware problems, the problem will persist. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If the block was part of the virtual memory buffer +cache, +.I fsck +will terminate with the message ``Fatal I/O error''. +.IP NO +terminate the program. +.sp +.LP +.B "bad inode number DDD to ginode" +.br +An internal error has attempted to read non-existent inode \fIDDD\fP. +This error causes +.I fsck +to exit. +See a guru. +.NH 2 +Phase 1 \- Check Blocks and Sizes +.PP +This phase concerns itself with +the inode list. +This section lists error conditions resulting from +checking inode types, +setting up the zero-link-count table, +examining inode block numbers for bad or duplicate blocks, +checking inode size, +and checking inode format. +All errors in this phase except +.B "INCORRECT BLOCK COUNT" +and +.B "PARTIALLY TRUNCATED INODE" +are fatal if the file system is being preen'ed. +.sp +.LP +.B "UNKNOWN FILE TYPE I=\fII\fP (CLEAR)" +.br +The mode word of the inode \fII\fP indicates that the inode is not a +special block inode, special character inode, socket inode, regular inode, +symbolic link, or directory inode. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +This will always invoke the UNALLOCATED error condition in Phase 2 +for each directory entry pointing to this inode. +.IP NO +ignore this error condition. +.sp +.LP +.B "PARTIALLY TRUNCATED INODE I=\fII\fP (SALVAGE)" +.br +.I Fsck +has found inode \fII\fP whose size is shorter than the number of +blocks allocated to it. +This condition should only occur if the system crashes while in the +midst of truncating a file. +When preen'ing the file system, +.I fsck +completes the truncation to the specified size. +.LP +Possible responses to SALVAGE are: +.IP YES +complete the truncation to the size specified in the inode. +.IP NO +ignore this error condition. +.sp +.LP +.B "LINK COUNT TABLE OVERFLOW (CONTINUE)" +.br +An internal table for +.I fsck +containing allocated inodes with a link count of +zero cannot allocate more memory. +Increase the virtual memory for +.I fsck . +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +continue with the program. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If another allocated inode with a zero link count is found, +this error condition is repeated. +.IP NO +terminate the program. +.sp +.LP +.B "\fIB\fP BAD I=\fII\fP" +.br +Inode \fII\fP contains block number \fIB\fP with a number +lower than the number of the first data block in the file system or +greater than the number of the last block +in the file system. +This error condition may invoke the +.B "EXCESSIVE BAD BLKS" +error condition in Phase 1 (see next paragraph) if +inode \fII\fP has too many block numbers outside the file system range. +This error condition will always invoke the +.B "BAD/DUP" +error condition in Phase 2 and Phase 4. +.sp +.LP +.B "EXCESSIVE BAD BLKS I=\fII\fP (CONTINUE)" +.br +There is more than a tolerable number (usually 10) of blocks with a number +lower than the number of the first data block in the file system or greater than +the number of last block in the file system associated with inode \fII\fP. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +ignore the rest of the blocks in this inode +and continue checking with the next inode in the file system. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +.IP NO +terminate the program. +.sp +.LP +.B "BAD STATE DDD TO BLKERR" +.br +An internal error has scrambled +.I fsck 's +state map to have the impossible value \fIDDD\fP. +.I Fsck +exits immediately. +See a guru. +.sp +.LP +.B "\fIB\fP DUP I=\fII\fP" +.br +Inode \fII\fP contains block number \fIB\fP that is already claimed by +another inode. +This error condition may invoke the +.B "EXCESSIVE DUP BLKS" +error condition in Phase 1 if +inode \fII\fP has too many block numbers claimed by other inodes. +This error condition will always invoke Phase 1b and the +.B "BAD/DUP" +error condition in Phase 2 and Phase 4. +.sp +.LP +.B "EXCESSIVE DUP BLKS I=\fII\fP (CONTINUE)" +.br +There is more than a tolerable number (usually 10) of blocks claimed by other +inodes. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +ignore the rest of the blocks in this inode +and continue checking with the next inode in the file system. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +.IP NO +terminate the program. +.sp +.LP +.B "DUP TABLE OVERFLOW (CONTINUE)" +.br +An internal table in +.I fsck +containing duplicate block numbers cannot allocate any more space. +Increase the amount of virtual memory available to +.I fsck . +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +continue with the program. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If another duplicate block is found, this error condition will repeat. +.IP NO +terminate the program. +.sp +.LP +.B "PARTIALLY ALLOCATED INODE I=\fII\fP (CLEAR)" +.br +Inode \fII\fP is neither allocated nor unallocated. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +.IP NO +ignore this error condition. +.sp +.LP +.B "INCORRECT BLOCK COUNT I=\fII\fP (\fIX\fP should be \fIY\fP) (CORRECT)" +.br +The block count for inode \fII\fP is \fIX\fP blocks, +but should be \fIY\fP blocks. +When preen'ing the count is corrected. +.LP +Possible responses to the CORRECT prompt are: +.IP YES +replace the block count of inode \fII\fP with \fIY\fP. +.IP NO +ignore this error condition. +.NH 2 +Phase 1B: Rescan for More Dups +.PP +When a duplicate block is found in the file system, the file system is +rescanned to find the inode that previously claimed that block. +This section lists the error condition when the duplicate block is found. +.sp +.LP +.B "\fIB\fP DUP I=\fII\fP" +.br +Inode \fII\fP contains block number \fIB\fP that +is already claimed by another inode. +This error condition will always invoke the +.B "BAD/DUP" +error condition in Phase 2. +You can determine which inodes have overlapping blocks by examining +this error condition and the DUP error condition in Phase 1. +.NH 2 +Phase 2 \- Check Pathnames +.PP +This phase concerns itself with removing directory entries +pointing to +error conditioned inodes +from Phase 1 and Phase 1b. +This section lists error conditions resulting from +root inode mode and status, +directory inode pointers in range, +and directory entries pointing to bad inodes, +and directory integrity checks. +All errors in this phase are fatal if the file system is being preen'ed, +except for directories not being a multiple of the blocks size +and extraneous hard links. +.sp +.LP +.B "ROOT INODE UNALLOCATED (ALLOCATE)" +.br +The root inode (usually inode number 2) has no allocate mode bits. +This should never happen. +.LP +Possible responses to the ALLOCATE prompt are: +.IP YES +allocate inode 2 as the root inode. +The files and directories usually found in the root will be recovered +in Phase 3 and put into +.I lost+found . +If the attempt to allocate the root fails, +.I fsck +will exit with the message: +.br +.B "CANNOT ALLOCATE ROOT INODE" . +.IP NO +.I fsck +will exit. +.sp +.LP +.B "ROOT INODE NOT DIRECTORY (REALLOCATE)" +.br +The root inode (usually inode number 2) +is not directory inode type. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +clear the existing contents of the root inode +and reallocate it. +The files and directories usually found in the root will be recovered +in Phase 3 and put into +.I lost+found . +If the attempt to allocate the root fails, +.I fsck +will exit with the message: +.br +.B "CANNOT ALLOCATE ROOT INODE" . +.IP NO +.I fsck +will then prompt with +.B "FIX" +.LP +Possible responses to the FIX prompt are: +.IP YES +replace the root inode's type to be a directory. +If the root inode's data blocks are not directory blocks, +many error conditions will be produced. +.IP NO +terminate the program. +.sp +.LP +.B "DUPS/BAD IN ROOT INODE (REALLOCATE)" +.br +Phase 1 or Phase 1b have found duplicate blocks +or bad blocks in the root inode (usually inode number 2) for the file system. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +clear the existing contents of the root inode +and reallocate it. +The files and directories usually found in the root will be recovered +in Phase 3 and put into +.I lost+found . +If the attempt to allocate the root fails, +.I fsck +will exit with the message: +.br +.B "CANNOT ALLOCATE ROOT INODE" . +.IP NO +.I fsck +will then prompt with +.B "CONTINUE" . +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +ignore the +.B "DUPS/BAD" +error condition in the root inode and +attempt to continue to run the file system check. +If the root inode is not correct, +then this may result in many other error conditions. +.IP NO +terminate the program. +.sp +.LP +.B "NAME TOO LONG \fIF\fP" +.br +An excessively long path name has been found. +This usually indicates loops in the file system name space. +This can occur if the super user has made circular links to directories. +The offending links must be removed (by a guru). +.sp +.LP +.B "I OUT OF RANGE I=\fII\fP NAME=\fIF\fP (REMOVE)" +.br +A directory entry \fIF\fP has an inode number \fII\fP that is greater than +the end of the inode list. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed. +.IP NO +ignore this error condition. +.sp +.LP +.B "UNALLOCATED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)" +.br +A directory or file entry \fIF\fP points to an unallocated inode \fII\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and name \fIF\fP are printed. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed. +.IP NO +ignore this error condition. +.sp +.LP +.B "DUP/BAD I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)" +.br +Phase 1 or Phase 1b have found duplicate blocks or bad blocks +associated with directory or file entry \fIF\fP, inode \fII\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and directory name \fIF\fP are printed. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed. +.IP NO +ignore this error condition. +.sp +.LP +.B "ZERO LENGTH DIRECTORY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (REMOVE)" +.br +A directory entry \fIF\fP has a size \fIS\fP that is zero. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and directory name \fIF\fP are printed. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed; +this will always invoke the BAD/DUP error condition in Phase 4. +.IP NO +ignore this error condition. +.sp +.LP +.B "DIRECTORY TOO SHORT I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fIF\fP has been found whose size \fIS\fP +is less than the minimum size directory. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and directory name \fIF\fP are printed. +.LP +Possible responses to the FIX prompt are: +.IP YES +increase the size of the directory to the minimum directory size. +.IP NO +ignore this directory. +.sp +.LP +.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST) +.br +A directory \fIF\fP has been found with size \fIS\fP that is not +a multiple of the directory blocksize \fIB\fP. +.LP +Possible responses to the ADJUST prompt are: +.IP YES +the length is rounded up to the appropriate block size. +This error can occur on 4.2BSD file systems. +Thus when preen'ing the file system only a warning is printed +and the directory is adjusted. +.IP NO +ignore the error condition. +.sp +.LP +.B "DIRECTORY CORRUPTED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (SALVAGE)" +.br +A directory with an inconsistent internal state has been found. +.LP +Possible responses to the FIX prompt are: +.IP YES +throw away all entries up to the next directory boundary (usually 512-byte) +boundary. +This drastic action can throw away up to 42 entries, +and should be taken only after other recovery efforts have failed. +.IP NO +skip up to the next directory boundary and resume reading, +but do not modify the directory. +.sp +.LP +.B "BAD INODE NUMBER FOR `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose inode number for `.' does +does not equal \fII\fP. +.LP +Possible responses to the FIX prompt are: +.IP YES +change the inode number for `.' to be equal to \fII\fP. +.IP NO +leave the inode number for `.' unchanged. +.sp +.LP +.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose first entry is unallocated. +.LP +Possible responses to the FIX prompt are: +.IP YES +build an entry for `.' with inode number equal to \fII\fP. +.IP NO +leave the directory unchanged. +.sp +.LP +.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS \fIF\fP" +.br +A directory \fII\fP has been found whose first entry is \fIF\fP. +.I Fsck +cannot resolve this problem. +The file system should be mounted and the offending entry \fIF\fP +moved elsewhere. +The file system should then be unmounted and +.I fsck +should be run again. +.sp +.LP +.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `.'" +.br +A directory \fII\fP has been found whose first entry is not `.'. +.I Fsck +cannot resolve this problem as it should never happen. +See a guru. +.sp +.LP +.B "EXTRA `.' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found that has more than one entry for `.'. +.LP +Possible responses to the FIX prompt are: +.IP YES +remove the extra entry for `.'. +.IP NO +leave the directory unchanged. +.sp +.LP +.B "BAD INODE NUMBER FOR `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose inode number for `..' does +does not equal the parent of \fII\fP. +.LP +Possible responses to the FIX prompt are: +.IP YES +change the inode number for `..' to be equal to the parent of \fII\fP +(``\fB..\fP'' in the root inode points to itself). +.IP NO +leave the inode number for `..' unchanged. +.sp +.LP +.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose second entry is unallocated. +.LP +Possible responses to the FIX prompt are: +.IP YES +build an entry for `..' with inode number equal to the parent of \fII\fP +(``\fB..\fP'' in the root inode points to itself). +.IP NO +leave the directory unchanged. +.sp +.LP +.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS \fIF\fP" +.br +A directory \fII\fP has been found whose second entry is \fIF\fP. +.I Fsck +cannot resolve this problem. +The file system should be mounted and the offending entry \fIF\fP +moved elsewhere. +The file system should then be unmounted and +.I fsck +should be run again. +.sp +.LP +.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `..'" +.br +A directory \fII\fP has been found whose second entry is not `..'. +.I Fsck +cannot resolve this problem. +The file system should be mounted and the second entry in the directory +moved elsewhere. +The file system should then be unmounted and +.I fsck +should be run again. +.sp +.LP +.B "EXTRA `..' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found that has more than one entry for `..'. +.LP +Possible responses to the FIX prompt are: +.IP YES +remove the extra entry for `..'. +.IP NO +leave the directory unchanged. +.sp +.LP +.B "\fIN\fP IS AN EXTRANEOUS HARD LINK TO A DIRECTORY \fID\fP (REMOVE) +.br +.I Fsck +has found a hard link, \fIN\fP, to a directory, \fID\fP. +When preen'ing the extraneous links are ignored. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +delete the extraneous entry, \fIN\fP. +.IP NO +ignore the error condition. +.sp +.LP +.B "BAD INODE \fIS\fP TO DESCEND" +.br +An internal error has caused an impossible state \fIS\fP to be passed to the +routine that descends the file system directory structure. +.I Fsck +exits. +See a guru. +.sp +.LP +.B "BAD RETURN STATE \fIS\fP FROM DESCEND" +.br +An internal error has caused an impossible state \fIS\fP to be returned +from the routine that descends the file system directory structure. +.I Fsck +exits. +See a guru. +.sp +.LP +.B "BAD STATE \fIS\fP FOR ROOT INODE" +.br +An internal error has caused an impossible state \fIS\fP to be assigned +to the root inode. +.I Fsck +exits. +See a guru. +.NH 2 +Phase 3 \- Check Connectivity +.PP +This phase concerns itself with the directory connectivity seen in +Phase 2. +This section lists error conditions resulting from +unreferenced directories, +and missing or full +.I lost+found +directories. +.sp +.LP +.B "UNREF DIR I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)" +.br +The directory inode \fII\fP was not connected to a directory entry +when the file system was traversed. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and +modify time \fIT\fP of directory inode \fII\fP are printed. +When preen'ing, the directory is reconnected if its size is non-zero, +otherwise it is cleared. +.LP +Possible responses to the RECONNECT prompt are: +.IP YES +reconnect directory inode \fII\fP to the file system in the +directory for lost files (usually \fIlost+found\fP). +This may invoke the +.I lost+found +error condition in Phase 3 +if there are problems connecting directory inode \fII\fP to \fIlost+found\fP. +This may also invoke the CONNECTED error condition in Phase 3 if the link +was successful. +.IP NO +ignore this error condition. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "NO lost+found DIRECTORY (CREATE)" +.br +There is no +.I lost+found +directory in the root directory of the file system; +When preen'ing +.I fsck +tries to create a \fIlost+found\fP directory. +.LP +Possible responses to the CREATE prompt are: +.IP YES +create a \fIlost+found\fP directory in the root of the file system. +This may raise the message: +.br +.B "NO SPACE LEFT IN / (EXPAND)" +.br +See below for the possible responses. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "lost+found IS NOT A DIRECTORY (REALLOCATE)" +.br +The entry for +.I lost+found +is not a directory. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +allocate a directory inode, and change \fIlost+found\fP to reference it. +The previous inode reference by the \fIlost+found\fP name is not cleared. +Thus it will either be reclaimed as an UNREF'ed inode or have its +link count ADJUST'ed later in this Phase. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "NO SPACE LEFT IN /lost+found (EXPAND)" +.br +There is no space to add another entry to the +.I lost+found +directory in the root directory +of the file system. +When preen'ing the +.I lost+found +directory is expanded. +.LP +Possible responses to the EXPAND prompt are: +.IP YES +the +.I lost+found +directory is expanded to make room for the new entry. +If the attempted expansion fails +.I fsck +prints the message: +.br +.B "SORRY. NO SPACE IN lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +Clean out unnecessary entries in +.I lost+found . +This error is fatal if the file system is being preen'ed. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "DIR I=\fII1\fP CONNECTED. PARENT WAS I=\fII2\fP" +.br +This is an advisory message indicating a directory inode \fII1\fP was +successfully connected to the +.I lost+found +directory. +The parent inode \fII2\fP of the directory inode \fII1\fP is +replaced by the inode number of the +.I lost+found +directory. +.sp +.LP +.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST) +.br +A directory \fIF\fP has been found with size \fIS\fP that is not +a multiple of the directory blocksize \fIB\fP +(this can reoccur in Phase 3 if it is not adjusted in Phase 2). +.LP +Possible responses to the ADJUST prompt are: +.IP YES +the length is rounded up to the appropriate block size. +This error can occur on 4.2BSD file systems. +Thus when preen'ing the file system only a warning is printed +and the directory is adjusted. +.IP NO +ignore the error condition. +.sp +.LP +.B "BAD INODE \fIS\fP TO DESCEND" +.br +An internal error has caused an impossible state \fIS\fP to be passed to the +routine that descends the file system directory structure. +.I Fsck +exits. +See a guru. +.NH 2 +Phase 4 \- Check Reference Counts +.PP +This phase concerns itself with the link count information +seen in Phase 2 and Phase 3. +This section lists error conditions resulting from +unreferenced files, +missing or full +.I lost+found +directory, +incorrect link counts for files, directories, symbolic links, or special files, +unreferenced files, symbolic links, and directories, +and bad or duplicate blocks in files, symbolic links, and directories. +All errors in this phase are correctable if the file system is being preen'ed +except running out of space in the \fIlost+found\fP directory. +.sp +.LP +.B "UNREF FILE I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)" +.br +Inode \fII\fP was not connected to a directory entry +when the file system was traversed. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and +modify time \fIT\fP of inode \fII\fP are printed. +When preen'ing the file is cleared if either its size or its +link count is zero, +otherwise it is reconnected. +.LP +Possible responses to the RECONNECT prompt are: +.IP YES +reconnect inode \fII\fP to the file system in the directory for +lost files (usually \fIlost+found\fP). +This may invoke the +.I lost+found +error condition in Phase 4 +if there are problems connecting inode \fII\fP to +.I lost+found . +.IP NO +ignore this error condition. +This will always invoke the CLEAR error condition in Phase 4. +.sp +.LP +.B "(CLEAR)" +.br +The inode mentioned in the immediately previous error condition can not be +reconnected. +This cannot occur if the file system is being preen'ed, +since lack of space to reconnect files is a fatal error. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate the inode mentioned in the immediately previous error condition by zeroing its contents. +.IP NO +ignore this error condition. +.sp +.LP +.B "NO lost+found DIRECTORY (CREATE)" +.br +There is no +.I lost+found +directory in the root directory of the file system; +When preen'ing +.I fsck +tries to create a \fIlost+found\fP directory. +.LP +Possible responses to the CREATE prompt are: +.IP YES +create a \fIlost+found\fP directory in the root of the file system. +This may raise the message: +.br +.B "NO SPACE LEFT IN / (EXPAND)" +.br +See below for the possible responses. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "lost+found IS NOT A DIRECTORY (REALLOCATE)" +.br +The entry for +.I lost+found +is not a directory. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +allocate a directory inode, and change \fIlost+found\fP to reference it. +The previous inode reference by the \fIlost+found\fP name is not cleared. +Thus it will either be reclaimed as an UNREF'ed inode or have its +link count ADJUST'ed later in this Phase. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "NO SPACE LEFT IN /lost+found (EXPAND)" +.br +There is no space to add another entry to the +.I lost+found +directory in the root directory +of the file system. +When preen'ing the +.I lost+found +directory is expanded. +.LP +Possible responses to the EXPAND prompt are: +.IP YES +the +.I lost+found +directory is expanded to make room for the new entry. +If the attempted expansion fails +.I fsck +prints the message: +.br +.B "SORRY. NO SPACE IN lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +Clean out unnecessary entries in +.I lost+found . +This error is fatal if the file system is being preen'ed. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "LINK COUNT \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP COUNT=\fIX\fP SHOULD BE \fIY\fP (ADJUST)" +.br +The link count for inode \fII\fP, +is \fIX\fP but should be \fIY\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and modify time \fIT\fP +are printed. +When preen'ing the link count is adjusted unless the number of references +is increasing, a condition that should never occur unless precipitated +by a hardware failure. +When the number of references is increasing under preen mode, +.I fsck +exits with the message: +.br +.B "LINK COUNT INCREASING" +.LP +Possible responses to the ADJUST prompt are: +.IP YES +replace the link count of file inode \fII\fP with \fIY\fP. +.IP NO +ignore this error condition. +.sp +.LP +.B "UNREF \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)" +.br +Inode \fII\fP, was not connected to a directory entry when the +file system was traversed. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, +and modify time \fIT\fP of inode \fII\fP +are printed. +When preen'ing, +this is a file that was not connected because its size or link count was zero, +hence it is cleared. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +.IP NO +ignore this error condition. +.sp +.LP +.B "BAD/DUP \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)" +.br +Phase 1 or Phase 1b have found duplicate blocks +or bad blocks associated with +inode \fII\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, +and modify time \fIT\fP of inode \fII\fP +are printed. +This error cannot arise when the file system is being preen'ed, +as it would have caused a fatal error earlier. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +.IP NO +ignore this error condition. +.NH 2 +Phase 5 - Check Cyl groups +.PP +This phase concerns itself with the free-block and used-inode maps. +This section lists error conditions resulting from +allocated blocks in the free-block maps, +free blocks missing from free-block maps, +and the total free-block count incorrect. +It also lists error conditions resulting from +free inodes in the used-inode maps, +allocated inodes missing from used-inode maps, +and the total used-inode count incorrect. +.sp +.LP +.B "CG \fIC\fP: BAD MAGIC NUMBER" +.br +The magic number of cylinder group \fIC\fP is wrong. +This usually indicates that the cylinder group maps have been destroyed. +When running manually the cylinder group is marked as needing +to be reconstructed. +This error is fatal if the file system is being preen'ed. +.sp +.LP +.B "BLK(S) MISSING IN BIT MAPS (SALVAGE)" +.br +A cylinder group block map is missing some free blocks. +During preen'ing the maps are reconstructed. +.LP +Possible responses to the SALVAGE prompt are: +.IP YES +reconstruct the free block map. +.IP NO +ignore this error condition. +.sp +.LP +.B "SUMMARY INFORMATION BAD (SALVAGE)" +.br +The summary information was found to be incorrect. +When preen'ing, +the summary information is recomputed. +.LP +Possible responses to the SALVAGE prompt are: +.IP YES +reconstruct the summary information. +.IP NO +ignore this error condition. +.sp +.LP +.B "FREE BLK COUNT(S) WRONG IN SUPERBLOCK (SALVAGE)" +.br +The superblock free block information was found to be incorrect. +When preen'ing, +the superblock free block information is recomputed. +.LP +Possible responses to the SALVAGE prompt are: +.IP YES +reconstruct the superblock free block information. +.IP NO +ignore this error condition. +.NH 2 +Cleanup +.PP +Once a file system has been checked, a few cleanup functions are performed. +This section lists advisory messages about +the file system +and modify status of the file system. +.sp +.LP +.B "\fIV\fP files, \fIW\fP used, \fIX\fP free (\fIY\fP frags, \fIZ\fP blocks)" +.br +This is an advisory message indicating that +the file system checked contained +\fIV\fP files using +\fIW\fP fragment sized blocks leaving +\fIX\fP fragment sized blocks free in the file system. +The numbers in parenthesis breaks the free count down into +\fIY\fP free fragments and +\fIZ\fP free full sized blocks. +.sp +.LP +.B "***** REBOOT UNIX *****" +.br +This is an advisory message indicating that +the root file system has been modified by +.I fsck. +If UNIX is not rebooted immediately, +the work done by +.I fsck +may be undone by the in-core copies of tables +UNIX keeps. +When preen'ing, +.I fsck +will exit with a code of 4. +The standard auto-reboot script distributed with 4.3BSD +interprets an exit code of 4 by issuing a reboot system call. +.sp +.LP +.B "***** FILE SYSTEM WAS MODIFIED *****" +.br +This is an advisory message indicating that +the current file system was modified by +.I fsck. +If this file system is mounted or is the current root file system, +.I fsck +should be halted and UNIX rebooted. +If UNIX is not rebooted immediately, +the work done by +.I fsck +may be undone by the in-core copies of tables +UNIX keeps. diff --git a/sbin/fsck/SMM.doc/Makefile b/sbin/fsck/SMM.doc/Makefile new file mode 100644 index 0000000..26823bc --- /dev/null +++ b/sbin/fsck/SMM.doc/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 6/8/93 + +DIR= smm/03.fsck +SRCS= 0.t 1.t 2.t 3.t 4.t +MACROS= -ms + +.include <bsd.doc.mk> diff --git a/sbin/fsck/dir.c b/sbin/fsck/dir.c new file mode 100644 index 0000000..ee5d095 --- /dev/null +++ b/sbin/fsck/dir.c @@ -0,0 +1,681 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dir.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <stdlib.h> +#include <string.h> +#include "fsck.h" + +char *lfname = "lost+found"; +int lfmode = 01777; +struct dirtemplate emptydir = { 0, DIRBLKSIZ }; +struct dirtemplate dirhead = { + 0, 12, DT_DIR, 1, ".", + 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." +}; +struct odirtemplate odirhead = { + 0, 12, 1, ".", + 0, DIRBLKSIZ - 12, 2, ".." +}; + +struct direct *fsck_readdir(); +struct bufarea *getdirblk(); + +/* + * Propagate connected state through the tree. + */ +propagate() +{ + register struct inoinfo **inpp, *inp; + struct inoinfo **inpend; + long change; + + inpend = &inpsort[inplast]; + do { + change = 0; + for (inpp = inpsort; inpp < inpend; inpp++) { + inp = *inpp; + if (inp->i_parent == 0) + continue; + if (statemap[inp->i_parent] == DFOUND && + statemap[inp->i_number] == DSTATE) { + statemap[inp->i_number] = DFOUND; + change++; + } + } + } while (change > 0); +} + +/* + * Scan each entry in a directory block. + */ +dirscan(idesc) + register struct inodesc *idesc; +{ + register struct direct *dp; + register struct bufarea *bp; + int dsize, n; + long blksiz; + char dbuf[DIRBLKSIZ]; + + if (idesc->id_type != DATA) + errexit("wrong type to dirscan %d\n", idesc->id_type); + if (idesc->id_entryno == 0 && + (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) + idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); + blksiz = idesc->id_numfrags * sblock.fs_fsize; + if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { + idesc->id_filesize -= blksiz; + return (SKIP); + } + idesc->id_loc = 0; + for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { + dsize = dp->d_reclen; + bcopy((char *)dp, dbuf, (size_t)dsize); +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (!newinofmt) { + struct direct *tdp = (struct direct *)dbuf; + u_char tmp; + + tmp = tdp->d_namlen; + tdp->d_namlen = tdp->d_type; + tdp->d_type = tmp; + } +# endif + idesc->id_dirp = (struct direct *)dbuf; + if ((n = (*idesc->id_func)(idesc)) & ALTERED) { +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (!newinofmt && !doinglevel2) { + struct direct *tdp; + u_char tmp; + + tdp = (struct direct *)dbuf; + tmp = tdp->d_namlen; + tdp->d_namlen = tdp->d_type; + tdp->d_type = tmp; + } +# endif + bp = getdirblk(idesc->id_blkno, blksiz); + bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize, + (size_t)dsize); + dirty(bp); + sbdirty(); + } + if (n & STOP) + return (n); + } + return (idesc->id_filesize > 0 ? KEEPON : STOP); +} + +/* + * get next entry in a directory. + */ +struct direct * +fsck_readdir(idesc) + register struct inodesc *idesc; +{ + register struct direct *dp, *ndp; + register struct bufarea *bp; + long size, blksiz, fix, dploc; + + blksiz = idesc->id_numfrags * sblock.fs_fsize; + bp = getdirblk(idesc->id_blkno, blksiz); + if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && + idesc->id_loc < blksiz) { + dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); + if (dircheck(idesc, dp)) + goto dpok; + fix = dofix(idesc, "DIRECTORY CORRUPTED"); + bp = getdirblk(idesc->id_blkno, blksiz); + dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); + dp->d_reclen = DIRBLKSIZ; + dp->d_ino = 0; + dp->d_type = 0; + dp->d_namlen = 0; + dp->d_name[0] = '\0'; + if (fix) + dirty(bp); + idesc->id_loc += DIRBLKSIZ; + idesc->id_filesize -= DIRBLKSIZ; + return (dp); + } +dpok: + if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) + return NULL; + dploc = idesc->id_loc; + dp = (struct direct *)(bp->b_un.b_buf + dploc); + idesc->id_loc += dp->d_reclen; + idesc->id_filesize -= dp->d_reclen; + if ((idesc->id_loc % DIRBLKSIZ) == 0) + return (dp); + ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); + if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && + dircheck(idesc, ndp) == 0) { + size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); + idesc->id_loc += size; + idesc->id_filesize -= size; + fix = dofix(idesc, "DIRECTORY CORRUPTED"); + bp = getdirblk(idesc->id_blkno, blksiz); + dp = (struct direct *)(bp->b_un.b_buf + dploc); + dp->d_reclen += size; + if (fix) + dirty(bp); + } + return (dp); +} + +/* + * Verify that a directory entry is valid. + * This is a superset of the checks made in the kernel. + */ +dircheck(idesc, dp) + struct inodesc *idesc; + register struct direct *dp; +{ + register int size; + register char *cp; + u_char namlen, type; + int spaceleft; + + size = DIRSIZ(!newinofmt, dp); + spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (!newinofmt) { + type = dp->d_namlen; + namlen = dp->d_type; + } else { + namlen = dp->d_namlen; + type = dp->d_type; + } +# else + namlen = dp->d_namlen; + type = dp->d_type; +# endif + if (dp->d_ino < maxino && + dp->d_reclen != 0 && + dp->d_reclen <= spaceleft && + (dp->d_reclen & 0x3) == 0 && + dp->d_reclen >= size && + idesc->id_filesize >= size && + namlen <= MAXNAMLEN && + type <= 15) { + if (dp->d_ino == 0) + return (1); + for (cp = dp->d_name, size = 0; size < namlen; size++) + if (*cp == 0 || (*cp++ == '/')) + return (0); + if (*cp == 0) + return (1); + } + return (0); +} + +direrror(ino, errmesg) + ino_t ino; + char *errmesg; +{ + + fileerror(ino, ino, errmesg); +} + +fileerror(cwd, ino, errmesg) + ino_t cwd, ino; + char *errmesg; +{ + register struct dinode *dp; + char pathbuf[MAXPATHLEN + 1]; + + pwarn("%s ", errmesg); + pinode(ino); + printf("\n"); + getpathname(pathbuf, cwd, ino); + if (ino < ROOTINO || ino > maxino) { + pfatal("NAME=%s\n", pathbuf); + return; + } + dp = ginode(ino); + if (ftypeok(dp)) + pfatal("%s=%s\n", + (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); + else + pfatal("NAME=%s\n", pathbuf); +} + +adjust(idesc, lcnt) + register struct inodesc *idesc; + short lcnt; +{ + register struct dinode *dp; + + dp = ginode(idesc->id_number); + if (dp->di_nlink == lcnt) { + if (linkup(idesc->id_number, (ino_t)0) == 0) + clri(idesc, "UNREF", 0); + } else { + pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : + ((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE")); + pinode(idesc->id_number); + printf(" COUNT %d SHOULD BE %d", + dp->di_nlink, dp->di_nlink - lcnt); + if (preen) { + if (lcnt < 0) { + printf("\n"); + pfatal("LINK COUNT INCREASING"); + } + printf(" (ADJUSTED)\n"); + } + if (preen || reply("ADJUST") == 1) { + dp->di_nlink -= lcnt; + inodirty(); + } + } +} + +mkentry(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + struct direct newent; + int newlen, oldlen; + + newent.d_namlen = strlen(idesc->id_name); + newlen = DIRSIZ(0, &newent); + if (dirp->d_ino != 0) + oldlen = DIRSIZ(0, dirp); + else + oldlen = 0; + if (dirp->d_reclen - oldlen < newlen) + return (KEEPON); + newent.d_reclen = dirp->d_reclen - oldlen; + dirp->d_reclen = oldlen; + dirp = (struct direct *)(((char *)dirp) + oldlen); + dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ + if (newinofmt) + dirp->d_type = typemap[idesc->id_parent]; + else + dirp->d_type = 0; + dirp->d_reclen = newent.d_reclen; + dirp->d_namlen = newent.d_namlen; + bcopy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1); + return (ALTERED|STOP); +} + +chgino(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) + return (KEEPON); + dirp->d_ino = idesc->id_parent; + if (newinofmt) + dirp->d_type = typemap[idesc->id_parent]; + else + dirp->d_type = 0; + return (ALTERED|STOP); +} + +linkup(orphan, parentdir) + ino_t orphan; + ino_t parentdir; +{ + register struct dinode *dp; + int lostdir; + ino_t oldlfdir; + struct inodesc idesc; + char tempname[BUFSIZ]; + extern int pass4check(); + + bzero((char *)&idesc, sizeof(struct inodesc)); + dp = ginode(orphan); + lostdir = (dp->di_mode & IFMT) == IFDIR; + pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); + pinode(orphan); + if (preen && dp->di_size == 0) + return (0); + if (preen) + printf(" (RECONNECTED)\n"); + else + if (reply("RECONNECT") == 0) + return (0); + if (lfdir == 0) { + dp = ginode(ROOTINO); + idesc.id_name = lfname; + idesc.id_type = DATA; + idesc.id_func = findino; + idesc.id_number = ROOTINO; + if ((ckinode(dp, &idesc) & FOUND) != 0) { + lfdir = idesc.id_parent; + } else { + pwarn("NO lost+found DIRECTORY"); + if (preen || reply("CREATE")) { + lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); + if (lfdir != 0) { + if (makeentry(ROOTINO, lfdir, lfname) != 0) { + if (preen) + printf(" (CREATED)\n"); + } else { + freedir(lfdir, ROOTINO); + lfdir = 0; + if (preen) + printf("\n"); + } + } + } + } + if (lfdir == 0) { + pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); + printf("\n\n"); + return (0); + } + } + dp = ginode(lfdir); + if ((dp->di_mode & IFMT) != IFDIR) { + pfatal("lost+found IS NOT A DIRECTORY"); + if (reply("REALLOCATE") == 0) + return (0); + oldlfdir = lfdir; + if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) { + pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); + return (0); + } + if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { + pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); + return (0); + } + inodirty(); + idesc.id_type = ADDR; + idesc.id_func = pass4check; + idesc.id_number = oldlfdir; + adjust(&idesc, lncntp[oldlfdir] + 1); + lncntp[oldlfdir] = 0; + dp = ginode(lfdir); + } + if (statemap[lfdir] != DFOUND) { + pfatal("SORRY. NO lost+found DIRECTORY\n\n"); + return (0); + } + (void)lftempname(tempname, orphan); + if (makeentry(lfdir, orphan, tempname) == 0) { + pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); + printf("\n\n"); + return (0); + } + lncntp[orphan]--; + if (lostdir) { + if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && + parentdir != (ino_t)-1) + (void)makeentry(orphan, lfdir, ".."); + dp = ginode(lfdir); + dp->di_nlink++; + inodirty(); + lncntp[lfdir]++; + pwarn("DIR I=%lu CONNECTED. ", orphan); + if (parentdir != (ino_t)-1) + printf("PARENT WAS I=%lu\n", parentdir); + if (preen == 0) + printf("\n"); + } + return (1); +} + +/* + * fix an entry in a directory. + */ +changeino(dir, name, newnum) + ino_t dir; + char *name; + ino_t newnum; +{ + struct inodesc idesc; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = DATA; + idesc.id_func = chgino; + idesc.id_number = dir; + idesc.id_fix = DONTKNOW; + idesc.id_name = name; + idesc.id_parent = newnum; /* new value for name */ + return (ckinode(ginode(dir), &idesc)); +} + +/* + * make an entry in a directory + */ +makeentry(parent, ino, name) + ino_t parent, ino; + char *name; +{ + struct dinode *dp; + struct inodesc idesc; + char pathbuf[MAXPATHLEN + 1]; + + if (parent < ROOTINO || parent >= maxino || + ino < ROOTINO || ino >= maxino) + return (0); + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = DATA; + idesc.id_func = mkentry; + idesc.id_number = parent; + idesc.id_parent = ino; /* this is the inode to enter */ + idesc.id_fix = DONTKNOW; + idesc.id_name = name; + dp = ginode(parent); + if (dp->di_size % DIRBLKSIZ) { + dp->di_size = roundup(dp->di_size, DIRBLKSIZ); + inodirty(); + } + if ((ckinode(dp, &idesc) & ALTERED) != 0) + return (1); + getpathname(pathbuf, parent, parent); + dp = ginode(parent); + if (expanddir(dp, pathbuf) == 0) + return (0); + return (ckinode(dp, &idesc) & ALTERED); +} + +/* + * Attempt to expand the size of a directory + */ +expanddir(dp, name) + register struct dinode *dp; + char *name; +{ + daddr_t lastbn, newblk; + register struct bufarea *bp; + char *cp, firstblk[DIRBLKSIZ]; + + lastbn = lblkno(&sblock, dp->di_size); + if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0) + return (0); + if ((newblk = allocblk(sblock.fs_frag)) == 0) + return (0); + dp->di_db[lastbn + 1] = dp->di_db[lastbn]; + dp->di_db[lastbn] = newblk; + dp->di_size += sblock.fs_bsize; + dp->di_blocks += btodb(sblock.fs_bsize); + bp = getdirblk(dp->di_db[lastbn + 1], + (long)dblksize(&sblock, dp, lastbn + 1)); + if (bp->b_errs) + goto bad; + bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ); + bp = getdirblk(newblk, sblock.fs_bsize); + if (bp->b_errs) + goto bad; + bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ); + for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; + cp < &bp->b_un.b_buf[sblock.fs_bsize]; + cp += DIRBLKSIZ) + bcopy((char *)&emptydir, cp, sizeof emptydir); + dirty(bp); + bp = getdirblk(dp->di_db[lastbn + 1], + (long)dblksize(&sblock, dp, lastbn + 1)); + if (bp->b_errs) + goto bad; + bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir); + pwarn("NO SPACE LEFT IN %s", name); + if (preen) + printf(" (EXPANDED)\n"); + else if (reply("EXPAND") == 0) + goto bad; + dirty(bp); + inodirty(); + return (1); +bad: + dp->di_db[lastbn] = dp->di_db[lastbn + 1]; + dp->di_db[lastbn + 1] = 0; + dp->di_size -= sblock.fs_bsize; + dp->di_blocks -= btodb(sblock.fs_bsize); + freeblk(newblk, sblock.fs_frag); + return (0); +} + +/* + * allocate a new directory + */ +allocdir(parent, request, mode) + ino_t parent, request; + int mode; +{ + ino_t ino; + char *cp; + struct dinode *dp; + register struct bufarea *bp; + struct dirtemplate *dirp; + + ino = allocino(request, IFDIR|mode); + if (newinofmt) + dirp = &dirhead; + else + dirp = (struct dirtemplate *)&odirhead; + dirp->dot_ino = ino; + dirp->dotdot_ino = parent; + dp = ginode(ino); + bp = getdirblk(dp->di_db[0], sblock.fs_fsize); + if (bp->b_errs) { + freeino(ino); + return (0); + } + bcopy((char *)dirp, bp->b_un.b_buf, sizeof(struct dirtemplate)); + for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; + cp < &bp->b_un.b_buf[sblock.fs_fsize]; + cp += DIRBLKSIZ) + bcopy((char *)&emptydir, cp, sizeof emptydir); + dirty(bp); + dp->di_nlink = 2; + inodirty(); + if (ino == ROOTINO) { + lncntp[ino] = dp->di_nlink; + cacheino(dp, ino); + return(ino); + } + if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { + freeino(ino); + return (0); + } + cacheino(dp, ino); + statemap[ino] = statemap[parent]; + if (statemap[ino] == DSTATE) { + lncntp[ino] = dp->di_nlink; + lncntp[parent]++; + } + dp = ginode(parent); + dp->di_nlink++; + inodirty(); + return (ino); +} + +/* + * free a directory inode + */ +freedir(ino, parent) + ino_t ino, parent; +{ + struct dinode *dp; + + if (ino != parent) { + dp = ginode(parent); + dp->di_nlink--; + inodirty(); + } + freeino(ino); +} + +/* + * generate a temporary name for the lost+found directory. + */ +lftempname(bufp, ino) + char *bufp; + ino_t ino; +{ + register ino_t in; + register char *cp; + int namlen; + + cp = bufp + 2; + for (in = maxino; in > 0; in /= 10) + cp++; + *--cp = 0; + namlen = cp - bufp; + in = ino; + while (cp > bufp) { + *--cp = (in % 10) + '0'; + in /= 10; + } + *cp = '#'; + return (namlen); +} + +/* + * Get a directory block. + * Insure that it is held until another is requested. + */ +struct bufarea * +getdirblk(blkno, size) + daddr_t blkno; + long size; +{ + + if (pdirbp != 0) + pdirbp->b_flags &= ~B_INUSE; + pdirbp = getdatablk(blkno, size); + return (pdirbp); +} diff --git a/sbin/fsck/fsck.8 b/sbin/fsck/fsck.8 new file mode 100644 index 0000000..cf8c499 --- /dev/null +++ b/sbin/fsck/fsck.8 @@ -0,0 +1,291 @@ +.\" Copyright (c) 1980, 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)fsck.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt FSCK 8 +.Os BSD 4 +.Sh NAME +.Nm fsck +.Nd filesystem consistency check and interactive repair +.Sh SYNOPSIS +.Nm fsck +.Fl p +.Op Fl m Ar mode +.Nm fsck +.Op Fl b Ar block# +.Op Fl c Ar level +.Op Fl l Ar maxparallel +.Op Fl y +.Op Fl n +.Op Fl m Ar mode +.Op Ar filesystem +.Ar ... +.Sh DESCRIPTION +The first form of +.Nm fsck +preens a standard set of filesystems or the specified filesystems. +It is normally used in the script +.Pa /etc/rc +during automatic reboot. +Here +.Nm fsck +reads the table +.Pa /etc/fstab +to determine which filesystems to check. +Only partitions in fstab that are mounted ``rw,'' ``rq'' or ``ro'' +and that have non-zero pass number are checked. +Filesystems with pass number 1 (normally just the root filesystem) +are checked one at a time. +When pass 1 completes, all remaining filesystems are checked, +running one process per disk drive. +The disk drive containing each filesystem is inferred from the longest prefix +of the device name that ends in a digit; the remaining characters are assumed +to be the partition designator. +.Pp +The kernel takes care that only a restricted class of innocuous filesystem +inconsistencies can happen unless hardware or software failures intervene. +These are limited to the following: +.Bl -item -compact +.It +Unreferenced inodes +.It +Link counts in inodes too large +.It +Missing blocks in the free map +.It +Blocks in the free map also in files +.It +Counts in the super-block wrong +.El +.Pp +These are the only inconsistencies that +.Nm fsck +with the +.Fl p +option will correct; if it encounters other inconsistencies, it exits +with an abnormal return status and an automatic reboot will then fail. +For each corrected inconsistency one or more lines will be printed +identifying the filesystem on which the correction will take place, +and the nature of the correction. After successfully correcting a filesystem, +.Nm fsck +will print the number of files on that filesystem, +the number of used and free blocks, +and the percentage of fragmentation. +.Pp +If sent a +.Dv QUIT +signal, +.Nm fsck +will finish the filesystem checks, then exit with an abnormal +return status that causes an automatic reboot to fail. +This is useful when you want to finish the filesystem checks during an +automatic reboot, +but do not want the machine to come up multiuser after the checks complete. +.Pp +Without the +.Fl p +option, +.Nm fsck +audits and interactively repairs inconsistent conditions for filesystems. +If the filesystem is inconsistent the operator is prompted for concurrence +before each correction is attempted. +It should be noted that some of the corrective actions which are not +correctable under the +.Fl p +option will result in some loss of data. +The amount and severity of data lost may be determined from the diagnostic +output. +The default action for each consistency correction +is to wait for the operator to respond +.Li yes +or +.Li no . +If the operator does not have write permission on the filesystem +.Nm fsck +will default to a +.Fl n +action. +.Pp +.Nm Fsck +has more consistency checks than +its predecessors +.Em check , dcheck , fcheck , +and +.Em icheck +combined. +.Pp +The following flags are interpreted by +.Nm fsck . +.Bl -tag -width indent +.It Fl b +Use the block specified immediately after the flag as +the super block for the filesystem. Block 32 is usually +an alternate super block. +.It Fl l +Limit the number of parallel checks to the number specified in the following +argument. +By default, the limit is the number of disks, running one process per disk. +If a smaller limit is given, the disks are checked round-robin, one filesystem +at a time. +.It Fl m +Use the mode specified in octal immediately after the flag as the +permission bits to use when creating the +.Pa lost+found +directory rather than the default 1777. +In particular, systems that do not wish to have lost files accessible +by all users on the system should use a more restrictive +set of permissions such as 700. +.It Fl y +Assume a yes response to all questions asked by +.Nm fsck ; +this should be used with great caution as this is a free license +to continue after essentially unlimited trouble has been encountered. +.It Fl n +Assume a no response to all questions asked by +.Nm fsck +except for +.Ql CONTINUE? , +which is assumed to be affirmative; +do not open the filesystem for writing. +.It Fl c +Convert the filesystem to the specified level. +Note that the level of a filesystem can only be raised. +.Bl -tag -width indent +There are currently three levels defined: +.It 0 +The filesystem is in the old (static table) format. +.It 1 +The filesystem is in the new (dynamic table) format. +.It 2 +The filesystem supports 32-bit uid's and gid's, +short symbolic links are stored in the inode, +and directories have an added field showing the file type. +.El +.Pp +In interactive mode, +.Nm fsck +will list the conversion to be made +and ask whether the conversion should be done. +If a negative answer is given, +no further operations are done on the filesystem. +In preen mode, +the conversion is listed and done if +possible without user interaction. +Conversion in preen mode is best used when all the filesystems +are being converted at once. +The format of a filesystem can be determined from the +first line of output from +.Xr dumpfs 8 . +.El +.Pp +If no filesystems are given to +.Nm fsck +then a default list of filesystems is read from +the file +.Pa /etc/fstab . +.Pp +.Bl -enum -indent indent -compact +Inconsistencies checked are as follows: +.It +Blocks claimed by more than one inode or the free map. +.It +Blocks claimed by an inode outside the range of the filesystem. +.It +Incorrect link counts. +.It +Size checks: +.Bl -item -indent indent -compact +.It +Directory size not a multiple of DIRBLKSIZ. +.It +Partially truncated file. +.El +.It +Bad inode format. +.It +Blocks not accounted for anywhere. +.It +Directory checks: +.Bl -item -indent indent -compact +.It +File pointing to unallocated inode. +.It +Inode number out of range. +.It +Dot or dot-dot not the first two entries of a directory +or having the wrong inode number. +.El +.It +Super Block checks: +.Bl -item -indent indent -compact +.It +More blocks for inodes than there are in the filesystem. +.It +Bad free block map format. +.It +Total free block and/or free inode count incorrect. +.El +.El +.Pp +Orphaned files and directories (allocated but unreferenced) are, +with the operator's concurrence, reconnected by +placing them in the +.Pa lost+found +directory. +The name assigned is the inode number. +If the +.Pa lost+found +directory does not exist, it is created. +If there is insufficient space its size is increased. +.Pp +Because of inconsistencies between the block device and the buffer cache, +the raw device should always be used. +.Sh FILES +.Bl -tag -width /etc/fstab -compact +.It Pa /etc/fstab +contains default list of filesystems to check. +.El +.Sh DIAGNOSTICS +The diagnostics produced by +.Nm fsck +are fully enumerated and explained in Appendix A of +.Rs +.%T "Fsck \- The UNIX File System Check Program" +.Re +.Sh SEE ALSO +.Xr fstab 5 , +.Xr fs 5 , +.Xr fsdb 8 , +.Xr newfs 8 , +.Xr mkfs 8 , +.Xr reboot 8 diff --git a/sbin/fsck/fsck.h b/sbin/fsck/fsck.h new file mode 100644 index 0000000..7fa831f --- /dev/null +++ b/sbin/fsck/fsck.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)fsck.h 8.1 (Berkeley) 6/5/93 + */ + +#define MAXDUP 10 /* limit on dup blks (per inode) */ +#define MAXBAD 10 /* limit on bad blks (per inode) */ +#define MAXBUFSPACE 40*1024 /* maximum space to allocate to buffers */ +#define INOBUFSIZE 56*1024 /* size of buffer to read inodes in pass1 */ + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif + +#define USTATE 01 /* inode not allocated */ +#define FSTATE 02 /* inode is file */ +#define DSTATE 03 /* inode is directory */ +#define DFOUND 04 /* directory found during descent */ +#define DCLEAR 05 /* directory is to be cleared */ +#define FCLEAR 06 /* file is to be cleared */ + +/* + * buffer cache structure. + */ +struct bufarea { + struct bufarea *b_next; /* free list queue */ + struct bufarea *b_prev; /* free list queue */ + daddr_t b_bno; + int b_size; + int b_errs; + int b_flags; + union { + char *b_buf; /* buffer space */ + daddr_t *b_indir; /* indirect block */ + struct fs *b_fs; /* super block */ + struct cg *b_cg; /* cylinder group */ + struct dinode *b_dinode; /* inode block */ + } b_un; + char b_dirty; +}; + +#define B_INUSE 1 + +#define MINBUFS 5 /* minimum number of buffers required */ +struct bufarea bufhead; /* head of list of other blks in filesys */ +struct bufarea sblk; /* file system superblock */ +struct bufarea cgblk; /* cylinder group blocks */ +struct bufarea *pdirbp; /* current directory contents */ +struct bufarea *pbp; /* current inode block */ +struct bufarea *getdatablk(); + +#define dirty(bp) (bp)->b_dirty = 1 +#define initbarea(bp) \ + (bp)->b_dirty = 0; \ + (bp)->b_bno = (daddr_t)-1; \ + (bp)->b_flags = 0; + +#define sbdirty() sblk.b_dirty = 1 +#define cgdirty() cgblk.b_dirty = 1 +#define sblock (*sblk.b_un.b_fs) +#define cgrp (*cgblk.b_un.b_cg) + +enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE}; + +struct inodesc { + enum fixstate id_fix; /* policy on fixing errors */ + int (*id_func)(); /* function to be applied to blocks of inode */ + ino_t id_number; /* inode number described */ + ino_t id_parent; /* for DATA nodes, their parent */ + daddr_t id_blkno; /* current block number being examined */ + int id_numfrags; /* number of frags contained in block */ + quad_t id_filesize; /* for DATA nodes, the size of the directory */ + int id_loc; /* for DATA nodes, current location in dir */ + int id_entryno; /* for DATA nodes, current entry number */ + struct direct *id_dirp; /* for DATA nodes, ptr to current entry */ + char *id_name; /* for DATA nodes, name to find or enter */ + char id_type; /* type of descriptor, DATA or ADDR */ +}; +/* file types */ +#define DATA 1 +#define ADDR 2 + +/* + * Linked list of duplicate blocks. + * + * The list is composed of two parts. The first part of the + * list (from duplist through the node pointed to by muldup) + * contains a single copy of each duplicate block that has been + * found. The second part of the list (from muldup to the end) + * contains duplicate blocks that have been found more than once. + * To check if a block has been found as a duplicate it is only + * necessary to search from duplist through muldup. To find the + * total number of times that a block has been found as a duplicate + * the entire list must be searched for occurences of the block + * in question. The following diagram shows a sample list where + * w (found twice), x (found once), y (found three times), and z + * (found once) are duplicate block numbers: + * + * w -> y -> x -> z -> y -> w -> y + * ^ ^ + * | | + * duplist muldup + */ +struct dups { + struct dups *next; + daddr_t dup; +}; +struct dups *duplist; /* head of dup list */ +struct dups *muldup; /* end of unique duplicate dup block numbers */ + +/* + * Linked list of inodes with zero link counts. + */ +struct zlncnt { + struct zlncnt *next; + ino_t zlncnt; +}; +struct zlncnt *zlnhead; /* head of zero link count list */ + +/* + * Inode cache data structures. + */ +struct inoinfo { + struct inoinfo *i_nexthash; /* next entry in hash chain */ + ino_t i_number; /* inode number of this entry */ + ino_t i_parent; /* inode number of parent */ + ino_t i_dotdot; /* inode number of `..' */ + size_t i_isize; /* size of inode */ + u_int i_numblks; /* size of block array in bytes */ + daddr_t i_blks[1]; /* actually longer */ +} **inphead, **inpsort; +long numdirs, listmax, inplast; + +char *cdevname; /* name of device being checked */ +long dev_bsize; /* computed value of DEV_BSIZE */ +long secsize; /* actual disk sector size */ +char nflag; /* assume a no response */ +char yflag; /* assume a yes response */ +int bflag; /* location of alternate super block */ +int debug; /* output debugging info */ +int cvtlevel; /* convert to newer file system format */ +int doinglevel1; /* converting to new cylinder group format */ +int doinglevel2; /* converting to new inode format */ +int newinofmt; /* filesystem has new inode format */ +char preen; /* just fix normal inconsistencies */ +char hotroot; /* checking root device */ +char havesb; /* superblock has been read */ +int fsmodified; /* 1 => write done to file system */ +int fsreadfd; /* file descriptor for reading file system */ +int fswritefd; /* file descriptor for writing file system */ + +daddr_t maxfsblock; /* number of blocks in the file system */ +char *blockmap; /* ptr to primary blk allocation map */ +ino_t maxino; /* number of inodes in file system */ +ino_t lastino; /* last inode in use */ +char *statemap; /* ptr to inode state table */ +char *typemap; /* ptr to inode type table */ +short *lncntp; /* ptr to link count table */ + +ino_t lfdir; /* lost & found directory inode number */ +char *lfname; /* lost & found directory name */ +int lfmode; /* lost & found directory creation mode */ + +daddr_t n_blks; /* number of blocks in use */ +daddr_t n_files; /* number of files in use */ + +#define clearinode(dp) (*(dp) = zino) +struct dinode zino; + +#define setbmap(blkno) setbit(blockmap, blkno) +#define testbmap(blkno) isset(blockmap, blkno) +#define clrbmap(blkno) clrbit(blockmap, blkno) + +#define STOP 0x01 +#define SKIP 0x02 +#define KEEPON 0x04 +#define ALTERED 0x08 +#define FOUND 0x10 + +time_t time(); +struct dinode *ginode(); +struct inoinfo *getinoinfo(); +void getblk(); +ino_t allocino(); +int findino(); diff --git a/sbin/fsck/inode.c b/sbin/fsck/inode.c new file mode 100644 index 0000000..f1c1758 --- /dev/null +++ b/sbin/fsck/inode.c @@ -0,0 +1,543 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)inode.c 8.4 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> +#include "fsck.h" + +static ino_t startinum; + +ckinode(dp, idesc) + struct dinode *dp; + register struct inodesc *idesc; +{ + register daddr_t *ap; + long ret, n, ndb, offset; + struct dinode dino; + quad_t remsize, sizepb; + mode_t mode; + + if (idesc->id_fix != IGNORE) + idesc->id_fix = DONTKNOW; + idesc->id_entryno = 0; + idesc->id_filesize = dp->di_size; + mode = dp->di_mode & IFMT; + if (mode == IFBLK || mode == IFCHR || (mode == IFLNK && + dp->di_size < sblock.fs_maxsymlinklen)) + return (KEEPON); + dino = *dp; + ndb = howmany(dino.di_size, sblock.fs_bsize); + for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) { + if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0) + idesc->id_numfrags = + numfrags(&sblock, fragroundup(&sblock, offset)); + else + idesc->id_numfrags = sblock.fs_frag; + if (*ap == 0) + continue; + idesc->id_blkno = *ap; + if (idesc->id_type == ADDR) + ret = (*idesc->id_func)(idesc); + else + ret = dirscan(idesc); + if (ret & STOP) + return (ret); + } + idesc->id_numfrags = sblock.fs_frag; + remsize = dino.di_size - sblock.fs_bsize * NDADDR; + sizepb = sblock.fs_bsize; + for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) { + if (*ap) { + idesc->id_blkno = *ap; + ret = iblock(idesc, n, remsize); + if (ret & STOP) + return (ret); + } + sizepb *= NINDIR(&sblock); + remsize -= sizepb; + } + return (KEEPON); +} + +iblock(idesc, ilevel, isize) + struct inodesc *idesc; + long ilevel; + quad_t isize; +{ + register daddr_t *ap; + register daddr_t *aplim; + register struct bufarea *bp; + int i, n, (*func)(), nif; + quad_t sizepb; + char buf[BUFSIZ]; + extern int dirscan(), pass1check(); + + if (idesc->id_type == ADDR) { + func = idesc->id_func; + if (((n = (*func)(idesc)) & KEEPON) == 0) + return (n); + } else + func = dirscan; + if (chkrange(idesc->id_blkno, idesc->id_numfrags)) + return (SKIP); + bp = getdatablk(idesc->id_blkno, sblock.fs_bsize); + ilevel--; + for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++) + sizepb *= NINDIR(&sblock); + nif = howmany(isize , sizepb); + if (nif > NINDIR(&sblock)) + nif = NINDIR(&sblock); + if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) { + aplim = &bp->b_un.b_indir[NINDIR(&sblock)]; + for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) { + if (*ap == 0) + continue; + (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu", + idesc->id_number); + if (dofix(idesc, buf)) { + *ap = 0; + dirty(bp); + } + } + flush(fswritefd, bp); + } + aplim = &bp->b_un.b_indir[nif]; + for (ap = bp->b_un.b_indir; ap < aplim; ap++) { + if (*ap) { + idesc->id_blkno = *ap; + if (ilevel == 0) + n = (*func)(idesc); + else + n = iblock(idesc, ilevel, isize); + if (n & STOP) { + bp->b_flags &= ~B_INUSE; + return (n); + } + } + isize -= sizepb; + } + bp->b_flags &= ~B_INUSE; + return (KEEPON); +} + +/* + * Check that a block in a legal block number. + * Return 0 if in range, 1 if out of range. + */ +chkrange(blk, cnt) + daddr_t blk; + int cnt; +{ + register int c; + + if ((unsigned)(blk + cnt) > maxfsblock) + return (1); + c = dtog(&sblock, blk); + if (blk < cgdmin(&sblock, c)) { + if ((blk + cnt) > cgsblock(&sblock, c)) { + if (debug) { + printf("blk %ld < cgdmin %ld;", + blk, cgdmin(&sblock, c)); + printf(" blk + cnt %ld > cgsbase %ld\n", + blk + cnt, cgsblock(&sblock, c)); + } + return (1); + } + } else { + if ((blk + cnt) > cgbase(&sblock, c+1)) { + if (debug) { + printf("blk %ld >= cgdmin %ld;", + blk, cgdmin(&sblock, c)); + printf(" blk + cnt %ld > sblock.fs_fpg %ld\n", + blk+cnt, sblock.fs_fpg); + } + return (1); + } + } + return (0); +} + +/* + * General purpose interface for reading inodes. + */ +struct dinode * +ginode(inumber) + ino_t inumber; +{ + daddr_t iblk; + + if (inumber < ROOTINO || inumber > maxino) + errexit("bad inode number %d to ginode\n", inumber); + if (startinum == 0 || + inumber < startinum || inumber >= startinum + INOPB(&sblock)) { + iblk = ino_to_fsba(&sblock, inumber); + if (pbp != 0) + pbp->b_flags &= ~B_INUSE; + pbp = getdatablk(iblk, sblock.fs_bsize); + startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock); + } + return (&pbp->b_un.b_dinode[inumber % INOPB(&sblock)]); +} + +/* + * Special purpose version of ginode used to optimize first pass + * over all the inodes in numerical order. + */ +ino_t nextino, lastinum; +long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; +struct dinode *inodebuf; + +struct dinode * +getnextinode(inumber) + ino_t inumber; +{ + long size; + daddr_t dblk; + static struct dinode *dp; + + if (inumber != nextino++ || inumber > maxino) + errexit("bad inode number %d to nextinode\n", inumber); + if (inumber >= lastinum) { + readcnt++; + dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); + if (readcnt % readpercg == 0) { + size = partialsize; + lastinum += partialcnt; + } else { + size = inobufsize; + lastinum += fullcnt; + } + (void)bread(fsreadfd, (char *)inodebuf, dblk, size); /* ??? */ + dp = inodebuf; + } + return (dp++); +} + +resetinodebuf() +{ + + startinum = 0; + nextino = 0; + lastinum = 0; + readcnt = 0; + inobufsize = blkroundup(&sblock, INOBUFSIZE); + fullcnt = inobufsize / sizeof(struct dinode); + readpercg = sblock.fs_ipg / fullcnt; + partialcnt = sblock.fs_ipg % fullcnt; + partialsize = partialcnt * sizeof(struct dinode); + if (partialcnt != 0) { + readpercg++; + } else { + partialcnt = fullcnt; + partialsize = inobufsize; + } + if (inodebuf == NULL && + (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL) + errexit("Cannot allocate space for inode buffer\n"); + while (nextino < ROOTINO) + (void)getnextinode(nextino); +} + +freeinodebuf() +{ + + if (inodebuf != NULL) + free((char *)inodebuf); + inodebuf = NULL; +} + +/* + * Routines to maintain information about directory inodes. + * This is built during the first pass and used during the + * second and third passes. + * + * Enter inodes into the cache. + */ +cacheino(dp, inumber) + register struct dinode *dp; + ino_t inumber; +{ + register struct inoinfo *inp; + struct inoinfo **inpp; + unsigned int blks; + + blks = howmany(dp->di_size, sblock.fs_bsize); + if (blks > NDADDR) + blks = NDADDR + NIADDR; + inp = (struct inoinfo *) + malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t)); + if (inp == NULL) + return; + inpp = &inphead[inumber % numdirs]; + inp->i_nexthash = *inpp; + *inpp = inp; + inp->i_parent = (ino_t)0; + inp->i_dotdot = (ino_t)0; + inp->i_number = inumber; + inp->i_isize = dp->di_size; + inp->i_numblks = blks * sizeof(daddr_t); + bcopy((char *)&dp->di_db[0], (char *)&inp->i_blks[0], + (size_t)inp->i_numblks); + if (inplast == listmax) { + listmax += 100; + inpsort = (struct inoinfo **)realloc((char *)inpsort, + (unsigned)listmax * sizeof(struct inoinfo *)); + if (inpsort == NULL) + errexit("cannot increase directory list"); + } + inpsort[inplast++] = inp; +} + +/* + * Look up an inode cache structure. + */ +struct inoinfo * +getinoinfo(inumber) + ino_t inumber; +{ + register struct inoinfo *inp; + + for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) { + if (inp->i_number != inumber) + continue; + return (inp); + } + errexit("cannot find inode %d\n", inumber); + return ((struct inoinfo *)0); +} + +/* + * Clean up all the inode cache structure. + */ +inocleanup() +{ + register struct inoinfo **inpp; + + if (inphead == NULL) + return; + for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) + free((char *)(*inpp)); + free((char *)inphead); + free((char *)inpsort); + inphead = inpsort = NULL; +} + +inodirty() +{ + + dirty(pbp); +} + +clri(idesc, type, flag) + register struct inodesc *idesc; + char *type; + int flag; +{ + register struct dinode *dp; + + dp = ginode(idesc->id_number); + if (flag == 1) { + pwarn("%s %s", type, + (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE"); + pinode(idesc->id_number); + } + if (preen || reply("CLEAR") == 1) { + if (preen) + printf(" (CLEARED)\n"); + n_files--; + (void)ckinode(dp, idesc); + clearinode(dp); + statemap[idesc->id_number] = USTATE; + inodirty(); + } +} + +findname(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (dirp->d_ino != idesc->id_parent) + return (KEEPON); + bcopy(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1); + return (STOP|FOUND); +} + +findino(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (dirp->d_ino == 0) + return (KEEPON); + if (strcmp(dirp->d_name, idesc->id_name) == 0 && + dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) { + idesc->id_parent = dirp->d_ino; + return (STOP|FOUND); + } + return (KEEPON); +} + +pinode(ino) + ino_t ino; +{ + register struct dinode *dp; + register char *p; + struct passwd *pw; + char *ctime(); + + printf(" I=%lu ", ino); + if (ino < ROOTINO || ino > maxino) + return; + dp = ginode(ino); + printf(" OWNER="); + if ((pw = getpwuid((int)dp->di_uid)) != 0) + printf("%s ", pw->pw_name); + else + printf("%u ", (unsigned)dp->di_uid); + printf("MODE=%o\n", dp->di_mode); + if (preen) + printf("%s: ", cdevname); + printf("SIZE=%qu ", dp->di_size); + p = ctime(&dp->di_mtime.ts_sec); + printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]); +} + +blkerror(ino, type, blk) + ino_t ino; + char *type; + daddr_t blk; +{ + + pfatal("%ld %s I=%lu", blk, type, ino); + printf("\n"); + switch (statemap[ino]) { + + case FSTATE: + statemap[ino] = FCLEAR; + return; + + case DSTATE: + statemap[ino] = DCLEAR; + return; + + case FCLEAR: + case DCLEAR: + return; + + default: + errexit("BAD STATE %d TO BLKERR", statemap[ino]); + /* NOTREACHED */ + } +} + +/* + * allocate an unused inode + */ +ino_t +allocino(request, type) + ino_t request; + int type; +{ + register ino_t ino; + register struct dinode *dp; + + if (request == 0) + request = ROOTINO; + else if (statemap[request] != USTATE) + return (0); + for (ino = request; ino < maxino; ino++) + if (statemap[ino] == USTATE) + break; + if (ino == maxino) + return (0); + switch (type & IFMT) { + case IFDIR: + statemap[ino] = DSTATE; + break; + case IFREG: + case IFLNK: + statemap[ino] = FSTATE; + break; + default: + return (0); + } + dp = ginode(ino); + dp->di_db[0] = allocblk((long)1); + if (dp->di_db[0] == 0) { + statemap[ino] = USTATE; + return (0); + } + dp->di_mode = type; + (void)time(&dp->di_atime.ts_sec); + dp->di_mtime = dp->di_ctime = dp->di_atime; + dp->di_size = sblock.fs_fsize; + dp->di_blocks = btodb(sblock.fs_fsize); + n_files++; + inodirty(); + if (newinofmt) + typemap[ino] = IFTODT(type); + return (ino); +} + +/* + * deallocate an inode + */ +freeino(ino) + ino_t ino; +{ + struct inodesc idesc; + extern int pass4check(); + struct dinode *dp; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass4check; + idesc.id_number = ino; + dp = ginode(ino); + (void)ckinode(dp, &idesc); + clearinode(dp); + inodirty(); + statemap[ino] = USTATE; + n_files--; +} diff --git a/sbin/fsck/main.c b/sbin/fsck/main.c new file mode 100644 index 0000000..2e69715 --- /dev/null +++ b/sbin/fsck/main.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1986, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/mount.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#include <fstab.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include "fsck.h" + +void catch(), catchquit(), voidquit(); +int returntosingle; + +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + int ret, maxrun = 0; + extern int docheck(), checkfilesys(); + extern char *optarg, *blockcheck(); + extern int optind; + + sync(); + while ((ch = getopt(argc, argv, "dpnNyYb:c:l:m:")) != EOF) { + switch (ch) { + case 'p': + preen++; + break; + + case 'b': + bflag = argtoi('b', "number", optarg, 10); + printf("Alternate super block location: %d\n", bflag); + break; + + case 'c': + cvtlevel = argtoi('c', "conversion level", optarg, 10); + break; + + case 'd': + debug++; + break; + + case 'l': + maxrun = argtoi('l', "number", optarg, 10); + break; + + case 'm': + lfmode = argtoi('m', "mode", optarg, 8); + if (lfmode &~ 07777) + errexit("bad mode to -m: %o\n", lfmode); + printf("** lost+found creation mode %o\n", lfmode); + break; + + case 'n': + case 'N': + nflag++; + yflag = 0; + break; + + case 'y': + case 'Y': + yflag++; + nflag = 0; + break; + + default: + errexit("%c option?\n", ch); + } + } + argc -= optind; + argv += optind; + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + (void)signal(SIGINT, catch); + if (preen) + (void)signal(SIGQUIT, catchquit); + if (argc) { + while (argc-- > 0) + (void)checkfilesys(blockcheck(*argv++), 0, 0L, 0); + exit(0); + } + ret = checkfstab(preen, maxrun, docheck, checkfilesys); + if (returntosingle) + exit(2); + exit(ret); +} + +argtoi(flag, req, str, base) + int flag; + char *req, *str; + int base; +{ + char *cp; + int ret; + + ret = (int)strtol(str, &cp, base); + if (cp == str || *cp) + errexit("-%c flag requires a %s\n", flag, req); + return (ret); +} + +/* + * Determine whether a filesystem should be checked. + */ +docheck(fsp) + register struct fstab *fsp; +{ + + if (strcmp(fsp->fs_vfstype, "ufs") || + (strcmp(fsp->fs_type, FSTAB_RW) && + strcmp(fsp->fs_type, FSTAB_RO)) || + fsp->fs_passno == 0) + return (0); + return (1); +} + +/* + * Check the specified filesystem. + */ +/* ARGSUSED */ +checkfilesys(filesys, mntpt, auxdata, child) + char *filesys, *mntpt; + long auxdata; +{ + daddr_t n_ffree, n_bfree; + struct dups *dp; + struct zlncnt *zlnp; + int cylno; + + if (preen && child) + (void)signal(SIGQUIT, voidquit); + cdevname = filesys; + if (debug && preen) + pwarn("starting\n"); + if (setup(filesys) == 0) { + if (preen) + pfatal("CAN'T CHECK FILE SYSTEM."); + return (0); + } + /* + * 1: scan inodes tallying blocks used + */ + if (preen == 0) { + printf("** Last Mounted on %s\n", sblock.fs_fsmnt); + if (hotroot) + printf("** Root file system\n"); + printf("** Phase 1 - Check Blocks and Sizes\n"); + } + pass1(); + + /* + * 1b: locate first references to duplicates, if any + */ + if (duplist) { + if (preen) + pfatal("INTERNAL ERROR: dups with -p"); + printf("** Phase 1b - Rescan For More DUPS\n"); + pass1b(); + } + + /* + * 2: traverse directories from root to mark all connected directories + */ + if (preen == 0) + printf("** Phase 2 - Check Pathnames\n"); + pass2(); + + /* + * 3: scan inodes looking for disconnected directories + */ + if (preen == 0) + printf("** Phase 3 - Check Connectivity\n"); + pass3(); + + /* + * 4: scan inodes looking for disconnected files; check reference counts + */ + if (preen == 0) + printf("** Phase 4 - Check Reference Counts\n"); + pass4(); + + /* + * 5: check and repair resource counts in cylinder groups + */ + if (preen == 0) + printf("** Phase 5 - Check Cyl groups\n"); + pass5(); + + /* + * print out summary statistics + */ + n_ffree = sblock.fs_cstotal.cs_nffree; + n_bfree = sblock.fs_cstotal.cs_nbfree; + pwarn("%ld files, %ld used, %ld free ", + n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree); + printf("(%ld frags, %ld blocks, %d.%d%% fragmentation)\n", + n_ffree, n_bfree, (n_ffree * 100) / sblock.fs_dsize, + ((n_ffree * 1000 + sblock.fs_dsize / 2) / sblock.fs_dsize) % 10); + if (debug && + (n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree)) + printf("%ld files missing\n", n_files); + if (debug) { + n_blks += sblock.fs_ncg * + (cgdmin(&sblock, 0) - cgsblock(&sblock, 0)); + n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0); + n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize); + if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree)) + printf("%ld blocks missing\n", n_blks); + if (duplist != NULL) { + printf("The following duplicate blocks remain:"); + for (dp = duplist; dp; dp = dp->next) + printf(" %ld,", dp->dup); + printf("\n"); + } + if (zlnhead != NULL) { + printf("The following zero link count inodes remain:"); + for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) + printf(" %lu,", zlnp->zlncnt); + printf("\n"); + } + } + zlnhead = (struct zlncnt *)0; + duplist = (struct dups *)0; + muldup = (struct dups *)0; + inocleanup(); + if (fsmodified) { + (void)time(&sblock.fs_time); + sbdirty(); + } + if (cvtlevel && sblk.b_dirty) { + /* + * Write out the duplicate super blocks + */ + for (cylno = 0; cylno < sblock.fs_ncg; cylno++) + bwrite(fswritefd, (char *)&sblock, + fsbtodb(&sblock, cgsblock(&sblock, cylno)), SBSIZE); + } + ckfini(); + free(blockmap); + free(statemap); + free((char *)lncntp); + if (!fsmodified) + return (0); + if (!preen) + printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); + if (hotroot) { + struct statfs stfs_buf; + /* + * We modified the root. Do a mount update on + * it, unless it is read-write, so we can continue. + */ + if (statfs("/", &stfs_buf) == 0) { + long flags = stfs_buf.f_flags; + struct ufs_args args; + int ret; + + if (flags & MNT_RDONLY) { + args.fspec = 0; + args.export.ex_flags = 0; + args.export.ex_root = 0; + flags |= MNT_UPDATE | MNT_RELOAD; + ret = mount(MOUNT_UFS, "/", flags, &args); + if (ret == 0) + return(0); + } + } + if (!preen) + printf("\n***** REBOOT NOW *****\n"); + sync(); + return (4); + } + return (0); +} diff --git a/sbin/fsck/pass1.c b/sbin/fsck/pass1.c new file mode 100644 index 0000000..ca255fe --- /dev/null +++ b/sbin/fsck/pass1.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass1.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <stdlib.h> +#include <string.h> +#include "fsck.h" + +static daddr_t badblk; +static daddr_t dupblk; +int pass1check(); +struct dinode *getnextinode(); + +pass1() +{ + ino_t inumber; + int c, i, cgd; + struct inodesc idesc; + + /* + * Set file system reserved blocks in used block map. + */ + for (c = 0; c < sblock.fs_ncg; c++) { + cgd = cgdmin(&sblock, c); + if (c == 0) { + i = cgbase(&sblock, c); + cgd += howmany(sblock.fs_cssize, sblock.fs_fsize); + } else + i = cgsblock(&sblock, c); + for (; i < cgd; i++) + setbmap(i); + } + /* + * Find all allocated blocks. + */ + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass1check; + inumber = 0; + n_files = n_blks = 0; + resetinodebuf(); + for (c = 0; c < sblock.fs_ncg; c++) { + for (i = 0; i < sblock.fs_ipg; i++, inumber++) { + if (inumber < ROOTINO) + continue; + checkinode(inumber, &idesc); + } + } + freeinodebuf(); +} + +checkinode(inumber, idesc) + ino_t inumber; + register struct inodesc *idesc; +{ + register struct dinode *dp; + struct zlncnt *zlnp; + int ndb, j; + mode_t mode; + char symbuf[MAXSYMLINKLEN]; + + dp = getnextinode(inumber); + mode = dp->di_mode & IFMT; + if (mode == 0) { + if (bcmp((char *)dp->di_db, (char *)zino.di_db, + NDADDR * sizeof(daddr_t)) || + bcmp((char *)dp->di_ib, (char *)zino.di_ib, + NIADDR * sizeof(daddr_t)) || + dp->di_mode || dp->di_size) { + pfatal("PARTIALLY ALLOCATED INODE I=%lu", inumber); + if (reply("CLEAR") == 1) { + dp = ginode(inumber); + clearinode(dp); + inodirty(); + } + } + statemap[inumber] = USTATE; + return; + } + lastino = inumber; + if (/* dp->di_size < 0 || */ + dp->di_size + sblock.fs_bsize - 1 < dp->di_size) { + if (debug) + printf("bad size %qu:", dp->di_size); + goto unknown; + } + if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) { + dp = ginode(inumber); + dp->di_size = sblock.fs_fsize; + dp->di_mode = IFREG|0600; + inodirty(); + } + ndb = howmany(dp->di_size, sblock.fs_bsize); + if (ndb < 0) { + if (debug) + printf("bad size %qu ndb %d:", + dp->di_size, ndb); + goto unknown; + } + if (mode == IFBLK || mode == IFCHR) + ndb++; + if (mode == IFLNK) { + if (doinglevel2 && + dp->di_size > 0 && dp->di_size < MAXSYMLINKLEN && + dp->di_blocks != 0) { + if (bread(fsreadfd, symbuf, + fsbtodb(&sblock, dp->di_db[0]), + (long)dp->di_size) != 0) + errexit("cannot read symlink"); + if (debug) { + symbuf[dp->di_size] = 0; + printf("convert symlink %d(%s) of size %d\n", + inumber, symbuf, (long)dp->di_size); + } + dp = ginode(inumber); + bcopy(symbuf, (caddr_t)dp->di_shortlink, + (long)dp->di_size); + dp->di_blocks = 0; + inodirty(); + } + /* + * Fake ndb value so direct/indirect block checks below + * will detect any garbage after symlink string. + */ + if (dp->di_size < sblock.fs_maxsymlinklen) { + ndb = howmany(dp->di_size, sizeof(daddr_t)); + if (ndb > NDADDR) { + j = ndb - NDADDR; + for (ndb = 1; j > 1; j--) + ndb *= NINDIR(&sblock); + ndb += NDADDR; + } + } + } + for (j = ndb; j < NDADDR; j++) + if (dp->di_db[j] != 0) { + if (debug) + printf("bad direct addr: %ld\n", dp->di_db[j]); + goto unknown; + } + for (j = 0, ndb -= NDADDR; ndb > 0; j++) + ndb /= NINDIR(&sblock); + for (; j < NIADDR; j++) + if (dp->di_ib[j] != 0) { + if (debug) + printf("bad indirect addr: %ld\n", + dp->di_ib[j]); + goto unknown; + } + if (ftypeok(dp) == 0) + goto unknown; + n_files++; + lncntp[inumber] = dp->di_nlink; + if (dp->di_nlink <= 0) { + zlnp = (struct zlncnt *)malloc(sizeof *zlnp); + if (zlnp == NULL) { + pfatal("LINK COUNT TABLE OVERFLOW"); + if (reply("CONTINUE") == 0) + errexit(""); + } else { + zlnp->zlncnt = inumber; + zlnp->next = zlnhead; + zlnhead = zlnp; + } + } + if (mode == IFDIR) { + if (dp->di_size == 0) + statemap[inumber] = DCLEAR; + else + statemap[inumber] = DSTATE; + cacheino(dp, inumber); + } else + statemap[inumber] = FSTATE; + typemap[inumber] = IFTODT(mode); + if (doinglevel2 && + (dp->di_ouid != (u_short)-1 || dp->di_ogid != (u_short)-1)) { + dp = ginode(inumber); + dp->di_uid = dp->di_ouid; + dp->di_ouid = -1; + dp->di_gid = dp->di_ogid; + dp->di_ogid = -1; + inodirty(); + } + badblk = dupblk = 0; + idesc->id_number = inumber; + (void)ckinode(dp, idesc); + idesc->id_entryno *= btodb(sblock.fs_fsize); + if (dp->di_blocks != idesc->id_entryno) { + pwarn("INCORRECT BLOCK COUNT I=%lu (%ld should be %ld)", + inumber, dp->di_blocks, idesc->id_entryno); + if (preen) + printf(" (CORRECTED)\n"); + else if (reply("CORRECT") == 0) + return; + dp = ginode(inumber); + dp->di_blocks = idesc->id_entryno; + inodirty(); + } + return; +unknown: + pfatal("UNKNOWN FILE TYPE I=%lu", inumber); + statemap[inumber] = FCLEAR; + if (reply("CLEAR") == 1) { + statemap[inumber] = USTATE; + dp = ginode(inumber); + clearinode(dp); + inodirty(); + } +} + +pass1check(idesc) + register struct inodesc *idesc; +{ + int res = KEEPON; + int anyout, nfrags; + daddr_t blkno = idesc->id_blkno; + register struct dups *dlp; + struct dups *new; + + if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) { + blkerror(idesc->id_number, "BAD", blkno); + if (badblk++ >= MAXBAD) { + pwarn("EXCESSIVE BAD BLKS I=%lu", + idesc->id_number); + if (preen) + printf(" (SKIPPING)\n"); + else if (reply("CONTINUE") == 0) + errexit(""); + return (STOP); + } + } + for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { + if (anyout && chkrange(blkno, 1)) { + res = SKIP; + } else if (!testbmap(blkno)) { + n_blks++; + setbmap(blkno); + } else { + blkerror(idesc->id_number, "DUP", blkno); + if (dupblk++ >= MAXDUP) { + pwarn("EXCESSIVE DUP BLKS I=%lu", + idesc->id_number); + if (preen) + printf(" (SKIPPING)\n"); + else if (reply("CONTINUE") == 0) + errexit(""); + return (STOP); + } + new = (struct dups *)malloc(sizeof(struct dups)); + if (new == NULL) { + pfatal("DUP TABLE OVERFLOW."); + if (reply("CONTINUE") == 0) + errexit(""); + return (STOP); + } + new->dup = blkno; + if (muldup == 0) { + duplist = muldup = new; + new->next = 0; + } else { + new->next = muldup->next; + muldup->next = new; + } + for (dlp = duplist; dlp != muldup; dlp = dlp->next) + if (dlp->dup == blkno) + break; + if (dlp == muldup && dlp->dup != blkno) + muldup = new; + } + /* + * count the number of blocks found in id_entryno + */ + idesc->id_entryno++; + } + return (res); +} diff --git a/sbin/fsck/pass1b.c b/sbin/fsck/pass1b.c new file mode 100644 index 0000000..27b2dfd --- /dev/null +++ b/sbin/fsck/pass1b.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass1b.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#include <string.h> +#include "fsck.h" + +int pass1bcheck(); +static struct dups *duphead; + +pass1b() +{ + register int c, i; + register struct dinode *dp; + struct inodesc idesc; + ino_t inumber; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass1bcheck; + duphead = duplist; + inumber = 0; + for (c = 0; c < sblock.fs_ncg; c++) { + for (i = 0; i < sblock.fs_ipg; i++, inumber++) { + if (inumber < ROOTINO) + continue; + dp = ginode(inumber); + if (dp == NULL) + continue; + idesc.id_number = inumber; + if (statemap[inumber] != USTATE && + (ckinode(dp, &idesc) & STOP)) + return; + } + } +} + +pass1bcheck(idesc) + register struct inodesc *idesc; +{ + register struct dups *dlp; + int nfrags, res = KEEPON; + daddr_t blkno = idesc->id_blkno; + + for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { + if (chkrange(blkno, 1)) + res = SKIP; + for (dlp = duphead; dlp; dlp = dlp->next) { + if (dlp->dup == blkno) { + blkerror(idesc->id_number, "DUP", blkno); + dlp->dup = duphead->dup; + duphead->dup = blkno; + duphead = duphead->next; + } + if (dlp == muldup) + break; + } + if (muldup == 0 || duphead == muldup->next) + return (STOP); + } + return (res); +} diff --git a/sbin/fsck/pass2.c b/sbin/fsck/pass2.c new file mode 100644 index 0000000..6314751 --- /dev/null +++ b/sbin/fsck/pass2.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass2.c 8.2 (Berkeley) 2/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <stdlib.h> +#include <string.h> +#include "fsck.h" + +#define MINDIRSIZE (sizeof (struct dirtemplate)) + +int pass2check(), blksort(); + +pass2() +{ + register struct dinode *dp; + register struct inoinfo **inpp, *inp; + struct inoinfo **inpend; + struct inodesc curino; + struct dinode dino; + char pathbuf[MAXPATHLEN + 1]; + + switch (statemap[ROOTINO]) { + + case USTATE: + pfatal("ROOT INODE UNALLOCATED"); + if (reply("ALLOCATE") == 0) + errexit(""); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + + case DCLEAR: + pfatal("DUPS/BAD IN ROOT INODE"); + if (reply("REALLOCATE")) { + freeino(ROOTINO); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + } + if (reply("CONTINUE") == 0) + errexit(""); + break; + + case FSTATE: + case FCLEAR: + pfatal("ROOT INODE NOT DIRECTORY"); + if (reply("REALLOCATE")) { + freeino(ROOTINO); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + } + if (reply("FIX") == 0) + errexit(""); + dp = ginode(ROOTINO); + dp->di_mode &= ~IFMT; + dp->di_mode |= IFDIR; + inodirty(); + break; + + case DSTATE: + break; + + default: + errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); + } + statemap[ROOTINO] = DFOUND; + /* + * Sort the directory list into disk block order. + */ + qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); + /* + * Check the integrity of each directory. + */ + bzero((char *)&curino, sizeof(struct inodesc)); + curino.id_type = DATA; + curino.id_func = pass2check; + dp = &dino; + inpend = &inpsort[inplast]; + for (inpp = inpsort; inpp < inpend; inpp++) { + inp = *inpp; + if (inp->i_isize == 0) + continue; + if (inp->i_isize < MINDIRSIZE) { + direrror(inp->i_number, "DIRECTORY TOO SHORT"); + inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); + if (reply("FIX") == 1) { + dp = ginode(inp->i_number); + dp->di_size = inp->i_isize; + inodirty(); + dp = &dino; + } + } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { + getpathname(pathbuf, inp->i_number, inp->i_number); + pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", + pathbuf, inp->i_isize, DIRBLKSIZ); + if (preen) + printf(" (ADJUSTED)\n"); + inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); + if (preen || reply("ADJUST") == 1) { + dp = ginode(inp->i_number); + dp->di_size = roundup(inp->i_isize, DIRBLKSIZ); + inodirty(); + dp = &dino; + } + } + bzero((char *)&dino, sizeof(struct dinode)); + dino.di_mode = IFDIR; + dp->di_size = inp->i_isize; + bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], + (size_t)inp->i_numblks); + curino.id_number = inp->i_number; + curino.id_parent = inp->i_parent; + (void)ckinode(dp, &curino); + } + /* + * Now that the parents of all directories have been found, + * make another pass to verify the value of `..' + */ + for (inpp = inpsort; inpp < inpend; inpp++) { + inp = *inpp; + if (inp->i_parent == 0 || inp->i_isize == 0) + continue; + if (statemap[inp->i_parent] == DFOUND && + statemap[inp->i_number] == DSTATE) + statemap[inp->i_number] = DFOUND; + if (inp->i_dotdot == inp->i_parent || + inp->i_dotdot == (ino_t)-1) + continue; + if (inp->i_dotdot == 0) { + inp->i_dotdot = inp->i_parent; + fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); + if (reply("FIX") == 0) + continue; + (void)makeentry(inp->i_number, inp->i_parent, ".."); + lncntp[inp->i_parent]--; + continue; + } + fileerror(inp->i_parent, inp->i_number, + "BAD INODE NUMBER FOR '..'"); + if (reply("FIX") == 0) + continue; + lncntp[inp->i_dotdot]++; + lncntp[inp->i_parent]--; + inp->i_dotdot = inp->i_parent; + (void)changeino(inp->i_number, "..", inp->i_parent); + } + /* + * Mark all the directories that can be found from the root. + */ + propagate(); +} + +pass2check(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + register struct inoinfo *inp; + int n, entrysize, ret = 0; + struct dinode *dp; + char *errmsg; + struct direct proto; + char namebuf[MAXPATHLEN + 1]; + char pathbuf[MAXPATHLEN + 1]; + + /* + * If converting, set directory entry type. + */ + if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) { + dirp->d_type = typemap[dirp->d_ino]; + ret |= ALTERED; + } + /* + * check for "." + */ + if (idesc->id_entryno != 0) + goto chk1; + if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { + if (dirp->d_ino != idesc->id_number) { + direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); + dirp->d_ino = idesc->id_number; + if (reply("FIX") == 1) + ret |= ALTERED; + } + if (newinofmt && dirp->d_type != DT_DIR) { + direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); + dirp->d_type = DT_DIR; + if (reply("FIX") == 1) + ret |= ALTERED; + } + goto chk1; + } + direrror(idesc->id_number, "MISSING '.'"); + proto.d_ino = idesc->id_number; + if (newinofmt) + proto.d_type = DT_DIR; + else + proto.d_type = 0; + proto.d_namlen = 1; + (void)strcpy(proto.d_name, "."); + entrysize = DIRSIZ(0, &proto); + if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { + pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", + dirp->d_name); + } else if (dirp->d_reclen < entrysize) { + pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); + } else if (dirp->d_reclen < 2 * entrysize) { + proto.d_reclen = dirp->d_reclen; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + if (reply("FIX") == 1) + ret |= ALTERED; + } else { + n = dirp->d_reclen - entrysize; + proto.d_reclen = entrysize; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + idesc->id_entryno++; + lncntp[dirp->d_ino]--; + dirp = (struct direct *)((char *)(dirp) + entrysize); + bzero((char *)dirp, (size_t)n); + dirp->d_reclen = n; + if (reply("FIX") == 1) + ret |= ALTERED; + } +chk1: + if (idesc->id_entryno > 1) + goto chk2; + inp = getinoinfo(idesc->id_number); + proto.d_ino = inp->i_parent; + if (newinofmt) + proto.d_type = DT_DIR; + else + proto.d_type = 0; + proto.d_namlen = 2; + (void)strcpy(proto.d_name, ".."); + entrysize = DIRSIZ(0, &proto); + if (idesc->id_entryno == 0) { + n = DIRSIZ(0, dirp); + if (dirp->d_reclen < n + entrysize) + goto chk2; + proto.d_reclen = dirp->d_reclen - n; + dirp->d_reclen = n; + idesc->id_entryno++; + lncntp[dirp->d_ino]--; + dirp = (struct direct *)((char *)(dirp) + n); + bzero((char *)dirp, (size_t)proto.d_reclen); + dirp->d_reclen = proto.d_reclen; + } + if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { + inp->i_dotdot = dirp->d_ino; + if (newinofmt && dirp->d_type != DT_DIR) { + direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); + dirp->d_type = DT_DIR; + if (reply("FIX") == 1) + ret |= ALTERED; + } + goto chk2; + } + if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { + fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", + dirp->d_name); + inp->i_dotdot = (ino_t)-1; + } else if (dirp->d_reclen < entrysize) { + fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); + inp->i_dotdot = (ino_t)-1; + } else if (inp->i_parent != 0) { + /* + * We know the parent, so fix now. + */ + inp->i_dotdot = inp->i_parent; + fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + proto.d_reclen = dirp->d_reclen; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + if (reply("FIX") == 1) + ret |= ALTERED; + } + idesc->id_entryno++; + if (dirp->d_ino != 0) + lncntp[dirp->d_ino]--; + return (ret|KEEPON); +chk2: + if (dirp->d_ino == 0) + return (ret|KEEPON); + if (dirp->d_namlen <= 2 && + dirp->d_name[0] == '.' && + idesc->id_entryno >= 2) { + if (dirp->d_namlen == 1) { + direrror(idesc->id_number, "EXTRA '.' ENTRY"); + dirp->d_ino = 0; + if (reply("FIX") == 1) + ret |= ALTERED; + return (KEEPON | ret); + } + if (dirp->d_name[1] == '.') { + direrror(idesc->id_number, "EXTRA '..' ENTRY"); + dirp->d_ino = 0; + if (reply("FIX") == 1) + ret |= ALTERED; + return (KEEPON | ret); + } + } + idesc->id_entryno++; + n = 0; + if (dirp->d_ino > maxino) { + fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); + n = reply("REMOVE"); + } else { +again: + switch (statemap[dirp->d_ino]) { + case USTATE: + if (idesc->id_entryno <= 2) + break; + fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); + n = reply("REMOVE"); + break; + + case DCLEAR: + case FCLEAR: + if (idesc->id_entryno <= 2) + break; + if (statemap[dirp->d_ino] == FCLEAR) + errmsg = "DUP/BAD"; + else if (!preen) + errmsg = "ZERO LENGTH DIRECTORY"; + else { + n = 1; + break; + } + fileerror(idesc->id_number, dirp->d_ino, errmsg); + if ((n = reply("REMOVE")) == 1) + break; + dp = ginode(dirp->d_ino); + statemap[dirp->d_ino] = + (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; + lncntp[dirp->d_ino] = dp->di_nlink; + goto again; + + case DSTATE: + if (statemap[idesc->id_number] == DFOUND) + statemap[dirp->d_ino] = DFOUND; + /* fall through */ + + case DFOUND: + inp = getinoinfo(dirp->d_ino); + if (inp->i_parent != 0 && idesc->id_entryno > 2) { + getpathname(pathbuf, idesc->id_number, + idesc->id_number); + getpathname(namebuf, dirp->d_ino, dirp->d_ino); + pwarn("%s %s %s\n", pathbuf, + "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", + namebuf); + if (preen) + printf(" (IGNORED)\n"); + else if ((n = reply("REMOVE")) == 1) + break; + } + if (idesc->id_entryno > 2) + inp->i_parent = idesc->id_number; + /* fall through */ + + case FSTATE: + if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) { + fileerror(idesc->id_number, dirp->d_ino, + "BAD TYPE VALUE"); + dirp->d_type = typemap[dirp->d_ino]; + if (reply("FIX") == 1) + ret |= ALTERED; + } + lncntp[dirp->d_ino]--; + break; + + default: + errexit("BAD STATE %d FOR INODE I=%d", + statemap[dirp->d_ino], dirp->d_ino); + } + } + if (n == 0) + return (ret|KEEPON); + dirp->d_ino = 0; + return (ret|KEEPON|ALTERED); +} + +/* + * Routine to sort disk blocks. + */ +blksort(inpp1, inpp2) + struct inoinfo **inpp1, **inpp2; +{ + + return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]); +} diff --git a/sbin/fsck/pass3.c b/sbin/fsck/pass3.c new file mode 100644 index 0000000..5c6f09d --- /dev/null +++ b/sbin/fsck/pass3.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass3.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#include "fsck.h" + +pass3() +{ + register struct inoinfo **inpp, *inp; + ino_t orphan; + int loopcnt; + + for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) { + inp = *inpp; + if (inp->i_number == ROOTINO || + !(inp->i_parent == 0 || statemap[inp->i_number] == DSTATE)) + continue; + if (statemap[inp->i_number] == DCLEAR) + continue; + for (loopcnt = 0; ; loopcnt++) { + orphan = inp->i_number; + if (inp->i_parent == 0 || + statemap[inp->i_parent] != DSTATE || + loopcnt > numdirs) + break; + inp = getinoinfo(inp->i_parent); + } + (void)linkup(orphan, inp->i_dotdot); + inp->i_parent = inp->i_dotdot = lfdir; + lncntp[lfdir]--; + statemap[orphan] = DFOUND; + propagate(); + } +} diff --git a/sbin/fsck/pass4.c b/sbin/fsck/pass4.c new file mode 100644 index 0000000..7450530 --- /dev/null +++ b/sbin/fsck/pass4.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass4.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#include <stdlib.h> +#include <string.h> +#include "fsck.h" + +int pass4check(); + +pass4() +{ + register ino_t inumber; + register struct zlncnt *zlnp; + struct dinode *dp; + struct inodesc idesc; + int n; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass4check; + for (inumber = ROOTINO; inumber <= lastino; inumber++) { + idesc.id_number = inumber; + switch (statemap[inumber]) { + + case FSTATE: + case DFOUND: + n = lncntp[inumber]; + if (n) + adjust(&idesc, (short)n); + else { + for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) + if (zlnp->zlncnt == inumber) { + zlnp->zlncnt = zlnhead->zlncnt; + zlnp = zlnhead; + zlnhead = zlnhead->next; + free((char *)zlnp); + clri(&idesc, "UNREF", 1); + break; + } + } + break; + + case DSTATE: + clri(&idesc, "UNREF", 1); + break; + + case DCLEAR: + dp = ginode(inumber); + if (dp->di_size == 0) { + clri(&idesc, "ZERO LENGTH", 1); + break; + } + /* fall through */ + case FCLEAR: + clri(&idesc, "BAD/DUP", 1); + break; + + case USTATE: + break; + + default: + errexit("BAD STATE %d FOR INODE I=%d", + statemap[inumber], inumber); + } + } +} + +pass4check(idesc) + register struct inodesc *idesc; +{ + register struct dups *dlp; + int nfrags, res = KEEPON; + daddr_t blkno = idesc->id_blkno; + + for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { + if (chkrange(blkno, 1)) { + res = SKIP; + } else if (testbmap(blkno)) { + for (dlp = duplist; dlp; dlp = dlp->next) { + if (dlp->dup != blkno) + continue; + dlp->dup = duplist->dup; + dlp = duplist; + duplist = duplist->next; + free((char *)dlp); + break; + } + if (dlp == 0) { + clrbmap(blkno); + n_blks--; + } + } + } + return (res); +} diff --git a/sbin/fsck/pass5.c b/sbin/fsck/pass5.c new file mode 100644 index 0000000..2e98f96 --- /dev/null +++ b/sbin/fsck/pass5.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass5.c 8.2 (Berkeley) 2/2/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#include <string.h> +#include "fsck.h" + +pass5() +{ + int c, blk, frags, basesize, sumsize, mapsize, savednrpos; + register struct fs *fs = &sblock; + register struct cg *cg = &cgrp; + daddr_t dbase, dmax; + register daddr_t d; + register long i, j; + struct csum *cs; + struct csum cstotal; + struct inodesc idesc[3]; + char buf[MAXBSIZE]; + register struct cg *newcg = (struct cg *)buf; + struct ocg *ocg = (struct ocg *)buf; + + bzero((char *)newcg, (size_t)fs->fs_cgsize); + newcg->cg_niblk = fs->fs_ipg; + if (cvtlevel > 3) { + if (fs->fs_maxcontig < 2 && fs->fs_contigsumsize > 0) { + if (preen) + pwarn("DELETING CLUSTERING MAPS\n"); + if (preen || reply("DELETE CLUSTERING MAPS")) { + fs->fs_contigsumsize = 0; + doinglevel1 = 1; + sbdirty(); + } + } + if (fs->fs_maxcontig > 1) { + char *doit = 0; + + if (fs->fs_contigsumsize < 1) { + doit = "CREAT"; + } else if (fs->fs_contigsumsize < fs->fs_maxcontig && + fs->fs_contigsumsize < FS_MAXCONTIG) { + doit = "EXPAND"; + } + if (doit) { + i = fs->fs_contigsumsize; + fs->fs_contigsumsize = + MIN(fs->fs_maxcontig, FS_MAXCONTIG); + if (CGSIZE(fs) > fs->fs_bsize) { + pwarn("CANNOT %s CLUSTER MAPS\n", doit); + fs->fs_contigsumsize = i; + } else if (preen || + reply("CREATE CLUSTER MAPS")) { + if (preen) + pwarn("%sING CLUSTER MAPS\n", + doit); + fs->fs_cgsize = + fragroundup(fs, CGSIZE(fs)); + doinglevel1 = 1; + sbdirty(); + } + } + } + } + switch ((int)fs->fs_postblformat) { + + case FS_42POSTBLFMT: + basesize = (char *)(&ocg->cg_btot[0]) - (char *)(&ocg->cg_link); + sumsize = &ocg->cg_iused[0] - (char *)(&ocg->cg_btot[0]); + mapsize = &ocg->cg_free[howmany(fs->fs_fpg, NBBY)] - + (u_char *)&ocg->cg_iused[0]; + ocg->cg_magic = CG_MAGIC; + savednrpos = fs->fs_nrpos; + fs->fs_nrpos = 8; + break; + + case FS_DYNAMICPOSTBLFMT: + newcg->cg_btotoff = + &newcg->cg_space[0] - (u_char *)(&newcg->cg_link); + newcg->cg_boff = + newcg->cg_btotoff + fs->fs_cpg * sizeof(long); + newcg->cg_iusedoff = newcg->cg_boff + + fs->fs_cpg * fs->fs_nrpos * sizeof(short); + newcg->cg_freeoff = + newcg->cg_iusedoff + howmany(fs->fs_ipg, NBBY); + if (fs->fs_contigsumsize <= 0) { + newcg->cg_nextfreeoff = newcg->cg_freeoff + + howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY); + } else { + newcg->cg_clustersumoff = newcg->cg_freeoff + + howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY) - + sizeof(long); + newcg->cg_clustersumoff = + roundup(newcg->cg_clustersumoff, sizeof(long)); + newcg->cg_clusteroff = newcg->cg_clustersumoff + + (fs->fs_contigsumsize + 1) * sizeof(long); + newcg->cg_nextfreeoff = newcg->cg_clusteroff + + howmany(fs->fs_cpg * fs->fs_spc / NSPB(fs), NBBY); + } + newcg->cg_magic = CG_MAGIC; + basesize = &newcg->cg_space[0] - (u_char *)(&newcg->cg_link); + sumsize = newcg->cg_iusedoff - newcg->cg_btotoff; + mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff; + break; + + default: + errexit("UNKNOWN ROTATIONAL TABLE FORMAT %d\n", + fs->fs_postblformat); + } + bzero((char *)&idesc[0], sizeof idesc); + for (i = 0; i < 3; i++) { + idesc[i].id_type = ADDR; + if (doinglevel2) + idesc[i].id_fix = FIX; + } + bzero((char *)&cstotal, sizeof(struct csum)); + j = blknum(fs, fs->fs_size + fs->fs_frag - 1); + for (i = fs->fs_size; i < j; i++) + setbmap(i); + for (c = 0; c < fs->fs_ncg; c++) { + getblk(&cgblk, cgtod(fs, c), fs->fs_cgsize); + if (!cg_chkmagic(cg)) + pfatal("CG %d: BAD MAGIC NUMBER\n", c); + dbase = cgbase(fs, c); + dmax = dbase + fs->fs_fpg; + if (dmax > fs->fs_size) + dmax = fs->fs_size; + newcg->cg_time = cg->cg_time; + newcg->cg_cgx = c; + if (c == fs->fs_ncg - 1) + newcg->cg_ncyl = fs->fs_ncyl % fs->fs_cpg; + else + newcg->cg_ncyl = fs->fs_cpg; + newcg->cg_ndblk = dmax - dbase; + if (fs->fs_contigsumsize > 0) + newcg->cg_nclusterblks = newcg->cg_ndblk / fs->fs_frag; + newcg->cg_cs.cs_ndir = 0; + newcg->cg_cs.cs_nffree = 0; + newcg->cg_cs.cs_nbfree = 0; + newcg->cg_cs.cs_nifree = fs->fs_ipg; + if (cg->cg_rotor < newcg->cg_ndblk) + newcg->cg_rotor = cg->cg_rotor; + else + newcg->cg_rotor = 0; + if (cg->cg_frotor < newcg->cg_ndblk) + newcg->cg_frotor = cg->cg_frotor; + else + newcg->cg_frotor = 0; + if (cg->cg_irotor < newcg->cg_niblk) + newcg->cg_irotor = cg->cg_irotor; + else + newcg->cg_irotor = 0; + bzero((char *)&newcg->cg_frsum[0], sizeof newcg->cg_frsum); + bzero((char *)&cg_blktot(newcg)[0], + (size_t)(sumsize + mapsize)); + if (fs->fs_postblformat == FS_42POSTBLFMT) + ocg->cg_magic = CG_MAGIC; + j = fs->fs_ipg * c; + for (i = 0; i < fs->fs_ipg; j++, i++) { + switch (statemap[j]) { + + case USTATE: + break; + + case DSTATE: + case DCLEAR: + case DFOUND: + newcg->cg_cs.cs_ndir++; + /* fall through */ + + case FSTATE: + case FCLEAR: + newcg->cg_cs.cs_nifree--; + setbit(cg_inosused(newcg), i); + break; + + default: + if (j < ROOTINO) + break; + errexit("BAD STATE %d FOR INODE I=%d", + statemap[j], j); + } + } + if (c == 0) + for (i = 0; i < ROOTINO; i++) { + setbit(cg_inosused(newcg), i); + newcg->cg_cs.cs_nifree--; + } + for (i = 0, d = dbase; + d < dmax; + d += fs->fs_frag, i += fs->fs_frag) { + frags = 0; + for (j = 0; j < fs->fs_frag; j++) { + if (testbmap(d + j)) + continue; + setbit(cg_blksfree(newcg), i + j); + frags++; + } + if (frags == fs->fs_frag) { + newcg->cg_cs.cs_nbfree++; + j = cbtocylno(fs, i); + cg_blktot(newcg)[j]++; + cg_blks(fs, newcg, j)[cbtorpos(fs, i)]++; + if (fs->fs_contigsumsize > 0) + setbit(cg_clustersfree(newcg), + i / fs->fs_frag); + } else if (frags > 0) { + newcg->cg_cs.cs_nffree += frags; + blk = blkmap(fs, cg_blksfree(newcg), i); + ffs_fragacct(fs, blk, newcg->cg_frsum, 1); + } + } + if (fs->fs_contigsumsize > 0) { + long *sump = cg_clustersum(newcg); + u_char *mapp = cg_clustersfree(newcg); + int map = *mapp++; + int bit = 1; + int run = 0; + + for (i = 0; i < newcg->cg_nclusterblks; i++) { + if ((map & bit) != 0) { + run++; + } else if (run != 0) { + if (run > fs->fs_contigsumsize) + run = fs->fs_contigsumsize; + sump[run]++; + run = 0; + } + if ((i & (NBBY - 1)) != (NBBY - 1)) { + bit <<= 1; + } else { + map = *mapp++; + bit = 1; + } + } + if (run != 0) { + if (run > fs->fs_contigsumsize) + run = fs->fs_contigsumsize; + sump[run]++; + } + } + cstotal.cs_nffree += newcg->cg_cs.cs_nffree; + cstotal.cs_nbfree += newcg->cg_cs.cs_nbfree; + cstotal.cs_nifree += newcg->cg_cs.cs_nifree; + cstotal.cs_ndir += newcg->cg_cs.cs_ndir; + cs = &fs->fs_cs(fs, c); + if (bcmp((char *)&newcg->cg_cs, (char *)cs, sizeof *cs) != 0 && + dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) { + bcopy((char *)&newcg->cg_cs, (char *)cs, sizeof *cs); + sbdirty(); + } + if (doinglevel1) { + bcopy((char *)newcg, (char *)cg, (size_t)fs->fs_cgsize); + cgdirty(); + continue; + } + if (bcmp(cg_inosused(newcg), + cg_inosused(cg), mapsize) != 0 && + dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) { + bcopy(cg_inosused(newcg), cg_inosused(cg), + (size_t)mapsize); + cgdirty(); + } + if ((bcmp((char *)newcg, (char *)cg, basesize) != 0 || + bcmp((char *)&cg_blktot(newcg)[0], + (char *)&cg_blktot(cg)[0], sumsize) != 0) && + dofix(&idesc[2], "SUMMARY INFORMATION BAD")) { + bcopy((char *)newcg, (char *)cg, (size_t)basesize); + bcopy((char *)&cg_blktot(newcg)[0], + (char *)&cg_blktot(cg)[0], (size_t)sumsize); + cgdirty(); + } + } + if (fs->fs_postblformat == FS_42POSTBLFMT) + fs->fs_nrpos = savednrpos; + if (bcmp((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs) != 0 + && dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) { + bcopy((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs); + fs->fs_ronly = 0; + fs->fs_fmod = 0; + sbdirty(); + } +} diff --git a/sbin/fsck/preen.c b/sbin/fsck/preen.c new file mode 100644 index 0000000..005a65d --- /dev/null +++ b/sbin/fsck/preen.c @@ -0,0 +1,354 @@ +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)preen.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <fstab.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> + +char *rawname(), *unrawname(), *blockcheck(); + +struct part { + struct part *next; /* forward link of partitions on disk */ + char *name; /* device name */ + char *fsname; /* mounted filesystem name */ + long auxdata; /* auxillary data for application */ +} *badlist, **badnext = &badlist; + +struct disk { + char *name; /* disk base name */ + struct disk *next; /* forward link for list of disks */ + struct part *part; /* head of list of partitions on disk */ + int pid; /* If != 0, pid of proc working on */ +} *disks; + +int nrun, ndisks; +char hotroot; + +checkfstab(preen, maxrun, docheck, chkit) + int preen, maxrun; + int (*docheck)(), (*chkit)(); +{ + register struct fstab *fsp; + register struct disk *dk, *nextdisk; + register struct part *pt; + int ret, pid, retcode, passno, sumstatus, status; + long auxdata; + char *name; + + sumstatus = 0; + for (passno = 1; passno <= 2; passno++) { + if (setfsent() == 0) { + fprintf(stderr, "Can't open checklist file: %s\n", + _PATH_FSTAB); + return (8); + } + while ((fsp = getfsent()) != 0) { + if ((auxdata = (*docheck)(fsp)) == 0) + continue; + if (preen == 0 || passno == 1 && fsp->fs_passno == 1) { + if (name = blockcheck(fsp->fs_spec)) { + if (sumstatus = (*chkit)(name, + fsp->fs_file, auxdata, 0)) + return (sumstatus); + } else if (preen) + return (8); + } else if (passno == 2 && fsp->fs_passno > 1) { + if ((name = blockcheck(fsp->fs_spec)) == NULL) { + fprintf(stderr, "BAD DISK NAME %s\n", + fsp->fs_spec); + sumstatus |= 8; + continue; + } + addpart(name, fsp->fs_file, auxdata); + } + } + if (preen == 0) + return (0); + } + if (preen) { + if (maxrun == 0) + maxrun = ndisks; + if (maxrun > ndisks) + maxrun = ndisks; + nextdisk = disks; + for (passno = 0; passno < maxrun; ++passno) { + while (ret = startdisk(nextdisk, chkit) && nrun > 0) + sleep(10); + if (ret) + return (ret); + nextdisk = nextdisk->next; + } + while ((pid = wait(&status)) != -1) { + for (dk = disks; dk; dk = dk->next) + if (dk->pid == pid) + break; + if (dk == 0) { + printf("Unknown pid %d\n", pid); + continue; + } + if (WIFEXITED(status)) + retcode = WEXITSTATUS(status); + else + retcode = 0; + if (WIFSIGNALED(status)) { + printf("%s (%s): EXITED WITH SIGNAL %d\n", + dk->part->name, dk->part->fsname, + WTERMSIG(status)); + retcode = 8; + } + if (retcode != 0) { + sumstatus |= retcode; + *badnext = dk->part; + badnext = &dk->part->next; + dk->part = dk->part->next; + *badnext = NULL; + } else + dk->part = dk->part->next; + dk->pid = 0; + nrun--; + if (dk->part == NULL) + ndisks--; + + if (nextdisk == NULL) { + if (dk->part) { + while (ret = startdisk(dk, chkit) && + nrun > 0) + sleep(10); + if (ret) + return (ret); + } + } else if (nrun < maxrun && nrun < ndisks) { + for ( ;; ) { + if ((nextdisk = nextdisk->next) == NULL) + nextdisk = disks; + if (nextdisk->part != NULL && + nextdisk->pid == 0) + break; + } + while (ret = startdisk(nextdisk, chkit) && + nrun > 0) + sleep(10); + if (ret) + return (ret); + } + } + } + if (sumstatus) { + if (badlist == 0) + return (sumstatus); + fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t", + badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:"); + for (pt = badlist; pt; pt = pt->next) + fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname, + pt->next ? ", " : "\n"); + return (sumstatus); + } + (void)endfsent(); + return (0); +} + +struct disk * +finddisk(name) + char *name; +{ + register struct disk *dk, **dkp; + register char *p; + size_t len; + + for (p = name + strlen(name) - 1; p >= name; --p) + if (isdigit(*p)) { + len = p - name + 1; + break; + } + if (p < name) + len = strlen(name); + + for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) { + if (strncmp(dk->name, name, len) == 0 && + dk->name[len] == 0) + return (dk); + } + if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + dk = *dkp; + if ((dk->name = malloc(len + 1)) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + (void)strncpy(dk->name, name, len); + dk->name[len] = '\0'; + dk->part = NULL; + dk->next = NULL; + dk->pid = 0; + ndisks++; + return (dk); +} + +addpart(name, fsname, auxdata) + char *name, *fsname; + long auxdata; +{ + struct disk *dk = finddisk(name); + register struct part *pt, **ppt = &dk->part; + + for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next) + if (strcmp(pt->name, name) == 0) { + printf("%s in fstab more than once!\n", name); + return; + } + if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + pt = *ppt; + if ((pt->name = malloc(strlen(name) + 1)) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + (void)strcpy(pt->name, name); + if ((pt->fsname = malloc(strlen(fsname) + 1)) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + (void)strcpy(pt->fsname, fsname); + pt->next = NULL; + pt->auxdata = auxdata; +} + +startdisk(dk, checkit) + register struct disk *dk; + int (*checkit)(); +{ + register struct part *pt = dk->part; + + dk->pid = fork(); + if (dk->pid < 0) { + perror("fork"); + return (8); + } + if (dk->pid == 0) + exit((*checkit)(pt->name, pt->fsname, pt->auxdata, 1)); + nrun++; + return (0); +} + +char * +blockcheck(name) + char *name; +{ + struct stat stslash, stblock, stchar; + char *raw; + int retried = 0; + + hotroot = 0; + if (stat("/", &stslash) < 0) { + perror("/"); + printf("Can't stat root\n"); + return (0); + } +retry: + if (stat(name, &stblock) < 0) { + perror(name); + printf("Can't stat %s\n", name); + return (0); + } + if ((stblock.st_mode & S_IFMT) == S_IFBLK) { + if (stslash.st_dev == stblock.st_rdev) + hotroot++; + raw = rawname(name); + if (stat(raw, &stchar) < 0) { + perror(raw); + printf("Can't stat %s\n", raw); + return (name); + } + if ((stchar.st_mode & S_IFMT) == S_IFCHR) { + return (raw); + } else { + printf("%s is not a character device\n", raw); + return (name); + } + } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) { + name = unrawname(name); + retried++; + goto retry; + } + printf("Can't make sense out of name %s\n", name); + return (0); +} + +char * +unrawname(name) + char *name; +{ + char *dp; + struct stat stb; + + if ((dp = rindex(name, '/')) == 0) + return (name); + if (stat(name, &stb) < 0) + return (name); + if ((stb.st_mode & S_IFMT) != S_IFCHR) + return (name); + if (dp[1] != 'r') + return (name); + (void)strcpy(&dp[1], &dp[2]); + return (name); +} + +char * +rawname(name) + char *name; +{ + static char rawbuf[32]; + char *dp; + + if ((dp = rindex(name, '/')) == 0) + return (0); + *dp = 0; + (void)strcpy(rawbuf, name); + *dp = '/'; + (void)strcat(rawbuf, "/r"); + (void)strcat(rawbuf, &dp[1]); + return (rawbuf); +} diff --git a/sbin/fsck/setup.c b/sbin/fsck/setup.c new file mode 100644 index 0000000..a32b23c --- /dev/null +++ b/sbin/fsck/setup.c @@ -0,0 +1,466 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)setup.c 8.2 (Berkeley) 2/21/94"; +#endif /* not lint */ + +#define DKTYPENAMES +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/disklabel.h> +#include <sys/file.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "fsck.h" + +struct bufarea asblk; +#define altsblock (*asblk.b_un.b_fs) +#define POWEROF2(num) (((num) & ((num) - 1)) == 0) + +struct disklabel *getdisklabel(); + +setup(dev) + char *dev; +{ + long cg, size, asked, i, j; + long bmapsize; + struct disklabel *lp; + off_t sizepb; + struct stat statb; + struct fs proto; + + havesb = 0; + fswritefd = -1; + if (stat(dev, &statb) < 0) { + printf("Can't stat %s: %s\n", dev, strerror(errno)); + return (0); + } + if ((statb.st_mode & S_IFMT) != S_IFCHR) { + pfatal("%s is not a character device", dev); + if (reply("CONTINUE") == 0) + return (0); + } + if ((fsreadfd = open(dev, O_RDONLY)) < 0) { + printf("Can't open %s: %s\n", dev, strerror(errno)); + return (0); + } + if (preen == 0) + printf("** %s", dev); + if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) { + fswritefd = -1; + if (preen) + pfatal("NO WRITE ACCESS"); + printf(" (NO WRITE)"); + } + if (preen == 0) + printf("\n"); + fsmodified = 0; + lfdir = 0; + initbarea(&sblk); + initbarea(&asblk); + sblk.b_un.b_buf = malloc(SBSIZE); + asblk.b_un.b_buf = malloc(SBSIZE); + if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL) + errexit("cannot allocate space for superblock\n"); + if (lp = getdisklabel((char *)NULL, fsreadfd)) + dev_bsize = secsize = lp->d_secsize; + else + dev_bsize = secsize = DEV_BSIZE; + /* + * Read in the superblock, looking for alternates if necessary + */ + if (readsb(1) == 0) { + if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0) + return(0); + if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0) + return (0); + for (cg = 0; cg < proto.fs_ncg; cg++) { + bflag = fsbtodb(&proto, cgsblock(&proto, cg)); + if (readsb(0) != 0) + break; + } + if (cg >= proto.fs_ncg) { + printf("%s %s\n%s %s\n%s %s\n", + "SEARCH FOR ALTERNATE SUPER-BLOCK", + "FAILED. YOU MUST USE THE", + "-b OPTION TO FSCK TO SPECIFY THE", + "LOCATION OF AN ALTERNATE", + "SUPER-BLOCK TO SUPPLY NEEDED", + "INFORMATION; SEE fsck(8)."); + return(0); + } + pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag); + } + maxfsblock = sblock.fs_size; + maxino = sblock.fs_ncg * sblock.fs_ipg; + /* + * Check and potentially fix certain fields in the super block. + */ + if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) { + pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK"); + if (reply("SET TO DEFAULT") == 1) { + sblock.fs_optim = FS_OPTTIME; + sbdirty(); + } + } + if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) { + pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK", + sblock.fs_minfree); + if (reply("SET TO DEFAULT") == 1) { + sblock.fs_minfree = 10; + sbdirty(); + } + } + if (sblock.fs_interleave < 1 || + sblock.fs_interleave > sblock.fs_nsect) { + pwarn("IMPOSSIBLE INTERLEAVE=%d IN SUPERBLOCK", + sblock.fs_interleave); + sblock.fs_interleave = 1; + if (preen) + printf(" (FIXED)\n"); + if (preen || reply("SET TO DEFAULT") == 1) { + sbdirty(); + dirty(&asblk); + } + } + if (sblock.fs_npsect < sblock.fs_nsect || + sblock.fs_npsect > sblock.fs_nsect*2) { + pwarn("IMPOSSIBLE NPSECT=%d IN SUPERBLOCK", + sblock.fs_npsect); + sblock.fs_npsect = sblock.fs_nsect; + if (preen) + printf(" (FIXED)\n"); + if (preen || reply("SET TO DEFAULT") == 1) { + sbdirty(); + dirty(&asblk); + } + } + if (sblock.fs_inodefmt >= FS_44INODEFMT) { + newinofmt = 1; + } else { + sblock.fs_qbmask = ~sblock.fs_bmask; + sblock.fs_qfmask = ~sblock.fs_fmask; + newinofmt = 0; + } + /* + * Convert to new inode format. + */ + if (cvtlevel >= 2 && sblock.fs_inodefmt < FS_44INODEFMT) { + if (preen) + pwarn("CONVERTING TO NEW INODE FORMAT\n"); + else if (!reply("CONVERT TO NEW INODE FORMAT")) + return(0); + doinglevel2++; + sblock.fs_inodefmt = FS_44INODEFMT; + sizepb = sblock.fs_bsize; + sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1; + for (i = 0; i < NIADDR; i++) { + sizepb *= NINDIR(&sblock); + sblock.fs_maxfilesize += sizepb; + } + sblock.fs_maxsymlinklen = MAXSYMLINKLEN; + sblock.fs_qbmask = ~sblock.fs_bmask; + sblock.fs_qfmask = ~sblock.fs_fmask; + sbdirty(); + dirty(&asblk); + } + /* + * Convert to new cylinder group format. + */ + if (cvtlevel >= 1 && sblock.fs_postblformat == FS_42POSTBLFMT) { + if (preen) + pwarn("CONVERTING TO NEW CYLINDER GROUP FORMAT\n"); + else if (!reply("CONVERT TO NEW CYLINDER GROUP FORMAT")) + return(0); + doinglevel1++; + sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT; + sblock.fs_nrpos = 8; + sblock.fs_postbloff = + (char *)(&sblock.fs_opostbl[0][0]) - + (char *)(&sblock.fs_link); + sblock.fs_rotbloff = &sblock.fs_space[0] - + (u_char *)(&sblock.fs_link); + sblock.fs_cgsize = + fragroundup(&sblock, CGSIZE(&sblock)); + sbdirty(); + dirty(&asblk); + } + if (asblk.b_dirty) { + bcopy((char *)&sblock, (char *)&altsblock, + (size_t)sblock.fs_sbsize); + flush(fswritefd, &asblk); + } + /* + * read in the summary info. + */ + asked = 0; + for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { + size = sblock.fs_cssize - i < sblock.fs_bsize ? + sblock.fs_cssize - i : sblock.fs_bsize; + sblock.fs_csp[j] = (struct csum *)calloc(1, (unsigned)size); + if (bread(fsreadfd, (char *)sblock.fs_csp[j], + fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), + size) != 0 && !asked) { + pfatal("BAD SUMMARY INFORMATION"); + if (reply("CONTINUE") == 0) + errexit(""); + asked++; + } + } + /* + * allocate and initialize the necessary maps + */ + bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(short)); + blockmap = calloc((unsigned)bmapsize, sizeof (char)); + if (blockmap == NULL) { + printf("cannot alloc %u bytes for blockmap\n", + (unsigned)bmapsize); + goto badsb; + } + statemap = calloc((unsigned)(maxino + 1), sizeof(char)); + if (statemap == NULL) { + printf("cannot alloc %u bytes for statemap\n", + (unsigned)(maxino + 1)); + goto badsb; + } + typemap = calloc((unsigned)(maxino + 1), sizeof(char)); + if (typemap == NULL) { + printf("cannot alloc %u bytes for typemap\n", + (unsigned)(maxino + 1)); + goto badsb; + } + lncntp = (short *)calloc((unsigned)(maxino + 1), sizeof(short)); + if (lncntp == NULL) { + printf("cannot alloc %u bytes for lncntp\n", + (unsigned)(maxino + 1) * sizeof(short)); + goto badsb; + } + numdirs = sblock.fs_cstotal.cs_ndir; + inplast = 0; + listmax = numdirs + 10; + inpsort = (struct inoinfo **)calloc((unsigned)listmax, + sizeof(struct inoinfo *)); + inphead = (struct inoinfo **)calloc((unsigned)numdirs, + sizeof(struct inoinfo *)); + if (inpsort == NULL || inphead == NULL) { + printf("cannot alloc %u bytes for inphead\n", + (unsigned)numdirs * sizeof(struct inoinfo *)); + goto badsb; + } + bufinit(); + return (1); + +badsb: + ckfini(); + return (0); +} + +/* + * Read in the super block and its summary info. + */ +readsb(listerr) + int listerr; +{ + daddr_t super = bflag ? bflag : SBOFF / dev_bsize; + + if (bread(fsreadfd, (char *)&sblock, super, (long)SBSIZE) != 0) + return (0); + sblk.b_bno = super; + sblk.b_size = SBSIZE; + /* + * run a few consistency checks of the super block + */ + if (sblock.fs_magic != FS_MAGIC) + { badsb(listerr, "MAGIC NUMBER WRONG"); return (0); } + if (sblock.fs_ncg < 1) + { badsb(listerr, "NCG OUT OF RANGE"); return (0); } + if (sblock.fs_cpg < 1) + { badsb(listerr, "CPG OUT OF RANGE"); return (0); } + if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl || + (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl) + { badsb(listerr, "NCYL LESS THAN NCG*CPG"); return (0); } + if (sblock.fs_sbsize > SBSIZE) + { badsb(listerr, "SIZE PREPOSTEROUSLY LARGE"); return (0); } + /* + * Compute block size that the filesystem is based on, + * according to fsbtodb, and adjust superblock block number + * so we can tell if this is an alternate later. + */ + super *= dev_bsize; + dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); + sblk.b_bno = super / dev_bsize; + if (bflag) { + havesb = 1; + return (1); + } + /* + * Set all possible fields that could differ, then do check + * of whole super block against an alternate super block. + * When an alternate super-block is specified this check is skipped. + */ + getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize); + if (asblk.b_errs) + return (0); + altsblock.fs_link = sblock.fs_link; + altsblock.fs_rlink = sblock.fs_rlink; + altsblock.fs_time = sblock.fs_time; + altsblock.fs_cstotal = sblock.fs_cstotal; + altsblock.fs_cgrotor = sblock.fs_cgrotor; + altsblock.fs_fmod = sblock.fs_fmod; + altsblock.fs_clean = sblock.fs_clean; + altsblock.fs_ronly = sblock.fs_ronly; + altsblock.fs_flags = sblock.fs_flags; + altsblock.fs_maxcontig = sblock.fs_maxcontig; + altsblock.fs_minfree = sblock.fs_minfree; + altsblock.fs_optim = sblock.fs_optim; + altsblock.fs_rotdelay = sblock.fs_rotdelay; + altsblock.fs_maxbpg = sblock.fs_maxbpg; + bcopy((char *)sblock.fs_csp, (char *)altsblock.fs_csp, + sizeof sblock.fs_csp); + bcopy((char *)sblock.fs_fsmnt, (char *)altsblock.fs_fsmnt, + sizeof sblock.fs_fsmnt); + bcopy((char *)sblock.fs_sparecon, (char *)altsblock.fs_sparecon, + sizeof sblock.fs_sparecon); + /* + * The following should not have to be copied. + */ + altsblock.fs_fsbtodb = sblock.fs_fsbtodb; + altsblock.fs_interleave = sblock.fs_interleave; + altsblock.fs_npsect = sblock.fs_npsect; + altsblock.fs_nrpos = sblock.fs_nrpos; + altsblock.fs_qbmask = sblock.fs_qbmask; + altsblock.fs_qfmask = sblock.fs_qfmask; + altsblock.fs_state = sblock.fs_state; + altsblock.fs_maxfilesize = sblock.fs_maxfilesize; + if (bcmp((char *)&sblock, (char *)&altsblock, (int)sblock.fs_sbsize)) { + badsb(listerr, + "VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE"); + return (0); + } + havesb = 1; + return (1); +} + +badsb(listerr, s) + int listerr; + char *s; +{ + + if (!listerr) + return; + if (preen) + printf("%s: ", cdevname); + pfatal("BAD SUPER BLOCK: %s\n", s); +} + +/* + * Calculate a prototype superblock based on information in the disk label. + * When done the cgsblock macro can be calculated and the fs_ncg field + * can be used. Do NOT attempt to use other macros without verifying that + * their needed information is available! + */ +calcsb(dev, devfd, fs) + char *dev; + int devfd; + register struct fs *fs; +{ + register struct disklabel *lp; + register struct partition *pp; + register char *cp; + int i; + + cp = index(dev, '\0') - 1; + if (cp == (char *)-1 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp)) { + pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev); + return (0); + } + lp = getdisklabel(dev, devfd); + if (isdigit(*cp)) + pp = &lp->d_partitions[0]; + else + pp = &lp->d_partitions[*cp - 'a']; + if (pp->p_fstype != FS_BSDFFS) { + pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n", + dev, pp->p_fstype < FSMAXTYPES ? + fstypenames[pp->p_fstype] : "unknown"); + return (0); + } + bzero((char *)fs, sizeof(struct fs)); + fs->fs_fsize = pp->p_fsize; + fs->fs_frag = pp->p_frag; + fs->fs_cpg = pp->p_cpg; + fs->fs_size = pp->p_size; + fs->fs_ntrak = lp->d_ntracks; + fs->fs_nsect = lp->d_nsectors; + fs->fs_spc = lp->d_secpercyl; + fs->fs_nspf = fs->fs_fsize / lp->d_secsize; + fs->fs_sblkno = roundup( + howmany(lp->d_bbsize + lp->d_sbsize, fs->fs_fsize), + fs->fs_frag); + fs->fs_cgmask = 0xffffffff; + for (i = fs->fs_ntrak; i > 1; i >>= 1) + fs->fs_cgmask <<= 1; + if (!POWEROF2(fs->fs_ntrak)) + fs->fs_cgmask <<= 1; + fs->fs_cgoffset = roundup( + howmany(fs->fs_nsect, NSPF(fs)), fs->fs_frag); + fs->fs_fpg = (fs->fs_cpg * fs->fs_spc) / NSPF(fs); + fs->fs_ncg = howmany(fs->fs_size / fs->fs_spc, fs->fs_cpg); + for (fs->fs_fsbtodb = 0, i = NSPF(fs); i > 1; i >>= 1) + fs->fs_fsbtodb++; + dev_bsize = lp->d_secsize; + return (1); +} + +struct disklabel * +getdisklabel(s, fd) + char *s; + int fd; +{ + static struct disklabel lab; + + if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) { + if (s == NULL) + return ((struct disklabel *)NULL); + pwarn("ioctl (GCINFO): %s\n", strerror(errno)); + errexit("%s: can't read disk label\n", s); + } + return (&lab); +} diff --git a/sbin/fsck/utilities.c b/sbin/fsck/utilities.c new file mode 100644 index 0000000..64a4cac --- /dev/null +++ b/sbin/fsck/utilities.c @@ -0,0 +1,566 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)utilities.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "fsck.h" + +long diskreads, totalreads; /* Disk cache statistics */ + +ftypeok(dp) + struct dinode *dp; +{ + switch (dp->di_mode & IFMT) { + + case IFDIR: + case IFREG: + case IFBLK: + case IFCHR: + case IFLNK: + case IFSOCK: + case IFIFO: + return (1); + + default: + if (debug) + printf("bad file type 0%o\n", dp->di_mode); + return (0); + } +} + +reply(question) + char *question; +{ + int persevere; + char c; + + if (preen) + pfatal("INTERNAL ERROR: GOT TO reply()"); + persevere = !strcmp(question, "CONTINUE"); + printf("\n"); + if (!persevere && (nflag || fswritefd < 0)) { + printf("%s? no\n\n", question); + return (0); + } + if (yflag || (persevere && nflag)) { + printf("%s? yes\n\n", question); + return (1); + } + do { + printf("%s? [yn] ", question); + (void) fflush(stdout); + c = getc(stdin); + while (c != '\n' && getc(stdin) != '\n') + if (feof(stdin)) + return (0); + } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); + printf("\n"); + if (c == 'y' || c == 'Y') + return (1); + return (0); +} + +/* + * Malloc buffers and set up cache. + */ +bufinit() +{ + register struct bufarea *bp; + long bufcnt, i; + char *bufp; + + pbp = pdirbp = (struct bufarea *)0; + bufp = malloc((unsigned int)sblock.fs_bsize); + if (bufp == 0) + errexit("cannot allocate buffer pool\n"); + cgblk.b_un.b_buf = bufp; + initbarea(&cgblk); + bufhead.b_next = bufhead.b_prev = &bufhead; + bufcnt = MAXBUFSPACE / sblock.fs_bsize; + if (bufcnt < MINBUFS) + bufcnt = MINBUFS; + for (i = 0; i < bufcnt; i++) { + bp = (struct bufarea *)malloc(sizeof(struct bufarea)); + bufp = malloc((unsigned int)sblock.fs_bsize); + if (bp == NULL || bufp == NULL) { + if (i >= MINBUFS) + break; + errexit("cannot allocate buffer pool\n"); + } + bp->b_un.b_buf = bufp; + bp->b_prev = &bufhead; + bp->b_next = bufhead.b_next; + bufhead.b_next->b_prev = bp; + bufhead.b_next = bp; + initbarea(bp); + } + bufhead.b_size = i; /* save number of buffers */ +} + +/* + * Manage a cache of directory blocks. + */ +struct bufarea * +getdatablk(blkno, size) + daddr_t blkno; + long size; +{ + register struct bufarea *bp; + + for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) + if (bp->b_bno == fsbtodb(&sblock, blkno)) + goto foundit; + for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) + if ((bp->b_flags & B_INUSE) == 0) + break; + if (bp == &bufhead) + errexit("deadlocked buffer pool\n"); + getblk(bp, blkno, size); + /* fall through */ +foundit: + totalreads++; + bp->b_prev->b_next = bp->b_next; + bp->b_next->b_prev = bp->b_prev; + bp->b_prev = &bufhead; + bp->b_next = bufhead.b_next; + bufhead.b_next->b_prev = bp; + bufhead.b_next = bp; + bp->b_flags |= B_INUSE; + return (bp); +} + +void +getblk(bp, blk, size) + register struct bufarea *bp; + daddr_t blk; + long size; +{ + daddr_t dblk; + + dblk = fsbtodb(&sblock, blk); + if (bp->b_bno != dblk) { + flush(fswritefd, bp); + diskreads++; + bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); + bp->b_bno = dblk; + bp->b_size = size; + } +} + +flush(fd, bp) + int fd; + register struct bufarea *bp; +{ + register int i, j; + + if (!bp->b_dirty) + return; + if (bp->b_errs != 0) + pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", + (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", + bp->b_bno); + bp->b_dirty = 0; + bp->b_errs = 0; + bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); + if (bp != &sblk) + return; + for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { + bwrite(fswritefd, (char *)sblock.fs_csp[j], + fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), + sblock.fs_cssize - i < sblock.fs_bsize ? + sblock.fs_cssize - i : sblock.fs_bsize); + } +} + +rwerror(mesg, blk) + char *mesg; + daddr_t blk; +{ + + if (preen == 0) + printf("\n"); + pfatal("CANNOT %s: BLK %ld", mesg, blk); + if (reply("CONTINUE") == 0) + errexit("Program terminated\n"); +} + +ckfini() +{ + register struct bufarea *bp, *nbp; + int cnt = 0; + + if (fswritefd < 0) { + (void)close(fsreadfd); + return; + } + flush(fswritefd, &sblk); + if (havesb && sblk.b_bno != SBOFF / dev_bsize && + !preen && reply("UPDATE STANDARD SUPERBLOCK")) { + sblk.b_bno = SBOFF / dev_bsize; + sbdirty(); + flush(fswritefd, &sblk); + } + flush(fswritefd, &cgblk); + free(cgblk.b_un.b_buf); + for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { + cnt++; + flush(fswritefd, bp); + nbp = bp->b_prev; + free(bp->b_un.b_buf); + free((char *)bp); + } + if (bufhead.b_size != cnt) + errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); + pbp = pdirbp = (struct bufarea *)0; + if (debug) + printf("cache missed %ld of %ld (%d%%)\n", diskreads, + totalreads, (int)(diskreads * 100 / totalreads)); + (void)close(fsreadfd); + (void)close(fswritefd); +} + +bread(fd, buf, blk, size) + int fd; + char *buf; + daddr_t blk; + long size; +{ + char *cp; + int i, errs; + off_t offset; + + offset = blk; + offset *= dev_bsize; + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + else if (read(fd, buf, (int)size) == size) + return (0); + rwerror("READ", blk); + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + errs = 0; + bzero(buf, (size_t)size); + printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); + for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { + if (read(fd, cp, (int)secsize) != secsize) { + (void)lseek(fd, offset + i + secsize, 0); + if (secsize != dev_bsize && dev_bsize != 1) + printf(" %ld (%ld),", + (blk * dev_bsize + i) / secsize, + blk + i / dev_bsize); + else + printf(" %ld,", blk + i / dev_bsize); + errs++; + } + } + printf("\n"); + return (errs); +} + +bwrite(fd, buf, blk, size) + int fd; + char *buf; + daddr_t blk; + long size; +{ + int i; + char *cp; + off_t offset; + + if (fd < 0) + return; + offset = blk; + offset *= dev_bsize; + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + else if (write(fd, buf, (int)size) == size) { + fsmodified = 1; + return; + } + rwerror("WRITE", blk); + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); + for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) + if (write(fd, cp, (int)dev_bsize) != dev_bsize) { + (void)lseek(fd, offset + i + dev_bsize, 0); + printf(" %ld,", blk + i / dev_bsize); + } + printf("\n"); + return; +} + +/* + * allocate a data block with the specified number of fragments + */ +allocblk(frags) + long frags; +{ + register int i, j, k; + + if (frags <= 0 || frags > sblock.fs_frag) + return (0); + for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { + for (j = 0; j <= sblock.fs_frag - frags; j++) { + if (testbmap(i + j)) + continue; + for (k = 1; k < frags; k++) + if (testbmap(i + j + k)) + break; + if (k < frags) { + j += k; + continue; + } + for (k = 0; k < frags; k++) + setbmap(i + j + k); + n_blks += frags; + return (i + j); + } + } + return (0); +} + +/* + * Free a previously allocated block + */ +freeblk(blkno, frags) + daddr_t blkno; + long frags; +{ + struct inodesc idesc; + + idesc.id_blkno = blkno; + idesc.id_numfrags = frags; + (void)pass4check(&idesc); +} + +/* + * Find a pathname + */ +getpathname(namebuf, curdir, ino) + char *namebuf; + ino_t curdir, ino; +{ + int len; + register char *cp; + struct inodesc idesc; + static int busy = 0; + extern int findname(); + + if (curdir == ino && ino == ROOTINO) { + (void)strcpy(namebuf, "/"); + return; + } + if (busy || + (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) { + (void)strcpy(namebuf, "?"); + return; + } + busy = 1; + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = DATA; + idesc.id_fix = IGNORE; + cp = &namebuf[MAXPATHLEN - 1]; + *cp = '\0'; + if (curdir != ino) { + idesc.id_parent = curdir; + goto namelookup; + } + while (ino != ROOTINO) { + idesc.id_number = ino; + idesc.id_func = findino; + idesc.id_name = ".."; + if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) + break; + namelookup: + idesc.id_number = idesc.id_parent; + idesc.id_parent = ino; + idesc.id_func = findname; + idesc.id_name = namebuf; + if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) + break; + len = strlen(namebuf); + cp -= len; + bcopy(namebuf, cp, (size_t)len); + *--cp = '/'; + if (cp < &namebuf[MAXNAMLEN]) + break; + ino = idesc.id_number; + } + busy = 0; + if (ino != ROOTINO) + *--cp = '?'; + bcopy(cp, namebuf, (size_t)(&namebuf[MAXPATHLEN] - cp)); +} + +void +catch() +{ + if (!doinglevel2) + ckfini(); + exit(12); +} + +/* + * When preening, allow a single quit to signal + * a special exit after filesystem checks complete + * so that reboot sequence may be interrupted. + */ +void +catchquit() +{ + extern returntosingle; + + printf("returning to single-user after filesystem check\n"); + returntosingle = 1; + (void)signal(SIGQUIT, SIG_DFL); +} + +/* + * Ignore a single quit signal; wait and flush just in case. + * Used by child processes in preen. + */ +void +voidquit() +{ + + sleep(1); + (void)signal(SIGQUIT, SIG_IGN); + (void)signal(SIGQUIT, SIG_DFL); +} + +/* + * determine whether an inode should be fixed. + */ +dofix(idesc, msg) + register struct inodesc *idesc; + char *msg; +{ + + switch (idesc->id_fix) { + + case DONTKNOW: + if (idesc->id_type == DATA) + direrror(idesc->id_number, msg); + else + pwarn(msg); + if (preen) { + printf(" (SALVAGED)\n"); + idesc->id_fix = FIX; + return (ALTERED); + } + if (reply("SALVAGE") == 0) { + idesc->id_fix = NOFIX; + return (0); + } + idesc->id_fix = FIX; + return (ALTERED); + + case FIX: + return (ALTERED); + + case NOFIX: + case IGNORE: + return (0); + + default: + errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); + } + /* NOTREACHED */ +} + +/* VARARGS1 */ +errexit(s1, s2, s3, s4) + char *s1; +{ + printf(s1, s2, s3, s4); + exit(8); +} + +/* + * An unexpected inconsistency occured. + * Die if preening, otherwise just print message and continue. + */ +/* VARARGS1 */ +pfatal(s, a1, a2, a3) + char *s; +{ + + if (preen) { + printf("%s: ", cdevname); + printf(s, a1, a2, a3); + printf("\n"); + printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", + cdevname); + exit(8); + } + printf(s, a1, a2, a3); +} + +/* + * Pwarn just prints a message when not preening, + * or a warning (preceded by filename) when preening. + */ +/* VARARGS1 */ +pwarn(s, a1, a2, a3, a4, a5, a6) + char *s; +{ + + if (preen) + printf("%s: ", cdevname); + printf(s, a1, a2, a3, a4, a5, a6); +} + +#ifndef lint +/* + * Stub for routines from kernel. + */ +panic(s) + char *s; +{ + + pfatal("INTERNAL INCONSISTENCY:"); + errexit(s); +} +#endif diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile new file mode 100644 index 0000000..8be2193 --- /dev/null +++ b/sbin/ifconfig/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= ifconfig +MAN8= ifconfig.0 + +.include <bsd.prog.mk> diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 new file mode 100644 index 0000000..58a2163 --- /dev/null +++ b/sbin/ifconfig/ifconfig.8 @@ -0,0 +1,275 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ifconfig.8 8.3 (Berkeley) 1/5/94 +.\" +.Dd January 5, 1994 +.Dt IFCONFIG 8 +.Os BSD 4.2 +.Sh NAME +.Nm ifconfig +.Nd configure network interface parameters +.Sh SYNOPSIS +.Nm ifconfig +.Ar interface address_family +.Oo +.Ar address +.Op Ar dest_address +.Oc +.Op Ar parameters +.Nm ifconfig +.Ar interface +.Op Ar protocol_family +.Sh DESCRIPTION +.Nm Ifconfig +is used to assign an address +to a network interface and/or configure +network interface parameters. +.Nm Ifconfig +must be used at boot time to define the network address +of each interface present on a machine; it may also be used at +a later time to redefine an interface's address +or other operating parameters. +.Pp +Available operands for +.Nm ifconfig: +.Bl -tag -width Ds +.It Ar Address +For the +.Tn DARPA-Internet +family, +the address is either a host name present in the host name data +base, +.Xr hosts 5 , +or a +.Tn DARPA +Internet address expressed in the Internet standard +.Dq dot notation . +For the Xerox Network Systems(tm) family, +addresses are +.Ar net:a.b.c.d.e.f , +where +.Ar net +is the assigned network number (in decimal), +and each of the six bytes of the host number, +.Ar a +through +.Ar f , +are specified in hexadecimal. +The host number may be omitted on 10Mb/s Ethernet interfaces, +which use the hardware physical address, +and on interfaces other than the first. +For the +.Tn ISO +family, addresses are specified as a long hexadecimal string, +as in the Xerox family. However, two consecutive dots imply a zero +byte, and the dots are optional, if the user wishes to (carefully) +count out long strings of digits in network byte order. +.It Ar address_family +Specifies the +.Ar address family +which affects interpretation of the remaining parameters. +Since an interface can receive transmissions in differing protocols +with different naming schemes, specifying the address family is recommeded. +The address or protocol families currently +supported are +.Dq inet , +.Dq iso , +and +.Dq ns . +.It Ar Interface +The +.Ar interface +parameter is a string of the form +.Dq name unit , +for example, +.Dq en0 +.El +.Pp +The following parameters may be set with +.Nm ifconfig : +.Bl -tag -width dest_addressxx +.It Cm alias +Establish an additional network address for this interface. +This is sometimes useful when changing network numbers, and +one wishes to accept packets addressed to the old interface. +.It Cm arp +Enable the use of the Address Resolution Protocol in mapping +between network level addresses and link level addresses (default). +This is currently implemented for mapping between +.Tn DARPA +Internet +addresses and 10Mb/s Ethernet addresses. +.It Fl arp +Disable the use of the Address Resolution Protocol. +.It Cm broadcast +(Inet only) +Specify the address to use to represent broadcasts to the +network. +The default broadcast address is the address with a host part of all 1's. +.It Cm debug +Enable driver dependent debugging code; usually, this turns on +extra console error logging. +.It Fl debug +Disable driver dependent debugging code. +.It Cm delete +Remove the network address specified. +This would be used if you incorrectly specified an alias, or it +was no longer needed. +If you have incorrectly set an NS address having the side effect +of specifying the host portion, removing all NS addresses will +allow you to respecify the host portion. +.It Cm dest_address +Specify the address of the correspondent on the other end +of a point to point link. +.It Cm down +Mark an interface ``down''. When an interface is +marked ``down'', the system will not attempt to +transmit messages through that interface. +If possible, the interface will be reset to disable reception as well. +This action does not automatically disable routes using the interface. +.It Cm ipdst +This is used to specify an Internet host who is willing to receive +ip packets encapsulating NS packets bound for a remote network. +An apparent point to point link is constructed, and +the address specified will be taken as the NS address and network +of the destination. +IP encapsulation of +.Tn CLNP +packets is done differently. +.It Cm metric Ar n +Set the routing metric of the interface to +.Ar n , +default 0. +The routing metric is used by the routing protocol +.Pq Xr routed 8 . +Higher metrics have the effect of making a route +less favorable; metrics are counted as addition hops +to the destination network or host. +.It Cm netmask Ar mask +(Inet and ISO) +Specify how much of the address to reserve for subdividing +networks into sub-networks. +The mask includes the network part of the local address +and the subnet part, which is taken from the host field of the address. +The mask can be specified as a single hexadecimal number +with a leading 0x, with a dot-notation Internet address, +or with a pseudo-network name listed in the network table +.Xr networks 5 . +The mask contains 1's for the bit positions in the 32-bit address +which are to be used for the network and subnet parts, +and 0's for the host part. +The mask should contain at least the standard network portion, +and the subnet field should be contiguous with the network +portion. +.\" see +.\" Xr eon 5 . +.It Cm nsellength Ar n +.Pf ( Tn ISO +only) +This specifies a trailing number of bytes for a received +.Tn NSAP +used for local identification, the remaining leading part of which is +taken to be the +.Tn NET +(Network Entity Title). +The default value is 1, which is conformant to US +.Tn GOSIP . +When an ISO address is set in an ifconfig command, +it is really the +.Tn NSAP +which is being specified. +For example, in +.Tn US GOSIP , +20 hex digits should be +specified in the +.Tn ISO NSAP +to be assigned to the interface. +There is some evidence that a number different from 1 may be useful +for +.Tn AFI +37 type addresses. +.It Cm trailers +Request the use of a ``trailer'' link level encapsulation when +sending (default). +If a network interface supports +.Cm trailers , +the system will, when possible, encapsulate outgoing +messages in a manner which minimizes the number of +memory to memory copy operations performed by the receiver. +On networks that support the Address Resolution Protocol (see +.Xr arp 4 ; +currently, only 10 Mb/s Ethernet), +this flag indicates that the system should request that other +systems use trailers when sending to this host. +Similarly, trailer encapsulations will be sent to other +hosts that have made such requests. +Currently used by Internet protocols only. +.It Fl trailers +Disable the use of a ``trailer'' link level encapsulation. +.It Cm link[0-2] +Enable special processing of the link level of the interface. +These three options are interface specific in actual effect, however, +they are in general used to select special modes of operation. An example +of this is to enable SLIP compression. Currently, only used by SLIP. +.It Fl link[0-2] +Disable special processing at the link level with the specified interface. +.It Cm up +Mark an interface ``up''. +This may be used to enable an interface after an ``ifconfig down.'' +It happens automatically when setting the first address on an interface. +If the interface was reset when previously marked down, +the hardware will be re-initialized. +.El +.Pp +.Pp +.Nm Ifconfig +displays the current configuration for a network interface +when no optional parameters are supplied. +If a protocol family is specified, +Ifconfig will report only the details specific to that protocol family. +.Pp +Only the super-user may modify the configuration of a network interface. +.Sh DIAGNOSTICS +Messages indicating the specified interface does not exit, the +requested address is unknown, or the user is not privileged and +tried to alter an interface's configuration. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr netintro 4 , +.Xr rc 8 , +.Xr routed 8 , +.\" .Xr eon 5 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c new file mode 100644 index 0000000..e9751b9 --- /dev/null +++ b/sbin/ifconfig/ifconfig.c @@ -0,0 +1,679 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#define NSIP +#include <netns/ns.h> +#include <netns/ns_if.h> +#include <netdb.h> + +#define EON +#include <netiso/iso.h> +#include <netiso/iso_var.h> +#include <sys/protosw.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +struct ifreq ifr, ridreq; +struct ifaliasreq addreq; +struct iso_ifreq iso_ridreq; +struct iso_aliasreq iso_addreq; +struct sockaddr_in netmask; + +char name[30]; +int flags; +int metric; +int nsellength = 1; +int setaddr; +int setipdst; +int doalias; +int clearaddr; +int newaddr = 1; +int s; +extern int errno; + +int setifflags(), setifaddr(), setifdstaddr(), setifnetmask(); +int setifmetric(), setifbroadaddr(), setifipdst(); +int notealias(), setsnpaoffset(), setnsellength(), notrailers(); + +#define NEXTARG 0xffffff + +struct cmd { + char *c_name; + int c_parameter; /* NEXTARG means next argv */ + int (*c_func)(); +} cmds[] = { + { "up", IFF_UP, setifflags } , + { "down", -IFF_UP, setifflags }, + { "trailers", -1, notrailers }, + { "-trailers", 1, notrailers }, + { "arp", -IFF_NOARP, setifflags }, + { "-arp", IFF_NOARP, setifflags }, + { "debug", IFF_DEBUG, setifflags }, + { "-debug", -IFF_DEBUG, setifflags }, + { "alias", IFF_UP, notealias }, + { "-alias", -IFF_UP, notealias }, + { "delete", -IFF_UP, notealias }, +#ifdef notdef +#define EN_SWABIPS 0x1000 + { "swabips", EN_SWABIPS, setifflags }, + { "-swabips", -EN_SWABIPS, setifflags }, +#endif + { "netmask", NEXTARG, setifnetmask }, + { "metric", NEXTARG, setifmetric }, + { "broadcast", NEXTARG, setifbroadaddr }, + { "ipdst", NEXTARG, setifipdst }, + { "snpaoffset", NEXTARG, setsnpaoffset }, + { "nsellength", NEXTARG, setnsellength }, + { "link0", IFF_LINK0, setifflags } , + { "-link0", -IFF_LINK0, setifflags } , + { "link1", IFF_LINK1, setifflags } , + { "-link1", -IFF_LINK1, setifflags } , + { "link2", IFF_LINK2, setifflags } , + { "-link2", -IFF_LINK2, setifflags } , + { 0, 0, setifaddr }, + { 0, 0, setifdstaddr }, +}; + +/* + * XNS support liberally adapted from code written at the University of + * Maryland principally by James O'Toole and Chris Torek. + */ +int in_status(), in_getaddr(); +int xns_status(), xns_getaddr(); +int iso_status(), iso_getaddr(); + +/* Known address families */ +struct afswtch { + char *af_name; + short af_af; + int (*af_status)(); + int (*af_getaddr)(); + int af_difaddr; + int af_aifaddr; + caddr_t af_ridreq; + caddr_t af_addreq; +} afs[] = { +#define C(x) ((caddr_t) &x) + { "inet", AF_INET, in_status, in_getaddr, + SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) }, + { "ns", AF_NS, xns_status, xns_getaddr, + SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) }, + { "iso", AF_ISO, iso_status, iso_getaddr, + SIOCDIFADDR_ISO, SIOCAIFADDR_ISO, C(iso_ridreq), C(iso_addreq) }, + { 0, 0, 0, 0 } +}; + +struct afswtch *afp; /*the address family being set or asked about*/ + +main(argc, argv) + int argc; + char *argv[]; +{ + int af = AF_INET; + register struct afswtch *rafp; + + if (argc < 2) { + fprintf(stderr, "usage: ifconfig interface\n%s%s%s%s%s", + "\t[ af [ address [ dest_addr ] ] [ up ] [ down ]", + "[ netmask mask ] ]\n", + "\t[ metric n ]\n", + "\t[ arp | -arp ]\n", + "\t[ link0 | -link0 ] [ link1 | -link1 ] [ link2 | -link2 ] \n"); + exit(1); + } + argc--, argv++; + strncpy(name, *argv, sizeof(name)); + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + argc--, argv++; + if (argc > 0) { + for (afp = rafp = afs; rafp->af_name; rafp++) + if (strcmp(rafp->af_name, *argv) == 0) { + afp = rafp; argc--; argv++; + break; + } + rafp = afp; + af = ifr.ifr_addr.sa_family = rafp->af_af; + } + s = socket(af, SOCK_DGRAM, 0); + if (s < 0) { + perror("ifconfig: socket"); + exit(1); + } + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { + Perror("ioctl (SIOCGIFFLAGS)"); + exit(1); + } + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + flags = ifr.ifr_flags; + if (ioctl(s, SIOCGIFMETRIC, (caddr_t)&ifr) < 0) + perror("ioctl (SIOCGIFMETRIC)"); + else + metric = ifr.ifr_metric; + if (argc == 0) { + status(); + exit(0); + } + while (argc > 0) { + register struct cmd *p; + + for (p = cmds; p->c_name; p++) + if (strcmp(*argv, p->c_name) == 0) + break; + if (p->c_name == 0 && setaddr) + p++; /* got src, do dst */ + if (p->c_func) { + if (p->c_parameter == NEXTARG) { + if (argv[1] == NULL) + errx(1, "'%s' requires argument", + p->c_name); + (*p->c_func)(argv[1]); + argc--, argv++; + } else + (*p->c_func)(*argv, p->c_parameter); + } + argc--, argv++; + } + if (af == AF_ISO) + adjust_nsellength(); + if (setipdst && af==AF_NS) { + struct nsip_req rq; + int size = sizeof(rq); + + rq.rq_ns = addreq.ifra_addr; + rq.rq_ip = addreq.ifra_dstaddr; + + if (setsockopt(s, 0, SO_NSIP_ROUTE, &rq, size) < 0) + Perror("Encapsulation Routing"); + } + if (clearaddr) { + int ret; + strncpy(rafp->af_ridreq, name, sizeof ifr.ifr_name); + if ((ret = ioctl(s, rafp->af_difaddr, rafp->af_ridreq)) < 0) { + if (errno == EADDRNOTAVAIL && (doalias >= 0)) { + /* means no previous address for interface */ + } else + Perror("ioctl (SIOCDIFADDR)"); + } + } + if (newaddr) { + strncpy(rafp->af_addreq, name, sizeof ifr.ifr_name); + if (ioctl(s, rafp->af_aifaddr, rafp->af_addreq) < 0) + Perror("ioctl (SIOCAIFADDR)"); + } + exit(0); +} +#define RIDADDR 0 +#define ADDR 1 +#define MASK 2 +#define DSTADDR 3 + +/*ARGSUSED*/ +setifaddr(addr, param) + char *addr; + short param; +{ + /* + * Delay the ioctl to set the interface addr until flags are all set. + * The address interpretation may depend on the flags, + * and the flags may change when the address is set. + */ + setaddr++; + if (doalias == 0) + clearaddr = 1; + (*afp->af_getaddr)(addr, (doalias >= 0 ? ADDR : RIDADDR)); +} + +setifnetmask(addr) + char *addr; +{ + (*afp->af_getaddr)(addr, MASK); +} + +setifbroadaddr(addr) + char *addr; +{ + (*afp->af_getaddr)(addr, DSTADDR); +} + +setifipdst(addr) + char *addr; +{ + in_getaddr(addr, DSTADDR); + setipdst++; + clearaddr = 0; + newaddr = 0; +} +#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr)) +/*ARGSUSED*/ +notealias(addr, param) + char *addr; +{ + if (setaddr && doalias == 0 && param < 0) + bcopy((caddr_t)rqtosa(af_addreq), + (caddr_t)rqtosa(af_ridreq), + rqtosa(af_addreq)->sa_len); + doalias = param; + if (param < 0) { + clearaddr = 1; + newaddr = 0; + } else + clearaddr = 0; +} + +/*ARGSUSED*/ +notrailers(vname, value) + char *vname; + int value; +{ + printf("Note: trailers are no longer sent, but always received\n"); +} + +/*ARGSUSED*/ +setifdstaddr(addr, param) + char *addr; + int param; +{ + (*afp->af_getaddr)(addr, DSTADDR); +} + +setifflags(vname, value) + char *vname; + short value; +{ + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { + Perror("ioctl (SIOCGIFFLAGS)"); + exit(1); + } + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + flags = ifr.ifr_flags; + + if (value < 0) { + value = -value; + flags &= ~value; + } else + flags |= value; + ifr.ifr_flags = flags; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) + Perror(vname); +} + +setifmetric(val) + char *val; +{ + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + ifr.ifr_metric = atoi(val); + if (ioctl(s, SIOCSIFMETRIC, (caddr_t)&ifr) < 0) + perror("ioctl (set metric)"); +} + +setsnpaoffset(val) + char *val; +{ + iso_addreq.ifra_snpaoffset = atoi(val); +} + +#define IFFBITS \ +"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6NOTRAILERS\7RUNNING\10NOARP\ +\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2\20MULTICAST" + +/* + * Print the status of the interface. If an address family was + * specified, show it and it only; otherwise, show them all. + */ +status() +{ + register struct afswtch *p = afp; + short af = ifr.ifr_addr.sa_family; + + printf("%s: ", name); + printb("flags", flags, IFFBITS); + if (metric) + printf(" metric %d", metric); + putchar('\n'); + if ((p = afp) != NULL) { + (*p->af_status)(1); + } else for (p = afs; p->af_name; p++) { + ifr.ifr_addr.sa_family = p->af_af; + (*p->af_status)(0); + } +} + +in_status(force) + int force; +{ + struct sockaddr_in *sin; + char *inet_ntoa(); + + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) { + if (!force) + return; + bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + } else + perror("ioctl (SIOCGIFADDR)"); + } + sin = (struct sockaddr_in *)&ifr.ifr_addr; + printf("\tinet %s ", inet_ntoa(sin->sin_addr)); + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + if (ioctl(s, SIOCGIFNETMASK, (caddr_t)&ifr) < 0) { + if (errno != EADDRNOTAVAIL) + perror("ioctl (SIOCGIFNETMASK)"); + bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + } else + netmask.sin_addr = + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; + if (flags & IFF_POINTOPOINT) { + if (ioctl(s, SIOCGIFDSTADDR, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL) + bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + else + perror("ioctl (SIOCGIFDSTADDR)"); + } + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + sin = (struct sockaddr_in *)&ifr.ifr_dstaddr; + printf("--> %s ", inet_ntoa(sin->sin_addr)); + } + printf("netmask 0x%x ", ntohl(netmask.sin_addr.s_addr)); + if (flags & IFF_BROADCAST) { + if (ioctl(s, SIOCGIFBRDADDR, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL) + bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + else + perror("ioctl (SIOCGIFADDR)"); + } + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + sin = (struct sockaddr_in *)&ifr.ifr_addr; + if (sin->sin_addr.s_addr != 0) + printf("broadcast %s", inet_ntoa(sin->sin_addr)); + } + putchar('\n'); +} + + +xns_status(force) + int force; +{ + struct sockaddr_ns *sns; + + close(s); + s = socket(AF_NS, SOCK_DGRAM, 0); + if (s < 0) { + if (errno == EPROTONOSUPPORT) + return; + perror("ifconfig: socket"); + exit(1); + } + if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) { + if (!force) + return; + bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + } else + perror("ioctl (SIOCGIFADDR)"); + } + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + sns = (struct sockaddr_ns *)&ifr.ifr_addr; + printf("\tns %s ", ns_ntoa(sns->sns_addr)); + if (flags & IFF_POINTOPOINT) { /* by W. Nesheim@Cornell */ + if (ioctl(s, SIOCGIFDSTADDR, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL) + bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + else + Perror("ioctl (SIOCGIFDSTADDR)"); + } + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + sns = (struct sockaddr_ns *)&ifr.ifr_dstaddr; + printf("--> %s ", ns_ntoa(sns->sns_addr)); + } + putchar('\n'); +} + +iso_status(force) + int force; +{ + struct sockaddr_iso *siso; + struct iso_ifreq ifr; + + close(s); + s = socket(AF_ISO, SOCK_DGRAM, 0); + if (s < 0) { + if (errno == EPROTONOSUPPORT) + return; + perror("ifconfig: socket"); + exit(1); + } + bzero((caddr_t)&ifr, sizeof(ifr)); + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCGIFADDR_ISO, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) { + if (!force) + return; + bzero((char *)&ifr.ifr_Addr, sizeof(ifr.ifr_Addr)); + } else { + perror("ioctl (SIOCGIFADDR_ISO)"); + exit(1); + } + } + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + siso = &ifr.ifr_Addr; + printf("\tiso %s ", iso_ntoa(&siso->siso_addr)); + if (ioctl(s, SIOCGIFNETMASK_ISO, (caddr_t)&ifr) < 0) { + if (errno != EADDRNOTAVAIL) + perror("ioctl (SIOCGIFNETMASK_ISO)"); + } else { + printf(" netmask %s ", iso_ntoa(&siso->siso_addr)); + } + if (flags & IFF_POINTOPOINT) { + if (ioctl(s, SIOCGIFDSTADDR_ISO, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL) + bzero((char *)&ifr.ifr_Addr, sizeof(ifr.ifr_Addr)); + else + Perror("ioctl (SIOCGIFDSTADDR_ISO)"); + } + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + siso = &ifr.ifr_Addr; + printf("--> %s ", iso_ntoa(&siso->siso_addr)); + } + putchar('\n'); +} + +Perror(cmd) + char *cmd; +{ + extern int errno; + + switch (errno) { + + case ENXIO: + errx(1, "%s: no such interface", cmd); + break; + + case EPERM: + errx(1, "%s: permission denied", cmd); + break; + + default: + err(1, "%s", cmd); + } +} + +struct in_addr inet_makeaddr(); + +#define SIN(x) ((struct sockaddr_in *) &(x)) +struct sockaddr_in *sintab[] = { +SIN(ridreq.ifr_addr), SIN(addreq.ifra_addr), +SIN(addreq.ifra_mask), SIN(addreq.ifra_broadaddr)}; + +in_getaddr(s, which) + char *s; +{ + register struct sockaddr_in *sin = sintab[which]; + struct hostent *hp; + struct netent *np; + int val; + + sin->sin_len = sizeof(*sin); + if (which != MASK) + sin->sin_family = AF_INET; + + if ((val = inet_addr(s)) != -1) + sin->sin_addr.s_addr = val; + else if (hp = gethostbyname(s)) + bcopy(hp->h_addr, (char *)&sin->sin_addr, hp->h_length); + else if (np = getnetbyname(s)) + sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); + else + errx(1, "%s: bad value", s); +} + +/* + * Print a value a la the %b format of the kernel's printf + */ +printb(s, v, bits) + char *s; + register char *bits; + register unsigned short v; +{ + register int i, any = 0; + register char c; + + if (bits && *bits == 8) + printf("%s=%o", s, v); + else + printf("%s=%x", s, v); + bits++; + if (bits) { + putchar('<'); + while (i = *bits++) { + if (v & (1 << (i-1))) { + if (any) + putchar(','); + any = 1; + for (; (c = *bits) > 32; bits++) + putchar(c); + } else + for (; *bits > 32; bits++) + ; + } + putchar('>'); + } +} + +#define SNS(x) ((struct sockaddr_ns *) &(x)) +struct sockaddr_ns *snstab[] = { +SNS(ridreq.ifr_addr), SNS(addreq.ifra_addr), +SNS(addreq.ifra_mask), SNS(addreq.ifra_broadaddr)}; + +xns_getaddr(addr, which) +char *addr; +{ + struct sockaddr_ns *sns = snstab[which]; + struct ns_addr ns_addr(); + + sns->sns_family = AF_NS; + sns->sns_len = sizeof(*sns); + sns->sns_addr = ns_addr(addr); + if (which == MASK) + printf("Attempt to set XNS netmask will be ineffectual\n"); +} + +#define SISO(x) ((struct sockaddr_iso *) &(x)) +struct sockaddr_iso *sisotab[] = { +SISO(iso_ridreq.ifr_Addr), SISO(iso_addreq.ifra_addr), +SISO(iso_addreq.ifra_mask), SISO(iso_addreq.ifra_dstaddr)}; + +iso_getaddr(addr, which) +char *addr; +{ + register struct sockaddr_iso *siso = sisotab[which]; + struct iso_addr *iso_addr(); + siso->siso_addr = *iso_addr(addr); + + if (which == MASK) { + siso->siso_len = TSEL(siso) - (caddr_t)(siso); + siso->siso_nlen = 0; + } else { + siso->siso_len = sizeof(*siso); + siso->siso_family = AF_ISO; + } +} + +setnsellength(val) + char *val; +{ + nsellength = atoi(val); + if (nsellength < 0) + errx(1, "Negative NSEL length is absurd"); + if (afp == 0 || afp->af_af != AF_ISO) + errx(1, "Setting NSEL length valid only for iso"); +} + +fixnsel(s) +register struct sockaddr_iso *s; +{ + if (s->siso_family == 0) + return; + s->siso_tlen = nsellength; +} + +adjust_nsellength() +{ + fixnsel(sisotab[RIDADDR]); + fixnsel(sisotab[ADDR]); + fixnsel(sisotab[DSTADDR]); +} diff --git a/sbin/init/Makefile b/sbin/init/Makefile new file mode 100644 index 0000000..1a796a1 --- /dev/null +++ b/sbin/init/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.1 (Berkeley) 7/19/93 + +PROG= init +MAN8= init.0 +DPADD= ${LIBUTIL} +LDADD= -lutil +BINMODE=500 +INSTALLFLAGS=-fschg +CFLAGS+=-DDEBUGSHELL -DSECURE + +.include <bsd.prog.mk> diff --git a/sbin/init/NOTES b/sbin/init/NOTES new file mode 100644 index 0000000..bf75101 --- /dev/null +++ b/sbin/init/NOTES @@ -0,0 +1,112 @@ +POSIX and init: +-------------- + +POSIX.1 does not define 'init' but it mentions it in a few places. + +B.2.2.2, p205 line 873: + + This is part of the extensive 'job control' glossary entry. + This specific reference says that 'init' must by default provide + protection from job control signals to jobs it starts -- + it sets SIGTSTP, SIGTTIN and SIGTTOU to SIG_IGN. + +B.2.2.2, p206 line 889: + + Here is a reference to 'vhangup'. It says, 'POSIX.1 does + not specify how controlling terminal access is affected by + a user logging out (that is, by a controlling process + terminating).' vhangup() is recognized as one way to handle + the problem. I'm not clear what happens in Reno; I have + the impression that when the controlling process terminates, + references to the controlling terminal are converted to + references to a 'dead' vnode. I don't know whether vhangup() + is required. + +B.2.2.2, p206 line 921: + + Orphaned process groups bear indirectly on this issue. A + session leader's process group is considered to be orphaned; + that is, it's immune to job control signals from the terminal. + +B.2.2.2, p233 line 2055: + + 'Historically, the implementation-dependent process that + inherits children whose parents have terminated without + waiting on them is called "init" and has a process ID of 1.' + + It goes on to note that it used to be the case that 'init' + was responsible for sending SIGHUP to the foreground process + group of a tty whose controlling process has exited, using + vhangup(). It is now the responsibility of the kernel to + do this when the controlling process calls _exit(). The + kernel is also responsible for sending SIGCONT to stopped + process groups that become orphaned. This is like old BSD + but entire process groups are signaled instead of individual + processes. + + In general it appears that the kernel now automatically + takes care of orphans, relieving 'init' of any responsibility. + Specifics are listed on the _exit() page (p50). + +On setsid(): +----------- + +It appears that neither getty nor login call setsid(), so init must +do this -- seems reasonable. B.4.3.2 p 248 implies that this is the +way that 'init' should work; it says that setsid() should be called +after forking. + +Process group leaders cannot call setsid() -- another reason to +fork! Of course setsid() causes the current process to become a +process group leader, so we can only call setsid() once. Note that +the controlling terminal acquires the session leader's process +group when opened. + +Controlling terminals: +--------------------- + +B.7.1.1.3 p276: 'POSIX.1 does not specify a mechanism by which to +allocate a controlling terminal. This is normally done by a system +utility (such as 'getty') and is considered ... outside the scope +of POSIX.1.' It goes on to say that historically the first open() +of a tty in a session sets the controlling terminal. P130 has the +full details; nothing particularly surprising. + +The glossary p12 describes a 'controlling process' as the first +process in a session that acquires a controlling terminal. Access +to the terminal from the session is revoked if the controlling +process exits (see p50, in the discussion of process termination). + +Design notes: +------------ + +your generic finite state machine +we are fascist about which signals we elect to receive, + even signals purportedly generated by hardware +handle fatal errors gracefully if possible (we reboot if we goof!!) + if we get a segmentation fault etc., print a message on the console + and spin for a while before rebooting + (this at least decreases the amount of paper consumed :-) +apply hysteresis to rapidly exiting gettys +check wait status of children we reap + don't wait for stopped children +don't use SIGCHILD, it's too expensive + but it may close windows and avoid races, sigh +look for EINTR in case we need to change state +init is responsible for utmp and wtmp maintenance (ick) + maybe now we can consider replacements? maintain them in parallel + init only removes utmp and closes out wtmp entries... + +necessary states and state transitions (gleaned from the man page): + 1: single user shell (with password checking?); on exit, go to 2 + 2: rc script: on exit 0, go to 3; on exit N (error), go to 1 + 3: read ttys file: on completion, go to 4 + 4: multi-user operation: on SIGTERM, go to 7; on SIGHUP, go to 5; + on SIGTSTP, go to 6 + 5: clean up mode (re-read ttys file, killing off controlling processes + on lines that are now 'off', starting them on lines newly 'on') + on completion, go to 4 + 6: boring mode (no new sessions); signals as in 4 + 7: death: send SIGHUP to all controlling processes, reap for 30 seconds, + then go to 1 (warn if not all processes died, i.e. wait blocks) +Given the -s flag, we start at state 1; otherwise state 2 diff --git a/sbin/init/init.8 b/sbin/init/init.8 new file mode 100644 index 0000000..71e3184 --- /dev/null +++ b/sbin/init/init.8 @@ -0,0 +1,291 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Donn Seeley at Berkeley Software Design, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)init.8 8.3 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt INIT 8 +.Os BSD 4 +.Sh NAME +.Nm init +.Nd process control initialization +.Sh SYNOPSIS +.Nm init +.Sh DESCRIPTION +The +.Nm init +program +is the last stage of the boot process. +It normally runs the automatic reboot sequence as described in +.Xr reboot 8 , +and if this succeeds, begins multi-user operation. +If the reboot scripts fail, +.Nm init +commences single user operation by giving +the super-user a shell on the console. +The +.Nm init +program may be passed parameters +from the boot program to +prevent the system from going multi-user and to instead execute +a single user shell without starting the normal daemons. +The system is then quiescent for maintenance work and may +later be made to go to multi-user by exiting the +single-user shell (with ^D). +This +causes +.Nm init +to run the +.Pa /etc/rc +start up command file in fastboot mode (skipping disk checks). +.Pp +If the +.Nm console +entry in the +.Xr ttys 5 +file is marked ``insecure'', +then +.Nm init +will require that the superuser password be +entered before the system will start a single-user shell. +The password check is skipped if the +.Nm console +is marked as ``secure''. +.Pp +The kernel runs with four different levels of security. +Any superuser process can raise the security level, but only +.Nm init +can lower it. +Security levels are defined as follows: +.Bl -tag -width flag +.It Ic -1 +Permanently insecure mode \- always run system in level 0 mode. +.It Ic 0 +Insecure mode \- immutable and append-only flags may be turned off. +All devices may be read or written subject to their permissions. +.It Ic 1 +Secure mode \- immutable and append-only flags may not be changed; +disks for mounted filesystems, +.Pa /dev/mem , +and +.Pa /dev/kmem +are read-only. +.It Ic 2 +Highly secure mode \- same as secure mode, plus disks are always +read-only whether mounted or not. +This level precludes tampering with filesystems by unmounting them, +but also inhibits running +.Xr newfs 8 +while the system is multi-user. +.El +.Pp +Normally, the system runs in level 0 mode while single user +and in level 1 mode while multiuser. +If the level 2 mode is desired while running multiuser, +it can be set in the startup script +.Pa /etc/rc +using +.Xr sysctl 8 . +If it is desired to run the system in level 0 mode while multiuser, +the administrator must build a kernel with the variable +.Nm securelevel +defined in the file +.Pa /sys/compile/MACHINE/param.c +and initialize it to -1. +.Pp +In multi-user operation, +.Nm init +maintains +processes for the terminal ports found in the file +.Xr ttys 5 . +.Nm Init +reads this file, and executes the command found in the second field. +This command is usually +.Xr getty 8 ; +.Xr getty +opens and initializes the tty line +and +executes the +.Xr login +program. +The +.Xr login +program, when a valid user logs in, +executes a shell for that user. When this shell +dies, either because the user logged out +or an abnormal termination occurred (a signal), +the +.Nm init +program wakes up, deletes the user +from the +.Xr utmp 5 +file of current users and records the logout in the +.Xr wtmp +file. +The cycle is +then restarted by +.Nm init +executing a new +.Xr getty +for the line. +.Pp +Line status (on, off, secure, getty, or window information) +may be changed in the +.Xr ttys +file without a reboot by sending the signal +.Dv SIGHUP +to +.Nm init +with the command +.Dq Li "kill -HUP 1" . +On receipt of this signal, +.Nm init +re-reads the +.Xr ttys +file. +When a line is turned off in +.Xr ttys , +.Nm init +will send a SIGHUP signal to the controlling process +for the session associated with the line. +For any lines that were previously turned off in the +.Xr ttys +file and are now on, +.Nm init +executes a new +.Xr getty +to enable a new login. +If the getty or window field for a line is changed, +the change takes effect at the end of the current +login session (e.g., the next time +.Nm init +starts a process on the line). +If a line is commented out or deleted from +.Xr ttys , +.Nm init +will not do anything at all to that line. +However, it will complain that the relationship between lines +in the +.Xr ttys +file and records in the +.Xr utmp +file is out of sync, +so this practice is not recommended. +.Pp +.Nm Init +will terminate multi-user operations and resume single-user mode +if sent a terminate +.Pq Dv TERM +signal, for example, +.Dq Li "kill \-TERM 1" . +If there are processes outstanding that are deadlocked (because of +hardware or software failure), +.Xr init +will not wait for them all to die (which might take forever), but +will time out after 30 seconds and print a warning message. +.Pp +.Nm Init +will cease creating new +.Xr getty Ns 's +and allow the system to slowly die away, if it is sent a terminal stop +.Pq Dv TSTP +signal, i.e. +.Dq Li "kill \-TSTP 1" . +A later hangup will resume full +multi-user operations, or a terminate will start a single user shell. +This hook is used by +.Xr reboot 8 +and +.Xr halt 8 . +.Pp +The role of +.Nm init +is so critical that if it dies, the system will reboot itself +automatically. +If, at bootstrap time, the +.Xr init +process cannot be located, the system will panic with the message +``panic: "init died (signal %d, exit %d)''. +.Sh DIAGNOSTICS +.Bl -diag +.It "getty repeating too quickly on port %s, sleeping" +A process being started to service a line is exiting quickly +each time it is started. +This is often caused by a ringing or noisy terminal line. +.Em "Init will sleep for 10 seconds" , +.Em "then continue trying to start the process" . +.Pp +.It "some processes would not die; ps axl advised." +A process +is hung and could not be killed when the system was shutting down. +This condition is usually caused by a process +that is stuck in a device driver because of +a persistent device error condition. +.El +.Sh FILES +.Bl -tag -width /var/log/wtmp -compact +.It Pa /dev/console +System console device. +.It Pa /dev/tty* +Terminal ports found in +.Xr ttys . +.It Pa /var/run/utmp +Record of Current users on the system. +.It Pa /var/log/wtmp +Record of all logins and logouts. +.It Pa /etc/ttys +The terminal initialization information file. +.It Pa /etc/rc +System startup commands. +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr kill 1 , +.Xr sh 1 , +.Xr ttys 5 , +.Xr crash 8 , +.Xr getty 8 , +.Xr rc 8 , +.Xr reboot 8 , +.Xr halt 8 , +.Xr shutdown 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . +.Sh BUGS +Systems without +.Xr sysctl +behave as though they have security level \-1. diff --git a/sbin/init/init.c b/sbin/init/init.c new file mode 100644 index 0000000..ff03598 --- /dev/null +++ b/sbin/init/init.c @@ -0,0 +1,1297 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Donn Seeley at Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/wait.h> + +#include <db.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <ttyent.h> +#include <unistd.h> + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#ifdef SECURE +#include <pwd.h> +#endif + +#include "pathnames.h" + +/* + * Until the mythical util.h arrives... + */ +extern int login_tty __P((int)); +extern int logout __P((const char *)); +extern void logwtmp __P((const char *, const char *, const char *)); + +/* + * Sleep times; used to prevent thrashing. + */ +#define GETTY_SPACING 5 /* N secs minimum getty spacing */ +#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ +#define WINDOW_WAIT 3 /* wait N secs after starting window */ +#define STALL_TIMEOUT 30 /* wait N secs after warning */ +#define DEATH_WATCH 10 /* wait N secs for procs to die */ + +void handle __P((sig_t, ...)); +void delset __P((sigset_t *, ...)); + +void stall __P((char *, ...)); +void warning __P((char *, ...)); +void emergency __P((char *, ...)); +void disaster __P((int)); +void badsys __P((int)); + +/* + * We really need a recursive typedef... + * The following at least guarantees that the return type of (*state_t)() + * is sufficiently wide to hold a function pointer. + */ +typedef long (*state_func_t) __P((void)); +typedef state_func_t (*state_t) __P((void)); + +state_func_t single_user __P((void)); +state_func_t runcom __P((void)); +state_func_t read_ttys __P((void)); +state_func_t multi_user __P((void)); +state_func_t clean_ttys __P((void)); +state_func_t catatonia __P((void)); +state_func_t death __P((void)); + +enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; + +void transition __P((state_t)); +state_t requested_transition = runcom; + +void setctty __P((char *)); + +typedef struct init_session { + int se_index; /* index of entry in ttys file */ + pid_t se_process; /* controlling process */ + time_t se_started; /* used to avoid thrashing */ + int se_flags; /* status of session */ +#define SE_SHUTDOWN 0x1 /* session won't be restarted */ + char *se_device; /* filename of port */ + char *se_getty; /* what to run on that port */ + char **se_getty_argv; /* pre-parsed argument array */ + char *se_window; /* window system (started only once) */ + char **se_window_argv; /* pre-parsed argument array */ + struct init_session *se_prev; + struct init_session *se_next; +} session_t; + +void free_session __P((session_t *)); +session_t *new_session __P((session_t *, int, struct ttyent *)); +session_t *sessions; + +char **construct_argv __P((char *)); +void start_window_system __P((session_t *)); +void collect_child __P((pid_t)); +pid_t start_getty __P((session_t *)); +void transition_handler __P((int)); +void alrm_handler __P((int)); +void setsecuritylevel __P((int)); +int getsecuritylevel __P((void)); +int setupargv __P((session_t *, struct ttyent *)); +int clang; + +void clear_session_logs __P((session_t *)); + +int start_session_db __P((void)); +void add_session __P((session_t *)); +void del_session __P((session_t *)); +session_t *find_session __P((pid_t)); +DB *session_db; + +/* + * The mother of all processes. + */ +int +main(argc, argv) + int argc; + char **argv; +{ + int c; + struct sigaction sa; + sigset_t mask; + + + /* Dispose of random users. */ + if (getuid() != 0) { + (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); + exit (1); + } + + /* System V users like to reexec init. */ + if (getpid() != 1) { + (void)fprintf(stderr, "init: already running\n"); + exit (1); + } + + /* + * Note that this does NOT open a file... + * Does 'init' deserve its own facility number? + */ + openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); + + /* + * Create an initial session. + */ + if (setsid() < 0) + warning("initial setsid() failed: %m"); + + /* + * Establish an initial user so that programs running + * single user do not freak out and die (like passwd). + */ + if (setlogin("root") < 0) + warning("setlogin() failed: %m"); + + /* + * This code assumes that we always get arguments through flags, + * never through bits set in some random machine register. + */ + while ((c = getopt(argc, argv, "sf")) != -1) + switch (c) { + case 's': + requested_transition = single_user; + break; + case 'f': + runcom_mode = FASTBOOT; + break; + default: + warning("unrecognized flag '-%c'", c); + break; + } + + if (optind != argc) + warning("ignoring excess arguments"); + + /* + * We catch or block signals rather than ignore them, + * so that they get reset on exec. + */ + handle(badsys, SIGSYS, 0); + handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, + SIGBUS, SIGXCPU, SIGXFSZ, 0); + handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); + handle(alrm_handler, SIGALRM, 0); + sigfillset(&mask); + delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, + SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); + (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); + + /* + * Paranoia. + */ + close(0); + close(1); + close(2); + + /* + * Start the state machine. + */ + transition(requested_transition); + + /* + * Should never reach here. + */ + return 1; +} + +/* + * Associate a function with a signal handler. + */ +void +#ifdef __STDC__ +handle(sig_t handler, ...) +#else +handle(va_alist) + va_dcl +#endif +{ + int sig; + struct sigaction sa; + int mask_everything; + va_list ap; +#ifndef __STDC__ + sig_t handler; + + va_start(ap); + handler = va_arg(ap, sig_t); +#else + va_start(ap, handler); +#endif + + sa.sa_handler = handler; + sigfillset(&mask_everything); + + while (sig = va_arg(ap, int)) { + sa.sa_mask = mask_everything; + /* XXX SA_RESTART? */ + sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; + sigaction(sig, &sa, (struct sigaction *) 0); + } + va_end(ap); +} + +/* + * Delete a set of signals from a mask. + */ +void +#ifdef __STDC__ +delset(sigset_t *maskp, ...) +#else +delset(va_alist) + va_dcl +#endif +{ + int sig; + va_list ap; +#ifndef __STDC__ + sigset_t *maskp; + + va_start(ap); + maskp = va_arg(ap, sigset_t *); +#else + va_start(ap, maskp); +#endif + + while (sig = va_arg(ap, int)) + sigdelset(maskp, sig); + va_end(ap); +} + +/* + * Log a message and sleep for a while (to give someone an opportunity + * to read it and to save log or hardcopy output if the problem is chronic). + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +stall(char *message, ...) +#else +stall(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); + sleep(STALL_TIMEOUT); +} + +/* + * Like stall(), but doesn't sleep. + * If cpp had variadic macros, the two functions could be #defines for another. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +warning(char *message, ...) +#else +warning(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); +} + +/* + * Log an emergency message. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +emergency(char *message, ...) +#else +emergency(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_EMERG, message, ap); + va_end(ap); +} + +/* + * Catch a SIGSYS signal. + * + * These may arise if a system does not support sysctl. + * We tolerate up to 25 of these, then throw in the towel. + */ +void +badsys(sig) + int sig; +{ + static int badcount = 0; + + if (badcount++ < 25) + return; + disaster(sig); +} + +/* + * Catch an unexpected signal. + */ +void +disaster(sig) + int sig; +{ + emergency("fatal signal: %s", + sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); + + sleep(STALL_TIMEOUT); + _exit(sig); /* reboot */ +} + +/* + * Get the security level of the kernel. + */ +int +getsecuritylevel() +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + size_t len; + extern int errno; + + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + len = sizeof curlevel; + if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { + emergency("cannot get kernel security level: %s", + strerror(errno)); + return (-1); + } + return (curlevel); +#else + return (-1); +#endif +} + +/* + * Set the security level of the kernel. + */ +void +setsecuritylevel(newlevel) + int newlevel; +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + extern int errno; + + curlevel = getsecuritylevel(); + if (newlevel == curlevel) + return; + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { + emergency( + "cannot change kernel security level from %d to %d: %s", + curlevel, newlevel, strerror(errno)); + return; + } +#ifdef SECURE + warning("kernel security level changed from %d to %d", + curlevel, newlevel); +#endif +#endif +} + +/* + * Change states in the finite state machine. + * The initial state is passed as an argument. + */ +void +transition(s) + state_t s; +{ + for (;;) + s = (state_t) (*s)(); +} + +/* + * Close out the accounting files for a login session. + * NB: should send a message to the session logger to avoid blocking. + */ +void +clear_session_logs(sp) + session_t *sp; +{ + char *line = sp->se_device + sizeof(_PATH_DEV) - 1; + + if (logout(line)) + logwtmp(line, "", ""); +} + +/* + * Start a session and allocate a controlling terminal. + * Only called by children of init after forking. + */ +void +setctty(name) + char *name; +{ + int fd; + + (void) revoke(name); + sleep (2); /* leave DTR low */ + if ((fd = open(name, O_RDWR)) == -1) { + stall("can't open %s: %m", name); + _exit(1); + } + if (login_tty(fd) == -1) { + stall("can't get %s for controlling terminal: %m", name); + _exit(1); + } +} + +/* + * Bring the system up single user. + */ +state_func_t +single_user() +{ + pid_t pid, wpid; + int status; + sigset_t mask; + char *shell = _PATH_BSHELL; + char *argv[2]; +#ifdef SECURE + struct ttyent *typ; + struct passwd *pp; + static const char banner[] = + "Enter root password, or ^D to go multi-user\n"; + char *clear, *password; +#endif + + /* + * If the kernel is in secure mode, downgrade it to insecure mode. + */ + if (getsecuritylevel() > 0) + setsecuritylevel(0); + + if ((pid = fork()) == 0) { + /* + * Start the single user session. + */ + setctty(_PATH_CONSOLE); + +#ifdef SECURE + /* + * Check the root password. + * We don't care if the console is 'on' by default; + * it's the only tty that can be 'off' and 'secure'. + */ + typ = getttynam("console"); + pp = getpwnam("root"); + if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { + write(2, banner, sizeof banner - 1); + for (;;) { + clear = getpass("Password:"); + if (clear == 0 || *clear == '\0') + _exit(0); + password = crypt(clear, pp->pw_passwd); + bzero(clear, _PASSWORD_LEN); + if (strcmp(password, pp->pw_passwd) == 0) + break; + warning("single-user login failed\n"); + } + } + endttyent(); + endpwent(); +#endif /* SECURE */ + +#ifdef DEBUGSHELL + { + char altshell[128], *cp = altshell; + int num; + +#define SHREQUEST \ + "Enter pathname of shell or RETURN for sh: " + (void)write(STDERR_FILENO, + SHREQUEST, sizeof(SHREQUEST) - 1); + while ((num = read(STDIN_FILENO, cp, 1)) != -1 && + num != 0 && *cp != '\n' && cp < &altshell[127]) + cp++; + *cp = '\0'; + if (altshell[0] != '\0') + shell = altshell; + } +#endif /* DEBUGSHELL */ + + /* + * Unblock signals. + * We catch all the interesting ones, + * and those are reset to SIG_DFL on exec. + */ + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + /* + * Fire off a shell. + * If the default one doesn't work, try the Bourne shell. + */ + argv[0] = "-sh"; + argv[1] = 0; + execv(shell, argv); + emergency("can't exec %s for single user: %m", shell); + execv(_PATH_BSHELL, argv); + emergency("can't exec %s for single user: %m", _PATH_BSHELL); + sleep(STALL_TIMEOUT); + _exit(1); + } + + if (pid == -1) { + /* + * We are seriously hosed. Do our best. + */ + emergency("can't fork single-user shell, trying again"); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + return (state_func_t) single_user; + } + + requested_transition = 0; + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for single-user shell failed: %m; restarting"); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: shell stopped, restarting\n"); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid && !requested_transition); + + if (requested_transition) + return (state_func_t) requested_transition; + + if (!WIFEXITED(status)) { + if (WTERMSIG(status) == SIGKILL) { + /* + * reboot(8) killed shell? + */ + warning("single user shell terminated."); + sleep(STALL_TIMEOUT); + _exit(0); + } else { + warning("single user shell terminated, restarting"); + return (state_func_t) single_user; + } + } + + runcom_mode = FASTBOOT; + return (state_func_t) runcom; +} + +/* + * Run the system startup script. + */ +state_func_t +runcom() +{ + pid_t pid, wpid; + int status; + char *argv[4]; + struct sigaction sa; + + if ((pid = fork()) == 0) { + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); + (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); + + setctty(_PATH_CONSOLE); + + argv[0] = "sh"; + argv[1] = _PATH_RUNCOM; + argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; + argv[3] = 0; + + sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); + + execv(_PATH_BSHELL, argv); + stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); + _exit(1); /* force single user mode */ + } + + if (pid == -1) { + emergency("can't fork for %s on %s: %m", + _PATH_BSHELL, _PATH_RUNCOM); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + sleep(STALL_TIMEOUT); + return (state_func_t) single_user; + } + + /* + * Copied from single_user(). This is a bit paranoid. + */ + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for %s on %s failed: %m; going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: %s on %s stopped, restarting\n", + _PATH_BSHELL, _PATH_RUNCOM); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid); + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && + requested_transition == catatonia) { + /* /etc/rc executed /sbin/reboot; wait for the end quietly */ + sigset_t s; + + sigfillset(&s); + for (;;) + sigsuspend(&s); + } + + if (!WIFEXITED(status)) { + warning("%s on %s terminated abnormally, going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + + if (WEXITSTATUS(status)) + return (state_func_t) single_user; + + runcom_mode = AUTOBOOT; /* the default */ + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "reboot", ""); + return (state_func_t) read_ttys; +} + +/* + * Open the session database. + * + * NB: We could pass in the size here; is it necessary? + */ +int +start_session_db() +{ + if (session_db && (*session_db->close)(session_db)) + emergency("session database close: %s", strerror(errno)); + if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { + emergency("session database open: %s", strerror(errno)); + return (1); + } + return (0); + +} + +/* + * Add a new login session. + */ +void +add_session(sp) + session_t *sp; +{ + DBT key; + DBT data; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + data.data = &sp; + data.size = sizeof sp; + + if ((*session_db->put)(session_db, &key, &data, 0)) + emergency("insert %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Delete an old login session. + */ +void +del_session(sp) + session_t *sp; +{ + DBT key; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + + if ((*session_db->del)(session_db, &key, 0)) + emergency("delete %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Look up a login session by pid. + */ +session_t * +#ifdef __STDC__ +find_session(pid_t pid) +#else +find_session(pid) + pid_t pid; +#endif +{ + DBT key; + DBT data; + session_t *ret; + + key.data = &pid; + key.size = sizeof pid; + if ((*session_db->get)(session_db, &key, &data, 0) != 0) + return 0; + bcopy(data.data, (char *)&ret, sizeof(ret)); + return ret; +} + +/* + * Construct an argument vector from a command line. + */ +char ** +construct_argv(command) + char *command; +{ + register int argc = 0; + register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) + * sizeof (char *)); + static const char separators[] = " \t"; + + if ((argv[argc++] = strtok(command, separators)) == 0) + return 0; + while (argv[argc++] = strtok((char *) 0, separators)) + continue; + return argv; +} + +/* + * Deallocate a session descriptor. + */ +void +free_session(sp) + register session_t *sp; +{ + free(sp->se_device); + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + if (sp->se_window) { + free(sp->se_window); + free(sp->se_window_argv); + } + free(sp); +} + +/* + * Allocate a new session descriptor. + */ +session_t * +new_session(sprev, session_index, typ) + session_t *sprev; + int session_index; + register struct ttyent *typ; +{ + register session_t *sp; + + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_name == 0 || + typ->ty_getty == 0) + return 0; + + sp = (session_t *) malloc(sizeof (session_t)); + bzero(sp, sizeof *sp); + + sp->se_index = session_index; + + sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); + (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); + + if (setupargv(sp, typ) == 0) { + free_session(sp); + return (0); + } + + sp->se_next = 0; + if (sprev == 0) { + sessions = sp; + sp->se_prev = 0; + } else { + sprev->se_next = sp; + sp->se_prev = sprev; + } + + return sp; +} + +/* + * Calculate getty and if useful window argv vectors. + */ +int +setupargv(sp, typ) + session_t *sp; + struct ttyent *typ; +{ + + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); + (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); + sp->se_getty_argv = construct_argv(sp->se_getty); + if (sp->se_getty_argv == 0) { + warning("can't parse getty for port %s", sp->se_device); + free(sp->se_getty); + sp->se_getty = 0; + return (0); + } + if (typ->ty_window) { + if (sp->se_window) + free(sp->se_window); + sp->se_window = strdup(typ->ty_window); + sp->se_window_argv = construct_argv(sp->se_window); + if (sp->se_window_argv == 0) { + warning("can't parse window for port %s", + sp->se_device); + free(sp->se_window); + sp->se_window = 0; + return (0); + } + } + return (1); +} + +/* + * Walk the list of ttys and create sessions for each active line. + */ +state_func_t +read_ttys() +{ + int session_index = 0; + register session_t *sp, *snext; + register struct ttyent *typ; + + /* + * Destroy any previous session state. + * There shouldn't be any, but just in case... + */ + for (sp = sessions; sp; sp = snext) { + if (sp->se_process) + clear_session_logs(sp); + snext = sp->se_next; + free_session(sp); + } + sessions = 0; + if (start_session_db()) + return (state_func_t) single_user; + + /* + * Allocate a session entry for each active port. + * Note that sp starts at 0. + */ + while (typ = getttyent()) + if (snext = new_session(sp, ++session_index, typ)) + sp = snext; + + endttyent(); + + return (state_func_t) multi_user; +} + +/* + * Start a window system running. + */ +void +start_window_system(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + + if ((pid = fork()) == -1) { + emergency("can't fork for window system on port %s: %m", + sp->se_device); + /* hope that getty fails and we can try again */ + return; + } + + if (pid) + return; + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + if (setsid() < 0) + emergency("setsid failed (window) %m"); + + execv(sp->se_window_argv[0], sp->se_window_argv); + stall("can't exec window system '%s' for port %s: %m", + sp->se_window_argv[0], sp->se_device); + _exit(1); +} + +/* + * Start a login session running. + */ +pid_t +start_getty(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + time_t current_time = time((time_t *) 0); + + /* + * fork(), not vfork() -- we can't afford to block. + */ + if ((pid = fork()) == -1) { + emergency("can't fork for getty on port %s: %m", sp->se_device); + return -1; + } + + if (pid) + return pid; + + if (current_time > sp->se_started && + current_time - sp->se_started < GETTY_SPACING) { + warning("getty repeating too quickly on port %s, sleeping", + sp->se_device); + sleep((unsigned) GETTY_SLEEP); + } + + if (sp->se_window) { + start_window_system(sp); + sleep(WINDOW_WAIT); + } + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + execv(sp->se_getty_argv[0], sp->se_getty_argv); + stall("can't exec getty '%s' for port %s: %m", + sp->se_getty_argv[0], sp->se_device); + _exit(1); +} + +/* + * Collect exit status for a child. + * If an exiting login, start a new login running. + */ +void +#ifdef __STDC__ +collect_child(pid_t pid) +#else +collect_child(pid) + pid_t pid; +#endif +{ + register session_t *sp, *sprev, *snext; + + if (! sessions) + return; + + if (! (sp = find_session(pid))) + return; + + clear_session_logs(sp); + del_session(sp); + sp->se_process = 0; + + if (sp->se_flags & SE_SHUTDOWN) { + if (sprev = sp->se_prev) + sprev->se_next = sp->se_next; + else + sessions = sp->se_next; + if (snext = sp->se_next) + snext->se_prev = sp->se_prev; + free_session(sp); + return; + } + + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + return; + } + + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); +} + +/* + * Catch a signal and request a state transition. + */ +void +transition_handler(sig) + int sig; +{ + + switch (sig) { + case SIGHUP: + requested_transition = clean_ttys; + break; + case SIGTERM: + requested_transition = death; + break; + case SIGTSTP: + requested_transition = catatonia; + break; + default: + requested_transition = 0; + break; + } +} + +/* + * Take the system multiuser. + */ +state_func_t +multi_user() +{ + pid_t pid; + register session_t *sp; + + requested_transition = 0; + + /* + * If the administrator has not set the security level to -1 + * to indicate that the kernel should not run multiuser in secure + * mode, and the run script has not set a higher level of security + * than level 1, then put the kernel into secure mode. + */ + if (getsecuritylevel() == 0) + setsecuritylevel(1); + + for (sp = sessions; sp; sp = sp->se_next) { + if (sp->se_process) + continue; + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + break; + } + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); + } + + while (!requested_transition) + if ((pid = waitpid(-1, (int *) 0, 0)) != -1) + collect_child(pid); + + return (state_func_t) requested_transition; +} + +/* + * This is an n-squared algorithm. We hope it isn't run often... + */ +state_func_t +clean_ttys() +{ + register session_t *sp, *sprev; + register struct ttyent *typ; + register int session_index = 0; + register int devlen; + + if (! sessions) + return (state_func_t) multi_user; + + devlen = sizeof(_PATH_DEV) - 1; + while (typ = getttyent()) { + ++session_index; + + for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) + if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) + break; + + if (sp) { + if (sp->se_index != session_index) { + warning("port %s changed utmp index from %d to %d", + sp->se_device, sp->se_index, + session_index); + sp->se_index = session_index; + } + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_getty == 0) { + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + continue; + } + sp->se_flags &= ~SE_SHUTDOWN; + if (setupargv(sp, typ) == 0) { + warning("can't parse getty for port %s", + sp->se_device); + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + } + continue; + } + + new_session(sprev, session_index, typ); + } + + endttyent(); + + return (state_func_t) multi_user; +} + +/* + * Block further logins. + */ +state_func_t +catatonia() +{ + register session_t *sp; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + return (state_func_t) multi_user; +} + +/* + * Note SIGALRM. + */ +void +alrm_handler(sig) + int sig; +{ + clang = 1; +} + +/* + * Bring the system down to single user. + */ +state_func_t +death() +{ + register session_t *sp; + register int i; + pid_t pid; + static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "shutdown", ""); + + for (i = 0; i < 3; ++i) { + if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) + return (state_func_t) single_user; + + clang = 0; + alarm(DEATH_WATCH); + do + if ((pid = waitpid(-1, (int *)0, 0)) != -1) + collect_child(pid); + while (clang == 0 && errno != ECHILD); + + if (errno == ECHILD) + return (state_func_t) single_user; + } + + warning("some processes would not die; ps axl advised"); + + return (state_func_t) single_user; +} diff --git a/sbin/init/pathnames.h b/sbin/init/pathnames.h new file mode 100644 index 0000000..abb874a --- /dev/null +++ b/sbin/init/pathnames.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Donn Seeley at Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + */ + +#include <paths.h> + +#define _PATH_SLOGGER "/sbin/session_logger" +#define _PATH_RUNCOM "/etc/rc" diff --git a/sbin/mknod/Makefile b/sbin/mknod/Makefile new file mode 100644 index 0000000..272753f --- /dev/null +++ b/sbin/mknod/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= mknod +MAN8= mknod.0 + +.include <bsd.prog.mk> diff --git a/sbin/mknod/mknod.8 b/sbin/mknod/mknod.8 new file mode 100644 index 0000000..73f71d3 --- /dev/null +++ b/sbin/mknod/mknod.8 @@ -0,0 +1,99 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mknod.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt MKNOD 8 +.Os BSD 4 +.Sh NAME +.Nm mknod +.Nd build special file +.Sh SYNOPSIS +.Nm mknod +.Ar name +.Op Cm c | Cm b +.Ar major minor +.Sh DESCRIPTION +The +.Nm mknod +command creates device special files. +Normally the shell script +.Pa /dev/MAKEDEV +is used to create special files for commonly known devices; it executes +.Nm mknod +with the appropriate arguments and can make all the files required for the +device. +.Pp +To make nodes manually, the four required arguments are: +.Pp +.Bl -tag -width majorx +.It Ar name +Device name, for example +.Dq sd +for a SCSI disk on an HP300 or a +.Dq pty +for pseudo-devices. +.It Cm b | Cm c +Type of device. If the +device is a block type device such as a tape or disk drive which needs +both cooked and raw special files, +the type is +.Cm b . +All other devices are character type devices, such as terminal +and pseudo devices, and are type +.Cm c . +.It Ar major +The major device number is an integer number which tells the kernel +which device driver entry point to use. To learn what +major device number to use for a particular device, check the file +.Pa /dev/MAKEDEV +to see if the device is known, or check +the system dependent device configuration file: +.Bd -filled -offset indent +.Dq Pa /usr/src/sys/conf/device. Ns Em architecture +.Ed +.Pp +(for example +.Pa device.hp300 ) . +.It Ar minor +The minor device number tells the kernel which subunit +the node corresponds to on the device; for example, +a subunit may be a filesystem partition +or a tty line. +.El +.Sh SEE ALSO +.Xr mknod 2 , +.Xr makedev 8 +.Sh HISTORY +A +.Nm +command appeared in Version 6 AT&T UNIX. diff --git a/sbin/mknod/mknod.c b/sbin/mknod/mknod.c new file mode 100644 index 0000000..b70acf1 --- /dev/null +++ b/sbin/mknod/mknod.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kevin Fall. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mknod.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> + +main(argc, argv) + int argc; + char **argv; +{ + extern int errno; + u_short mode; + char *strerror(); + + if (argc != 5) { + (void)fprintf(stderr, + "usage: mknod name [b | c] major minor\n"); + exit(1); + } + + mode = 0666; + if (argv[2][0] == 'c') + mode |= S_IFCHR; + else if (argv[2][0] == 'b') + mode |= S_IFBLK; + else { + (void)fprintf(stderr, + "mknod: node must be type 'b' or 'c'.\n"); + exit(1); + } + + if (mknod(argv[1], mode, makedev(atoi(argv[3]), atoi(argv[4]))) < 0) { + (void)fprintf(stderr, + "mknod: %s: %s\n", argv[1], strerror(errno)); + exit(1); + } + exit(0); +} diff --git a/sbin/mount/Makefile b/sbin/mount/Makefile new file mode 100644 index 0000000..b3efd4e --- /dev/null +++ b/sbin/mount/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.5 (Berkeley) 3/27/94 + +PROG= mount +SRCS= mount.c mount_ufs.c getmntopts.c +MAN8= mount.0 +# We do NOT install the getmntopts.3 man page. + +.include <bsd.prog.mk> diff --git a/sbin/mount/getmntopts.3 b/sbin/mount/getmntopts.3 new file mode 100644 index 0000000..642c57a --- /dev/null +++ b/sbin/mount/getmntopts.3 @@ -0,0 +1,163 @@ +.\" Copyright (c) 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getmntopts.3 8.1 (Berkeley) 3/27/94 +.\" +.Dd March 27, 1994 +.Dt GETMNTOPTS 3 +.Os BSD 4.4 +.Sh NAME +.Nm getmntopts +.Nd scan mount options +.Sh SYNOPSIS +.Fd #include <mntopts.h> +.Ft void +.Fn getmntopts "char *options" "struct mntopt *mopts" "int *flagp" +.Sh DESCRIPTION +The +.Nm getmntopts +function takes a comma separated option list and a list +of valid option names, and computes the bitmask +corresponding to the requested set of options. +.Pp +The string +.Dv options +is broken down into a sequence of comma separated tokens. +Each token is looked up in the table described by +.Dv mopts +and the bits in +the word referenced by +.Dv flagp +are updated. +The flag word is not initialized by +.Nm getmntopt . +The table, +.Dv mopts , +has the following format: +.Bd -literal +struct mntopt { + char *m_option; /* option name */ + int m_inverse; /* is this a negative option, eg "dev" */ + int m_flag; /* bit to set, eg MNT_RDONLY */ +}; +.Ed +.Pp +The members of this structure are: +.Bl -tag -width m_inverse +.It Fa m_option +the option name, +for example +.Dq suid . +.It Fa m_inverse +tells +.Nm getmntopts +that the name has the inverse meaning of the +bit. +For example, +.Dq suid +is the string, whereas the +mount flag is +.Dv MNT_NOSUID . +In this case, the sense of the string and the flag +are inverted, so the +.Dv m_inverse +flag should be set. +.It Fa m_flag +the value of the bit to be set or cleared in +the flag word when the option is recognized. +The bit is set when the option is discovered, +but cleared if the option name was preceded +by the letters +.Dq no . +The +.Dv m_inverse +flag causes these two operations to be reversed. +.El +.Pp +Each of the user visible +.Dv MNT_ +flags has a corresponding +.Dv MOPT_ +macro which defines an appropriate +.Li "struct mntopt" +entry. +To simplify the program interface and ensure consistency across all +programs, a general purpose macro, +.Dv MOPT_STDOPTS , +is defined which +contains an entry for all the generic VFS options. +In addition, the macros +.Dv MOPT_FORCE +and +.Dv MOPT_UPDATE +exist to enable the +.Dv MNT_FORCE +and +.Dv MNT_UPDATE +flags to be set. +Finally, the table must be terminated by an entry with a NULL +first element. +.Sh EXAMPLES +Most commands will use the standard option set. +Local filesystems which support the +.Dv MNT_UPDATE +flag, would also have an +.Dv MOPT_UPDATE +entry. +This can be declared and used as follows: +.Bd -literal +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_UPDATE, + { NULL } +}; + + ... + mntflags = 0; + ... + getmntopts(options, mopts, &mntflags) + ... +.Ed +.Sh DIAGNOSTICS +The +.Nm getmntopts +function displays an error message and exits if an +unrecognized option is encountered. +.Sh SEE ALSO +.Xr err 3 , +.Xr mount 8 +.Sh HISTORY +The +.Fn getmntopts +function appeared in +.Bx 4.4 . diff --git a/sbin/mount/getmntopts.c b/sbin/mount/getmntopts.c new file mode 100644 index 0000000..2c84ae5 --- /dev/null +++ b/sbin/mount/getmntopts.c @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)getmntopts.c 8.1 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> + +#include <err.h> +#include <errno.h> +#include <fstab.h> +#include <stdlib.h> +#include <string.h> + +#include "mntopts.h" + +void +getmntopts(options, m0, flagp) + const char *options; + const struct mntopt *m0; + int *flagp; +{ + const struct mntopt *m; + int negative; + char *opt, *optbuf; + + /* Copy option string, since it is about to be torn asunder... */ + if ((optbuf = strdup(options)) == NULL) + err(1, NULL); + + for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) { + /* Check for "no" prefix. */ + if (opt[0] == 'n' && opt[1] == 'o') { + negative = 1; + opt += 2; + } else + negative = 0; + + /* Scan option table. */ + for (m = m0; m->m_option != NULL; ++m) + if (strcasecmp(opt, m->m_option) == 0) + break; + + /* Save flag, or fail if option is not recognised. */ + if (m->m_option) { + if (negative == m->m_inverse) + *flagp |= m->m_flag; + else + *flagp &= ~m->m_flag; + } else + errx(1, "-o %s: option not supported", opt); + } + + free(optbuf); +} diff --git a/sbin/mount/mntopts.h b/sbin/mount/mntopts.h new file mode 100644 index 0000000..a89f63d --- /dev/null +++ b/sbin/mount/mntopts.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mntopts.h 8.3 (Berkeley) 3/27/94 + */ + +struct mntopt { + const char *m_option; /* option name */ + int m_inverse; /* if a negative option, eg "dev" */ + int m_flag; /* bit to set, eg. MNT_RDONLY */ +}; + +/* User-visible MNT_ flags. */ +#define MOPT_ASYNC { "async", 0, MNT_ASYNC } +#define MOPT_NODEV { "dev", 1, MNT_NODEV } +#define MOPT_NOEXEC { "exec", 1, MNT_NOEXEC } +#define MOPT_NOSUID { "suid", 1, MNT_NOSUID } +#define MOPT_RDONLY { "rdonly", 0, MNT_RDONLY } +#define MOPT_SYNC { "sync", 0, MNT_SYNCHRONOUS } +#define MOPT_UNION { "union", 0, MNT_UNION } + +/* Control flags. */ +#define MOPT_FORCE { "force", 1, MNT_FORCE } +#define MOPT_UPDATE { "update", 0, MNT_UPDATE } + +/* Support for old-style "ro", "rw" flags. */ +#define MOPT_RO { "ro", 0, MNT_RDONLY } +#define MOPT_RW { "rw", 1, MNT_RDONLY } + +#define MOPT_FSTAB_COMPAT \ + MOPT_RO, \ + MOPT_RW + +/* Standard options which all mounts can understand. */ +#define MOPT_STDOPTS \ + MOPT_FSTAB_COMPAT, \ + MOPT_NODEV, \ + MOPT_NOEXEC, \ + MOPT_NOSUID, \ + MOPT_RDONLY, \ + MOPT_UNION + +void getmntopts __P((const char *, const struct mntopt *, int *)); diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8 new file mode 100644 index 0000000..e2c53c8 --- /dev/null +++ b/sbin/mount/mount.8 @@ -0,0 +1,264 @@ +.\" Copyright (c) 1980, 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount.8 8.7 (Berkeley) 3/27/94 +.\" +.Dd March 27, 1994 +.Dt MOUNT 8 +.Os BSD 4 +.Sh NAME +.Nm mount +.Nd mount file systems +.Sh SYNOPSIS +.Nm mount +.Op Fl adfruvw +.Op Fl t Ar ufs | lfs | external_type +.Nm mount +.Op Fl dfruvw +.Ar special | node +.Nm mount +.Op Fl dfruvw +.Op Fl o Ar options +.Op Fl t Ar ufs | lfs | external_type +.Ar special node +.Sh DESCRIPTION +The +.Nm mount +command +calls the +.Xr mount 2 +system call to prepare and graft a +.Ar "special device" +or the remote node (rhost:path) on to the file system tree at the point +.Ar node . +If either +.Ar special +or +.Ar node +are not provided, the appropriate information is taken from the +.Xr fstab 5 +file. +.Pp +The system maintains a list of currently mounted file systems. +If no arguments are given to +.Nm mount, +this list is printed. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl d +Causes everything to be done except for the actual system call. +This option is useful in conjunction with the +.Fl v +flag to +determine what the +.Nm mount +command is trying to do. +.It Fl f +Forces the revocation of write access when trying to downgrade +a filesystem mount status from read-write to read-only. +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +The following options are available: +.Bl -tag -width indent +.It async +All +.Tn I/O +to the file system should be done asynchronously. +This is a +.Em dangerous +flag to set, +and should not be used unless you are prepared to recreate the file +system should your system crash. +.It force +The same as +.Fl f ; +forces the revocation of write access when trying to downgrade +a filesystem mount status from read-write to read-only. +.It nodev +Do not interpret character or block special devices on the file system. +This option is useful for a server that has file systems containing +special devices for architectures other than its own. +.It noexec +Do not allow execution of any binaries on the mounted file system. +This option is useful for a server that has file systems containing +binaries for architectures other than its own. +.It nosuid +Do not allow set-user-identifier or set-group-identifier bits to take effect. +.It rdonly +The same as +.Fl r ; +mount the file system read-only (even the super-user may not write it). +.It sync +All +.Tn I/O +to the file system should be done synchronously. +.It update +The same as +.Fl u ; +indicate that the status of an already mounted file system should be changed. +.It union +Causes the namespace at the mount point to appear as the union +of the mounted filesystem root and the existing directory. +Lookups will be done in the mounted filesystem first. +If those operations fail due to a non-existent file the underlying +directory is then accessed. +All creates are done in the mounted filesystem. +.El +.Pp +Any additional options specific to a filesystem type that is not +one of the internally known types (see the +.Fl t +option) may be passed as a comma separated list; these options are +distinguished by a leading +.Dq \&- +(dash). +Options that take a value are specified using the syntax -option=value. +For example, the mount command: +.Bd -literal -offset indent +mount -t mfs -o nosuid,-N,-s=4000 /dev/dk0b /tmp +.Ed +.Pp +causes +.Nm mount +to execute the equivalent of: +.Bd -literal -offset indent +/sbin/mount_mfs -o nosuid -N -s 4000 /dev/dk0b /tmp +.Ed +.It Fl r +The file system is to be mounted read-only. +Mount the file system read-only (even the super-user may not write it). +The same as the +.Dq rdonly +argument to the +.Fl o +option. +.It Fl t Ar "ufs \\*(Ba lfs \\*(Ba external type" +The argument following the +.Fl t +is used to indicate the file system type. +The type +.Ar ufs +is the default. +The \fI-t\fP option can be used +to indicate that the actions should only be taken on +filesystems of the specified type. +More than one type may be specified in a comma separated list. +The list of filesystem types can be prefixed with +.Dq no +to specify the filesystem types for which action should +.Em not +be taken. +For example, the +.Nm mount +command: +.Bd -literal -offset indent +mount -a -t nonfs,mfs +.Ed +.Pp +mounts all filesystems except those of type +.Tn NFS +and +.Tn MFS . +.Pp +If the type is not one of the internally known types, +mount will attempt to execute a program in +.Pa /sbin/mount_ Ns Em XXX +where +.Em XXX +is replaced by the type name. +For example, nfs filesystems are mounted by the program +.Pa /sbin/mount_nfs . +.It Fl u +The +.Fl u +flag indicates that the status of an already mounted file +system should be changed. +Any of the options discussed above (the +.Fl o +option) +may be changed; +also a file system can be changed from read-only to read-write +or vice versa. +An attempt to change from read-write to read-only will fail if any +files on the filesystem are currently open for writing unless the +.Fl f +flag is also specified. +The set of options is determined by first extracting the options +for the file system from the +.Xr fstab +table, +then applying any options specified by the +.Fl o +argument, +and finally applying the +.Fl r +or +.Fl w +option. +.It Fl v +Verbose mode. +.It Fl w +The file system object is to be read and write. +.Pp +The options specific to NFS filesystems are described in the +.Xr mount_nfs 8 +manual page. +.Sh FILES +.Bl -tag -width /etc/fstab -compact +.It Pa /etc/fstab +file system table +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr fstab 5 , +.Xr mount_cd9660 8 , +.Xr mount_fdesc 8 , +.Xr mount_kernfs 8 , +.Xr mount_lfs 8 , +.Xr mount_lofs 8 , +.Xr mount_mfs 8 , +.Xr mount_nfs 8 , +.Xr mount_null 8 , +.Xr mount_portal 8 , +.Xr mount_procfs 8 , +.Xr mount_umap 8 , +.Xr mount_union 8 , +.Xr umount 8 +.Sh BUGS +It is possible for a corrupted file system to cause a crash. +.Sh HISTORY +A +.Nm mount +command appeared in +.At v6 . diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c new file mode 100644 index 0000000..f0ff782 --- /dev/null +++ b/sbin/mount/mount.c @@ -0,0 +1,512 @@ +/* + * Copyright (c) 1980, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount.c 8.19 (Berkeley) 4/19/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/wait.h> + +#include <err.h> +#include <errno.h> +#include <fstab.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pathnames.h" + +int debug, verbose, skipvfs; + +int badvfsname __P((const char *, const char **)); +int badvfstype __P((int, const char **)); +char *catopt __P((char *, const char *)); +struct statfs + *getmntpt __P((const char *)); +const char + **makevfslist __P((char *)); +void mangle __P((char *, int *, const char **)); +int mountfs __P((const char *, const char *, const char *, + int, const char *, const char *)); +void prmount __P((const char *, const char *, int)); +void usage __P((void)); + +/* From mount_ufs.c. */ +int mount_ufs __P((int, char * const *)); + +/* Map from mount otions to printable formats. */ +static struct opt { + int o_opt; + const char *o_name; +} optnames[] = { + { MNT_ASYNC, "asynchronous" }, + { MNT_EXPORTED, "NFS exported" }, + { MNT_LOCAL, "local" }, + { MNT_NODEV, "nodev" }, + { MNT_NOEXEC, "noexec" }, + { MNT_NOSUID, "nosuid" }, + { MNT_QUOTA, "with quotas" }, + { MNT_RDONLY, "read-only" }, + { MNT_SYNCHRONOUS, "synchronous" }, + { MNT_UNION, "union" }, + { MNT_USER, "user mount" }, + { NULL } +}; + +int +main(argc, argv) + int argc; + char * const argv[]; +{ + const char *mntonname, **vfslist, *vfstype; + struct fstab *fs; + struct statfs *mntbuf; + FILE *mountdfp; + pid_t pid; + int all, ch, i, init_flags, mntsize, rval; + char *options; + + all = init_flags = 0; + options = NULL; + vfslist = NULL; + vfstype = "ufs"; + while ((ch = getopt(argc, argv, "adfo:rwt:uv")) != EOF) + switch (ch) { + case 'a': + all = 1; + break; + case 'd': + debug = 1; + break; + case 'f': + init_flags |= MNT_FORCE; + break; + case 'o': + if (*optarg) + options = catopt(options, optarg); + break; + case 'r': + init_flags |= MNT_RDONLY; + break; + case 't': + if (vfslist != NULL) + errx(1, "only one -t option may be specified."); + vfslist = makevfslist(optarg); + vfstype = optarg; + break; + case 'u': + init_flags |= MNT_UPDATE; + break; + case 'v': + verbose = 1; + break; + case 'w': + init_flags &= ~MNT_RDONLY; + break; + case '?': + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + +#define BADTYPE(type) \ + (strcmp(type, FSTAB_RO) && \ + strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ)) + + rval = 0; + switch (argc) { + case 0: + if (all) + while ((fs = getfsent()) != NULL) { + if (BADTYPE(fs->fs_type)) + continue; + if (badvfsname(fs->fs_vfstype, vfslist)) + continue; + if (mountfs(fs->fs_vfstype, fs->fs_spec, + fs->fs_file, init_flags, options, + fs->fs_mntops)) + rval = 1; + } + else { + if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) + err(1, "getmntinfo"); + for (i = 0; i < mntsize; i++) { + if (badvfstype(mntbuf[i].f_type, vfslist)) + continue; + prmount(mntbuf[i].f_mntfromname, + mntbuf[i].f_mntonname, mntbuf[i].f_flags); + } + } + exit(rval); + case 1: + if (vfslist != NULL) + usage(); + + if (init_flags & MNT_UPDATE) { + if ((mntbuf = getmntpt(*argv)) == NULL) + errx(1, + "unknown special file or file system %s.", + *argv); + if ((fs = getfsfile(mntbuf->f_mntonname)) == NULL) + errx(1, "can't find fstab entry for %s.", + *argv); + /* If it's an update, ignore the fstab file options. */ + fs->fs_mntops = NULL; + mntonname = mntbuf->f_mntonname; + } else { + if ((fs = getfsfile(*argv)) == NULL && + (fs = getfsspec(*argv)) == NULL) + errx(1, + "%s: unknown special file or file system.", + *argv); + if (BADTYPE(fs->fs_type)) + errx(1, "%s has unknown file system type.", + *argv); + mntonname = fs->fs_file; + } + rval = mountfs(fs->fs_vfstype, fs->fs_spec, + mntonname, init_flags, options, fs->fs_mntops); + break; + case 2: + /* + * If -t flag has not been specified, and spec contains either + * a ':' or a '@' then assume that an NFS filesystem is being + * specified ala Sun. + */ + if (vfslist == NULL && strpbrk(argv[0], ":@") != NULL) + vfstype = "nfs"; + rval = mountfs(vfstype, + argv[0], argv[1], init_flags, options, NULL); + break; + default: + usage(); + /* NOTREACHED */ + } + + /* + * If the mount was successfully, and done by root, tell mountd the + * good news. Pid checks are probably unnecessary, but don't hurt. + */ + if (rval == 0 && getuid() == 0 && + (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) { + if (fscanf(mountdfp, "%ld", &pid) == 1 && + pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH) + err(1, "signal mountd"); + (void)fclose(mountdfp); + } + + exit(rval); +} + +int +mountfs(vfstype, spec, name, flags, options, mntopts) + const char *vfstype, *spec, *name, *options, *mntopts; + int flags; +{ + /* List of directories containing mount_xxx subcommands. */ + static const char *edirs[] = { + _PATH_SBIN, + _PATH_USRSBIN, + NULL + }; + const char *argv[100], **edir; + struct statfs sf; + pid_t pid; + int argc, i, status; + char *optbuf, execname[MAXPATHLEN + 1], mntpath[MAXPATHLEN]; + + if (realpath(name, mntpath) == NULL) { + warn("%s", mntpath); + return (1); + } + + name = mntpath; + + if (options == NULL) { + if (mntopts == NULL || *mntopts == '\0') + options = "rw"; + else + options = mntopts; + mntopts = ""; + } + optbuf = catopt(strdup(mntopts), options); + + if (strcmp(name, "/") == 0) + flags |= MNT_UPDATE; + if (flags & MNT_FORCE) + optbuf = catopt(optbuf, "force"); + if (flags & MNT_RDONLY) + optbuf = catopt(optbuf, "ro"); + /* + * XXX + * The mount_mfs (newfs) command uses -o to select the + * optimisation mode. We don't pass the default "-o rw" + * for that reason. + */ + if (flags & MNT_UPDATE) + optbuf = catopt(optbuf, "update"); + + argc = 0; + argv[argc++] = vfstype; + mangle(optbuf, &argc, argv); + argv[argc++] = spec; + argv[argc++] = name; + argv[argc] = NULL; + + if (debug) { + (void)printf("exec: mount_%s", vfstype); + for (i = 1; i < argc; i++) + (void)printf(" %s", argv[i]); + (void)printf("\n"); + return (0); + } + + switch (pid = vfork()) { + case -1: /* Error. */ + warn("vfork"); + free(optbuf); + return (1); + case 0: /* Child. */ + if (strcmp(vfstype, "ufs") == 0) + exit(mount_ufs(argc, (char * const *) argv)); + + /* Go find an executable. */ + edir = edirs; + do { + (void)snprintf(execname, + sizeof(execname), "%s/mount_%s", *edir, vfstype); + execv(execname, (char * const *)argv); + if (errno != ENOENT) + warn("exec %s for %s", execname, name); + } while (*++edir != NULL); + + if (errno == ENOENT) + warn("exec %s for %s", execname, name); + exit(1); + /* NOTREACHED */ + default: /* Parent. */ + free(optbuf); + + if (waitpid(pid, &status, 0) < 0) { + warn("waitpid"); + return (1); + } + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) + return (WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + warnx("%s: %s", name, sys_siglist[WTERMSIG(status)]); + return (1); + } + + if (verbose) { + if (statfs(name, &sf) < 0) { + warn("%s", name); + return (1); + } + prmount(sf.f_mntfromname, sf.f_mntonname, sf.f_flags); + } + break; + } + + return (0); +} + +void +prmount(spec, name, flags) + const char *spec, *name; + int flags; +{ + struct opt *o; + int f; + + (void)printf("%s on %s", spec, name); + + flags &= MNT_VISFLAGMASK; + for (f = 0, o = optnames; flags && o->o_opt; o++) + if (flags & o->o_opt) { + (void)printf("%s%s", !f++ ? " (" : ", ", o->o_name); + flags &= ~o->o_opt; + } + (void)printf(f ? ")\n" : "\n"); +} + +struct statfs * +getmntpt(name) + const char *name; +{ + struct statfs *mntbuf; + int i, mntsize; + + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + for (i = 0; i < mntsize; i++) + if (strcmp(mntbuf[i].f_mntfromname, name) == 0 || + strcmp(mntbuf[i].f_mntonname, name) == 0) + return (&mntbuf[i]); + return (NULL); +} + +int +badvfsname(vfsname, vfslist) + const char *vfsname; + const char **vfslist; +{ + + if (vfslist == NULL) + return (0); + while (*vfslist != NULL) { + if (strcmp(vfsname, *vfslist) == 0) + return (skipvfs); + ++vfslist; + } + return (!skipvfs); +} + +int +badvfstype(vfstype, vfslist) + int vfstype; + const char **vfslist; +{ +static const char *vfsnames[] = INITMOUNTNAMES; + + if ((vfstype < 0) || (vfstype > MOUNT_MAXTYPE)) + return (0); + + return (badvfsname(vfsnames[vfstype], vfslist)); +} + +const char ** +makevfslist(fslist) + char *fslist; +{ + const char **av; + int i; + char *nextcp; + + if (fslist == NULL) + return (NULL); + if (fslist[0] == 'n' && fslist[1] == 'o') { + fslist += 2; + skipvfs = 1; + } + for (i = 0, nextcp = fslist; *nextcp; nextcp++) + if (*nextcp == ',') + i++; + if ((av = malloc((size_t)(i + 2) * sizeof(char *))) == NULL) { + warn(NULL); + return (NULL); + } + nextcp = fslist; + i = 0; + av[i++] = nextcp; + while ((nextcp = strchr(nextcp, ',')) != NULL) { + *nextcp++ = '\0'; + av[i++] = nextcp; + } + av[i++] = NULL; + return (av); +} + +char * +catopt(s0, s1) + char *s0; + const char *s1; +{ + size_t i; + char *cp; + + if (s0 && *s0) { + i = strlen(s0) + strlen(s1) + 1 + 1; + if ((cp = malloc(i)) == NULL) + err(1, NULL); + (void)snprintf(cp, i, "%s,%s", s0, s1); + } else + cp = strdup(s1); + + if (s0) + free(s0); + return (cp); +} + +void +mangle(options, argcp, argv) + char *options; + int *argcp; + const char **argv; +{ + char *p, *s; + int argc; + + argc = *argcp; + for (s = options; (p = strsep(&s, ",")) != NULL;) + if (*p != '\0') + if (*p == '-') { + argv[argc++] = p; + p = strchr(p, '='); + if (p) { + *p = '\0'; + argv[argc++] = p+1; + } + } else if (strcmp(p, "rw") != 0) { + argv[argc++] = "-o"; + argv[argc++] = p; + } + + *argcp = argc; +} + +void +usage() +{ + + (void)fprintf(stderr, + "usage: mount %s %s\n mount %s\n mount %s\n", + "[-dfruvw] [-o options] [-t ufs | external_type]", + "special node", + "[-adfruvw] [-t ufs | external_type]", + "[-dfruvw] special | node"); + exit(1); +} diff --git a/sbin/mount/mount_ufs.c b/sbin/mount/mount_ufs.c new file mode 100644 index 0000000..babb760 --- /dev/null +++ b/sbin/mount/mount_ufs.c @@ -0,0 +1,131 @@ +/*- + * Copyright (c) 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_ufs.c 8.2 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mntopts.h" + +void ufs_usage __P((void)); + +static struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_ASYNC, + MOPT_SYNC, + MOPT_UPDATE, + { NULL } +}; + +int +mount_ufs(argc, argv) + int argc; + char * const argv[]; +{ + extern int optreset; + struct ufs_args args; + int ch, mntflags; + char *fs_name; + + mntflags = 0; + optind = optreset = 1; /* Reset for parse of new argv. */ + while ((ch = getopt(argc, argv, "o:")) != EOF) + switch (ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case '?': + default: + ufs_usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + ufs_usage(); + + args.fspec = argv[0]; /* The name of the device file. */ + fs_name = argv[1]; /* The mount point. */ + +#define DEFAULT_ROOTUID -2 + args.export.ex_root = DEFAULT_ROOTUID; + if (mntflags & MNT_RDONLY) + args.export.ex_flags = MNT_EXRDONLY; + else + args.export.ex_flags = 0; + + if (mount(MOUNT_UFS, fs_name, mntflags, &args) < 0) { + (void)fprintf(stderr, "%s on %s: ", args.fspec, fs_name); + switch (errno) { + case EMFILE: + (void)fprintf(stderr, "mount table full.\n"); + break; + case EINVAL: + if (mntflags & MNT_UPDATE) + (void)fprintf(stderr, + "Specified device does not match mounted device.\n"); + else + (void)fprintf(stderr, + "Incorrect super block.\n"); + break; + default: + (void)fprintf(stderr, "%s\n", strerror(errno)); + break; + } + return (1); + } + return (0); +} + +void +ufs_usage() +{ + (void)fprintf(stderr, "usage: mount_ufs [-o options] special node\n"); + exit(1); +} diff --git a/sbin/mount/pathnames.h b/sbin/mount/pathnames.h new file mode 100644 index 0000000..45a4a01 --- /dev/null +++ b/sbin/mount/pathnames.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.2 (Berkeley) 3/27/94 + */ + +#define _PATH_SBIN "/sbin" +#define _PATH_USRSBIN "/usr/sbin" +#define _PATH_MOUNTDPID "/var/run/mountd.pid" diff --git a/sbin/mount_cd9660/Makefile b/sbin/mount_cd9660/Makefile new file mode 100644 index 0000000..2491dff --- /dev/null +++ b/sbin/mount_cd9660/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_cd9660 +SRCS= mount_cd9660.c getmntopts.c +MAN8= mount_cd9660.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I${MOUNT} +.PATH: ${MOUNT} + +.include <bsd.prog.mk> diff --git a/sbin/mount_cd9660/mount_cd9660.8 b/sbin/mount_cd9660/mount_cd9660.8 new file mode 100644 index 0000000..920dc6c --- /dev/null +++ b/sbin/mount_cd9660/mount_cd9660.8 @@ -0,0 +1,99 @@ +.\" Copyright (c) 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Christopher G. Demetriou. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_cd9660.8 8.3 (Berkeley) 3/27/94 +.Dd March 27, 1994 +.Dt MOUNT_CD9660 8 +.Os BSD 4 +.Sh NAME +.Nm mount_cd9660 +.Nd mount an ISO-9660 filesystem +.Sh SYNOPSIS +.Nm mount_cd9660 +.Op Fl egr +.Op Fl o Ar options +.Ar special | node +.Sh DESCRIPTION +The +.Nm mount_cd9660 +command attaches the ISO-9660 filesystem residing on the device +.Pa special +to the global filesystem namespace at the location indicated by +.Pa node . +This command is normally executed by +.Xr mount 8 +at boot time. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl e +Enable the use of extended attributes. +.It Fl g +Do not strip version numbers on files. +(By default, if there are files with different version numbers on the disk, +only the last one will be listed.) +In either case, files may be opened without explicitly stating a +version number. +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.It Fl r +Do not use any Rockridge extensions included in the filesystem. +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr mount 8 +.Sh BUGS +The cd9660 filesystem does not support the original "High Sierra" +("CDROM001") format. +.Pp +POSIX device node mapping is currently not supported. +.Pp +Version numbers are not stripped if Rockridge extensions are in use. +In this case, accessing files that don't have Rockridge names without +version numbers gets the one with the lowest version number and not +the one with the highest. +.Pp +There is no ECMA support. +.Sh HISTORY +The +.Nm mount_cd9660 +utility first appeared 4.4BSD. diff --git a/sbin/mount_cd9660/mount_cd9660.c b/sbin/mount_cd9660/mount_cd9660.c new file mode 100644 index 0000000..9ef716d --- /dev/null +++ b/sbin/mount_cd9660/mount_cd9660.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley + * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension + * Support code is derived from software contributed to Berkeley + * by Atsushi Murai (amurai@spec.co.jp). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mount_cd9660.c 8.4 (Berkeley) 3/27/94 + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_cd9660.c 8.4 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#define CD9660 +#include <sys/mount.h> + +#include <err.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_UPDATE, + { NULL } +}; + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char **argv; +{ + struct iso_args args; + int ch, mntflags, opts; + char *dev, *dir, *options; + + options = NULL; + mntflags = opts = 0; + while ((ch = getopt(argc, argv, "ego:r")) != EOF) + switch (ch) { + case 'e': + opts |= ISOFSMNT_EXTATT; + break; + case 'g': + opts |= ISOFSMNT_GENS; + break; + case 'o': + getmntopts(options, mopts, &mntflags); + break; + case 'r': + opts |= ISOFSMNT_NORRIP; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + dev = argv[0]; + dir = argv[1]; + +#define DEFAULT_ROOTUID -2 + args.fspec = dev; + args.export.ex_root = DEFAULT_ROOTUID; + + if (mntflags & MNT_RDONLY) + args.export.ex_flags = MNT_EXRDONLY; + else + args.export.ex_flags = 0; + args.flags = opts; + + if (mount(MOUNT_CD9660, dir, mntflags, &args) < 0) + err(1, NULL); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_cd9660 [-egrt] [-o options] special node\n"); + exit(1); +} diff --git a/sbin/mount_fdesc/Makefile b/sbin/mount_fdesc/Makefile new file mode 100644 index 0000000..2f8f3cd --- /dev/null +++ b/sbin/mount_fdesc/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.2 (Berkeley) 3/27/94 + +PROG= mount_fdesc +SRCS= mount_fdesc.c getmntopts.c +MAN8= mount_fdesc.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I${MOUNT} +.PATH: ${MOUNT} + +.include <bsd.prog.mk> diff --git a/sbin/mount_fdesc/mount_fdesc.8 b/sbin/mount_fdesc/mount_fdesc.8 new file mode 100644 index 0000000..a61c952 --- /dev/null +++ b/sbin/mount_fdesc/mount_fdesc.8 @@ -0,0 +1,171 @@ +.\" +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_fdesc.8 8.2 (Berkeley) 3/27/94 +.\" +.\" +.Dd March 27, 1994 +.Dt MOUNT_FDESC 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_fdesc +.Nd mount the file-descriptor file system +.Sh SYNOPSIS +.Nm mount_fdesc +.Op Fl o Ar options +.Ar fdesc +.Ar mount_point +.Sh DESCRIPTION +The +.Nm mount_fdesc +command attaches an instance of the per-process file descriptor +namespace to the global filesystem namespace. +The conventional mount point is +.Pa /dev +and the filesystem should be union mounted in order to augment, +rather than replace, the existing entries in +.Pa /dev . +This command is normally executed by +.Xr mount 8 +at boot time. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Pp +The contents of the mount point are +.Pa fd , +.Pa stderr , +.Pa stdin , +.Pa stdout +and +.Pa tty . +.Pp +.Pa fd +is a directory whose contents +appear as a list of numbered files +which correspond to the open files of the process reading the +directory. +The files +.Pa /dev/fd/0 +through +.Pa /dev/fd/# +refer to file descriptors which can be accessed through the file +system. +If the file descriptor is open and the mode the file is being opened +with is a subset of the mode of the existing descriptor, the call: +.Bd -literal -offset indent +fd = open("/dev/fd/0", mode); +.Ed +.Pp +and the call: +.Bd -literal -offset indent +fd = fcntl(0, F_DUPFD, 0); +.Ed +.Pp +are equivalent. +.Pp +The files +.Pa /dev/stdin , +.Pa /dev/stdout +and +.Pa /dev/stderr +appear as symlinks to the relevant entry in the +.Pa /dev/fd +sub-directory. +Opening them is equivalent to the following calls: +.Bd -literal -offset indent +fd = fcntl(STDIN_FILENO, F_DUPFD, 0); +fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); +fd = fcntl(STDERR_FILENO, F_DUPFD, 0); +.Ed +.Pp +Flags to the +.Xr open 2 +call other than +.Dv O_RDONLY , +.Dv O_WRONLY +and +.Dv O_RDWR +are ignored. +.Pp +The +.Pa /dev/tty +entry is an indirect reference to the current process's controlling terminal. +It appears as a named pipe (FIFO) but behaves in exactly the same way as +the real controlling terminal device. +.Sh FILES +.Bl -tag -width /dev/stderr -compact +.It Pa /dev/fd/# +.It Pa /dev/stdin +.It Pa /dev/stdout +.It Pa /dev/stderr +.It Pa /dev/tty +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr tty 4 , +.Xr fstab 5 , +.Xr mount 8 +.Sh CAVEATS +No +.Pa . +and +.Pa .. +entries appear when listing the contents of the +.Pa /dev/fd +directory. +This makes sense in the context of this filesystem, but is inconsistent +with usual filesystem conventions. +However, it is still possible to refer to both +.Pa . +and +.Pa .. +in a pathname. +.Pp +This filesystem may not be NFS-exported. +.Sh HISTORY +The +.Nm mount_fdesc +utility first appeared in 4.4BSD. diff --git a/sbin/mount_fdesc/mount_fdesc.c b/sbin/mount_fdesc/mount_fdesc.c new file mode 100644 index 0000000..55927d8 --- /dev/null +++ b/sbin/mount_fdesc/mount_fdesc.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1990, 1992 Jan-Simon Pendry + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_fdesc.c 8.2 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> + +#include <err.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, mntflags; + + mntflags = 0; + while ((ch = getopt(argc, argv, "o:")) != EOF) + switch (ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (mount(MOUNT_FDESC, argv[1], mntflags, NULL)) + err(1, NULL); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_fdesc [-o options] fdesc mount_point\n"); + exit(1); +} diff --git a/sbin/mount_kernfs/Makefile b/sbin/mount_kernfs/Makefile new file mode 100644 index 0000000..6bd6d7c --- /dev/null +++ b/sbin/mount_kernfs/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.2 (Berkeley) 3/27/94 + +PROG= mount_kernfs +SRCS= mount_kernfs.c getmntopts.c +MAN8= mount_kernfs.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I${MOUNT} +.PATH: ${MOUNT} + +.include <bsd.prog.mk> diff --git a/sbin/mount_kernfs/mount_kernfs.8 b/sbin/mount_kernfs/mount_kernfs.8 new file mode 100644 index 0000000..787c174 --- /dev/null +++ b/sbin/mount_kernfs/mount_kernfs.8 @@ -0,0 +1,131 @@ +.\" +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_kernfs.8 8.2 (Berkeley) 3/27/94 +.\" +.\" +.Dd March 27, 1994 +.Dt MOUNT_KERNFS 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_kernfs +.Nd mount the /kern file system +.Sh SYNOPSIS +.Nm mount_kernfs +.Op Fl o Ar options +.Ar /kern +.Ar mount_point +.Sh DESCRIPTION +The +.Nm mount_kern +command attaches an instance of the kernel parameter +namespace to the global filesystem namespace. +The conventional mount point is +.Pa /kern . +This command is normally executed by +.Xr mount 8 +at boot time. +.Pp +The filesystem includes several regular files which can be read, +some of which can also be written. +The contents of the files is in a machine-independent format, +either a string, or an integer in decimal ASCII. +Where numbers are returned, a trailing newline character is also added. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Sh FILES +.Bl -tag -width copyright -compact +.It Pa boottime +the time at which the system was last booted (decimal ASCII). +.It Pa copyright +kernel copyright message. +.It Pa hostname +the hostname, with a trailing newline. +The hostname can be changed by writing to this file. +A trailing newline will be stripped from the hostname being written. +.It Pa hz +the frequency of the system clock (decimal ASCII). +.It Pa loadavg +the 1, 5 and 15 minute load average in kernel fixed-point format. +The final integer is the fix-point scaling factor. +All numbers are in decimal ASCII. +.It Pa pagesize +the machine pagesize (decimal ASCII). +.It Pa physmem +the number of pages of physical memory in the machine (decimal ASCII). +.It Pa root +the system root directory. +In a chroot'ed environment, +.Nm +can be used to create a new +.Pa /kern +mount point. +.Pa /kern/root +will then refer to the system global root, not the current process root. +.It Pa rootdev +the root device. +.It Pa rrootdev +the raw root device. +.It Pa time +the second and microsecond value of the system clock. +Both numbers are in decimal ASCII. +.It Pa version +the kernel version string. +The head line for +.Pa /etc/motd +can be generated by running: +.Dq Ic "sed 1q /kern/version" +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr mount 8 +.Sh CAVEATS +This filesystem may not be NFS-exported. +.Sh HISTORY +The +.Nm mount_kernfs +utility first appeared in 4.4BSD. diff --git a/sbin/mount_kernfs/mount_kernfs.c b/sbin/mount_kernfs/mount_kernfs.c new file mode 100644 index 0000000..33a6b5e --- /dev/null +++ b/sbin/mount_kernfs/mount_kernfs.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1990, 1992 Jan-Simon Pendry + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_kernfs.c 8.2 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> + +#include <err.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, mntflags; + + mntflags = 0; + while ((ch = getopt(argc, argv, "o:")) != EOF) + switch (ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (mount(MOUNT_KERNFS, argv[1], mntflags, NULL)) + err(1, NULL); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_kernfs [-o options] /kern mount_point\n"); + exit(1); +} diff --git a/sbin/mount_lfs/Makefile b/sbin/mount_lfs/Makefile new file mode 100644 index 0000000..d3fb12b --- /dev/null +++ b/sbin/mount_lfs/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.2 (Berkeley) 3/27/94 + +PROG= mount_lfs +SRCS= mount_lfs.c getmntopts.c +MAN8= mount_lfs.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I${MOUNT} +.PATH: ${MOUNT} + +.include <bsd.prog.mk> diff --git a/sbin/mount_lfs/mount_lfs.8 b/sbin/mount_lfs/mount_lfs.8 new file mode 100644 index 0000000..bd1bd8c --- /dev/null +++ b/sbin/mount_lfs/mount_lfs.8 @@ -0,0 +1,125 @@ +.\" Copyright (c) 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_lfs.8 8.5 (Berkeley) 3/30/94 +.\" +.Dd "March 30, 1994" +.Dt MOUNT_LFS 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_lfs +.Nd mount a log-structured file system +.Sh SYNOPSIS +.Nm mount_lfs +.Op Fl dns +.Op Fl o Ar options +.Ar special +.Ar node +.Sh DESCRIPTION +The +.Nm mount_lfs +command attaches a log-structured file system +.Ar special +device on to the file system tree at the point +.Ar node . +In addition, the +.Xr lfs_cleanerd 8 +utility is invoked to clean the file system periodically. +.Pp +This command is normally executed by +.Xr mount 8 +at boot time. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d +Run +.Xr lfs_cleanerd 8 +in debug mode. +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.It Fl n +Don't start +.Xr lfs_cleanerd 8 +on the file system. +.It Fl s +Cause +.Xr lfs_cleanerd 8 +to read data in small chunks when cleaning the file system. +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr lfs_cleanerd 8 , +.Xr mount 8 +.sp +.Rs +.%A Ousterhout and Douglis +.%D 1989 +.%T "Beating the I/O Bottleneck: A Case for Log-structured File Systems" +.%J "Operating Systems Review" +.%V Vol. 23 +.%N No. 1 +.%P pp. 11-27 +.%O "also available as Technical Report UCB/CSD 88/467" +.Re +.Rs +.%A Rosenblum and Ousterhout +.%D 1991 +.%T "The Design and Implementation of a Log-Structured File System" +.%J "ACM SIGOPS Operating Systems Review" +.%V Vol. 25 +.%N No. 5 +.Re +.Rs +.%A Seltzer +.%D 1992 +.%T "File System Performance and Transaction Support" +.%B "PhD Thesis, University of California, Berkeley" +.%O "also available as Technical Report UCB/ERL M92" +.Re +.Rs +.%A Seltzer, Bostic, McKusick and Staelin +.%D 1993 +.%T "An Implementation of a Log-Structured File System for UNIX" +.%J "Proc. of the Winter 1993 USENIX Conf." +.%P pp. 315-331 +.Re +.Sh HISTORY +The +.Nm mount_lfs +function first appeared in 4.4BSD. diff --git a/sbin/mount_lfs/mount_lfs.c b/sbin/mount_lfs/mount_lfs.c new file mode 100644 index 0000000..374c930 --- /dev/null +++ b/sbin/mount_lfs/mount_lfs.c @@ -0,0 +1,147 @@ +/*- + * Copyright (c) 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_lfs.c 8.3 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mntopts.h" +#include "pathnames.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_UPDATE, + { NULL } +}; + +void usage __P((void)); +void invoke_cleaner __P((char *)); + +int short_rds, cleaner_debug; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct ufs_args args; + int ch, mntflags, noclean; + char *fs_name, *options; + + options = NULL; + mntflags = noclean = 0; + while ((ch = getopt(argc, argv, "dno:s")) != EOF) + switch (ch) { + case 'd': + cleaner_debug = 1; + break; + case 'n': + noclean = 1; + break; + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case 's': + short_rds = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + args.fspec = argv[0]; /* the name of the device file */ + fs_name = argv[1]; /* the mount point */ + +#define DEFAULT_ROOTUID -2 + args.export.ex_root = DEFAULT_ROOTUID; + if (mntflags & MNT_RDONLY) + args.export.ex_flags = MNT_EXRDONLY; + else + args.export.ex_flags = 0; + + if (mount(MOUNT_LFS, fs_name, mntflags, &args)) + err(1, NULL); + + if (!noclean) + invoke_cleaner(fs_name); + /* NOTREACHED */ + + exit(0); +} + +void +invoke_cleaner(name) + char *name; +{ + char *args[6], **ap = args; + + /* Build the argument list. */ + *ap++ = _PATH_LFS_CLEANERD; + if (short_rds) + *ap++ = "-s"; + if (cleaner_debug) + *ap++ = "-d"; + *ap++ = name; + *ap = NULL; + + execv(args[0], args); + err(1, "exec %s", _PATH_LFS_CLEANERD); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_lfs [-dns] [-o options] special node\n"); + exit(1); +} diff --git a/sbin/mount_lfs/pathnames.h b/sbin/mount_lfs/pathnames.h new file mode 100644 index 0000000..fafbf71 --- /dev/null +++ b/sbin/mount_lfs/pathnames.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/8/93 + */ + +#define _PATH_LFS_CLEANERD "/usr/libexec/lfs_cleanerd" diff --git a/sbin/mount_nfs/Makefile b/sbin/mount_nfs/Makefile new file mode 100644 index 0000000..f33b461 --- /dev/null +++ b/sbin/mount_nfs/Makefile @@ -0,0 +1,14 @@ +# @(#)Makefile 8.2 (Berkeley) 3/27/94 + +PROG= mount_nfs +SRCS= mount_nfs.c getmntopts.c +MAN8= mount_nfs.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -DNFS -I${MOUNT} +.PATH: ${MOUNT} + +DPADD= ${LIBRPC} +LDADD= -lrpc + +.include <bsd.prog.mk> diff --git a/sbin/mount_nfs/mount_nfs.8 b/sbin/mount_nfs/mount_nfs.8 new file mode 100644 index 0000000..decac6e --- /dev/null +++ b/sbin/mount_nfs/mount_nfs.8 @@ -0,0 +1,222 @@ +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_nfs.8 8.2 (Berkeley) 3/27/94 +.\" +.Dd March 27, 1994 +.Dt MOUNT_NFS 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_nfs +.Nd mount nfs file systems +.Sh SYNOPSIS +.Nm mount_nfs +.Op Fl KMPTbcdiklqs +.Op Fl D Ar deadthresh +.Op Fl L Ar leaseterm +.Op Fl R Ar retrycnt +.Op Fl a Ar maxreadahead +.Op Fl g Ar maxgroups +.Op Fl m Ar realm +.Op Fl o Ar options +.Op Fl r Ar readsize +.Op Fl t Ar timeout +.Op Fl w Ar writesize +.Op Fl x Ar retrans +.Ar rhost:path node +.Sh DESCRIPTION +The +.Nm mount_nfs +command +calls the +.Xr mount 2 +system call to prepare and graft a remote nfs file system (rhost:path) +on to the file system tree at the point +.Ar node. +This command is normally executed by +.Xr mount 8 . +It implements the mount protocol as described in RFC 1094, Appendix A. +.Pp +The options are: +.Bl -tag -width indent +.It Fl D +Used with NQNFS to set the +.Dq "dead server threshold" +to the specified number of round trip timeout intervals. +After a +.Dq "dead server threshold" +of retransmit timeouts, +cached data for the unresponsive server is assumed to still be valid. +Values may be set in the range of 1 - 9, with 9 referring to an +.Dq "infinite dead threshold" +(i.e. never assume cached data still valid). +This option is not generally recommended and is really an experimental +feature. +.It Fl K +Pass Kerberos authenticators to the server for client-to-server +user-credential mapping. +This may only be used over TCP mounts between 4.4BSD clients and servers. +.It Fl L +Used with NQNFS to set the lease term to the specified number of seconds. +Only use this argument for mounts with a large round trip delay. +Values are normally in the 10-30 second range. +.It Fl M +Assume that other clients are not writing a file concurrently with this client. +This implements a slightly less strict consistency criteria than 4.3BSD-Reno +did, that is more in line with most commercial client implementations. +This is recommended for servers that do not support leasing. +.It Fl P +Use a reserved socket port number. +This is useful for mounting servers that require clients to use a +reserved port number. +.It Fl R +Set the retry count for doing the mount to the specified value. +.It Fl T +Use TCP transport instead of UDP. +This is recommended for servers that are not on the same LAN cable as +the client. +(NB: This is NOT supported by most non-BSD servers.) +.It Fl a +Set the read-ahead count to the specified value. +This may be in the range of 0 - 4, and determines how many blocks +will be read ahead when a large file is being read sequentially. +This is recommended for mounts with a large bandwidth * delay product. +.It Fl b +If an initial attempt to contact the server fails, fork off a child to keep +trying the mount in the background. +Useful for +.Xr fstab 5 , +where the filesystem mount is not critical to multiuser operation. +.It Fl c +For UDP mount points, do not do a +.Xr connect 2 . +This must be used for servers that do not reply to requests from the +standard port number. +.It Fl d +Do not estimate retransmit timeout dynamically. +This may be useful for UDP mounts that exhibit high retry rates. +.It Fl g +Set the maximum size of the group list for the credentials to the +specified value. +This should be used for mounts on old servers that cannot handle a +group list size of 16, as specified in RFC 1057. +Try 8, if users in a lot of groups cannot get response from the mount +point. +.It Fl i +Make the mount interruptible, which implies that file system calls that +are delayed due to an unresponsive server will fail with EINTR when a +termination signal is posted for the process. +.It Fl k +Used with NQNFS to specify +.Dq get a lease +for the file name being looked up. +This is recommended unless the server is complaining about excessive +lease load. +.It Fl l +Used with NQNFS to specify that the \fBReaddir_and_Lookup\fR RPC should +be used. +This option reduces RPC traffic for cases such as +.Dq "ls -l" , +but increases the lease load on the server. +This is recommended unless the server is complaining about excessive +lease load. +.It Fl m +Set the Kerberos realm to the string argument. +Used with the +.Fl K +option for mounts to other realms. +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.It Fl q +Use the leasing extensions to the protocol to maintain cache consistency. +This protocol, referred to as Not Quite Nfs (NQNFS), +is only supported by 4.4BSD servers. +.It Fl r +Set the read data size to the specified value. +It should be a power of 2 greater than or equal to 1024. +This should be used for UDP mounts when the +.Dq "fragments dropped due to timeout" +value is getting large while actively using a mount point. +(Use +.Xr netstat 1 +with the +.Fl s +option to see what the +.Dq "fragments dropped due to timeout" +value is.) +See the +.Fl w +option as well. +.It Fl s +A soft mount, which implies that file system calls will fail +after \fBRetry\fR round trip timeout intervals. +.It Fl t +Set the initial retransmit timeout to the specified value. +May be useful for fine tuning UDP mounts over internetworks +with high packet loss rates or an overloaded server. +Try increasing the interval if +.Xr nfsstat 1 +shows high retransmit rates while the file system is active or reducing the +value if there is a low retransmit rate but long response delay observed. +.It Fl w +Set the write data size to the specified value. +Ditto the comments w.r.t. the +.Fl r +option, but using the +.Dq "fragments dropped due to timeout" +value on the server instead of the client. +Note that both the +.Fl r +and +.Fl w +options should only be used as a last ditch effort at improving performance +when mounting servers that do not support TCP mounts. +.It Fl x +Set the retransmit timeout count for soft mounts to the specified value. +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr mount 8 +.Sh BUGS +Due to the way that Sun RPC is implemented on top of UDP (unreliable datagram) +transport, tuning such mounts is really a black art that can only be expected +to have limited success. +For clients mounting servers that are not on the same +LAN cable or that tend to be overloaded, +TCP transport is strongly recommended, +but unfortunately this is restricted to mostly 4.4BSD servers. diff --git a/sbin/mount_nfs/mount_nfs.c b/sbin/mount_nfs/mount_nfs.c new file mode 100644 index 0000000..cbb3ddb --- /dev/null +++ b/sbin/mount_nfs/mount_nfs.c @@ -0,0 +1,551 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rick Macklem at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_nfs.c 8.3 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/stat.h> +#include <sys/syslog.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_clnt.h> +#include <rpc/pmap_prot.h> + +#ifdef ISO +#include <netiso/iso.h> +#endif + +#ifdef KERBEROS +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> +#endif + +#include <nfs/rpcv2.h> +#include <nfs/nfsv2.h> +#define KERNEL +#include <nfs/nfs.h> +#undef KERNEL +#include <nfs/nqnfs.h> + +#include <arpa/inet.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_FORCE, + MOPT_UPDATE, + { NULL } +}; + +struct nfs_args nfsdefargs = { + (struct sockaddr *)0, + sizeof (struct sockaddr_in), + SOCK_DGRAM, + 0, + (nfsv2fh_t *)0, + 0, + NFS_WSIZE, + NFS_RSIZE, + NFS_TIMEO, + NFS_RETRANS, + NFS_MAXGRPS, + NFS_DEFRAHEAD, + NQ_DEFLEASE, + NQ_DEADTHRESH, + (char *)0, +}; + +struct nfhret { + u_long stat; + nfsv2fh_t nfh; +}; +#define DEF_RETRY 10000 +#define BGRND 1 +#define ISBGRND 2 +int retrycnt = DEF_RETRY; +int opflags = 0; + +#ifdef KERBEROS +char inst[INST_SZ]; +char realm[REALM_SZ]; +KTEXT_ST kt; +#endif + +int getnfsargs __P((char *, struct nfs_args *)); +#ifdef ISO +struct iso_addr *iso_addr __P((const char *)); +#endif +void set_rpc_maxgrouplist __P((int)); +__dead void usage __P((void)); +int xdr_dir __P((XDR *, char *)); +int xdr_fh __P((XDR *, struct nfhret *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register int c; + register struct nfs_args *nfsargsp; + struct nfs_args nfsargs; + struct nfsd_cargs ncd; + int mntflags, i, nfssvc_flag, num; + char *name, *p, *spec; + int error = 0; +#ifdef KERBEROS + uid_t last_ruid; +#endif + +#ifdef KERBEROS + last_ruid = -1; + (void)strcpy(realm, KRB_REALM); +#endif + retrycnt = DEF_RETRY; + + mntflags = 0; + nfsargs = nfsdefargs; + nfsargsp = &nfsargs; + while ((c = getopt(argc, argv, + "a:bcdD:g:iKklL:Mm:o:PpqR:r:sTt:w:x:")) != EOF) + switch (c) { + case 'a': + num = strtol(optarg, &p, 10); + if (*p || num < 0) + errx(1, "illegal -a value -- %s", optarg); + nfsargsp->readahead = num; + nfsargsp->flags |= NFSMNT_READAHEAD; + break; + case 'b': + opflags |= BGRND; + break; + case 'c': + nfsargsp->flags |= NFSMNT_NOCONN; + break; + case 'D': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -D value -- %s", optarg); + nfsargsp->deadthresh = num; + nfsargsp->flags |= NFSMNT_DEADTHRESH; + break; + case 'd': + nfsargsp->flags |= NFSMNT_DUMBTIMR; + break; + case 'g': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -g value -- %s", optarg); + set_rpc_maxgrouplist(num); + nfsargsp->maxgrouplist = num; + nfsargsp->flags |= NFSMNT_MAXGRPS; + break; + case 'i': + nfsargsp->flags |= NFSMNT_INT; + break; +#ifdef KERBEROS + case 'K': + nfsargsp->flags |= NFSMNT_KERB; + break; +#endif + case 'k': + nfsargsp->flags |= NFSMNT_NQLOOKLEASE; + break; + case 'L': + num = strtol(optarg, &p, 10); + if (*p || num < 2) + errx(1, "illegal -L value -- %s", optarg); + nfsargsp->leaseterm = num; + nfsargsp->flags |= NFSMNT_LEASETERM; + break; + case 'l': + nfsargsp->flags |= NFSMNT_RDIRALOOK; + break; + case 'M': + nfsargsp->flags |= NFSMNT_MYWRITE; + break; +#ifdef KERBEROS + case 'm': + (void)strncpy(realm, optarg, REALM_SZ - 1); + realm[REALM_SZ - 1] = '\0'; + break; +#endif + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case 'P': + nfsargsp->flags |= NFSMNT_RESVPORT; + break; +#ifdef ISO + case 'p': + nfsargsp->sotype = SOCK_SEQPACKET; + break; +#endif + case 'q': + nfsargsp->flags |= NFSMNT_NQNFS; + break; + case 'R': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -R value -- %s", optarg); + retrycnt = num; + break; + case 'r': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -r value -- %s", optarg); + nfsargsp->rsize = num; + nfsargsp->flags |= NFSMNT_RSIZE; + break; + case 's': + nfsargsp->flags |= NFSMNT_SOFT; + break; + case 'T': + nfsargsp->sotype = SOCK_STREAM; + break; + case 't': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -t value -- %s", optarg); + nfsargsp->timeo = num; + nfsargsp->flags |= NFSMNT_TIMEO; + break; + case 'w': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -w value -- %s", optarg); + nfsargsp->wsize = num; + nfsargsp->flags |= NFSMNT_WSIZE; + break; + case 'x': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -x value -- %s", optarg); + nfsargsp->retrans = num; + nfsargsp->flags |= NFSMNT_RETRANS; + break; + default: + usage(); + break; + } + argc -= optind; + argv += optind; + + if (argc != 2) + error = 1; + + spec = *argv++; + name = *argv; + + if (!getnfsargs(spec, nfsargsp)) + exit(1); + if (mount(MOUNT_NFS, name, mntflags, nfsargsp)) + err(1, "%s", name); + if (nfsargsp->flags & (NFSMNT_NQNFS | NFSMNT_KERB)) { + if ((opflags & ISBGRND) == 0) { + if (i = fork()) { + if (i == -1) + err(1, "nqnfs 1"); + exit(0); + } + (void) setsid(); + (void) close(STDIN_FILENO); + (void) close(STDOUT_FILENO); + (void) close(STDERR_FILENO); + (void) chdir("/"); + } + openlog("mount_nfs:", LOG_PID, LOG_DAEMON); + nfssvc_flag = NFSSVC_MNTD; + ncd.ncd_dirp = name; + while (nfssvc(nfssvc_flag, (caddr_t)&ncd) < 0) { + if (errno != ENEEDAUTH) { + syslog(LOG_ERR, "nfssvc err %m"); + continue; + } + nfssvc_flag = + NFSSVC_MNTD | NFSSVC_GOTAUTH | NFSSVC_AUTHINFAIL; +#ifdef KERBEROS + /* + * Set up as ncd_authuid for the kerberos call. + * Must set ruid to ncd_authuid and reset the + * ticket name iff ncd_authuid is not the same + * as last time, so that the right ticket file + * is found. + */ + if (ncd.ncd_authuid != last_ruid) { + krb_set_tkt_string(""); + last_ruid = ncd.ncd_authuid; + } + setreuid(ncd.ncd_authuid, 0); + if (krb_mk_req(&kt, "rcmd", inst, realm, 0) == + KSUCCESS && + kt.length <= (RPCAUTH_MAXSIZ - 2 * NFSX_UNSIGNED)) { + ncd.ncd_authtype = RPCAUTH_NQNFS; + ncd.ncd_authlen = kt.length; + ncd.ncd_authstr = (char *)kt.dat; + nfssvc_flag = NFSSVC_MNTD | NFSSVC_GOTAUTH; + } + setreuid(0, 0); +#endif /* KERBEROS */ + } + } + exit(0); +} + +int +getnfsargs(spec, nfsargsp) + char *spec; + struct nfs_args *nfsargsp; +{ + register CLIENT *clp; + struct hostent *hp; + static struct sockaddr_in saddr; +#ifdef ISO + static struct sockaddr_iso isoaddr; + struct iso_addr *isop; + int isoflag = 0; +#endif + struct timeval pertry, try; + enum clnt_stat clnt_stat; + int so = RPC_ANYSOCK, i; + char *hostp, *delimp; +#ifdef KERBEROS + char *cp; +#endif + u_short tport; + static struct nfhret nfhret; + static char nam[MNAMELEN + 1]; + + strncpy(nam, spec, MNAMELEN); + nam[MNAMELEN] = '\0'; + if ((delimp = strchr(spec, '@')) != NULL) { + hostp = delimp + 1; + } else if ((delimp = strchr(spec, ':')) != NULL) { + hostp = spec; + spec = delimp + 1; + } else { + warnx("no <host>:<dirpath> or <dirpath>@<host> spec"); + return (0); + } + *delimp = '\0'; + /* + * DUMB!! Until the mount protocol works on iso transport, we must + * supply both an iso and an inet address for the host. + */ +#ifdef ISO + if (!strncmp(hostp, "iso=", 4)) { + u_short isoport; + + hostp += 4; + isoflag++; + if ((delimp = strchr(hostp, '+')) == NULL) { + warnx("no iso+inet address"); + return (0); + } + *delimp = '\0'; + if ((isop = iso_addr(hostp)) == NULL) { + warnx("bad ISO address"); + return (0); + } + bzero((caddr_t)&isoaddr, sizeof (isoaddr)); + bcopy((caddr_t)isop, (caddr_t)&isoaddr.siso_addr, + sizeof (struct iso_addr)); + isoaddr.siso_len = sizeof (isoaddr); + isoaddr.siso_family = AF_ISO; + isoaddr.siso_tlen = 2; + isoport = htons(NFS_PORT); + bcopy((caddr_t)&isoport, TSEL(&isoaddr), isoaddr.siso_tlen); + hostp = delimp + 1; + } +#endif /* ISO */ + + /* + * Handle an internet host address and reverse resolve it if + * doing Kerberos. + */ + if (isdigit(*hostp)) { + if ((saddr.sin_addr.s_addr = inet_addr(hostp)) == -1) { + warnx("bad net address %s", hostp); + return (0); + } + if ((nfsargsp->flags & NFSMNT_KERB) && + (hp = gethostbyaddr((char *)&saddr.sin_addr.s_addr, + sizeof (u_long), AF_INET)) == (struct hostent *)0) { + warnx("can't reverse resolve net address"); + return (0); + } + } else if ((hp = gethostbyname(hostp)) == NULL) { + warnx("can't get net id for host"); + return (0); + } +#ifdef KERBEROS + if (nfsargsp->flags & NFSMNT_KERB) { + strncpy(inst, hp->h_name, INST_SZ); + inst[INST_SZ - 1] = '\0'; + if (cp = strchr(inst, '.')) + *cp = '\0'; + } +#endif /* KERBEROS */ + + bcopy(hp->h_addr, (caddr_t)&saddr.sin_addr, hp->h_length); + nfhret.stat = EACCES; /* Mark not yet successful */ + while (retrycnt > 0) { + saddr.sin_family = AF_INET; + saddr.sin_port = htons(PMAPPORT); + if ((tport = pmap_getport(&saddr, RPCPROG_NFS, + NFS_VER2, IPPROTO_UDP)) == 0) { + if ((opflags & ISBGRND) == 0) + clnt_pcreateerror("NFS Portmap"); + } else { + saddr.sin_port = 0; + pertry.tv_sec = 10; + pertry.tv_usec = 0; + if ((clp = clntudp_create(&saddr, RPCPROG_MNT, + RPCMNT_VER1, pertry, &so)) == NULL) { + if ((opflags & ISBGRND) == 0) + clnt_pcreateerror("Cannot MNT PRC"); + } else { + clp->cl_auth = authunix_create_default(); + try.tv_sec = 10; + try.tv_usec = 0; + clnt_stat = clnt_call(clp, RPCMNT_MOUNT, + xdr_dir, spec, xdr_fh, &nfhret, try); + if (clnt_stat != RPC_SUCCESS) { + if ((opflags & ISBGRND) == 0) + warnx("%s", clnt_sperror(clp, + "bad MNT RPC")); + } else { + auth_destroy(clp->cl_auth); + clnt_destroy(clp); + retrycnt = 0; + } + } + } + if (--retrycnt > 0) { + if (opflags & BGRND) { + opflags &= ~BGRND; + if (i = fork()) { + if (i == -1) + err(1, "nqnfs 2"); + exit(0); + } + (void) setsid(); + (void) close(STDIN_FILENO); + (void) close(STDOUT_FILENO); + (void) close(STDERR_FILENO); + (void) chdir("/"); + opflags |= ISBGRND; + } + sleep(60); + } + } + if (nfhret.stat) { + if (opflags & ISBGRND) + exit(1); + warn("can't access %s", spec); + return (0); + } + saddr.sin_port = htons(tport); +#ifdef ISO + if (isoflag) { + nfsargsp->addr = (struct sockaddr *) &isoaddr; + nfsargsp->addrlen = sizeof (isoaddr); + } else +#endif /* ISO */ + { + nfsargsp->addr = (struct sockaddr *) &saddr; + nfsargsp->addrlen = sizeof (saddr); + } + nfsargsp->fh = &nfhret.nfh; + nfsargsp->hostname = nam; + return (1); +} + +/* + * xdr routines for mount rpc's + */ +int +xdr_dir(xdrsp, dirp) + XDR *xdrsp; + char *dirp; +{ + return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); +} + +int +xdr_fh(xdrsp, np) + XDR *xdrsp; + struct nfhret *np; +{ + if (!xdr_u_long(xdrsp, &(np->stat))) + return (0); + if (np->stat) + return (1); + return (xdr_opaque(xdrsp, (caddr_t)&(np->nfh), NFSX_FH)); +} + +__dead void +usage() +{ + (void)fprintf(stderr, "usage: mount_nfs %s\n%s\n%s\n%s\n", +"[-bcdiKklMPqsT] [-a maxreadahead] [-D deadthresh]", +"\t[-g maxgroups] [-L leaseterm] [-m realm] [-o options] [-R retrycnt]", +"\t[-r readsize] [-t timeout] [-w writesize] [-x retrans]", +"\trhost:path node"); + exit(1); +} diff --git a/sbin/mount_null/Makefile b/sbin/mount_null/Makefile new file mode 100644 index 0000000..aae87e1 --- /dev/null +++ b/sbin/mount_null/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_null +SRCS= mount_null.c getmntopts.c +MAN8= mount_null.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I/sys -I${MOUNT} +.PATH: ${MOUNT} + +.include <bsd.prog.mk> diff --git a/sbin/mount_null/mount_null.8 b/sbin/mount_null/mount_null.8 new file mode 100644 index 0000000..6c8106f --- /dev/null +++ b/sbin/mount_null/mount_null.8 @@ -0,0 +1,220 @@ +.\" +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" John Heidemann of the UCLA Ficus project. +.\" +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_null.8 8.4 (Berkeley) 4/19/94 +.\" +.\" +.Dd April 19, 1994 +.Dt MOUNT_NULL 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_null +.Nd demonstrate the use of a null file system layer +.Sh SYNOPSIS +.Nm mount_null +.Op Fl o Ar options +.Ar target +.Ar mount-point +.Sh DESCRIPTION +The +.Nm mount_null +command creates a +null layer, duplicating a sub-tree of the file system +name space under another part of the global file system namespace. +In this respect, it is +similar to the loopback file system (see +.Xr mount_lofs 8 ) . +It differs from +the loopback file system in two respects: it is implemented using +a stackable layers techniques, and it's +.Do +null-node +.Dc s +stack above +all lower-layer vnodes, not just over directory vnodes. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Pp +The null layer has two purposes. +First, it serves as a demonstration of layering by proving a layer +which does nothing. +(It actually does everything the loopback file system does, +which is slightly more than nothing.) +Second, the null layer can serve as a prototype layer. +Since it provides all necessary layer framework, +new file system layers can be created very easily be starting +with a null layer. +.Pp +The remainder of this man page examines the null layer as a basis +for constructing new layers. +.\" +.\" +.Sh INSTANTIATING NEW NULL LAYERS +New null layers are created with +.Xr mount_null 8 . +.Xr Mount_null 8 +takes two arguments, the pathname +of the lower vfs (target-pn) and the pathname where the null +layer will appear in the namespace (mount-point-pn). After +the null layer is put into place, the contents +of target-pn subtree will be aliased under mount-point-pn. +.\" +.\" +.Sh OPERATION OF A NULL LAYER +The null layer is the minimum file system layer, +simply bypassing all possible operations to the lower layer +for processing there. The majority of its activity centers +on the bypass routine, though which nearly all vnode operations +pass. +.Pp +The bypass routine accepts arbitrary vnode operations for +handling by the lower layer. It begins by examing vnode +operation arguments and replacing any null-nodes by their +lower-layer equivalents. It then invokes the operation +on the lower layer. Finally, it replaces the null-nodes +in the arguments and, if a vnode is returned by the operation, +stacks a null-node on top of the returned vnode. +.Pp +Although bypass handles most operations, +.Em vop_getattr , +.Em vop_inactive , +.Em vop_reclaim , +and +.Em vop_print +are not bypassed. +.Em Vop_getattr +must change the fsid being returned. +.Em Vop_inactive +and vop_reclaim are not bypassed so that +they can handle freeing null-layer specific data. +.Em Vop_print +is not bypassed to avoid excessive debugging +information. +.\" +.\" +.Sh INSTANTIATING VNODE STACKS +Mounting associates the null layer with a lower layer, +in effect stacking two VFSes. Vnode stacks are instead +created on demand as files are accessed. +.Pp +The initial mount creates a single vnode stack for the +root of the new null layer. All other vnode stacks +are created as a result of vnode operations on +this or other null vnode stacks. +.Pp +New vnode stacks come into existence as a result of +an operation which returns a vnode. +The bypass routine stacks a null-node above the new +vnode before returning it to the caller. +.Pp +For example, imagine mounting a null layer with +.Bd -literal -offset indent +mount_null /usr/include /dev/layer/null +.Ed +Changing directory to +.Pa /dev/layer/null +will assign +the root null-node (which was created when the null layer was mounted). +Now consider opening +.Pa sys . +A vop_lookup would be +done on the root null-node. This operation would bypass through +to the lower layer which would return a vnode representing +the UFS +.Pa sys . +Null_bypass then builds a null-node +aliasing the UFS +.Pa sys +and returns this to the caller. +Later operations on the null-node +.Pa sys +will repeat this +process when constructing other vnode stacks. +.\" +.\" +.Sh CREATING OTHER FILE SYSTEM LAYERS +One of the easiest ways to construct new file system layers is to make +a copy of the null layer, rename all files and variables, and +then begin modifyng the copy. Sed can be used to easily rename +all variables. +.Pp +The umap layer is an example of a layer descended from the +null layer. +.\" +.\" +.Sh INVOKING OPERATIONS ON LOWER LAYERS +There are two techniques to invoke operations on a lower layer +when the operation cannot be completely bypassed. Each method +is appropriate in different situations. In both cases, +it is the responsibility of the aliasing layer to make +the operation arguments "correct" for the lower layer +by mapping an vnode arguments to the lower layer. +.Pp +The first approach is to call the aliasing layer's bypass routine. +This method is most suitable when you wish to invoke the operation +currently being handled on the lower layer. It has the advantage +the the bypass routine already must do argument mapping. +An example of this is +.Em null_getattrs +in the null layer. +.Pp +A second approach is to directly invoked vnode operations on +the lower layer with the +.Em VOP_OPERATIONNAME +interface. +The advantage of this method is that it is easy to invoke +arbitrary operations on the lower layer. The disadvantage +is that vnodes arguments must be manually mapped. +.\" +.\" +.Sh SEE ALSO +.Xr mount 8 +.sp +UCLA Technical Report CSD-910056, +.Em "Stackable Layers: an Architecture for File System Development" . +.Sh HISTORY +The +.Nm mount_null +utility first appeared in 4.4BSD. diff --git a/sbin/mount_null/mount_null.c b/sbin/mount_null/mount_null.c new file mode 100644 index 0000000..e8c26df --- /dev/null +++ b/sbin/mount_null/mount_null.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_null.c 8.5 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> +#include <miscfs/nullfs/null.h> + +#include <err.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +int subdir __P((const char *, const char *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct null_args args; + int ch, mntflags; + char target[MAXPATHLEN]; + + mntflags = 0; + while ((ch = getopt(argc, argv, "o:")) != EOF) + switch(ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (realpath(argv[0], target) == 0) + err(1, "%s", target); + + if (subdir(target, argv[1]) || subdir(argv[1], target)) + errx(1, "%s (%s) and %s are not distinct paths", + argv[0], target, argv[1]); + + args.target = target; + + if (mount(MOUNT_NULL, argv[1], mntflags, &args)) + err(1, NULL); + exit(0); +} + +int +subdir(p, dir) + const char *p; + const char *dir; +{ + int l; + + l = strlen(dir); + if (l <= 1) + return (1); + + if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0')) + return (1); + + return (0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_null [-o options] target_fs mount_point\n"); + exit(1); +} diff --git a/sbin/mount_portal/Makefile b/sbin/mount_portal/Makefile new file mode 100644 index 0000000..1f17d24 --- /dev/null +++ b/sbin/mount_portal/Makefile @@ -0,0 +1,15 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_portal +SRCS= mount_portal.c activate.c conf.c getmntopts.c pt_conf.c \ + pt_exec.c pt_file.c pt_tcp.c +MAN8= mount_portal.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I/sys -I${MOUNT} +.PATH: ${MOUNT} + +DPADD= $(LIBCOMPAT) +LDADD= -lcompat + +.include <bsd.prog.mk> diff --git a/sbin/mount_portal/activate.c b/sbin/mount_portal/activate.c new file mode 100644 index 0000000..3361798 --- /dev/null +++ b/sbin/mount_portal/activate.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)activate.c 8.2 (Berkeley) 3/27/94 + * + * $Id: activate.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/syslog.h> +#include <sys/uio.h> + +#include "portald.h" + +/* + * Scan the providers list and call the + * appropriate function. + */ +static int activate_argv(pcr, key, v, so, fdp) +struct portal_cred *pcr; +char *key; +char **v; +int so; +int *fdp; +{ + provider *pr; + + for (pr = providers; pr->pr_match; pr++) + if (strcmp(v[0], pr->pr_match) == 0) + return ((*pr->pr_func)(pcr, key, v, so, fdp)); + + return (ENOENT); +} + +static int get_request(so, pcr, key, klen) +int so; +struct portal_cred *pcr; +char *key; +int klen; +{ + struct iovec iov[2]; + struct msghdr msg; + int n; + + iov[0].iov_base = (caddr_t) pcr; + iov[0].iov_len = sizeof(*pcr); + iov[1].iov_base = key; + iov[1].iov_len = klen; + + bzero((char *) &msg, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + + n = recvmsg(so, &msg, 0); + if (n < 0) + return (errno); + + if (n <= sizeof(*pcr)) + return (EINVAL); + + n -= sizeof(*pcr); + key[n] = '\0'; + + return (0); +} + +static void send_reply(so, fd, error) +int so; +int fd; +int error; +{ + int n; + struct iovec iov; + struct msghdr msg; + struct { + struct cmsghdr cmsg; + int fd; + } ctl; + + /* + * Line up error code. Don't worry about byte ordering + * because we must be sending to the local machine. + */ + iov.iov_base = (caddr_t) &error; + iov.iov_len = sizeof(error); + + /* + * Build a msghdr + */ + bzero((char *) &msg, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + /* + * If there is a file descriptor to send then + * construct a suitable rights control message. + */ + if (fd >= 0) { + ctl.fd = fd; + ctl.cmsg.cmsg_len = sizeof(ctl); + ctl.cmsg.cmsg_level = SOL_SOCKET; + ctl.cmsg.cmsg_type = SCM_RIGHTS; + msg.msg_control = (caddr_t) &ctl; + msg.msg_controllen = ctl.cmsg.cmsg_len; + } + + /* + * Send to kernel... + */ + if ((n = sendmsg(so, &msg, MSG_EOR)) < 0) + syslog(LOG_ERR, "send: %s", strerror(errno)); +#ifdef DEBUG + fprintf(stderr, "sent %d bytes\n", n); +#endif + sleep(1); /*XXX*/ +#ifdef notdef + if (shutdown(so, 2) < 0) + syslog(LOG_ERR, "shutdown: %s", strerror(errno)); +#endif + /* + * Throw away the open file descriptor + */ + (void) close(fd); +} + +void activate(q, so) +qelem *q; +int so; +{ + struct portal_cred pcred; + char key[MAXPATHLEN+1]; + int error; + char **v; + int fd = -1; + + /* + * Read the key from the socket + */ + error = get_request(so, &pcred, key, sizeof(key)); + if (error) { + syslog(LOG_ERR, "activate: recvmsg: %s", strerror(error)); + goto drop; + } + +#ifdef DEBUG + fprintf(stderr, "lookup key %s\n", key); +#endif + + /* + * Find a match in the configuration file + */ + v = conf_match(q, key); + + /* + * If a match existed, then find an appropriate portal + * otherwise simply return ENOENT. + */ + if (v) { + error = activate_argv(&pcred, key, v, so, &fd); + if (error) + fd = -1; + else if (fd < 0) + error = -1; + } else { + error = ENOENT; + } + + if (error >= 0) + send_reply(so, fd, error); + +drop:; + close(so); +} diff --git a/sbin/mount_portal/conf.c b/sbin/mount_portal/conf.c new file mode 100644 index 0000000..18833b6 --- /dev/null +++ b/sbin/mount_portal/conf.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)conf.c 8.2 (Berkeley) 3/27/94 + * + * $Id: conf.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <regexp.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/syslog.h> + +#include "portald.h" + +#define ALLOC(ty) (xmalloc(sizeof(ty))) + +typedef struct path path; +struct path { + qelem p_q; /* 2-way linked list */ + int p_lno; /* Line number of this record */ + char *p_args; /* copy of arg string (malloc) */ + char *p_key; /* Pathname to match (also p_argv[0]) */ + regexp *p_re; /* RE to match against pathname (malloc) */ + int p_argc; /* number of elements in arg string */ + char **p_argv; /* argv[] pointers into arg string (malloc) */ +}; + +static char *conf_file; /* XXX for regerror */ +static path *curp; /* XXX for regerror */ + +/* + * Add an element to a 2-way list, + * just after (pred) + */ +static void ins_que(elem, pred) +qelem *elem, *pred; +{ + qelem *p = pred->q_forw; + elem->q_back = pred; + elem->q_forw = p; + pred->q_forw = elem; + p->q_back = elem; +} + +/* + * Remove an element from a 2-way list + */ +static void rem_que(elem) +qelem *elem; +{ + qelem *p = elem->q_forw; + qelem *p2 = elem->q_back; + p2->q_forw = p; + p->q_back = p2; +} + +/* + * Error checking malloc + */ +static void *xmalloc(siz) +unsigned siz; +{ + void *p = malloc(siz); + if (p) + return (p); + syslog(LOG_ALERT, "malloc: failed to get %d bytes", siz); + exit(1); +} + +/* + * Insert the path in the list. + * If there is already an element with the same key then + * the *second* one is ignored (return 0). If the key is + * not found then the path is added to the end of the list + * and 1 is returned. + */ +static int pinsert(p0, q0) +path *p0; +qelem *q0; +{ + qelem *q; + + if (p0->p_argc == 0) + return (0); + + for (q = q0->q_forw; q != q0; q = q->q_forw) { + path *p = (path *) q; + if (strcmp(p->p_key, p0->p_key) == 0) + return (0); + } + ins_que(&p0->p_q, q0->q_back); + return (1); + +} + +void regerror(s) +const char *s; +{ + syslog(LOG_ERR, "%s:%s: regcomp %s: %s", + conf_file, curp->p_lno, curp->p_key, s); +} + +static path *palloc(cline, lno) +char *cline; +int lno; +{ + int c; + char *s; + char *key; + path *p; + char **ap; + + /* + * Implement comment chars + */ + s = strchr(cline, '#'); + if (s) + *s = 0; + + /* + * Do a pass through the string to count the number + * of arguments + */ + c = 0; + key = strdup(cline); + for (s = key; s != NULL; ) { + char *val; + while ((val = strsep(&s, " \t\n")) != NULL && *val == '\0') + ; + if (val) + c++; + } + c++; + free(key); + + if (c <= 1) + return (0); + + /* + * Now do another pass and generate a new path structure + */ + p = ALLOC(path); + p->p_argc = 0; + p->p_argv = xmalloc(c * sizeof(char *)); + p->p_args = strdup(cline); + ap = p->p_argv; + for (s = p->p_args; s != NULL; ) { + char *val; + while ((val = strsep(&s, " \t\n")) != NULL && *val == '\0') + ; + if (val) { + *ap++ = val; + p->p_argc++; + } + } + *ap = 0; + +#ifdef DEBUG + for (c = 0; c < p->p_argc; c++) + printf("%sv[%d] = %s\n", c?"\t":"", c, p->p_argv[c]); +#endif + + p->p_key = p->p_argv[0]; + if (strpbrk(p->p_key, RE_CHARS)) { + curp = p; /* XXX */ + p->p_re = regcomp(p->p_key); + curp = 0; /* XXX */ + } else { + p->p_re = 0; + } + p->p_lno = lno; + + return (p); +} + +/* + * Free a path structure + */ +static void pfree(p) +path *p; +{ + free(p->p_args); + if (p->p_re) + free((char *) p->p_re); + free((char *) p->p_argv); + free((char *) p); +} + +/* + * Discard all currently held path structures on q0. + * and add all the ones on xq. + */ +static void preplace(q0, xq) +qelem *q0; +qelem *xq; +{ + /* + * While the list is not empty, + * take the first element off the list + * and free it. + */ + while (q0->q_forw != q0) { + qelem *q = q0->q_forw; + rem_que(q); + pfree((path *) q); + } + while (xq->q_forw != xq) { + qelem *q = xq->q_forw; + rem_que(q); + ins_que(q, q0); + } +} + +/* + * Read the lines from the configuration file and + * add them to the list of paths. + */ +static void readfp(q0, fp) +qelem *q0; +FILE *fp; +{ + char cline[LINE_MAX]; + int nread = 0; + qelem q; + + /* + * Make a new empty list. + */ + q.q_forw = q.q_back = &q; + + /* + * Read the lines from the configuration file. + */ + while (fgets(cline, sizeof(cline), fp)) { + path *p = palloc(cline, nread+1); + if (p && !pinsert(p, &q)) + pfree(p); + nread++; + } + + /* + * If some records were read, then throw + * away the old list and replace with the + * new one. + */ + if (nread) + preplace(q0, &q); +} + +/* + * Read the configuration file (conf) and replace + * the existing path list with the new version. + * If the file is not readable, then no changes take place + */ +void conf_read(q, conf) +qelem *q; +char *conf; +{ + FILE *fp = fopen(conf, "r"); + if (fp) { + conf_file = conf; /* XXX */ + readfp(q, fp); + conf_file = 0; /* XXX */ + (void) fclose(fp); + } else { + syslog(LOG_ERR, "open config file \"%s\": %s", conf, strerror(errno)); + } +} + + +char **conf_match(q0, key) +qelem *q0; +char *key; +{ + qelem *q; + + for (q = q0->q_forw; q != q0; q = q->q_forw) { + path *p = (path *) q; + if (p->p_re) { + if (regexec(p->p_re, key)) + return (p->p_argv+1); + } else { + if (strncmp(p->p_key, key, strlen(p->p_key)) == 0) + return (p->p_argv+1); + } + } + + return (0); +} diff --git a/sbin/mount_portal/mount_portal.8 b/sbin/mount_portal/mount_portal.8 new file mode 100644 index 0000000..0df6531 --- /dev/null +++ b/sbin/mount_portal/mount_portal.8 @@ -0,0 +1,136 @@ +.\" +.\" Copyright (c) 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_portal.8 8.3 (Berkeley) 3/27/94 +.\" +.\" +.Dd March 27, 1994 +.Dt MOUNT_PORTAL 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_portal +.Nd mount the portal daemon +.Sh SYNOPSIS +.Nm mount_portal +.Op Fl o Ar options +.Ar /etc/portal.conf +.Ar mount_point +.Sh DESCRIPTION +The +.Nm mount_portal +command attaches an instance of the portal daemon +to the global filesystem namespace. +The conventional mount point is +.Pa /p . +.PA /dev . +This command is normally executed by +.Xr mount 8 +at boot time. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Pp +The portal daemon provides an +.Em open +service. +Objects opened under the portal mount point are +dynamically created by the portal daemon according +to rules specified in the named configuration file. +Using this mechanism allows descriptors such as sockets +to be made available in the filesystem namespace. +.Pp +The portal daemon works by being passed the full pathname +of the object being opened. +The daemon creates an appropriate descriptor according +to the rules in the configuration file, and then passes the descriptor back +to the calling process as the result of the open system call. +.Sh NAMESPACE +By convention, the portal daemon divides the namespace into sub-namespaces, +each of which handles objects of a particular type. +.Pp +Currently, two sub-namespaces are implemented: +.Pa tcp +and +.Pa fs . +The +.Pa tcp +namespace takes a hostname and a port (slash separated) and +creates an open TCP/IP connection. +The +.Pa fs +namespace opens the named file, starting back at the root directory. +This can be used to provide a controlled escape path from +a chrooted environment. +.Sh "CONFIGURATION FILE" +The configuration file contains a list of rules. +Each rule takes one line and consists of two or more +whitespace separated fields. +A hash (``#'') character causes the remainder of a line to +be ignored. Blank lines are ignored. +.Pp +The first field is a pathname prefix to match +against the requested pathname. +If a match is found, the second field +tells the daemon what type of object to create. +Subsequent fields are passed to the creation function. +.Bd -literal +# @(#)portal.conf 5.1 (Berkeley) 7/13/92 +tcp/ tcp tcp/ +fs/ file fs/ +.Ed +.Sh FILES +.Bl -tag -width /p/* -compact +.It Pa /p/* +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr mount 8 +.Sh CAVEATS +This filesystem may not be NFS-exported. +.Sh HISTORY +The +.Nm mount_portal +utility first appeared in 4.4BSD. diff --git a/sbin/mount_portal/mount_portal.c b/sbin/mount_portal/mount_portal.c new file mode 100644 index 0000000..ae5345d --- /dev/null +++ b/sbin/mount_portal/mount_portal.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_portal.c 8.4 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/syslog.h> +#include <sys/mount.h> + +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mntopts.h" +#include "pathnames.h" +#include "portald.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +static void usage __P((void)); + +static sig_atomic_t readcf; /* Set when SIGHUP received */ + +static void sigchld(sig) +int sig; +{ + pid_t pid; + + while ((pid = waitpid((pid_t) -1, (int *) 0, WNOHANG)) > 0) + ; + if (pid < 0) + syslog(LOG_WARNING, "waitpid: %s", strerror(errno)); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct portal_args args; + struct sockaddr_un un; + char *conf; + char *mountpt; + int mntflags = 0; + char tag[32]; + + qelem q; + int rc; + int so; + int error = 0; + + /* + * Crack command line args + */ + int ch; + + while ((ch = getopt(argc, argv, "o:")) != EOF) { + switch (ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + default: + error = 1; + break; + } + } + + if (optind != (argc - 2)) + error = 1; + + if (error) + usage(); + + /* + * Get config file and mount point + */ + conf = argv[optind]; + mountpt = argv[optind+1]; + + /* + * Construct the listening socket + */ + un.sun_family = AF_UNIX; + if (sizeof(_PATH_TMPPORTAL) >= sizeof(un.sun_path)) { + fprintf(stderr, "mount_portal: portal socket name too long\n"); + exit(1); + } + strcpy(un.sun_path, _PATH_TMPPORTAL); + mktemp(un.sun_path); + un.sun_len = strlen(un.sun_path); + + so = socket(AF_UNIX, SOCK_STREAM, 0); + if (so < 0) { + fprintf(stderr, "mount_portal: socket: %s\n", strerror(errno)); + exit(1); + } + (void) unlink(un.sun_path); + if (bind(so, (struct sockaddr *) &un, sizeof(un)) < 0) + err(1, NULL); + (void) unlink(un.sun_path); + + (void) listen(so, 5); + + args.pa_socket = so; + sprintf(tag, "portal:%d", getpid()); + args.pa_config = tag; + + rc = mount(MOUNT_PORTAL, mountpt, mntflags, &args); + if (rc < 0) + err(1, NULL); + +#ifdef notdef + /* + * Everything is ready to go - now is a good time to fork + */ + daemon(0, 0); +#endif + + /* + * Start logging (and change name) + */ + openlog("portald", LOG_CONS|LOG_PID, LOG_DAEMON); + + q.q_forw = q.q_back = &q; + readcf = 1; + + signal(SIGCHLD, sigchld); + + /* + * Just loop waiting for new connections and activating them + */ + for (;;) { + struct sockaddr_un un2; + int len2 = sizeof(un2); + int so2; + pid_t pid; + fd_set fdset; + int rc; + + /* + * Check whether we need to re-read the configuration file + */ + if (readcf) { + readcf = 0; + conf_read(&q, conf); + continue; + } + + /* + * Accept a new connection + * Will get EINTR if a signal has arrived, so just + * ignore that error code + */ + FD_SET(so, &fdset); + rc = select(so+1, &fdset, (void *) 0, (void *) 0, (void *) 0); + if (rc < 0) { + if (errno == EINTR) + continue; + syslog(LOG_ERR, "select: %s", strerror(errno)); + exit(1); + } + if (rc == 0) + break; + so2 = accept(so, (struct sockaddr *) &un2, &len2); + if (so2 < 0) { + /* + * The unmount function does a shutdown on the socket + * which will generated ECONNABORTED on the accept. + */ + if (errno == ECONNABORTED) + break; + if (errno != EINTR) { + syslog(LOG_ERR, "accept: %s", strerror(errno)); + exit(1); + } + continue; + } + + /* + * Now fork a new child to deal with the connection + */ + eagain:; + switch (pid = fork()) { + case -1: + if (errno == EAGAIN) { + sleep(1); + goto eagain; + } + syslog(LOG_ERR, "fork: %s", strerror(errno)); + break; + case 0: + (void) close(so); + activate(&q, so2); + break; + default: + (void) close(so2); + break; + } + } + syslog(LOG_INFO, "%s unmounted", mountpt); + exit(0); +} + +static void +usage() +{ + (void)fprintf(stderr, + "usage: mount_portal [-o options] config mount-point\n"); + exit(1); +} diff --git a/sbin/mount_portal/pathnames.h b/sbin/mount_portal/pathnames.h new file mode 100644 index 0000000..2532114 --- /dev/null +++ b/sbin/mount_portal/pathnames.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + * + * $Id: pathnames.h,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $ + */ + +#include <paths.h> + +#define _PATH_TMPPORTAL "/tmp/portalXXXXXX" /* Scratch socket name */ diff --git a/sbin/mount_portal/portal.conf b/sbin/mount_portal/portal.conf new file mode 100644 index 0000000..5b5a773 --- /dev/null +++ b/sbin/mount_portal/portal.conf @@ -0,0 +1,7 @@ +# @(#)portal.conf 8.1 (Berkeley) 6/5/93 +# $Id: portal.conf,v 1.1 1992/05/27 06:50:13 jsp Exp jsp $ +tcplisten/ tcplisten tcplisten/ +tcp/ tcp tcp/ +fs/ file fs/ +pipe/ pipe +foo/ exec ./bar bar baz diff --git a/sbin/mount_portal/portald.h b/sbin/mount_portal/portald.h new file mode 100644 index 0000000..fbe111b --- /dev/null +++ b/sbin/mount_portal/portald.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)portald.h 8.1 (Berkeley) 6/5/93 + * + * $Id: portald.h,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $ + */ + +#include <sys/cdefs.h> +#include <miscfs/portal/portal.h> + +/* + * Meta-chars in an RE. Paths in the config file containing + * any of these characters will be matched using regexec, other + * paths will be prefix-matched. + */ +#define RE_CHARS ".|()[]*+?\\^$" + +typedef struct qelem qelem; + +struct qelem { + qelem *q_forw; + qelem *q_back; +}; + +typedef struct provider provider; +struct provider { + char *pr_match; + int (*pr_func) __P((struct portal_cred *, + char *key, char **v, int so, int *fdp)); +}; +extern provider providers[]; + +/* + * Portal providers + */ +extern int portal_exec __P((struct portal_cred *, + char *key, char **v, int so, int *fdp)); +extern int portal_file __P((struct portal_cred *, + char *key, char **v, int so, int *fdp)); +extern int portal_tcp __P((struct portal_cred *, + char *key, char **v, int so, int *fdp)); + +/* + * Global functions + */ +extern void activate __P((qelem *q, int so)); +extern char **conf_match __P((qelem *q, char *key)); +extern void conf_read __P((qelem *q, char *conf)); diff --git a/sbin/mount_portal/pt_conf.c b/sbin/mount_portal/pt_conf.c new file mode 100644 index 0000000..d1eba94 --- /dev/null +++ b/sbin/mount_portal/pt_conf.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pt_conf.c 8.1 (Berkeley) 6/5/93 + * + * $Id: pt_conf.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $ + */ + +#include <sys/types.h> +#include <sys/param.h> +#include "portald.h" + +provider providers[] = { + { "exec", portal_exec }, + { "file", portal_file }, + { "tcp", portal_tcp }, + { 0, 0 } +}; diff --git a/sbin/mount_portal/pt_exec.c b/sbin/mount_portal/pt_exec.c new file mode 100644 index 0000000..06e3382 --- /dev/null +++ b/sbin/mount_portal/pt_exec.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pt_exec.c 8.1 (Berkeley) 6/5/93 + * + * $Id: pt_exec.c,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $ + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/syslog.h> + +#include "portald.h" + +int portal_exec(pcr, key, v, so, fdp) +struct portal_cred *pcr; +char *key; +char **v; +int so; +int *fdp; +{ + return (ENOEXEC); +} + diff --git a/sbin/mount_portal/pt_file.c b/sbin/mount_portal/pt_file.c new file mode 100644 index 0000000..ace35c0 --- /dev/null +++ b/sbin/mount_portal/pt_file.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pt_file.c 8.2 (Berkeley) 3/27/94 + * + * $Id: pt_file.c,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $ + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/syslog.h> + +#include "portald.h" + +int portal_file(pcr, key, v, so, fdp) +struct portal_cred *pcr; +char *key; +char **v; +int so; +int *fdp; +{ + int fd; + char pbuf[MAXPATHLEN]; + int error; + int gidset[NGROUPS]; + int i; + + pbuf[0] = '/'; + strcpy(pbuf+1, key + (v[1] ? strlen(v[1]) : 0)); + +#ifdef DEBUG + printf("path = %s, uid = %d, gid = %d\n", pbuf, pcr->pcr_uid, pcr->pcr_groups[0]); +#endif + + for (i = 0; i < pcr->pcr_ngroups; i++) + gidset[i] = pcr->pcr_groups[i]; + + if (setgroups(pcr->pcr_ngroups, gidset) < 0) + return (errno); + + if (seteuid(pcr->pcr_uid) < 0) + return (errno); + + fd = open(pbuf, O_RDWR|O_CREAT, 0666); + if (fd < 0) + error = errno; + else + error = 0; + + if (seteuid((uid_t) 0) < 0) { /* XXX - should reset gidset too */ + error = errno; + syslog(LOG_ERR, "setcred: %s", strerror(error)); + if (fd >= 0) { + (void) close(fd); + fd = -1; + } + } + + if (error == 0) + *fdp = fd; + +#ifdef DEBUG + fprintf(stderr, "pt_file returns *fdp = %d, error = %d\n", *fdp, error); +#endif + + return (error); +} diff --git a/sbin/mount_portal/pt_tcp.c b/sbin/mount_portal/pt_tcp.c new file mode 100644 index 0000000..18a53ce --- /dev/null +++ b/sbin/mount_portal/pt_tcp.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pt_tcp.c 8.3 (Berkeley) 3/27/94 + * + * $Id: pt_tcp.c,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $ + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/syslog.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include "portald.h" + +/* + * Key will be tcp/host/port[/"priv"] + * Create a TCP socket connected to the + * requested host and port. + * Some trailing suffix values have special meanings. + * An unrecognised suffix is an error. + */ +int portal_tcp(pcr, key, v, kso, fdp) +struct portal_cred *pcr; +char *key; +char **v; +int kso; +int *fdp; +{ + char host[MAXHOSTNAMELEN]; + char port[MAXHOSTNAMELEN]; + char *p = key + (v[1] ? strlen(v[1]) : 0); + char *q; + struct hostent *hp; + struct servent *sp; + struct in_addr **ipp; + struct in_addr *ip[2]; + struct in_addr ina; + int s_port; + int priv = 0; + struct sockaddr_in sain; + + q = strchr(p, '/'); + if (q == 0 || q - p >= sizeof(host)) + return (EINVAL); + *q = '\0'; + strcpy(host, p); + p = q + 1; + + q = strchr(p, '/'); + if (q) + *q = '\0'; + if (strlen(p) >= sizeof(port)) + return (EINVAL); + strcpy(port, p); + if (q) { + p = q + 1; + if (strcmp(p, "priv") == 0) { + if (pcr->pcr_uid == 0) + priv = 1; + else + return (EPERM); + } else { + return (EINVAL); + } + } + + hp = gethostbyname(host); + if (hp != 0) { + ipp = (struct in_addr **) hp->h_addr_list; + } else { + ina.s_addr = inet_addr(host); + if (ina.s_addr == INADDR_NONE) + return (EINVAL); + ip[0] = &ina; + ip[1] = 0; + ipp = ip; + } + + sp = getservbyname(port, "tcp"); + if (sp != 0) + s_port = sp->s_port; + else { + s_port = atoi(port); + if (s_port == 0) + return (EINVAL); + } + + bzero(&sain, sizeof(sain)); + sain.sin_len = sizeof(sain); + sain.sin_family = AF_INET; + sain.sin_port = s_port; + + while (ipp[0]) { + int so; + + if (priv) + so = rresvport((int *) 0); + else + so = socket(AF_INET, SOCK_STREAM, 0); + if (so < 0) { + syslog(LOG_ERR, "socket: %m"); + return (errno); + } + + sain.sin_addr = *ipp[0]; + if (connect(so, (struct sockaddr *) &sain, sizeof(sain)) == 0) { + *fdp = so; + return (0); + } + (void) close(so); + + ipp++; + } + + return (errno); +} diff --git a/sbin/mount_procfs/Makefile b/sbin/mount_procfs/Makefile new file mode 100644 index 0000000..ca0c872 --- /dev/null +++ b/sbin/mount_procfs/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.4 (Berkeley) 3/27/94 + +PROG= mount_procfs +SRCS= mount_procfs.c getmntopts.c +MAN8= mount_procfs.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I${MOUNT} +.PATH: ${MOUNT} + +.include <bsd.prog.mk> diff --git a/sbin/mount_procfs/mount_procfs.8 b/sbin/mount_procfs/mount_procfs.8 new file mode 100644 index 0000000..3aa20e0 --- /dev/null +++ b/sbin/mount_procfs/mount_procfs.8 @@ -0,0 +1,253 @@ +.\" +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_procfs.8 8.2 (Berkeley) 3/27/94 +.\" +.\" +.Dd March 27, 1994 +.Dt MOUNT_PROCFS 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_procfs +.Nd mount the process file system +.Sh SYNOPSIS +.Nm mount_procfs +.Op Fl o Ar options +.Pa /proc +.Pa mount_point +.Sh DESCRIPTION +The +.Nm mount_procfs +command attaches an instance of the process +namespace to the global filesystem namespace. +The conventional mount point is +.Pa /proc . +This command is normally executed by +.Xr mount 8 +at boot time. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Pp +The root of the process filesystem +contains an entry for each active process. +These processes are visible as a directory whose +name is the process' pid. +In addition, the special entry +.Pa curproc +references the current process. +.Pp +Each directory contains several files. +.Bl -tag -width status +.It Pa ctl +a writeonly file which supports a variety +of control operations. +Control commands are written as strings to the +.Pa ctl +file. +The control commands are: +.Bl -tag -width detach -compact +.It attach +stops the target process and arranges for the sending +process to become the debug control process. +.It detach +continue execution of the target process and +remove it from control by the debug process (which +need not be the sending process). +.It run +continue running the target process until +a signal is delivered, a breakpoint is hit, or the +target process exits. +.It step +single step the target process, with no signal delivery. +.It wait +wait for the target process to come to a steady +state ready for debugging. +The target process must be in this state before +any of the other commands are allowed. +.El +.Pp +The string can also be the name of a signal, lower case +and without the +.Dv SIG +prefix, +in which case that signal is delivered to the process +(see +.Xr sigaction 2 ). +.It Pa file +A reference to the vnode from which the process text was read. +This can be used to gain access to the process' symbol table, +or to start another copy of the process. +.It Pa mem +The complete virtual memory image of the process. +Only those address which exist in the process can be accessed. +Reads and writes to this file modify the process. +Writes to the text segment remain private to the process. +.It Pa note +Not implemented. +.It Pa notepg +Not implemented. +.It Pa regs +Allows read and write access to the process' register set. +This file contains a binary data structure +.Dv "struct regs" +defined in +.Pa <machine/reg.h> . +.Pa regs +can only be written when the process is stopped. +.It Pa fpregs +The floating point registers as defined by +.Dv "struct fpregs" +in +.Pa <machine/reg.h> . +.Pa fpregs +is only implemented on machines which have distinct general +purpose and floating point register sets. +.It Pa status +The process status. +This file is readonly and returns a single line containing +multiple space-separated fields as follows: +.Pp +.Bl -bullet -compact +.It +command name +.It +process id +.It +parent process id +.It +process group id +.It +session id +.It +.Ar major,minor +of the controlling terminal, or +.Dv -1,-1 +if there is no controlling terminal. +.It +a list of process flags: +.Dv ctty +if there is a controlling terminal, +.Dv sldr +if the process is a session leader, +.Dv noflags +if neither of the other two flags are set. +.It +the process start time in seconds and microseconds, +comma separated. +.It +the user time in seconds and microseconds, +comma separated. +.It +the system time in seconds and microseconds, +comma separated. +.It +the wait channel message +.It +the process credentials consisting of +the effective user id +and the list of groups (whose first member +is the effective group id) +all comma separated. +.El +.El +.Pp +In a normal debugging environment, +where the target is fork/exec'd by the debugger, +the debugger should fork and the child should stop +itself (with a self-inflicted +.Dv SIGSTOP +for example). +The parent should issue a +.Dv wait +and then an +.Dv attach +command via the appropriate +.Pa ctl +file. +The child process will receive a +.Dv SIGTRAP +immediately after the call to exec (see +.Xr execve 2 ). +.Sh FILES +.Bl -tag -width /proc/curproc -compact +.It Pa /proc/# +.It Pa /proc/curproc +.It Pa /proc/curproc/ctl +.It Pa /proc/curproc/file +.It Pa /proc/curproc/mem +.It Pa /proc/curproc/note +.It Pa /proc/curproc/notepg +.It Pa /proc/curproc/regs +.It Pa /proc/curproc/fpregs +.It Pa /proc/curproc/status +.El +.Sh SEE ALSO +.Xr sigaction 2 , +.Xr mount 2 , +.Xr unmount 2 , +.Sh CAVEATS +No +.Pa . +and +.Pa .. +entries appear when listing the contents of the +.Pa /proc +directory. +This makes sense in the context of this filesystem, but is inconsistent +with usual filesystem conventions. +However, it is still possible to refer to both +.Pa . +and +.Pa .. +in a pathname. +.Pp +This filesystem may not be NFS-exported +since most of the functionality of +.Dv procfs +requires that state be maintained. +.Sh HISTORY +The +.Nm mount_procfs +utility first appeared in 4.4BSD. diff --git a/sbin/mount_procfs/mount_procfs.c b/sbin/mount_procfs/mount_procfs.c new file mode 100644 index 0000000..79016c8 --- /dev/null +++ b/sbin/mount_procfs/mount_procfs.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1990, 1992, 1993 Jan-Simon Pendry + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_procfs.c 8.3 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> + +#include <err.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, mntflags; + + mntflags = 0; + while ((ch = getopt(argc, argv, "o:")) != EOF) + switch (ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (mount(MOUNT_PROCFS, argv[1], mntflags, NULL)) + err(1, NULL); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_procfs [-o options] /proc mount_point\n"); + exit(1); +} diff --git a/sbin/mount_umap/Makefile b/sbin/mount_umap/Makefile new file mode 100644 index 0000000..af1bf2f --- /dev/null +++ b/sbin/mount_umap/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_umap +SRCS= mount_umap.c getmntopts.c +MAN8= mount_umap.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I/sys -I${MOUNT} +.PATH: ${MOUNT} + +.include <bsd.prog.mk> diff --git a/sbin/mount_umap/mount_umap.8 b/sbin/mount_umap/mount_umap.8 new file mode 100644 index 0000000..e90ce26 --- /dev/null +++ b/sbin/mount_umap/mount_umap.8 @@ -0,0 +1,131 @@ +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry and from John Heidemann of the UCLA Ficus project. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_umap.8 8.3 (Berkeley) 3/27/94 +.\" +.Dd "March 27, 1994" +.Dt MOUNT_UMAP 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_umap +.Nd sample file system layer +.Sh SYNOPSIS +.Nm mount_umap +.Op Fl o Ar options +.Ar target +.Ar mount-point +.Ar uid-mapfile +.Ar gid-mapfile +.Sh DESCRIPTION +The +.Nm mount_umap +command is used to mount a sub-tree of an existing file system +that uses a different set of uids and gids than the local system. +Such a file system could be mounted from a remote site via NFS or +it could be a file system on removable media brought from some +foreign location that uses a different password file. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Pp +The +.Nm mount_umap +command uses a set of files provided by the user to make correspondences +between uids and gids in the sub-tree's original environment and +some other set of ids in the local environment. For instance, user +smith might have uid 1000 in the original environment, while having +uid 2000 in the local environment. The +.Nm mount_umap +command allows the subtree from smith's original environment to be +mapped in such a way that all files with owning uid 1000 look like +they are actually owned by uid 2000. +.Pp +.Em target +should be the current location of the sub-tree in the +local system's name space. +.Em mount-point +should be a directory +where the mapped subtree is to be placed. +.Em uid-mapfile +and +.Em gid-mapfile +describe the mappings to be made between identifiers. +Briefly, the format of these files is a count of the number of +mappings on the first line, with each subsequent line containing +a single mapping. Each of these mappings consists of an id from +the original environment and the corresponding id in the local environment, +separated by white space. +.Em uid-mapfile +should contain all uid +mappings, and +.Em gid-mapfile +should contain all gid mappings. +Any uids not mapped in +.Em uid-mapfile +will be treated as user NOBODY, +and any gids not mapped in +.Em gid-mapfile +will be treated as group +NULLGROUP. At most 64 uids can be mapped for a given subtree, and +at most 16 groups can be mapped by a given subtree. +.Pp +The mapfiles can be located anywhere in the file hierarchy, but they +must be owned by root, and they must be writable only by root. +.Nm mount_umap +will refuse to map the sub-tree if the ownership or permissions on +these files are improper. It will also balk if the count of mappings +in the first line of the map files is not correct. +.Pp +The layer created by the +.Nm mount_umap +command is meant to serve as a simple example of file system layering. +It is not meant for production use. The implementation is not very +sophisticated. +.Sh SEE ALSO +.Xr mount 8 , +.Xr mount_null 8 , +.Xr mount_lofs 8 +.Sh HISTORY +The +.Nm mount_umap +utility first appeared in 4.4BSD. diff --git a/sbin/mount_umap/mount_umap.c b/sbin/mount_umap/mount_umap.c new file mode 100644 index 0000000..a069fe5 --- /dev/null +++ b/sbin/mount_umap/mount_umap.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_umap.c 8.3 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/stat.h> + +#include <miscfs/umapfs/umap.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mntopts.h" + +#define ROOTUSER 0 +/* + * This define controls whether any user but the superuser can own and + * write mapfiles. If other users can, system security can be gravely + * compromised. If this is not a concern, undefine SECURITY. + */ +#define MAPSECURITY 1 + +/* + * This routine provides the user interface to mounting a umap layer. + * It takes 4 mandatory parameters. The mandatory arguments are the place + * where the next lower level is mounted, the place where the umap layer is to + * be mounted, the name of the user mapfile, and the name of the group + * mapfile. The routine checks the ownerships and permissions on the + * mapfiles, then opens and reads them. Then it calls mount(), which + * will, in turn, call the umap version of mount. + */ + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + static char not[] = "; not mounted."; + struct stat statbuf; + struct umap_args args; + FILE *fp, *gfp; + u_long gmapdata[GMAPFILEENTRIES][2], mapdata[MAPFILEENTRIES][2]; + int ch, count, gnentries, mntflags, nentries; + char *gmapfile, *mapfile, *source, *target, buf[20]; + + mntflags = 0; + mapfile = gmapfile = NULL; + while ((ch = getopt(argc, argv, "g:o:u:")) != EOF) + switch (ch) { + case 'g': + gmapfile = optarg; + break; + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case 'u': + mapfile = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2 || mapfile == NULL || gmapfile == NULL) + usage(); + + source = argv[0]; + target = argv[1]; + + /* Read in uid mapping data. */ + if ((fp = fopen(mapfile, "r")) == NULL) + err(1, "%s%s", mapfile, not); + +#ifdef MAPSECURITY + /* + * Check that group and other don't have write permissions on + * this mapfile, and that the mapfile belongs to root. + */ + if (fstat(fileno(fp), &statbuf)) + err(1, "%s%s", mapfile, not); + if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) { + strmode(statbuf.st_mode, buf); + err(1, "%s: improper write permissions (%s)%s", + mapfile, buf, not); + } + if (statbuf.st_uid != ROOTUSER) + errx(1, "%s does not belong to root%s", mapfile, not); +#endif /* MAPSECURITY */ + + if ((fscanf(fp, "%d\n", &nentries)) != 1) + errx(1, "%s: nentries not found%s", mapfile, not); + if (nentries > MAPFILEENTRIES) + errx(1, + "maximum number of entries is %d%s", MAPFILEENTRIES, not); +#if 0 + (void)printf("reading %d entries\n", nentries); +#endif + for (count = 0; count < nentries; ++count) { + if ((fscanf(fp, "%lu %lu\n", + &(mapdata[count][0]), &(mapdata[count][1]))) != 2) { + if (ferror(fp)) + err(1, "%s%s", mapfile, not); + if (feof(fp)) + errx(1, "%s: unexpected end-of-file%s", + mapfile, not); + errx(1, "%s: illegal format (line %d)%s", + mapfile, count + 2, not); + } +#if 0 + /* Fix a security hole. */ + if (mapdata[count][1] == 0) + errx(1, "mapping id 0 not permitted (line %d)%s", + count + 2, not); +#endif + } + + /* Read in gid mapping data. */ + if ((gfp = fopen(gmapfile, "r")) == NULL) + err(1, "%s%s", gmapfile, not); + +#ifdef MAPSECURITY + /* + * Check that group and other don't have write permissions on + * this group mapfile, and that the file belongs to root. + */ + if (fstat(fileno(gfp), &statbuf)) + err(1, "%s%s", gmapfile, not); + if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) { + strmode(statbuf.st_mode, buf); + err(1, "%s: improper write permissions (%s)%s", + gmapfile, buf, not); + } + if (statbuf.st_uid != ROOTUSER) + errx(1, "%s does not belong to root%s", gmapfile, not); +#endif /* MAPSECURITY */ + + if ((fscanf(fp, "%d\n", &gnentries)) != 1) + errx(1, "nentries not found%s", gmapfile, not); + if (gnentries > MAPFILEENTRIES) + errx(1, + "maximum number of entries is %d%s", GMAPFILEENTRIES, not); +#if 0 + (void)printf("reading %d group entries\n", gnentries); +#endif + + for (count = 0; count < gnentries; ++count) + if ((fscanf(fp, "%lu %lu\n", + &(gmapdata[count][0]), &(gmapdata[count][1]))) != 2) { + if (ferror(fp)) + err(1, "%s%s", gmapfile, not); + if (feof(fp)) + errx(1, "%s: unexpected end-of-file%s", + gmapfile, not); + errx(1, "%s: illegal format (line %d)%s", + gmapfile, count + 2, not); + } + + + /* Setup mount call args. */ + args.target = source; + args.nentries = nentries; + args.mapdata = mapdata; + args.gnentries = gnentries; + args.gmapdata = gmapdata; + + if (mount(MOUNT_UMAP, argv[1], mntflags, &args)) + err(1, NULL); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, +"usage: mount_umap [-o options] -u usermap -g groupmap target_fs mount_point\n"); + exit(1); +} diff --git a/sbin/mount_umap/sample.group.mapfile b/sbin/mount_umap/sample.group.mapfile new file mode 100644 index 0000000..69d3d77 --- /dev/null +++ b/sbin/mount_umap/sample.group.mapfile @@ -0,0 +1,2 @@ +1 +1200 1200 diff --git a/sbin/mount_umap/sample.user.mapfile b/sbin/mount_umap/sample.user.mapfile new file mode 100644 index 0000000..03c59e9 --- /dev/null +++ b/sbin/mount_umap/sample.user.mapfile @@ -0,0 +1,3 @@ +2 +5217 5217 +3 3 diff --git a/sbin/mount_umap/umap_manual b/sbin/mount_umap/umap_manual new file mode 100644 index 0000000..059939f --- /dev/null +++ b/sbin/mount_umap/umap_manual @@ -0,0 +1,175 @@ + +\appendix +\section{The umap Layer} \label{sect:umap} + +\subsection{Introduction} + +Normally, the file system is expected to span a single administrative domain. +An administrative domain, for these purposes, is a machine or set of +machines that share common password file information, usually through +the yellow pages mechanism. File hierarchies that span more +than one domain leads to certain problems, since the same numerical +UID in one domain may correspond to a different user in another domain. +If the system administrator is very careful to ensure that both domains +contain identical user ID information, The umap layer can be used to +run between those domains without changes + +The umap layer is a file system layer that sits on top of the normal +file layer. The umap layer maps Unix-style UIDs from +one domain into the UIDs in the other domain. By setting up the mappings +properly, the same user with different UIDs in two domains can be seen +as the same user, from the system point of view, or, conversely, two +different users with the same UID in the two domains can be distinguished. + +First, we define some terms. ``User'' refers to the human (or daemon) that +has privileges to login, run programs, and access files. ``UID''refers to +the numerical identifier that uniquely identifies the user within a +single domain. ``Login name'' refers to the character string the user +types to log into the system. ``GID'' refers to the numerical group +identifier used by Unix systems to identify groups of users. ``Group +name'' is the character string name attached to a particular GID in the +local {\sf /etc/groups} file or the yellow pages groups file. + +In order for the umap layer to work properly, all users +in either domain must have password file entries in both domains. +They do not, however, have to have the same numerical UID, nor even the +same character string login name (the latter is highly recommended, +if possible, however). Any user not having a UID in one domain will be +treated as the special user NOBODY by the other domain, probably with +undesirable consequences. Any user not owning any files in the shared +sub-trees need not be given a UID in the other domain. + +Groups work similarly. The umap layer can translate group ID's between +domains in the same manner as UID's. Again, any group that wishes to +participate must have a group ID in both domains, +though it need not be the same GID in both. If a group in one domain is not +known in the other domain, that group will be treated as being NULLGROUP. +The umap layer has no provisions for enrolling UID's from other domains +as group members, but, since each user from each domain must have some +UID in every domain, the UID in the local domain can be used to enroll +the user in the local groups. + +NOBODY and NULLGROUP are special reserved UID's and GID's, respectively. +NOBODY is user 32767. NULLGROUP is group 65534. If the system administrator +wants to have an appropriate text string appear when these UID's are +encountered by programs like {\sf ls -l}, he should add these values to +the password and {\sf /etc/groups} file, or to the appropriate yellow pages. +If these IDs are already in use in that domain, different values can be +used for NOBODY and NULLGROUP, but that will require a recompilation of +the umap layer code and, as a result, the entire kernel. These +values are defined in the {\sf umap\_info.h} file, kept with the rest of the +umap source code. + +When the umap layer is in use, one of the participating domains is declared +to be the master. All UID and GID information stored for participating files +will be stored in vnodes using its mappings, no matter what site the copies of +the files are stored at. The master domain therefore need not run a copy +of the umap layer, as it already has all of the correct mappings. All +other domains must run a umap layer on top of any other layers they use. + +\subsection{Setting Up a umap Layer} + +The system administrator of a system needing to use the umap layer +must take several actions. +First, he must create files containing the necessary UID +and GID mappings. There is a separate file for user and group IDs. The +format of the files is the same. The first line contains the total number +of entries in the file. Each subsequent line contains one mapping. A +mapping line consists of two numerical UIDs, separated by white space. +The first is the UID of a user on the local machine. The second is the +UID for the same user on the master machine. The maximum number of users +that can be mapped for a single shared sub-tree is 64. The maximum number of +groups that can be mapped for a single sub-tree is 16. These constants +are set in the {\sf umap\_info.h} file, and can be changed, but changing them +requires recompilation. Separate mapping files can be used for each shared +subtree, or the same mapping files can be shared by several sub-trees. + +Below is a sample UID mapping file. There are four entries. UID 5 is mapped +to 5, 521 to 521, and 7000 to 7000. UID 2002 is mapped to 604. On this +machine, the UID's for users 5, 521, and 7000 are the same as on the master, +but UID 2002 is for a user whose UID on the master machine is 604. All +files in the sub-tree belonging to that user have UID 604 in their inodes, +even on this machine, but the umap layer will ensure that anyone running +under UID 2002 will have all files in this sub-tree owned by 604 treated as if +they were owned by 2002. An {\sf ls -l} on a file owned by 604 in this sub-tree +will show the login name associated with UID 2002 as the owner. + +\noindent4\newline +5 5\newline +521 521\newline +2002 604\newline +7000 7000\newline + +The user and group mapping files should be owned by the root user, and +should be writable only by that user. If they are not owned by root, or +are writable by some other user, the umap mounting command will abort. + +Normally, the sub-treeis grafted directly into the place in +the file hierarchy where the it should appear to users.Using the umap +layer requires that the sub-tree be grafted somewhere else, and +the umap layer be mounted in the desired position in the file hierarchy. +Depending on the situation, the underlying sub-tree can be wherever is +convenient. + +\subsection{Troubleshooting umap Layer Problems} + +The umap layer code was not built with special convenience or +robustness in mind, as it is expected to be superseded with a better +user ID mapping strategy in the near future. As a result, it is not +very forgiving of errors in being set up. Here are some possible +problems, and what to do about them. + +\begin{itemize} + + +\item{Problem: A file belongs to NOBODY, or group NULLGROUP. + +Fixes: The mapping files don't know about this file's real user or group. +Either they are not in the mapping files, or the counts on the number of +entries in the mapping files are too low, so entries at the end (including +these) are being ignored. Add the entries or fix the counts, and either +unmount and remount the sub-tree, or reboot.} + +\item{Problem: A normal operation does not work. + +Fixes: Possibly, some mapping has not been set properly. Check to +see which files are used by the operation and who they appear to be +owned by. If they are owned by NOBODY or some other suspicious user, +there may be a problem in the mapping files. Be sure to check groups, +too. As above, if the counts of mappings in the mapping files are lower +than the actual numbers of pairs, pairs at the end of the file will be +ignored. If any changes are made in the mapping files, you will need to +either unmount and remount or reboot before they will take effect. + +Another possible problem can arise because not all Unix utilities +rely exclusively on numeric UID for identification. For instance, +SCCS saves the login name in files. If a user's login name on two machines +isn't the same, SCCS may veto an operation even though Unix file permissions, +as checked by the umap layer, may say it's OK. There's not much to be +done in such cases, unless the login name can be changed or one fiddles +improperly with SCCS information. There may be other, undiscovered cases +where similar problems arise, some of which may be even harder to handle.} + +\item{Problem: Someone has access permissions he should not have. + +Fixes: This is probably caused by a mistake in the mapping files. Check +both user and group mapping files. If any changes are made in the mapping +files, you will need to unmount and remount the sub-tree or reboot before they +will take effect.} + +\item{Problem: {\sf ls -l} (or a similar program) shows the wrong user for a file. + +Fixes: Probably a mistake in the mapping files. In particular, if +two local UIDs are mapped to a single master UID, stat calls will assign +ownership to the first local UID occurring in the file, which may or may +not be what was intended. (Generally speaking, mapping two local UIDs to +a single master UID is a bad idea, but the software will not prevent it. +Similarly, mapping a single local UID to two master UIDs is a bad idea, +but will not be prevented. In this case, only the first mapping of the +local UID will be done. The second, and all subsequent ones, will be +ignored.) If any changes are made in the mapping files, you will need to +unmount and remount the sub-tree or reboot before they will take effect.} + +\end{itemize} + +\end{document} diff --git a/sbin/mount_umapfs/sample.group.mapfile b/sbin/mount_umapfs/sample.group.mapfile new file mode 100644 index 0000000..69d3d77 --- /dev/null +++ b/sbin/mount_umapfs/sample.group.mapfile @@ -0,0 +1,2 @@ +1 +1200 1200 diff --git a/sbin/mount_umapfs/sample.user.mapfile b/sbin/mount_umapfs/sample.user.mapfile new file mode 100644 index 0000000..03c59e9 --- /dev/null +++ b/sbin/mount_umapfs/sample.user.mapfile @@ -0,0 +1,3 @@ +2 +5217 5217 +3 3 diff --git a/sbin/mount_union/Makefile b/sbin/mount_union/Makefile new file mode 100644 index 0000000..a6b33af --- /dev/null +++ b/sbin/mount_union/Makefile @@ -0,0 +1,14 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_union +SRCS= mount_union.c getmntopts.c +MAN8= mount_union.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I/sys -I${MOUNT} +.PATH: ${MOUNT} + +BINOWN= root +BINMODE=4555 + +.include <bsd.prog.mk> diff --git a/sbin/mount_union/mount_union.8 b/sbin/mount_union/mount_union.8 new file mode 100644 index 0000000..65a6497 --- /dev/null +++ b/sbin/mount_union/mount_union.8 @@ -0,0 +1,204 @@ +.\" Copyright (c) 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_union.8 8.6 (Berkeley) 3/27/94 +.\" +.Dd March 27, 1994 +.Dt MOUNT_UNION 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_union +.Nd mount union filesystems +.Sh SYNOPSIS +.Nm mount_union +.Op Fl br +.Op Fl o Ar options +.Ar directory +.Ar uniondir +.Sh DESCRIPTION +The +.Nm mount_union +command +attaches +.Ar directory +above +.Ar uniondir +in such a way that the contents of both directory trees remain visible. +By default, +.Ar directory +becomes the +.Em upper +layer and +.Ar uniondir +becomes the +.Em lower +layer. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl b +Invert the default position, so that +.Ar directory +becomes the lower layer and +.Ar uniondir +becomes the upper layer. +However, +.Ar uniondir +remains the mount point. +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.It Fl r +Hide the lower layer completely in the same way as mounting with +.Xr mount_lofs 8 +or +.Xr mount_null 8 . +.El +.Pp +To enforce filesystem security, the user mounting the filesystem +must be superuser or else have write permission on the mounted-on +directory. +.Pp +Filenames are looked up in the upper layer and then in the +lower layer. +If a directory is found in the lower layer, and there is no entry +in the upper layer, then a +.Em shadow +directory will be created in the upper layer. +It will be owned by the user who originally did the union mount, +with mode +.Dq rwxrwxrwx +(0777) modified by the umask in effect at that time. +.Pp +If a file exists in the upper layer then there is no way to access +a file with the same name in the lower layer. +If necessary, a combination of loopback and union mounts can be made +which will still allow the lower files to be accessed by a different +pathname. +.Pp +Except in the case of a directory, +access to an object is granted via the normal filesystem access checks. +For directories, the current user must have access to both the upper +and lower directories (should they both exist). +.Pp +Requests to create or modify objects in +.Ar uniondir +are passed to the upper layer with the exception of a few special cases. +An attempt to open for writing a file which exists in the lower layer +causes a copy of the +.Em entire +file to be made to the upper layer, and then for the upper layer copy +to be opened. +Similarly, an attempt to truncate a lower layer file to zero length +causes an empty file to be created in the upper layer. +Any other operation which would ultimately require modification to +the lower layer fails with +.Dv EROFS . +.Pp +The union filesystem manipulates the namespace, rather than +individual filesystems. +The union operation applies recursively down the directory tree +now rooted at +.Ar uniondir . +Thus any filesystems which are mounted under +.Ar uniondir +will take part in the union operation. +This differs from the +.Em union +option to +.Xr mount 8 +which only applies the union operation to the mount point itself, +and then only for lookups. +.Sh EXAMPLES +The commands +.Bd -literal -offset indent +mount -t cd9660 -o ro /dev/cd0a /usr/src +mount -t union -o /var/obj /usr/src +.Ed +.Pp +mount the CD-ROM drive +.Pa /dev/cd0a +on +.Pa /usr/src +and then attaches +.Pa /var/obj +on top. +For most purposes the effect of this is to make the +source tree appear writable +even though it is stored on a CD-ROM. +.Pp +The command +.Bd -literal -offset indent +mount -t union -o -b /sys $HOME/sys +.Ed +.Pp +attaches the system source tree below the +.Pa sys +directory in the user's home directory. +This allows individual users to make private changes +to the source, and build new kernels, without those +changes becoming visible to other users. +Note that the files in the lower layer remain +accessible via +.Pa /sys . +.Sh SEE ALSO +.Xr intro 2 , +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr mount 8 , +.Xr mount_lofs 8 , +.Xr mount_null 8 +.Sh BUGS +Without whiteout support from the filesystem backing the upper layer, +there is no way that delete and rename operations on lower layer +objects can be done. +.Dv EROFS +is returned for this kind of operations along with any others +which would make modifications to the lower layer, such as +.Xr chmod 1 . +.Pp +Running +.Xr find 1 +over a union tree has the side-effect of creating +a tree of shadow directories in the upper layer. +.Sh HISTORY +The +.Nm mount_union +command first appeared in +.Bx 4.4 . diff --git a/sbin/mount_union/mount_union.c b/sbin/mount_union/mount_union.c new file mode 100644 index 0000000..90d075b --- /dev/null +++ b/sbin/mount_union/mount_union.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_union.c 8.5 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> + +#include <miscfs/union/union.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +int subdir __P((const char *, const char *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct union_args args; + int ch, mntflags; + char target[MAXPATHLEN]; + + mntflags = 0; + args.mntflags = UNMNT_ABOVE; + while ((ch = getopt(argc, argv, "bo:r")) != EOF) + switch (ch) { + case 'b': + args.mntflags &= ~UNMNT_OPMASK; + args.mntflags |= UNMNT_BELOW; + break; + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case 'r': + args.mntflags &= ~UNMNT_OPMASK; + args.mntflags |= UNMNT_REPLACE; + break; + case '?': + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (realpath(argv[0], target) == 0) + err(1, "%s", target); + + if (subdir(target, argv[1]) || subdir(argv[1], target)) + errx(1, "%s (%s) and %s are not distinct paths", + argv[0], target, argv[1]); + + args.target = target; + + if (mount(MOUNT_UNION, argv[1], mntflags, &args)) + err(1, NULL); + exit(0); +} + +int +subdir(p, dir) + const char *p; + const char *dir; +{ + int l; + + l = strlen(dir); + if (l <= 1) + return (1); + + if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0')) + return (1); + + return (0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_union [-br] [-o options] target_fs mount_point\n"); + exit(1); +} diff --git a/sbin/mountd/Makefile b/sbin/mountd/Makefile new file mode 100644 index 0000000..36e2a05 --- /dev/null +++ b/sbin/mountd/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.3 (Berkeley) 1/25/94 + +PROG= mountd +CFLAGS+=-DNFS -DMFS -DCD9660 +MAN5= exports.0 netgroup.0 +MAN8= mountd.0 +DPADD= ${LIBRPC} +LDADD= -lrpc + +.include <bsd.prog.mk> diff --git a/sbin/mountd/exports.5 b/sbin/mountd/exports.5 new file mode 100644 index 0000000..d32527f --- /dev/null +++ b/sbin/mountd/exports.5 @@ -0,0 +1,250 @@ +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)exports.5 8.2 (Berkeley) 1/28/94 +.\" +.Dd January 28, 1994 +.Dt EXPORTS 5 +.Os +.Sh NAME +.Nm exports +.Nd define remote mount points for +.Tn NFS +mount requests +.Sh SYNOPSIS +.Nm exports +.Sh DESCRIPTION +The +.Nm exports +file specifies remote mount points for the +.Tn NFS +mount protocol per the +.Tn NFS +server specification; see +.%T "Network File System Protocol Specification \\*(tNRFC\\*(sP 1094, Appendix A" . +.Pp +Each line in the file +(other than comment lines that begin with a #) +specifies the mount point(s) and export flags within one local server +filesystem for one or more hosts. +A host may be specified only once for each local filesystem on the +server and there may be only one default entry for each server +filesystem that applies to all other hosts. +The latter exports the filesystem to the ``world'' and should +be used only when the filesystem contains public information. +.Pp +In a mount entry, +the first field(s) specify the directory path(s) within a server filesystem +that can be mounted on by the corresponding client(s). +There are two forms of this specification. +The first is to list all mount points as absolute +directory paths separated by whitespace. +The second is to specify the pathname of the root of the filesystem +followed by the +.Fl alldirs +flag; +this form allows the host(s) to mount any directory within the filesystem. +The pathnames must not have any symbolic links in them and should not have +any "." or ".." components. +Mount points for a filesystem may appear on multiple lines each with +different sets of hosts and export options. +.Pp +The second component of a line specifies how the filesystem is to be +exported to the host set. +The option flags specify whether the filesystem +is exported read-only or read-write and how the client uid is mapped to +user credentials on the server. +.Pp +Export options are specified as follows: +.Pp +.Sm off +.Fl maproot No = Sy user +.Sm on +The credential of the specified user is used for remote access by root. +The credential includes all the groups to which the user is a member +on the local machine (see +.Xr id 1 ). +The user may be specified by name or number. +.Pp +.Sm off +.Fl maproot No = Sy user:group1:group2:... +.Sm on +The colon separated list is used to specify the precise credential +to be used for remote access by root. +The elements of the list may be either names or numbers. +Note that user: should be used to distinguish a credential containing +no groups from a complete credential for that user. +.Pp +.Sm off +.Fl mapall No = Sy user +.Sm on +or +.Sm off +.Fl mapall No = Sy user:group1:group2:... +.Sm on +specifies a mapping for all client uids (including root) +using the same semantics as +.Fl maproot . +.Pp +The option +.Fl r +is a synonym for +.Fl maproot +in an effort to be backward compatible with older export file formats. +.Pp +In the absence of +.Fl maproot +and +.Fl mapall +options, remote accesses by root will result in using a credential of -2:-2. +All other users will be mapped to their remote credential. +If a +.Fl maproot +option is given, +remote access by root will be mapped to that credential instead of -2:-2. +If a +.Fl mapall +option is given, +all users (including root) will be mapped to that credential in +place of their own. +.Pp +The +.Fl kerb +option specifies that the Kerberos authentication server should be +used to authenticate and map client credentials. +(Note that this is NOT Sun NFS compatible and +is supported for TCP transport only.) +.Pp +The +.Fl ro +option specifies that the filesystem should be exported read-only +(default read/write). +The option +.Fl o +is a synonym for +.Fl ro +in an effort to be backward compatible with older export file formats. +.Pp +The third component of a line specifies the host set to which the line applies. +The set may be specified in three ways. +The first way is to list the host name(s) separated by white space. +(Standard internet ``dot'' addresses may be used in place of names.) +The second way is to specify a ``netgroup'' as defined in the netgroup file (see +.Xr netgroup 5 ). +The third way is to specify an internet subnetwork using a network and +network mask that is defined as the set of all hosts with addresses within +the subnetwork. +This latter approach requires less overhead within the +kernel and is recommended for cases where the export line refers to a +large number of clients within an administrative subnet. +.Pp +The first two cases are specified by simply listing the name(s) separated +by whitespace. +All names are checked to see if they are ``netgroup'' names +first and are assumed to be hostnames otherwise. +Using the full domain specification for a hostname can normally +circumvent the problem of a host that has the same name as a netgroup. +The third case is specified by the flag +.Sm off +.Fl network No = Sy netname +.Sm on +and optionally +.Sm off +.Fl mask No = Sy netmask . +.Sm on +If the mask is not specified, it will default to the mask for that network +class (A, B or C; see +.Xr inet 5 ). +.Pp +For example: +.Bd -literal -offset indent +/usr /usr/local -maproot=0:10 friends +/usr -maproot=daemon grumpy.cis.uoguelph.ca 131.104.48.16 +/usr -ro -mapall=nobody +/u -maproot=bin: -network 131.104.48 -mask 255.255.255.0 +/u2 -maproot=root friends +/u2 -alldirs -kerb -network cis-net -mask cis-mask +.Ed +.Pp +Given that +.Sy /usr , +.Sy /u +and +.Sy /u2 +are +local filesystem mount points, the above example specifies the following: +.Sy /usr +is exported to hosts +.Em friends +where friends is specified in the netgroup file +with users mapped to their remote credentials and +root mapped to uid 0 and group 10. +It is exported read-write and the hosts in ``friends'' can mount either /usr +or /usr/local. +It is exported to +.Em 131.104.48.16 +and +.Em grumpy.cis.uoguelph.ca +with users mapped to their remote credentials and +root mapped to the user and groups associated with ``daemon''; +it is exported to the rest of the world as read-only with +all users mapped to the user and groups associated with ``nobody''. +.Pp +.Sy /u +is exported to all hosts on the subnetwork +.Em 131.104.48 +with root mapped to the uid for ``bin'' and with no group access. +.Pp +.Sy /u2 +is exported to the hosts in ``friends'' with root mapped to uid and groups +associated with ``root''; +it is exported to all hosts on network ``cis-net'' allowing mounts at any +directory within /u2 and mapping all uids to credentials for the principal +that is authenticated by a Kerberos ticket. +.Sh FILES +.Bl -tag -width /etc/exports -compact +.It Pa /etc/exports +The default remote mount-point file. +.El +.Sh SEE ALSO +.Xr netgroup 5 , +.Xr mountd 8 , +.Xr nfsd 8 , +.Xr showmount 8 +.Sh BUGS +The export options are tied to the local mount points in the kernel and +must be non-contradictory for any exported subdirectory of the local +server mount point. +It is recommended that all exported directories within the same server +filesystem be specified on adjacent lines going down the tree. +You cannot specify a hostname that is also the name of a netgroup. +Specifying the full domain specification for a hostname can normally +circumvent the problem. diff --git a/sbin/mountd/mountd.8 b/sbin/mountd/mountd.8 new file mode 100644 index 0000000..47a6cfc --- /dev/null +++ b/sbin/mountd/mountd.8 @@ -0,0 +1,100 @@ +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mountd.8 8.1 (Berkeley) 6/9/93 +.\" +.Dd June 9, 1993 +.Dt MOUNTD 8 +.Os +.Sh NAME +.Nm mountd +.Nd service remote +.Tn NFS +mount requests +.Sh SYNOPSIS +.Nm /sbin/mountd +.Op Fl n +.Op Ar exportsfile +.Sh DESCRIPTION +.Xr Mountd +is the server for +.Tn NFS +mount requests from other client machines. +.Xr Mountd +listens for service requests at the port indicated in the +.Tn NFS +server specification; see +.%T "Network File System Protocol Specification" , +RFC1094. +.Pp +Options and operands available for +.Nm mountd : +.Bl -tag -width Ds +.It Fl n +The +.Fl n +option allows non-root mount requests to be served. +This should only be specified if there are clients such as PC's, +that require it. +.It Ar exportsfile +The +.Ar exportsfile +argument specifies an alternate location +for the exports file. +.El +.Pp +When mountd is started, +it loads the export host addresses and options into the kernel +using the mount(2) system call. +After changing the exports file, +a hangup signal should be sent to the mountd daemon +to get it to reload the export information. +After sending the SIGHUP +(kill -HUP `cat /var/run/mountd.pid`), +check the syslog output to see if mountd logged any parsing +errors in the exports file. +.Sh FILES +.Bl -tag -width /var/run/mountd.pid -compact +.It Pa /etc/exports +the list of exported filesystems +.It Pa /var/run/mountd.pid +the pid of the currently running mountd +.El +.Sh SEE ALSO +.Xr nfsstat 1 , +.Xr exports 5 , +.Xr nfsd 8 , +.Xr portmap 8 , +.Xr showmount 8 +.Sh HISTORY +The +.Nm mountd +utility first appeared in 4.4BSD. diff --git a/sbin/mountd/mountd.c b/sbin/mountd/mountd.c new file mode 100644 index 0000000..d7c31df --- /dev/null +++ b/sbin/mountd/mountd.c @@ -0,0 +1,2005 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Herb Hasler and Rick Macklem at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif not lint + +#ifndef lint +static char sccsid[] = "@(#)mountd.c 8.8 (Berkeley) 2/20/94"; +#endif not lint + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/syslog.h> +#include <sys/ucred.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_clnt.h> +#include <rpc/pmap_prot.h> +#ifdef ISO +#include <netiso/iso.h> +#endif +#include <nfs/rpcv2.h> +#include <nfs/nfsv2.h> + +#include <arpa/inet.h> + +#include <ctype.h> +#include <errno.h> +#include <grp.h> +#include <netdb.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "pathnames.h" + +#ifdef DEBUG +#include <stdarg.h> +#endif + +/* + * Structures for keeping the mount list and export list + */ +struct mountlist { + struct mountlist *ml_next; + char ml_host[RPCMNT_NAMELEN+1]; + char ml_dirp[RPCMNT_PATHLEN+1]; +}; + +struct dirlist { + struct dirlist *dp_left; + struct dirlist *dp_right; + int dp_flag; + struct hostlist *dp_hosts; /* List of hosts this dir exported to */ + char dp_dirp[1]; /* Actually malloc'd to size of dir */ +}; +/* dp_flag bits */ +#define DP_DEFSET 0x1 + +struct exportlist { + struct exportlist *ex_next; + struct dirlist *ex_dirl; + struct dirlist *ex_defdir; + int ex_flag; + fsid_t ex_fs; + char *ex_fsdir; +}; +/* ex_flag bits */ +#define EX_LINKED 0x1 + +struct netmsk { + u_long nt_net; + u_long nt_mask; + char *nt_name; +}; + +union grouptypes { + struct hostent *gt_hostent; + struct netmsk gt_net; +#ifdef ISO + struct sockaddr_iso *gt_isoaddr; +#endif +}; + +struct grouplist { + int gr_type; + union grouptypes gr_ptr; + struct grouplist *gr_next; +}; +/* Group types */ +#define GT_NULL 0x0 +#define GT_HOST 0x1 +#define GT_NET 0x2 +#define GT_ISO 0x4 + +struct hostlist { + struct grouplist *ht_grp; + struct hostlist *ht_next; +}; + +/* Global defs */ +char *add_expdir __P((struct dirlist **, char *, int)); +void add_dlist __P((struct dirlist **, struct dirlist *, + struct grouplist *)); +void add_mlist __P((char *, char *)); +int check_dirpath __P((char *)); +int check_options __P((struct dirlist *)); +int chk_host __P((struct dirlist *, u_long, int *)); +void del_mlist __P((char *, char *)); +struct dirlist *dirp_search __P((struct dirlist *, char *)); +int do_mount __P((struct exportlist *, struct grouplist *, int, + struct ucred *, char *, int, struct statfs *)); +int do_opt __P((char **, char **, struct exportlist *, struct grouplist *, + int *, int *, struct ucred *)); +struct exportlist *ex_search __P((fsid_t *)); +struct exportlist *get_exp __P((void)); +void free_dir __P((struct dirlist *)); +void free_exp __P((struct exportlist *)); +void free_grp __P((struct grouplist *)); +void free_host __P((struct hostlist *)); +void get_exportlist __P((void)); +int get_host __P((char *, struct grouplist *)); +struct hostlist *get_ht __P((void)); +int get_line __P((void)); +void get_mountlist __P((void)); +int get_net __P((char *, struct netmsk *, int)); +void getexp_err __P((struct exportlist *, struct grouplist *)); +struct grouplist *get_grp __P((void)); +void hang_dirp __P((struct dirlist *, struct grouplist *, + struct exportlist *, int)); +void mntsrv __P((struct svc_req *, SVCXPRT *)); +void nextfield __P((char **, char **)); +void out_of_mem __P((void)); +void parsecred __P((char *, struct ucred *)); +int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *)); +int scan_tree __P((struct dirlist *, u_long)); +void send_umntall __P((void)); +int umntall_each __P((caddr_t, struct sockaddr_in *)); +int xdr_dir __P((XDR *, char *)); +int xdr_explist __P((XDR *, caddr_t)); +int xdr_fhs __P((XDR *, nfsv2fh_t *)); +int xdr_mlist __P((XDR *, caddr_t)); + +/* C library */ +int getnetgrent(); +void endnetgrent(); +void setnetgrent(); + +#ifdef ISO +struct iso_addr *iso_addr(); +#endif + +struct exportlist *exphead; +struct mountlist *mlhead; +struct grouplist *grphead; +char exname[MAXPATHLEN]; +struct ucred def_anon = { + 1, + (uid_t) -2, + 1, + { (gid_t) -2 } +}; +int root_only = 1; +int opt_flags; +/* Bits for above */ +#define OP_MAPROOT 0x01 +#define OP_MAPALL 0x02 +#define OP_KERB 0x04 +#define OP_MASK 0x08 +#define OP_NET 0x10 +#define OP_ISO 0x20 +#define OP_ALLDIRS 0x40 + +#ifdef DEBUG +int debug = 1; +void SYSLOG __P((int, const char *, ...)); +#define syslog SYSLOG +#else +int debug = 0; +#endif + +/* + * Mountd server for NFS mount protocol as described in: + * NFS: Network File System Protocol Specification, RFC1094, Appendix A + * The optional arguments are the exports file name + * default: _PATH_EXPORTS + * and "-n" to allow nonroot mount. + */ +int +main(argc, argv) + int argc; + char **argv; +{ + SVCXPRT *transp; + int c; + + while ((c = getopt(argc, argv, "n")) != EOF) + switch (c) { + case 'n': + root_only = 0; + break; + default: + fprintf(stderr, "Usage: mountd [-n] [export_file]\n"); + exit(1); + }; + argc -= optind; + argv += optind; + grphead = (struct grouplist *)NULL; + exphead = (struct exportlist *)NULL; + mlhead = (struct mountlist *)NULL; + if (argc == 1) { + strncpy(exname, *argv, MAXPATHLEN-1); + exname[MAXPATHLEN-1] = '\0'; + } else + strcpy(exname, _PATH_EXPORTS); + openlog("mountd", LOG_PID, LOG_DAEMON); + if (debug) + fprintf(stderr,"Getting export list.\n"); + get_exportlist(); + if (debug) + fprintf(stderr,"Getting mount list.\n"); + get_mountlist(); + if (debug) + fprintf(stderr,"Here we go.\n"); + if (debug == 0) { + daemon(0, 0); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + } + signal(SIGHUP, (void (*) __P((int))) get_exportlist); + signal(SIGTERM, (void (*) __P((int))) send_umntall); + { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w"); + if (pidfile != NULL) { + fprintf(pidfile, "%d\n", getpid()); + fclose(pidfile); + } + } + if ((transp = svcudp_create(RPC_ANYSOCK)) == NULL) { + syslog(LOG_ERR, "Can't create socket"); + exit(1); + } + pmap_unset(RPCPROG_MNT, RPCMNT_VER1); + if (!svc_register(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv, + IPPROTO_UDP)) { + syslog(LOG_ERR, "Can't register mount"); + exit(1); + } + svc_run(); + syslog(LOG_ERR, "Mountd died"); + exit(1); +} + +/* + * The mount rpc service + */ +void +mntsrv(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + struct exportlist *ep; + struct dirlist *dp; + nfsv2fh_t nfh; + struct authunix_parms *ucr; + struct stat stb; + struct statfs fsb; + struct hostent *hp; + u_long saddr; + char rpcpath[RPCMNT_PATHLEN+1], dirpath[MAXPATHLEN]; + int bad = ENOENT, omask, defset; + uid_t uid = -2; + + /* Get authorization */ + switch (rqstp->rq_cred.oa_flavor) { + case AUTH_UNIX: + ucr = (struct authunix_parms *)rqstp->rq_clntcred; + uid = ucr->aup_uid; + break; + case AUTH_NULL: + default: + break; + } + + saddr = transp->xp_raddr.sin_addr.s_addr; + hp = (struct hostent *)NULL; + switch (rqstp->rq_proc) { + case NULLPROC: + if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) + syslog(LOG_ERR, "Can't send reply"); + return; + case RPCMNT_MOUNT: + if ((uid != 0 && root_only) || uid == -2) { + svcerr_weakauth(transp); + return; + } + if (!svc_getargs(transp, xdr_dir, rpcpath)) { + svcerr_decode(transp); + return; + } + + /* + * Get the real pathname and make sure it is a directory + * that exists. + */ + if (realpath(rpcpath, dirpath) == 0 || + stat(dirpath, &stb) < 0 || + (stb.st_mode & S_IFMT) != S_IFDIR || + statfs(dirpath, &fsb) < 0) { + chdir("/"); /* Just in case realpath doesn't */ + if (debug) + fprintf(stderr, "stat failed on %s\n", dirpath); + if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad)) + syslog(LOG_ERR, "Can't send reply"); + return; + } + + /* Check in the exports list */ + omask = sigblock(sigmask(SIGHUP)); + ep = ex_search(&fsb.f_fsid); + defset = 0; + if (ep && (chk_host(ep->ex_defdir, saddr, &defset) || + ((dp = dirp_search(ep->ex_dirl, dirpath)) && + chk_host(dp, saddr, &defset)) || + (defset && scan_tree(ep->ex_defdir, saddr) == 0 && + scan_tree(ep->ex_dirl, saddr) == 0))) { + /* Get the file handle */ + bzero((caddr_t)&nfh, sizeof(nfh)); + if (getfh(dirpath, (fhandle_t *)&nfh) < 0) { + bad = errno; + syslog(LOG_ERR, "Can't get fh for %s", dirpath); + if (!svc_sendreply(transp, xdr_long, + (caddr_t)&bad)) + syslog(LOG_ERR, "Can't send reply"); + sigsetmask(omask); + return; + } + if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&nfh)) + syslog(LOG_ERR, "Can't send reply"); + if (hp == NULL) + hp = gethostbyaddr((caddr_t)&saddr, + sizeof(saddr), AF_INET); + if (hp) + add_mlist(hp->h_name, dirpath); + else + add_mlist(inet_ntoa(transp->xp_raddr.sin_addr), + dirpath); + if (debug) + fprintf(stderr,"Mount successfull.\n"); + } else { + bad = EACCES; + if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad)) + syslog(LOG_ERR, "Can't send reply"); + } + sigsetmask(omask); + return; + case RPCMNT_DUMP: + if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL)) + syslog(LOG_ERR, "Can't send reply"); + return; + case RPCMNT_UMOUNT: + if ((uid != 0 && root_only) || uid == -2) { + svcerr_weakauth(transp); + return; + } + if (!svc_getargs(transp, xdr_dir, dirpath)) { + svcerr_decode(transp); + return; + } + if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) + syslog(LOG_ERR, "Can't send reply"); + hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); + if (hp) + del_mlist(hp->h_name, dirpath); + del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath); + return; + case RPCMNT_UMNTALL: + if ((uid != 0 && root_only) || uid == -2) { + svcerr_weakauth(transp); + return; + } + if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) + syslog(LOG_ERR, "Can't send reply"); + hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); + if (hp) + del_mlist(hp->h_name, (char *)NULL); + del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), (char *)NULL); + return; + case RPCMNT_EXPORT: + if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL)) + syslog(LOG_ERR, "Can't send reply"); + return; + default: + svcerr_noproc(transp); + return; + } +} + +/* + * Xdr conversion for a dirpath string + */ +int +xdr_dir(xdrsp, dirp) + XDR *xdrsp; + char *dirp; +{ + return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); +} + +/* + * Xdr routine to generate fhstatus + */ +int +xdr_fhs(xdrsp, nfh) + XDR *xdrsp; + nfsv2fh_t *nfh; +{ + int ok = 0; + + if (!xdr_long(xdrsp, &ok)) + return (0); + return (xdr_opaque(xdrsp, (caddr_t)nfh, NFSX_FH)); +} + +int +xdr_mlist(xdrsp, cp) + XDR *xdrsp; + caddr_t cp; +{ + struct mountlist *mlp; + int true = 1; + int false = 0; + char *strp; + + mlp = mlhead; + while (mlp) { + if (!xdr_bool(xdrsp, &true)) + return (0); + strp = &mlp->ml_host[0]; + if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) + return (0); + strp = &mlp->ml_dirp[0]; + if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) + return (0); + mlp = mlp->ml_next; + } + if (!xdr_bool(xdrsp, &false)) + return (0); + return (1); +} + +/* + * Xdr conversion for export list + */ +int +xdr_explist(xdrsp, cp) + XDR *xdrsp; + caddr_t cp; +{ + struct exportlist *ep; + int false = 0; + int omask, putdef; + + omask = sigblock(sigmask(SIGHUP)); + ep = exphead; + while (ep) { + putdef = 0; + if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef)) + goto errout; + if (ep->ex_defdir && putdef == 0 && + put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL, + &putdef)) + goto errout; + ep = ep->ex_next; + } + sigsetmask(omask); + if (!xdr_bool(xdrsp, &false)) + return (0); + return (1); +errout: + sigsetmask(omask); + return (0); +} + +/* + * Called from xdr_explist() to traverse the tree and export the + * directory paths. + */ +int +put_exlist(dp, xdrsp, adp, putdefp) + struct dirlist *dp; + XDR *xdrsp; + struct dirlist *adp; + int *putdefp; +{ + struct grouplist *grp; + struct hostlist *hp; + int true = 1; + int false = 0; + int gotalldir = 0; + char *strp; + + if (dp) { + if (put_exlist(dp->dp_left, xdrsp, adp, putdefp)) + return (1); + if (!xdr_bool(xdrsp, &true)) + return (1); + strp = dp->dp_dirp; + if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) + return (1); + if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { + gotalldir = 1; + *putdefp = 1; + } + if ((dp->dp_flag & DP_DEFSET) == 0 && + (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { + hp = dp->dp_hosts; + while (hp) { + grp = hp->ht_grp; + if (grp->gr_type == GT_HOST) { + if (!xdr_bool(xdrsp, &true)) + return (1); + strp = grp->gr_ptr.gt_hostent->h_name; + if (!xdr_string(xdrsp, &strp, + RPCMNT_NAMELEN)) + return (1); + } else if (grp->gr_type == GT_NET) { + if (!xdr_bool(xdrsp, &true)) + return (1); + strp = grp->gr_ptr.gt_net.nt_name; + if (!xdr_string(xdrsp, &strp, + RPCMNT_NAMELEN)) + return (1); + } + hp = hp->ht_next; + if (gotalldir && hp == (struct hostlist *)NULL) { + hp = adp->dp_hosts; + gotalldir = 0; + } + } + } + if (!xdr_bool(xdrsp, &false)) + return (1); + if (put_exlist(dp->dp_right, xdrsp, adp, putdefp)) + return (1); + } + return (0); +} + +#define LINESIZ 10240 +char line[LINESIZ]; +FILE *exp_file; + +/* + * Get the export list + */ +void +get_exportlist() +{ + struct exportlist *ep, *ep2; + struct grouplist *grp, *tgrp; + struct exportlist **epp; + struct dirlist *dirhead; + struct statfs fsb, *fsp; + struct hostent *hpe; + struct ucred anon; + char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; + int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp; + + /* + * First, get rid of the old list + */ + ep = exphead; + while (ep) { + ep2 = ep; + ep = ep->ex_next; + free_exp(ep2); + } + exphead = (struct exportlist *)NULL; + + grp = grphead; + while (grp) { + tgrp = grp; + grp = grp->gr_next; + free_grp(tgrp); + } + grphead = (struct grouplist *)NULL; + + /* + * And delete exports that are in the kernel for all local + * file systems. + * XXX: Should know how to handle all local exportable file systems + * instead of just MOUNT_UFS. + */ + num = getmntinfo(&fsp, MNT_NOWAIT); + for (i = 0; i < num; i++) { + union { + struct ufs_args ua; + struct iso_args ia; + struct mfs_args ma; + } targs; + + switch (fsp->f_type) { + case MOUNT_MFS: + case MOUNT_UFS: + case MOUNT_CD9660: + targs.ua.fspec = NULL; + targs.ua.export.ex_flags = MNT_DELEXPORT; + if (mount(fsp->f_type, fsp->f_mntonname, + fsp->f_flags | MNT_UPDATE, + (caddr_t)&targs) < 0) + syslog(LOG_ERR, "Can't delete exports for %s", + fsp->f_mntonname); + } + fsp++; + } + + /* + * Read in the exports file and build the list, calling + * mount() as we go along to push the export rules into the kernel. + */ + if ((exp_file = fopen(exname, "r")) == NULL) { + syslog(LOG_ERR, "Can't open %s", exname); + exit(2); + } + dirhead = (struct dirlist *)NULL; + while (get_line()) { + if (debug) + fprintf(stderr,"Got line %s\n",line); + cp = line; + nextfield(&cp, &endcp); + if (*cp == '#') + goto nextline; + + /* + * Set defaults. + */ + has_host = FALSE; + anon = def_anon; + exflags = MNT_EXPORTED; + got_nondir = 0; + opt_flags = 0; + ep = (struct exportlist *)NULL; + + /* + * Create new exports list entry + */ + len = endcp-cp; + tgrp = grp = get_grp(); + while (len > 0) { + if (len > RPCMNT_NAMELEN) { + getexp_err(ep, tgrp); + goto nextline; + } + if (*cp == '-') { + if (ep == (struct exportlist *)NULL) { + getexp_err(ep, tgrp); + goto nextline; + } + if (debug) + fprintf(stderr, "doing opt %s\n", cp); + got_nondir = 1; + if (do_opt(&cp, &endcp, ep, grp, &has_host, + &exflags, &anon)) { + getexp_err(ep, tgrp); + goto nextline; + } + } else if (*cp == '/') { + savedc = *endcp; + *endcp = '\0'; + if (check_dirpath(cp) && + statfs(cp, &fsb) >= 0) { + if (got_nondir) { + syslog(LOG_ERR, "Dirs must be first"); + getexp_err(ep, tgrp); + goto nextline; + } + if (ep) { + if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] || + ep->ex_fs.val[1] != fsb.f_fsid.val[1]) { + getexp_err(ep, tgrp); + goto nextline; + } + } else { + /* + * See if this directory is already + * in the list. + */ + ep = ex_search(&fsb.f_fsid); + if (ep == (struct exportlist *)NULL) { + ep = get_exp(); + ep->ex_fs = fsb.f_fsid; + ep->ex_fsdir = (char *) + malloc(strlen(fsb.f_mntonname) + 1); + if (ep->ex_fsdir) + strcpy(ep->ex_fsdir, + fsb.f_mntonname); + else + out_of_mem(); + if (debug) + fprintf(stderr, + "Making new ep fs=0x%x,0x%x\n", + fsb.f_fsid.val[0], + fsb.f_fsid.val[1]); + } else if (debug) + fprintf(stderr, + "Found ep fs=0x%x,0x%x\n", + fsb.f_fsid.val[0], + fsb.f_fsid.val[1]); + } + + /* + * Add dirpath to export mount point. + */ + dirp = add_expdir(&dirhead, cp, len); + dirplen = len; + } else { + getexp_err(ep, tgrp); + goto nextline; + } + *endcp = savedc; + } else { + savedc = *endcp; + *endcp = '\0'; + got_nondir = 1; + if (ep == (struct exportlist *)NULL) { + getexp_err(ep, tgrp); + goto nextline; + } + + /* + * Get the host or netgroup. + */ + setnetgrent(cp); + netgrp = getnetgrent(&hst, &usr, &dom); + do { + if (has_host) { + grp->gr_next = get_grp(); + grp = grp->gr_next; + } + if (netgrp) { + if (get_host(hst, grp)) { + syslog(LOG_ERR, "Bad netgroup %s", cp); + getexp_err(ep, tgrp); + goto nextline; + } + } else if (get_host(cp, grp)) { + getexp_err(ep, tgrp); + goto nextline; + } + has_host = TRUE; + } while (netgrp && getnetgrent(&hst, &usr, &dom)); + endnetgrent(); + *endcp = savedc; + } + cp = endcp; + nextfield(&cp, &endcp); + len = endcp - cp; + } + if (check_options(dirhead)) { + getexp_err(ep, tgrp); + goto nextline; + } + if (!has_host) { + grp->gr_type = GT_HOST; + if (debug) + fprintf(stderr,"Adding a default entry\n"); + /* add a default group and make the grp list NULL */ + hpe = (struct hostent *)malloc(sizeof(struct hostent)); + if (hpe == (struct hostent *)NULL) + out_of_mem(); + hpe->h_name = "Default"; + hpe->h_addrtype = AF_INET; + hpe->h_length = sizeof (u_long); + hpe->h_addr_list = (char **)NULL; + grp->gr_ptr.gt_hostent = hpe; + + /* + * Don't allow a network export coincide with a list of + * host(s) on the same line. + */ + } else if ((opt_flags & OP_NET) && tgrp->gr_next) { + getexp_err(ep, tgrp); + goto nextline; + } + + /* + * Loop through hosts, pushing the exports into the kernel. + * After loop, tgrp points to the start of the list and + * grp points to the last entry in the list. + */ + grp = tgrp; + do { + if (do_mount(ep, grp, exflags, &anon, dirp, + dirplen, &fsb)) { + getexp_err(ep, tgrp); + goto nextline; + } + } while (grp->gr_next && (grp = grp->gr_next)); + + /* + * Success. Update the data structures. + */ + if (has_host) { + hang_dirp(dirhead, tgrp, ep, (opt_flags & OP_ALLDIRS)); + grp->gr_next = grphead; + grphead = tgrp; + } else { + hang_dirp(dirhead, (struct grouplist *)NULL, ep, + (opt_flags & OP_ALLDIRS)); + free_grp(grp); + } + dirhead = (struct dirlist *)NULL; + if ((ep->ex_flag & EX_LINKED) == 0) { + ep2 = exphead; + epp = &exphead; + + /* + * Insert in the list in alphabetical order. + */ + while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) { + epp = &ep2->ex_next; + ep2 = ep2->ex_next; + } + if (ep2) + ep->ex_next = ep2; + *epp = ep; + ep->ex_flag |= EX_LINKED; + } +nextline: + if (dirhead) { + free_dir(dirhead); + dirhead = (struct dirlist *)NULL; + } + } + fclose(exp_file); +} + +/* + * Allocate an export list element + */ +struct exportlist * +get_exp() +{ + struct exportlist *ep; + + ep = (struct exportlist *)malloc(sizeof (struct exportlist)); + if (ep == (struct exportlist *)NULL) + out_of_mem(); + bzero((caddr_t)ep, sizeof (struct exportlist)); + return (ep); +} + +/* + * Allocate a group list element + */ +struct grouplist * +get_grp() +{ + struct grouplist *gp; + + gp = (struct grouplist *)malloc(sizeof (struct grouplist)); + if (gp == (struct grouplist *)NULL) + out_of_mem(); + bzero((caddr_t)gp, sizeof (struct grouplist)); + return (gp); +} + +/* + * Clean up upon an error in get_exportlist(). + */ +void +getexp_err(ep, grp) + struct exportlist *ep; + struct grouplist *grp; +{ + struct grouplist *tgrp; + + syslog(LOG_ERR, "Bad exports list line %s", line); + if (ep && (ep->ex_flag & EX_LINKED) == 0) + free_exp(ep); + while (grp) { + tgrp = grp; + grp = grp->gr_next; + free_grp(tgrp); + } +} + +/* + * Search the export list for a matching fs. + */ +struct exportlist * +ex_search(fsid) + fsid_t *fsid; +{ + struct exportlist *ep; + + ep = exphead; + while (ep) { + if (ep->ex_fs.val[0] == fsid->val[0] && + ep->ex_fs.val[1] == fsid->val[1]) + return (ep); + ep = ep->ex_next; + } + return (ep); +} + +/* + * Add a directory path to the list. + */ +char * +add_expdir(dpp, cp, len) + struct dirlist **dpp; + char *cp; + int len; +{ + struct dirlist *dp; + + dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len); + dp->dp_left = *dpp; + dp->dp_right = (struct dirlist *)NULL; + dp->dp_flag = 0; + dp->dp_hosts = (struct hostlist *)NULL; + strcpy(dp->dp_dirp, cp); + *dpp = dp; + return (dp->dp_dirp); +} + +/* + * Hang the dir list element off the dirpath binary tree as required + * and update the entry for host. + */ +void +hang_dirp(dp, grp, ep, alldirs) + struct dirlist *dp; + struct grouplist *grp; + struct exportlist *ep; + int alldirs; +{ + struct hostlist *hp; + struct dirlist *dp2; + + if (alldirs) { + if (ep->ex_defdir) + free((caddr_t)dp); + else + ep->ex_defdir = dp; + if (grp == (struct grouplist *)NULL) + ep->ex_defdir->dp_flag |= DP_DEFSET; + else while (grp) { + hp = get_ht(); + hp->ht_grp = grp; + hp->ht_next = ep->ex_defdir->dp_hosts; + ep->ex_defdir->dp_hosts = hp; + grp = grp->gr_next; + } + } else { + + /* + * Loop throught the directories adding them to the tree. + */ + while (dp) { + dp2 = dp->dp_left; + add_dlist(&ep->ex_dirl, dp, grp); + dp = dp2; + } + } +} + +/* + * Traverse the binary tree either updating a node that is already there + * for the new directory or adding the new node. + */ +void +add_dlist(dpp, newdp, grp) + struct dirlist **dpp; + struct dirlist *newdp; + struct grouplist *grp; +{ + struct dirlist *dp; + struct hostlist *hp; + int cmp; + + dp = *dpp; + if (dp) { + cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); + if (cmp > 0) { + add_dlist(&dp->dp_left, newdp, grp); + return; + } else if (cmp < 0) { + add_dlist(&dp->dp_right, newdp, grp); + return; + } else + free((caddr_t)newdp); + } else { + dp = newdp; + dp->dp_left = (struct dirlist *)NULL; + *dpp = dp; + } + if (grp) { + + /* + * Hang all of the host(s) off of the directory point. + */ + do { + hp = get_ht(); + hp->ht_grp = grp; + hp->ht_next = dp->dp_hosts; + dp->dp_hosts = hp; + grp = grp->gr_next; + } while (grp); + } else + dp->dp_flag |= DP_DEFSET; +} + +/* + * Search for a dirpath on the export point. + */ +struct dirlist * +dirp_search(dp, dirpath) + struct dirlist *dp; + char *dirpath; +{ + int cmp; + + if (dp) { + cmp = strcmp(dp->dp_dirp, dirpath); + if (cmp > 0) + return (dirp_search(dp->dp_left, dirpath)); + else if (cmp < 0) + return (dirp_search(dp->dp_right, dirpath)); + else + return (dp); + } + return (dp); +} + +/* + * Scan for a host match in a directory tree. + */ +int +chk_host(dp, saddr, defsetp) + struct dirlist *dp; + u_long saddr; + int *defsetp; +{ + struct hostlist *hp; + struct grouplist *grp; + u_long **addrp; + + if (dp) { + if (dp->dp_flag & DP_DEFSET) + *defsetp = 1; + hp = dp->dp_hosts; + while (hp) { + grp = hp->ht_grp; + switch (grp->gr_type) { + case GT_HOST: + addrp = (u_long **) + grp->gr_ptr.gt_hostent->h_addr_list; + while (*addrp) { + if (**addrp == saddr) + return (1); + addrp++; + } + break; + case GT_NET: + if ((saddr & grp->gr_ptr.gt_net.nt_mask) == + grp->gr_ptr.gt_net.nt_net) + return (1); + break; + }; + hp = hp->ht_next; + } + } + return (0); +} + +/* + * Scan tree for a host that matches the address. + */ +int +scan_tree(dp, saddr) + struct dirlist *dp; + u_long saddr; +{ + int defset; + + if (dp) { + if (scan_tree(dp->dp_left, saddr)) + return (1); + if (chk_host(dp, saddr, &defset)) + return (1); + if (scan_tree(dp->dp_right, saddr)) + return (1); + } + return (0); +} + +/* + * Traverse the dirlist tree and free it up. + */ +void +free_dir(dp) + struct dirlist *dp; +{ + + if (dp) { + free_dir(dp->dp_left); + free_dir(dp->dp_right); + free_host(dp->dp_hosts); + free((caddr_t)dp); + } +} + +/* + * Parse the option string and update fields. + * Option arguments may either be -<option>=<value> or + * -<option> <value> + */ +int +do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr) + char **cpp, **endcpp; + struct exportlist *ep; + struct grouplist *grp; + int *has_hostp; + int *exflagsp; + struct ucred *cr; +{ + char *cpoptarg, *cpoptend; + char *cp, *endcp, *cpopt, savedc, savedc2; + int allflag, usedarg; + + cpopt = *cpp; + cpopt++; + cp = *endcpp; + savedc = *cp; + *cp = '\0'; + while (cpopt && *cpopt) { + allflag = 1; + usedarg = -2; + if (cpoptend = index(cpopt, ',')) { + *cpoptend++ = '\0'; + if (cpoptarg = index(cpopt, '=')) + *cpoptarg++ = '\0'; + } else { + if (cpoptarg = index(cpopt, '=')) + *cpoptarg++ = '\0'; + else { + *cp = savedc; + nextfield(&cp, &endcp); + **endcpp = '\0'; + if (endcp > cp && *cp != '-') { + cpoptarg = cp; + savedc2 = *endcp; + *endcp = '\0'; + usedarg = 0; + } + } + } + if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { + *exflagsp |= MNT_EXRDONLY; + } else if (cpoptarg && (!strcmp(cpopt, "maproot") || + !(allflag = strcmp(cpopt, "mapall")) || + !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { + usedarg++; + parsecred(cpoptarg, cr); + if (allflag == 0) { + *exflagsp |= MNT_EXPORTANON; + opt_flags |= OP_MAPALL; + } else + opt_flags |= OP_MAPROOT; + } else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) { + *exflagsp |= MNT_EXKERB; + opt_flags |= OP_KERB; + } else if (cpoptarg && (!strcmp(cpopt, "mask") || + !strcmp(cpopt, "m"))) { + if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) { + syslog(LOG_ERR, "Bad mask: %s", cpoptarg); + return (1); + } + usedarg++; + opt_flags |= OP_MASK; + } else if (cpoptarg && (!strcmp(cpopt, "network") || + !strcmp(cpopt, "n"))) { + if (grp->gr_type != GT_NULL) { + syslog(LOG_ERR, "Network/host conflict"); + return (1); + } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) { + syslog(LOG_ERR, "Bad net: %s", cpoptarg); + return (1); + } + grp->gr_type = GT_NET; + *has_hostp = 1; + usedarg++; + opt_flags |= OP_NET; + } else if (!strcmp(cpopt, "alldirs")) { + opt_flags |= OP_ALLDIRS; +#ifdef ISO + } else if (cpoptarg && !strcmp(cpopt, "iso")) { + if (get_isoaddr(cpoptarg, grp)) { + syslog(LOG_ERR, "Bad iso addr: %s", cpoptarg); + return (1); + } + *has_hostp = 1; + usedarg++; + opt_flags |= OP_ISO; +#endif /* ISO */ + } else { + syslog(LOG_ERR, "Bad opt %s", cpopt); + return (1); + } + if (usedarg >= 0) { + *endcp = savedc2; + **endcpp = savedc; + if (usedarg > 0) { + *cpp = cp; + *endcpp = endcp; + } + return (0); + } + cpopt = cpoptend; + } + **endcpp = savedc; + return (0); +} + +/* + * Translate a character string to the corresponding list of network + * addresses for a hostname. + */ +int +get_host(cp, grp) + char *cp; + struct grouplist *grp; +{ + struct hostent *hp, *nhp; + char **addrp, **naddrp; + struct hostent t_host; + int i; + u_long saddr; + char *aptr[2]; + + if (grp->gr_type != GT_NULL) + return (1); + if ((hp = gethostbyname(cp)) == NULL) { + if (isdigit(*cp)) { + saddr = inet_addr(cp); + if (saddr == -1) { + syslog(LOG_ERR, "Inet_addr failed"); + return (1); + } + if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr), + AF_INET)) == NULL) { + hp = &t_host; + hp->h_name = cp; + hp->h_addrtype = AF_INET; + hp->h_length = sizeof (u_long); + hp->h_addr_list = aptr; + aptr[0] = (char *)&saddr; + aptr[1] = (char *)NULL; + } + } else { + syslog(LOG_ERR, "Gethostbyname failed"); + return (1); + } + } + grp->gr_type = GT_HOST; + nhp = grp->gr_ptr.gt_hostent = (struct hostent *) + malloc(sizeof(struct hostent)); + if (nhp == (struct hostent *)NULL) + out_of_mem(); + bcopy((caddr_t)hp, (caddr_t)nhp, + sizeof(struct hostent)); + i = strlen(hp->h_name)+1; + nhp->h_name = (char *)malloc(i); + if (nhp->h_name == (char *)NULL) + out_of_mem(); + bcopy(hp->h_name, nhp->h_name, i); + addrp = hp->h_addr_list; + i = 1; + while (*addrp++) + i++; + naddrp = nhp->h_addr_list = (char **) + malloc(i*sizeof(char *)); + if (naddrp == (char **)NULL) + out_of_mem(); + addrp = hp->h_addr_list; + while (*addrp) { + *naddrp = (char *) + malloc(hp->h_length); + if (*naddrp == (char *)NULL) + out_of_mem(); + bcopy(*addrp, *naddrp, + hp->h_length); + addrp++; + naddrp++; + } + *naddrp = (char *)NULL; + if (debug) + fprintf(stderr, "got host %s\n", hp->h_name); + return (0); +} + +/* + * Free up an exports list component + */ +void +free_exp(ep) + struct exportlist *ep; +{ + + if (ep->ex_defdir) { + free_host(ep->ex_defdir->dp_hosts); + free((caddr_t)ep->ex_defdir); + } + if (ep->ex_fsdir) + free(ep->ex_fsdir); + free_dir(ep->ex_dirl); + free((caddr_t)ep); +} + +/* + * Free hosts. + */ +void +free_host(hp) + struct hostlist *hp; +{ + struct hostlist *hp2; + + while (hp) { + hp2 = hp; + hp = hp->ht_next; + free((caddr_t)hp2); + } +} + +struct hostlist * +get_ht() +{ + struct hostlist *hp; + + hp = (struct hostlist *)malloc(sizeof (struct hostlist)); + if (hp == (struct hostlist *)NULL) + out_of_mem(); + hp->ht_next = (struct hostlist *)NULL; + return (hp); +} + +#ifdef ISO +/* + * Translate an iso address. + */ +get_isoaddr(cp, grp) + char *cp; + struct grouplist *grp; +{ + struct iso_addr *isop; + struct sockaddr_iso *isoaddr; + + if (grp->gr_type != GT_NULL) + return (1); + if ((isop = iso_addr(cp)) == NULL) { + syslog(LOG_ERR, + "iso_addr failed, ignored"); + return (1); + } + isoaddr = (struct sockaddr_iso *) + malloc(sizeof (struct sockaddr_iso)); + if (isoaddr == (struct sockaddr_iso *)NULL) + out_of_mem(); + bzero((caddr_t)isoaddr, sizeof (struct sockaddr_iso)); + bcopy((caddr_t)isop, (caddr_t)&isoaddr->siso_addr, + sizeof (struct iso_addr)); + isoaddr->siso_len = sizeof (struct sockaddr_iso); + isoaddr->siso_family = AF_ISO; + grp->gr_type = GT_ISO; + grp->gr_ptr.gt_isoaddr = isoaddr; + return (0); +} +#endif /* ISO */ + +/* + * Out of memory, fatal + */ +void +out_of_mem() +{ + + syslog(LOG_ERR, "Out of memory"); + exit(2); +} + +/* + * Do the mount syscall with the update flag to push the export info into + * the kernel. + */ +int +do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb) + struct exportlist *ep; + struct grouplist *grp; + int exflags; + struct ucred *anoncrp; + char *dirp; + int dirplen; + struct statfs *fsb; +{ + char *cp = (char *)NULL; + u_long **addrp; + int done; + char savedc = '\0'; + struct sockaddr_in sin, imask; + union { + struct ufs_args ua; + struct iso_args ia; + struct mfs_args ma; + } args; + u_long net; + + args.ua.fspec = 0; + args.ua.export.ex_flags = exflags; + args.ua.export.ex_anon = *anoncrp; + bzero((char *)&sin, sizeof(sin)); + bzero((char *)&imask, sizeof(imask)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + imask.sin_family = AF_INET; + imask.sin_len = sizeof(sin); + if (grp->gr_type == GT_HOST) + addrp = (u_long **)grp->gr_ptr.gt_hostent->h_addr_list; + else + addrp = (u_long **)NULL; + done = FALSE; + while (!done) { + switch (grp->gr_type) { + case GT_HOST: + if (addrp) { + sin.sin_addr.s_addr = **addrp; + args.ua.export.ex_addrlen = sizeof(sin); + } else + args.ua.export.ex_addrlen = 0; + args.ua.export.ex_addr = (struct sockaddr *)&sin; + args.ua.export.ex_masklen = 0; + break; + case GT_NET: + if (grp->gr_ptr.gt_net.nt_mask) + imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask; + else { + net = ntohl(grp->gr_ptr.gt_net.nt_net); + if (IN_CLASSA(net)) + imask.sin_addr.s_addr = inet_addr("255.0.0.0"); + else if (IN_CLASSB(net)) + imask.sin_addr.s_addr = + inet_addr("255.255.0.0"); + else + imask.sin_addr.s_addr = + inet_addr("255.255.255.0"); + grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr; + } + sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net; + args.ua.export.ex_addr = (struct sockaddr *)&sin; + args.ua.export.ex_addrlen = sizeof (sin); + args.ua.export.ex_mask = (struct sockaddr *)&imask; + args.ua.export.ex_masklen = sizeof (imask); + break; +#ifdef ISO + case GT_ISO: + args.ua.export.ex_addr = + (struct sockaddr *)grp->gr_ptr.gt_isoaddr; + args.ua.export.ex_addrlen = + sizeof(struct sockaddr_iso); + args.ua.export.ex_masklen = 0; + break; +#endif /* ISO */ + default: + syslog(LOG_ERR, "Bad grouptype"); + if (cp) + *cp = savedc; + return (1); + }; + + /* + * XXX: + * Maybe I should just use the fsb->f_mntonname path instead + * of looping back up the dirp to the mount point?? + * Also, needs to know how to export all types of local + * exportable file systems and not just MOUNT_UFS. + */ + while (mount(fsb->f_type, dirp, + fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) { + if (cp) + *cp-- = savedc; + else + cp = dirp + dirplen - 1; + if (errno == EPERM) { + syslog(LOG_ERR, + "Can't change attributes for %s.\n", dirp); + return (1); + } + if (opt_flags & OP_ALLDIRS) { + syslog(LOG_ERR, "Not root dir"); + return (1); + } + /* back up over the last component */ + while (*cp == '/' && cp > dirp) + cp--; + while (*(cp - 1) != '/' && cp > dirp) + cp--; + if (cp == dirp) { + if (debug) + fprintf(stderr,"mnt unsucc\n"); + syslog(LOG_ERR, "Can't export %s", dirp); + return (1); + } + savedc = *cp; + *cp = '\0'; + } + if (addrp) { + ++addrp; + if (*addrp == (u_long *)NULL) + done = TRUE; + } else + done = TRUE; + } + if (cp) + *cp = savedc; + return (0); +} + +/* + * Translate a net address. + */ +int +get_net(cp, net, maskflg) + char *cp; + struct netmsk *net; + int maskflg; +{ + struct netent *np; + long netaddr; + struct in_addr inetaddr, inetaddr2; + char *name; + + if (np = getnetbyname(cp)) + inetaddr = inet_makeaddr(np->n_net, 0); + else if (isdigit(*cp)) { + if ((netaddr = inet_network(cp)) == -1) + return (1); + inetaddr = inet_makeaddr(netaddr, 0); + /* + * Due to arbritrary subnet masks, you don't know how many + * bits to shift the address to make it into a network, + * however you do know how to make a network address into + * a host with host == 0 and then compare them. + * (What a pest) + */ + if (!maskflg) { + setnetent(0); + while (np = getnetent()) { + inetaddr2 = inet_makeaddr(np->n_net, 0); + if (inetaddr2.s_addr == inetaddr.s_addr) + break; + } + endnetent(); + } + } else + return (1); + if (maskflg) + net->nt_mask = inetaddr.s_addr; + else { + if (np) + name = np->n_name; + else + name = inet_ntoa(inetaddr); + net->nt_name = (char *)malloc(strlen(name) + 1); + if (net->nt_name == (char *)NULL) + out_of_mem(); + strcpy(net->nt_name, name); + net->nt_net = inetaddr.s_addr; + } + return (0); +} + +/* + * Parse out the next white space separated field + */ +void +nextfield(cp, endcp) + char **cp; + char **endcp; +{ + char *p; + + p = *cp; + while (*p == ' ' || *p == '\t') + p++; + if (*p == '\n' || *p == '\0') + *cp = *endcp = p; + else { + *cp = p++; + while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') + p++; + *endcp = p; + } +} + +/* + * Get an exports file line. Skip over blank lines and handle line + * continuations. + */ +int +get_line() +{ + char *p, *cp; + int len; + int totlen, cont_line; + + /* + * Loop around ignoring blank lines and getting all continuation lines. + */ + p = line; + totlen = 0; + do { + if (fgets(p, LINESIZ - totlen, exp_file) == NULL) + return (0); + len = strlen(p); + cp = p + len - 1; + cont_line = 0; + while (cp >= p && + (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) { + if (*cp == '\\') + cont_line = 1; + cp--; + len--; + } + *++cp = '\0'; + if (len > 0) { + totlen += len; + if (totlen >= LINESIZ) { + syslog(LOG_ERR, "Exports line too long"); + exit(2); + } + p = cp; + } + } while (totlen == 0 || cont_line); + return (1); +} + +/* + * Parse a description of a credential. + */ +void +parsecred(namelist, cr) + char *namelist; + struct ucred *cr; +{ + char *name; + int cnt; + char *names; + struct passwd *pw; + struct group *gr; + int ngroups, groups[NGROUPS + 1]; + + /* + * Set up the unpriviledged user. + */ + cr->cr_ref = 1; + cr->cr_uid = -2; + cr->cr_groups[0] = -2; + cr->cr_ngroups = 1; + /* + * Get the user's password table entry. + */ + names = strsep(&namelist, " \t\n"); + name = strsep(&names, ":"); + if (isdigit(*name) || *name == '-') + pw = getpwuid(atoi(name)); + else + pw = getpwnam(name); + /* + * Credentials specified as those of a user. + */ + if (names == NULL) { + if (pw == NULL) { + syslog(LOG_ERR, "Unknown user: %s", name); + return; + } + cr->cr_uid = pw->pw_uid; + ngroups = NGROUPS + 1; + if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) + syslog(LOG_ERR, "Too many groups"); + /* + * Convert from int's to gid_t's and compress out duplicate + */ + cr->cr_ngroups = ngroups - 1; + cr->cr_groups[0] = groups[0]; + for (cnt = 2; cnt < ngroups; cnt++) + cr->cr_groups[cnt - 1] = groups[cnt]; + return; + } + /* + * Explicit credential specified as a colon separated list: + * uid:gid:gid:... + */ + if (pw != NULL) + cr->cr_uid = pw->pw_uid; + else if (isdigit(*name) || *name == '-') + cr->cr_uid = atoi(name); + else { + syslog(LOG_ERR, "Unknown user: %s", name); + return; + } + cr->cr_ngroups = 0; + while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) { + name = strsep(&names, ":"); + if (isdigit(*name) || *name == '-') { + cr->cr_groups[cr->cr_ngroups++] = atoi(name); + } else { + if ((gr = getgrnam(name)) == NULL) { + syslog(LOG_ERR, "Unknown group: %s", name); + continue; + } + cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid; + } + } + if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS) + syslog(LOG_ERR, "Too many groups"); +} + +#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50) +/* + * Routines that maintain the remote mounttab + */ +void +get_mountlist() +{ + struct mountlist *mlp, **mlpp; + char *eos, *dirp; + int len; + char str[STRSIZ]; + FILE *mlfile; + + if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { + syslog(LOG_ERR, "Can't open %s", _PATH_RMOUNTLIST); + return; + } + mlpp = &mlhead; + while (fgets(str, STRSIZ, mlfile) != NULL) { + if ((dirp = index(str, '\t')) == NULL && + (dirp = index(str, ' ')) == NULL) + continue; + mlp = (struct mountlist *)malloc(sizeof (*mlp)); + len = dirp-str; + if (len > RPCMNT_NAMELEN) + len = RPCMNT_NAMELEN; + bcopy(str, mlp->ml_host, len); + mlp->ml_host[len] = '\0'; + while (*dirp == '\t' || *dirp == ' ') + dirp++; + if ((eos = index(dirp, '\t')) == NULL && + (eos = index(dirp, ' ')) == NULL && + (eos = index(dirp, '\n')) == NULL) + len = strlen(dirp); + else + len = eos-dirp; + if (len > RPCMNT_PATHLEN) + len = RPCMNT_PATHLEN; + bcopy(dirp, mlp->ml_dirp, len); + mlp->ml_dirp[len] = '\0'; + mlp->ml_next = (struct mountlist *)NULL; + *mlpp = mlp; + mlpp = &mlp->ml_next; + } + fclose(mlfile); +} + +void +del_mlist(hostp, dirp) + char *hostp, *dirp; +{ + struct mountlist *mlp, **mlpp; + struct mountlist *mlp2; + FILE *mlfile; + int fnd = 0; + + mlpp = &mlhead; + mlp = mlhead; + while (mlp) { + if (!strcmp(mlp->ml_host, hostp) && + (!dirp || !strcmp(mlp->ml_dirp, dirp))) { + fnd = 1; + mlp2 = mlp; + *mlpp = mlp = mlp->ml_next; + free((caddr_t)mlp2); + } else { + mlpp = &mlp->ml_next; + mlp = mlp->ml_next; + } + } + if (fnd) { + if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { + syslog(LOG_ERR,"Can't update %s", _PATH_RMOUNTLIST); + return; + } + mlp = mlhead; + while (mlp) { + fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); + mlp = mlp->ml_next; + } + fclose(mlfile); + } +} + +void +add_mlist(hostp, dirp) + char *hostp, *dirp; +{ + struct mountlist *mlp, **mlpp; + FILE *mlfile; + + mlpp = &mlhead; + mlp = mlhead; + while (mlp) { + if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp)) + return; + mlpp = &mlp->ml_next; + mlp = mlp->ml_next; + } + mlp = (struct mountlist *)malloc(sizeof (*mlp)); + strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN); + mlp->ml_host[RPCMNT_NAMELEN] = '\0'; + strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN); + mlp->ml_dirp[RPCMNT_PATHLEN] = '\0'; + mlp->ml_next = (struct mountlist *)NULL; + *mlpp = mlp; + if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { + syslog(LOG_ERR, "Can't update %s", _PATH_RMOUNTLIST); + return; + } + fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); + fclose(mlfile); +} + +/* + * This function is called via. SIGTERM when the system is going down. + * It sends a broadcast RPCMNT_UMNTALL. + */ +void +send_umntall() +{ + (void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL, + xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each); + exit(0); +} + +int +umntall_each(resultsp, raddr) + caddr_t resultsp; + struct sockaddr_in *raddr; +{ + return (1); +} + +/* + * Free up a group list. + */ +void +free_grp(grp) + struct grouplist *grp; +{ + char **addrp; + + if (grp->gr_type == GT_HOST) { + if (grp->gr_ptr.gt_hostent->h_name) { + addrp = grp->gr_ptr.gt_hostent->h_addr_list; + while (addrp && *addrp) + free(*addrp++); + free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list); + free(grp->gr_ptr.gt_hostent->h_name); + } + free((caddr_t)grp->gr_ptr.gt_hostent); + } else if (grp->gr_type == GT_NET) { + if (grp->gr_ptr.gt_net.nt_name) + free(grp->gr_ptr.gt_net.nt_name); + } +#ifdef ISO + else if (grp->gr_type == GT_ISO) + free((caddr_t)grp->gr_ptr.gt_isoaddr); +#endif + free((caddr_t)grp); +} + +#ifdef DEBUG +void +SYSLOG(int pri, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} +#endif /* DEBUG */ + +/* + * Check options for consistency. + */ +int +check_options(dp) + struct dirlist *dp; +{ + + if (dp == (struct dirlist *)NULL) + return (1); + if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL) || + (opt_flags & (OP_MAPROOT | OP_KERB)) == (OP_MAPROOT | OP_KERB) || + (opt_flags & (OP_MAPALL | OP_KERB)) == (OP_MAPALL | OP_KERB)) { + syslog(LOG_ERR, "-mapall, -maproot and -kerb mutually exclusive"); + return (1); + } + if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { + syslog(LOG_ERR, "-mask requires -net"); + return (1); + } + if ((opt_flags & (OP_NET | OP_ISO)) == (OP_NET | OP_ISO)) { + syslog(LOG_ERR, "-net and -iso mutually exclusive"); + return (1); + } + if ((opt_flags & OP_ALLDIRS) && dp->dp_left) { + syslog(LOG_ERR, "-alldir has multiple directories"); + return (1); + } + return (0); +} + +/* + * Check an absolute directory path for any symbolic links. Return true + * if no symbolic links are found. + */ +int +check_dirpath(dirp) + char *dirp; +{ + char *cp; + int ret = 1; + struct stat sb; + + cp = dirp + 1; + while (*cp && ret) { + if (*cp == '/') { + *cp = '\0'; + if (lstat(dirp, &sb) < 0 || + (sb.st_mode & S_IFMT) != S_IFDIR) + ret = 0; + *cp = '/'; + } + cp++; + } + if (lstat(dirp, &sb) < 0 || + (sb.st_mode & S_IFMT) != S_IFDIR) + ret = 0; + return (ret); +} diff --git a/sbin/mountd/netgroup.5 b/sbin/mountd/netgroup.5 new file mode 100644 index 0000000..9ad8c48 --- /dev/null +++ b/sbin/mountd/netgroup.5 @@ -0,0 +1,91 @@ +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)netgroup.5 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt NETGROUP 5 +.Os +.Sh NAME +.Nm netgroup +.Nd defines network groups +.Sh SYNOPSIS +.Nm netgroup +.Sh DESCRIPTION +The +.Nm netgroup +file +specifies ``netgroups'', which are sets of +.Sy (host, user, domain) +tuples that are to be given similar network access. +.Pp +Each line in the file +consists of a netgroup name followed by a list of the members of the +netgroup. +Each member can be either the name of another netgroup or a specification +of a tuple as follows: +.Bd -literal -offset indent +(host, user, domain) +.Ed +where the +.Sy host , +.Sy user , +and +.Sy domain +are character string names for the corresponding component. +Any of the comma separated fields may be empty to specify a ``wildcard'' value +or may consist of the string ``-'' to specify ``no valid value''. +The members of the list may be separated by whitespace and/or commas; +the ``\e'' character may be used at the end of a line to specify +line continuation. +The functions specified in +.Xr getnetgrent 3 +should normally be used to access the +.Nm netgroup +database. +.Pp +Lines that begin with a # are treated as comments. +.Sh FILES +.Bl -tag -width /etc/netgroup -compact +.It Pa /etc/netgroup +the netgroup database. +.El +.Sh SEE ALSO +.Xr getnetgrent 3 , +.Xr exports 5 +.Sh COMPATIBILITY +The file format is compatible with that of various vendors, however it +appears that not all vendors use an identical format. +.Sh BUGS +The interpretation of access restrictions based on the member tuples of a +netgroup is left up to the various network applications. +Also, it is not obvious how the domain specification +applies to the BSD environment. diff --git a/sbin/mountd/pathnames.h b/sbin/mountd/pathnames.h new file mode 100644 index 0000000..aa1c555 --- /dev/null +++ b/sbin/mountd/pathnames.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + */ +#include <paths.h> + +#define _PATH_EXPORTS "/etc/exports" +#define _PATH_RMOUNTLIST "/var/db/mountdtab" +#define _PATH_MOUNTDPID "/var/run/mountd.pid" diff --git a/sbin/newfs/Makefile b/sbin/newfs/Makefile new file mode 100644 index 0000000..2394272 --- /dev/null +++ b/sbin/newfs/Makefile @@ -0,0 +1,14 @@ +# @(#)Makefile 8.2 (Berkeley) 3/27/94 + +PROG= newfs +SRCS= dkcksum.c getmntopts.c newfs.c mkfs.c +MAN8= newfs.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+=-DMFS -I${MOUNT} +.PATH: ${MOUNT} ${.CURDIR}/../disklabel + +LINKS= ${BINDIR}/newfs ${BINDIR}/mount_mfs +MLINKS= newfs.8 mount_mfs.8 newfs.8 mfs.8 + +.include <bsd.prog.mk> diff --git a/sbin/newfs/mkfs.c b/sbin/newfs/mkfs.c new file mode 100644 index 0000000..80cb734 --- /dev/null +++ b/sbin/newfs/mkfs.c @@ -0,0 +1,1227 @@ +/* + * Copyright (c) 1980, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)mkfs.c 8.3 (Berkeley) 2/3/94"; +#endif /* not lint */ + +#include <unistd.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <sys/resource.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <sys/disklabel.h> + +#ifndef STANDALONE +#include <a.out.h> +#include <stdio.h> +#endif + +/* + * make file system for cylinder-group style file systems + */ + +/* + * We limit the size of the inode map to be no more than a + * third of the cylinder group space, since we must leave at + * least an equal amount of space for the block map. + * + * N.B.: MAXIPG must be a multiple of INOPB(fs). + */ +#define MAXIPG(fs) roundup((fs)->fs_bsize * NBBY / 3, INOPB(fs)) + +#define UMASK 0755 +#define MAXINOPB (MAXBSIZE / sizeof(struct dinode)) +#define POWEROF2(num) (((num) & ((num) - 1)) == 0) + +/* + * variables set up by front end. + */ +extern int mfs; /* run as the memory based filesystem */ +extern int Nflag; /* run mkfs without writing file system */ +extern int Oflag; /* format as an 4.3BSD file system */ +extern int fssize; /* file system size */ +extern int ntracks; /* # tracks/cylinder */ +extern int nsectors; /* # sectors/track */ +extern int nphyssectors; /* # sectors/track including spares */ +extern int secpercyl; /* sectors per cylinder */ +extern int sectorsize; /* bytes/sector */ +extern int rpm; /* revolutions/minute of drive */ +extern int interleave; /* hardware sector interleave */ +extern int trackskew; /* sector 0 skew, per track */ +extern int headswitch; /* head switch time, usec */ +extern int trackseek; /* track-to-track seek, usec */ +extern int fsize; /* fragment size */ +extern int bsize; /* block size */ +extern int cpg; /* cylinders/cylinder group */ +extern int cpgflg; /* cylinders/cylinder group flag was given */ +extern int minfree; /* free space threshold */ +extern int opt; /* optimization preference (space or time) */ +extern int density; /* number of bytes per inode */ +extern int maxcontig; /* max contiguous blocks to allocate */ +extern int rotdelay; /* rotational delay between blocks */ +extern int maxbpg; /* maximum blocks per file in a cyl group */ +extern int nrpos; /* # of distinguished rotational positions */ +extern int bbsize; /* boot block size */ +extern int sbsize; /* superblock size */ +extern u_long memleft; /* virtual memory available */ +extern caddr_t membase; /* start address of memory based filesystem */ +extern caddr_t malloc(), calloc(); + +union { + struct fs fs; + char pad[SBSIZE]; +} fsun; +#define sblock fsun.fs +struct csum *fscs; + +union { + struct cg cg; + char pad[MAXBSIZE]; +} cgun; +#define acg cgun.cg + +struct dinode zino[MAXBSIZE / sizeof(struct dinode)]; + +int fsi, fso; +daddr_t alloc(); + +mkfs(pp, fsys, fi, fo) + struct partition *pp; + char *fsys; + int fi, fo; +{ + register long i, mincpc, mincpg, inospercg; + long cylno, rpos, blk, j, warn = 0; + long used, mincpgcnt, bpcg; + long mapcramped, inodecramped; + long postblsize, rotblsize, totalsbsize; + int ppid, status; + time_t utime; + quad_t sizepb; + void started(); + +#ifndef STANDALONE + time(&utime); +#endif + if (mfs) { + ppid = getpid(); + (void) signal(SIGUSR1, started); + if (i = fork()) { + if (i == -1) { + perror("mfs"); + exit(10); + } + if (waitpid(i, &status, 0) != -1 && WIFEXITED(status)) + exit(WEXITSTATUS(status)); + exit(11); + /* NOTREACHED */ + } + (void)malloc(0); + if (fssize * sectorsize > memleft) + fssize = (memleft - 16384) / sectorsize; + if ((membase = malloc(fssize * sectorsize)) == 0) + exit(12); + } + fsi = fi; + fso = fo; + if (Oflag) { + sblock.fs_inodefmt = FS_42INODEFMT; + sblock.fs_maxsymlinklen = 0; + } else { + sblock.fs_inodefmt = FS_44INODEFMT; + sblock.fs_maxsymlinklen = MAXSYMLINKLEN; + } + /* + * Validate the given file system size. + * Verify that its last block can actually be accessed. + */ + if (fssize <= 0) + printf("preposterous size %d\n", fssize), exit(13); + wtfs(fssize - 1, sectorsize, (char *)&sblock); + /* + * collect and verify the sector and track info + */ + sblock.fs_nsect = nsectors; + sblock.fs_ntrak = ntracks; + if (sblock.fs_ntrak <= 0) + printf("preposterous ntrak %d\n", sblock.fs_ntrak), exit(14); + if (sblock.fs_nsect <= 0) + printf("preposterous nsect %d\n", sblock.fs_nsect), exit(15); + /* + * collect and verify the block and fragment sizes + */ + sblock.fs_bsize = bsize; + sblock.fs_fsize = fsize; + if (!POWEROF2(sblock.fs_bsize)) { + printf("block size must be a power of 2, not %d\n", + sblock.fs_bsize); + exit(16); + } + if (!POWEROF2(sblock.fs_fsize)) { + printf("fragment size must be a power of 2, not %d\n", + sblock.fs_fsize); + exit(17); + } + if (sblock.fs_fsize < sectorsize) { + printf("fragment size %d is too small, minimum is %d\n", + sblock.fs_fsize, sectorsize); + exit(18); + } + if (sblock.fs_bsize < MINBSIZE) { + printf("block size %d is too small, minimum is %d\n", + sblock.fs_bsize, MINBSIZE); + exit(19); + } + if (sblock.fs_bsize < sblock.fs_fsize) { + printf("block size (%d) cannot be smaller than fragment size (%d)\n", + sblock.fs_bsize, sblock.fs_fsize); + exit(20); + } + sblock.fs_bmask = ~(sblock.fs_bsize - 1); + sblock.fs_fmask = ~(sblock.fs_fsize - 1); + sblock.fs_qbmask = ~sblock.fs_bmask; + sblock.fs_qfmask = ~sblock.fs_fmask; + for (sblock.fs_bshift = 0, i = sblock.fs_bsize; i > 1; i >>= 1) + sblock.fs_bshift++; + for (sblock.fs_fshift = 0, i = sblock.fs_fsize; i > 1; i >>= 1) + sblock.fs_fshift++; + sblock.fs_frag = numfrags(&sblock, sblock.fs_bsize); + for (sblock.fs_fragshift = 0, i = sblock.fs_frag; i > 1; i >>= 1) + sblock.fs_fragshift++; + if (sblock.fs_frag > MAXFRAG) { + printf("fragment size %d is too small, minimum with block size %d is %d\n", + sblock.fs_fsize, sblock.fs_bsize, + sblock.fs_bsize / MAXFRAG); + exit(21); + } + sblock.fs_nrpos = nrpos; + sblock.fs_nindir = sblock.fs_bsize / sizeof(daddr_t); + sblock.fs_inopb = sblock.fs_bsize / sizeof(struct dinode); + sblock.fs_nspf = sblock.fs_fsize / sectorsize; + for (sblock.fs_fsbtodb = 0, i = NSPF(&sblock); i > 1; i >>= 1) + sblock.fs_fsbtodb++; + sblock.fs_sblkno = + roundup(howmany(bbsize + sbsize, sblock.fs_fsize), sblock.fs_frag); + sblock.fs_cblkno = (daddr_t)(sblock.fs_sblkno + + roundup(howmany(sbsize, sblock.fs_fsize), sblock.fs_frag)); + sblock.fs_iblkno = sblock.fs_cblkno + sblock.fs_frag; + sblock.fs_cgoffset = roundup( + howmany(sblock.fs_nsect, NSPF(&sblock)), sblock.fs_frag); + for (sblock.fs_cgmask = 0xffffffff, i = sblock.fs_ntrak; i > 1; i >>= 1) + sblock.fs_cgmask <<= 1; + if (!POWEROF2(sblock.fs_ntrak)) + sblock.fs_cgmask <<= 1; + sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1; + for (sizepb = sblock.fs_bsize, i = 0; i < NIADDR; i++) { + sizepb *= NINDIR(&sblock); + sblock.fs_maxfilesize += sizepb; + } + /* + * Validate specified/determined secpercyl + * and calculate minimum cylinders per group. + */ + sblock.fs_spc = secpercyl; + for (sblock.fs_cpc = NSPB(&sblock), i = sblock.fs_spc; + sblock.fs_cpc > 1 && (i & 1) == 0; + sblock.fs_cpc >>= 1, i >>= 1) + /* void */; + mincpc = sblock.fs_cpc; + bpcg = sblock.fs_spc * sectorsize; + inospercg = roundup(bpcg / sizeof(struct dinode), INOPB(&sblock)); + if (inospercg > MAXIPG(&sblock)) + inospercg = MAXIPG(&sblock); + used = (sblock.fs_iblkno + inospercg / INOPF(&sblock)) * NSPF(&sblock); + mincpgcnt = howmany(sblock.fs_cgoffset * (~sblock.fs_cgmask) + used, + sblock.fs_spc); + mincpg = roundup(mincpgcnt, mincpc); + /* + * Ensure that cylinder group with mincpg has enough space + * for block maps. + */ + sblock.fs_cpg = mincpg; + sblock.fs_ipg = inospercg; + if (maxcontig > 1) + sblock.fs_contigsumsize = MIN(maxcontig, FS_MAXCONTIG); + mapcramped = 0; + while (CGSIZE(&sblock) > sblock.fs_bsize) { + mapcramped = 1; + if (sblock.fs_bsize < MAXBSIZE) { + sblock.fs_bsize <<= 1; + if ((i & 1) == 0) { + i >>= 1; + } else { + sblock.fs_cpc <<= 1; + mincpc <<= 1; + mincpg = roundup(mincpgcnt, mincpc); + sblock.fs_cpg = mincpg; + } + sblock.fs_frag <<= 1; + sblock.fs_fragshift += 1; + if (sblock.fs_frag <= MAXFRAG) + continue; + } + if (sblock.fs_fsize == sblock.fs_bsize) { + printf("There is no block size that"); + printf(" can support this disk\n"); + exit(22); + } + sblock.fs_frag >>= 1; + sblock.fs_fragshift -= 1; + sblock.fs_fsize <<= 1; + sblock.fs_nspf <<= 1; + } + /* + * Ensure that cylinder group with mincpg has enough space for inodes. + */ + inodecramped = 0; + used *= sectorsize; + inospercg = roundup((mincpg * bpcg - used) / density, INOPB(&sblock)); + sblock.fs_ipg = inospercg; + while (inospercg > MAXIPG(&sblock)) { + inodecramped = 1; + if (mincpc == 1 || sblock.fs_frag == 1 || + sblock.fs_bsize == MINBSIZE) + break; + printf("With a block size of %d %s %d\n", sblock.fs_bsize, + "minimum bytes per inode is", + (mincpg * bpcg - used) / MAXIPG(&sblock) + 1); + sblock.fs_bsize >>= 1; + sblock.fs_frag >>= 1; + sblock.fs_fragshift -= 1; + mincpc >>= 1; + sblock.fs_cpg = roundup(mincpgcnt, mincpc); + if (CGSIZE(&sblock) > sblock.fs_bsize) { + sblock.fs_bsize <<= 1; + break; + } + mincpg = sblock.fs_cpg; + inospercg = + roundup((mincpg * bpcg - used) / density, INOPB(&sblock)); + sblock.fs_ipg = inospercg; + } + if (inodecramped) { + if (inospercg > MAXIPG(&sblock)) { + printf("Minimum bytes per inode is %d\n", + (mincpg * bpcg - used) / MAXIPG(&sblock) + 1); + } else if (!mapcramped) { + printf("With %d bytes per inode, ", density); + printf("minimum cylinders per group is %d\n", mincpg); + } + } + if (mapcramped) { + printf("With %d sectors per cylinder, ", sblock.fs_spc); + printf("minimum cylinders per group is %d\n", mincpg); + } + if (inodecramped || mapcramped) { + if (sblock.fs_bsize != bsize) + printf("%s to be changed from %d to %d\n", + "This requires the block size", + bsize, sblock.fs_bsize); + if (sblock.fs_fsize != fsize) + printf("\t%s to be changed from %d to %d\n", + "and the fragment size", + fsize, sblock.fs_fsize); + exit(23); + } + /* + * Calculate the number of cylinders per group + */ + sblock.fs_cpg = cpg; + if (sblock.fs_cpg % mincpc != 0) { + printf("%s groups must have a multiple of %d cylinders\n", + cpgflg ? "Cylinder" : "Warning: cylinder", mincpc); + sblock.fs_cpg = roundup(sblock.fs_cpg, mincpc); + if (!cpgflg) + cpg = sblock.fs_cpg; + } + /* + * Must ensure there is enough space for inodes. + */ + sblock.fs_ipg = roundup((sblock.fs_cpg * bpcg - used) / density, + INOPB(&sblock)); + while (sblock.fs_ipg > MAXIPG(&sblock)) { + inodecramped = 1; + sblock.fs_cpg -= mincpc; + sblock.fs_ipg = roundup((sblock.fs_cpg * bpcg - used) / density, + INOPB(&sblock)); + } + /* + * Must ensure there is enough space to hold block map. + */ + while (CGSIZE(&sblock) > sblock.fs_bsize) { + mapcramped = 1; + sblock.fs_cpg -= mincpc; + sblock.fs_ipg = roundup((sblock.fs_cpg * bpcg - used) / density, + INOPB(&sblock)); + } + sblock.fs_fpg = (sblock.fs_cpg * sblock.fs_spc) / NSPF(&sblock); + if ((sblock.fs_cpg * sblock.fs_spc) % NSPB(&sblock) != 0) { + printf("panic (fs_cpg * fs_spc) % NSPF != 0"); + exit(24); + } + if (sblock.fs_cpg < mincpg) { + printf("cylinder groups must have at least %d cylinders\n", + mincpg); + exit(25); + } else if (sblock.fs_cpg != cpg) { + if (!cpgflg) + printf("Warning: "); + else if (!mapcramped && !inodecramped) + exit(26); + if (mapcramped && inodecramped) + printf("Block size and bytes per inode restrict"); + else if (mapcramped) + printf("Block size restricts"); + else + printf("Bytes per inode restrict"); + printf(" cylinders per group to %d.\n", sblock.fs_cpg); + if (cpgflg) + exit(27); + } + sblock.fs_cgsize = fragroundup(&sblock, CGSIZE(&sblock)); + /* + * Now have size for file system and nsect and ntrak. + * Determine number of cylinders and blocks in the file system. + */ + sblock.fs_size = fssize = dbtofsb(&sblock, fssize); + sblock.fs_ncyl = fssize * NSPF(&sblock) / sblock.fs_spc; + if (fssize * NSPF(&sblock) > sblock.fs_ncyl * sblock.fs_spc) { + sblock.fs_ncyl++; + warn = 1; + } + if (sblock.fs_ncyl < 1) { + printf("file systems must have at least one cylinder\n"); + exit(28); + } + /* + * Determine feasability/values of rotational layout tables. + * + * The size of the rotational layout tables is limited by the + * size of the superblock, SBSIZE. The amount of space available + * for tables is calculated as (SBSIZE - sizeof (struct fs)). + * The size of these tables is inversely proportional to the block + * size of the file system. The size increases if sectors per track + * are not powers of two, because more cylinders must be described + * by the tables before the rotational pattern repeats (fs_cpc). + */ + sblock.fs_interleave = interleave; + sblock.fs_trackskew = trackskew; + sblock.fs_npsect = nphyssectors; + sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT; + sblock.fs_sbsize = fragroundup(&sblock, sizeof(struct fs)); + if (sblock.fs_ntrak == 1) { + sblock.fs_cpc = 0; + goto next; + } + postblsize = sblock.fs_nrpos * sblock.fs_cpc * sizeof(short); + rotblsize = sblock.fs_cpc * sblock.fs_spc / NSPB(&sblock); + totalsbsize = sizeof(struct fs) + rotblsize; + if (sblock.fs_nrpos == 8 && sblock.fs_cpc <= 16) { + /* use old static table space */ + sblock.fs_postbloff = (char *)(&sblock.fs_opostbl[0][0]) - + (char *)(&sblock.fs_link); + sblock.fs_rotbloff = &sblock.fs_space[0] - + (u_char *)(&sblock.fs_link); + } else { + /* use dynamic table space */ + sblock.fs_postbloff = &sblock.fs_space[0] - + (u_char *)(&sblock.fs_link); + sblock.fs_rotbloff = sblock.fs_postbloff + postblsize; + totalsbsize += postblsize; + } + if (totalsbsize > SBSIZE || + sblock.fs_nsect > (1 << NBBY) * NSPB(&sblock)) { + printf("%s %s %d %s %d.%s", + "Warning: insufficient space in super block for\n", + "rotational layout tables with nsect", sblock.fs_nsect, + "and ntrak", sblock.fs_ntrak, + "\nFile system performance may be impaired.\n"); + sblock.fs_cpc = 0; + goto next; + } + sblock.fs_sbsize = fragroundup(&sblock, totalsbsize); + /* + * calculate the available blocks for each rotational position + */ + for (cylno = 0; cylno < sblock.fs_cpc; cylno++) + for (rpos = 0; rpos < sblock.fs_nrpos; rpos++) + fs_postbl(&sblock, cylno)[rpos] = -1; + for (i = (rotblsize - 1) * sblock.fs_frag; + i >= 0; i -= sblock.fs_frag) { + cylno = cbtocylno(&sblock, i); + rpos = cbtorpos(&sblock, i); + blk = fragstoblks(&sblock, i); + if (fs_postbl(&sblock, cylno)[rpos] == -1) + fs_rotbl(&sblock)[blk] = 0; + else + fs_rotbl(&sblock)[blk] = + fs_postbl(&sblock, cylno)[rpos] - blk; + fs_postbl(&sblock, cylno)[rpos] = blk; + } +next: + /* + * Compute/validate number of cylinder groups. + */ + sblock.fs_ncg = sblock.fs_ncyl / sblock.fs_cpg; + if (sblock.fs_ncyl % sblock.fs_cpg) + sblock.fs_ncg++; + sblock.fs_dblkno = sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock); + i = MIN(~sblock.fs_cgmask, sblock.fs_ncg - 1); + if (cgdmin(&sblock, i) - cgbase(&sblock, i) >= sblock.fs_fpg) { + printf("inode blocks/cyl group (%d) >= data blocks (%d)\n", + cgdmin(&sblock, i) - cgbase(&sblock, i) / sblock.fs_frag, + sblock.fs_fpg / sblock.fs_frag); + printf("number of cylinders per cylinder group (%d) %s.\n", + sblock.fs_cpg, "must be increased"); + exit(29); + } + j = sblock.fs_ncg - 1; + if ((i = fssize - j * sblock.fs_fpg) < sblock.fs_fpg && + cgdmin(&sblock, j) - cgbase(&sblock, j) > i) { + if (j == 0) { + printf("Filesystem must have at least %d sectors\n", + NSPF(&sblock) * + (cgdmin(&sblock, 0) + 3 * sblock.fs_frag)); + exit(30); + } + printf("Warning: inode blocks/cyl group (%d) >= data blocks (%d) in last\n", + (cgdmin(&sblock, j) - cgbase(&sblock, j)) / sblock.fs_frag, + i / sblock.fs_frag); + printf(" cylinder group. This implies %d sector(s) cannot be allocated.\n", + i * NSPF(&sblock)); + sblock.fs_ncg--; + sblock.fs_ncyl -= sblock.fs_ncyl % sblock.fs_cpg; + sblock.fs_size = fssize = sblock.fs_ncyl * sblock.fs_spc / + NSPF(&sblock); + warn = 0; + } + if (warn && !mfs) { + printf("Warning: %d sector(s) in last cylinder unallocated\n", + sblock.fs_spc - + (fssize * NSPF(&sblock) - (sblock.fs_ncyl - 1) + * sblock.fs_spc)); + } + /* + * fill in remaining fields of the super block + */ + sblock.fs_csaddr = cgdmin(&sblock, 0); + sblock.fs_cssize = + fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum)); + i = sblock.fs_bsize / sizeof(struct csum); + sblock.fs_csmask = ~(i - 1); + for (sblock.fs_csshift = 0; i > 1; i >>= 1) + sblock.fs_csshift++; + fscs = (struct csum *)calloc(1, sblock.fs_cssize); + sblock.fs_magic = FS_MAGIC; + sblock.fs_rotdelay = rotdelay; + sblock.fs_minfree = minfree; + sblock.fs_maxcontig = maxcontig; + sblock.fs_headswitch = headswitch; + sblock.fs_trkseek = trackseek; + sblock.fs_maxbpg = maxbpg; + sblock.fs_rps = rpm / 60; + sblock.fs_optim = opt; + sblock.fs_cgrotor = 0; + sblock.fs_cstotal.cs_ndir = 0; + sblock.fs_cstotal.cs_nbfree = 0; + sblock.fs_cstotal.cs_nifree = 0; + sblock.fs_cstotal.cs_nffree = 0; + sblock.fs_fmod = 0; + sblock.fs_ronly = 0; + /* + * Dump out summary information about file system. + */ + if (!mfs) { + printf("%s:\t%d sectors in %d %s of %d tracks, %d sectors\n", + fsys, sblock.fs_size * NSPF(&sblock), sblock.fs_ncyl, + "cylinders", sblock.fs_ntrak, sblock.fs_nsect); +#define B2MBFACTOR (1 / (1024.0 * 1024.0)) + printf("\t%.1fMB in %d cyl groups (%d c/g, %.2fMB/g, %d i/g)\n", + (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR, + sblock.fs_ncg, sblock.fs_cpg, + (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR, + sblock.fs_ipg); +#undef B2MBFACTOR + } + /* + * Now build the cylinders group blocks and + * then print out indices of cylinder groups. + */ + if (!mfs) + printf("super-block backups (for fsck -b #) at:"); + for (cylno = 0; cylno < sblock.fs_ncg; cylno++) { + initcg(cylno, utime); + if (mfs) + continue; + if (cylno % 9 == 0) + printf("\n"); + printf(" %d,", fsbtodb(&sblock, cgsblock(&sblock, cylno))); + } + if (!mfs) + printf("\n"); + if (Nflag && !mfs) + exit(0); + /* + * Now construct the initial file system, + * then write out the super-block. + */ + fsinit(utime); + sblock.fs_time = utime; + wtfs((int)SBOFF / sectorsize, sbsize, (char *)&sblock); + for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) + wtfs(fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)), + sblock.fs_cssize - i < sblock.fs_bsize ? + sblock.fs_cssize - i : sblock.fs_bsize, + ((char *)fscs) + i); + /* + * Write out the duplicate super blocks + */ + for (cylno = 0; cylno < sblock.fs_ncg; cylno++) + wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)), + sbsize, (char *)&sblock); + /* + * Update information about this partion in pack + * label, to that it may be updated on disk. + */ + pp->p_fstype = FS_BSDFFS; + pp->p_fsize = sblock.fs_fsize; + pp->p_frag = sblock.fs_frag; + pp->p_cpg = sblock.fs_cpg; + /* + * Notify parent process of success. + * Dissociate from session and tty. + */ + if (mfs) { + kill(ppid, SIGUSR1); + (void) setsid(); + (void) close(0); + (void) close(1); + (void) close(2); + (void) chdir("/"); + } +} + +/* + * Initialize a cylinder group. + */ +initcg(cylno, utime) + int cylno; + time_t utime; +{ + daddr_t cbase, d, dlower, dupper, dmax, blkno; + long i, j, s; + register struct csum *cs; + + /* + * Determine block bounds for cylinder group. + * Allow space for super block summary information in first + * cylinder group. + */ + cbase = cgbase(&sblock, cylno); + dmax = cbase + sblock.fs_fpg; + if (dmax > sblock.fs_size) + dmax = sblock.fs_size; + dlower = cgsblock(&sblock, cylno) - cbase; + dupper = cgdmin(&sblock, cylno) - cbase; + if (cylno == 0) + dupper += howmany(sblock.fs_cssize, sblock.fs_fsize); + cs = fscs + cylno; + bzero(&acg, sblock.fs_cgsize); + acg.cg_time = utime; + acg.cg_magic = CG_MAGIC; + acg.cg_cgx = cylno; + if (cylno == sblock.fs_ncg - 1) + acg.cg_ncyl = sblock.fs_ncyl % sblock.fs_cpg; + else + acg.cg_ncyl = sblock.fs_cpg; + acg.cg_niblk = sblock.fs_ipg; + acg.cg_ndblk = dmax - cbase; + if (sblock.fs_contigsumsize > 0) + acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag; + acg.cg_btotoff = &acg.cg_space[0] - (u_char *)(&acg.cg_link); + acg.cg_boff = acg.cg_btotoff + sblock.fs_cpg * sizeof(long); + acg.cg_iusedoff = acg.cg_boff + + sblock.fs_cpg * sblock.fs_nrpos * sizeof(short); + acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, NBBY); + if (sblock.fs_contigsumsize <= 0) { + acg.cg_nextfreeoff = acg.cg_freeoff + + howmany(sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock), NBBY); + } else { + acg.cg_clustersumoff = acg.cg_freeoff + howmany + (sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock), NBBY) - + sizeof(long); + acg.cg_clustersumoff = + roundup(acg.cg_clustersumoff, sizeof(long)); + acg.cg_clusteroff = acg.cg_clustersumoff + + (sblock.fs_contigsumsize + 1) * sizeof(long); + acg.cg_nextfreeoff = acg.cg_clusteroff + howmany + (sblock.fs_cpg * sblock.fs_spc / NSPB(&sblock), NBBY); + } + if (acg.cg_nextfreeoff - (long)(&acg.cg_link) > sblock.fs_cgsize) { + printf("Panic: cylinder group too big\n"); + exit(37); + } + acg.cg_cs.cs_nifree += sblock.fs_ipg; + if (cylno == 0) + for (i = 0; i < ROOTINO; i++) { + setbit(cg_inosused(&acg), i); + acg.cg_cs.cs_nifree--; + } + for (i = 0; i < sblock.fs_ipg / INOPF(&sblock); i += sblock.fs_frag) + wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i), + sblock.fs_bsize, (char *)zino); + if (cylno > 0) { + /* + * In cylno 0, beginning space is reserved + * for boot and super blocks. + */ + for (d = 0; d < dlower; d += sblock.fs_frag) { + blkno = d / sblock.fs_frag; + setblock(&sblock, cg_blksfree(&acg), blkno); + if (sblock.fs_contigsumsize > 0) + setbit(cg_clustersfree(&acg), blkno); + acg.cg_cs.cs_nbfree++; + cg_blktot(&acg)[cbtocylno(&sblock, d)]++; + cg_blks(&sblock, &acg, cbtocylno(&sblock, d)) + [cbtorpos(&sblock, d)]++; + } + sblock.fs_dsize += dlower; + } + sblock.fs_dsize += acg.cg_ndblk - dupper; + if (i = dupper % sblock.fs_frag) { + acg.cg_frsum[sblock.fs_frag - i]++; + for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) { + setbit(cg_blksfree(&acg), dupper); + acg.cg_cs.cs_nffree++; + } + } + for (d = dupper; d + sblock.fs_frag <= dmax - cbase; ) { + blkno = d / sblock.fs_frag; + setblock(&sblock, cg_blksfree(&acg), blkno); + if (sblock.fs_contigsumsize > 0) + setbit(cg_clustersfree(&acg), blkno); + acg.cg_cs.cs_nbfree++; + cg_blktot(&acg)[cbtocylno(&sblock, d)]++; + cg_blks(&sblock, &acg, cbtocylno(&sblock, d)) + [cbtorpos(&sblock, d)]++; + d += sblock.fs_frag; + } + if (d < dmax - cbase) { + acg.cg_frsum[dmax - cbase - d]++; + for (; d < dmax - cbase; d++) { + setbit(cg_blksfree(&acg), d); + acg.cg_cs.cs_nffree++; + } + } + if (sblock.fs_contigsumsize > 0) { + long *sump = cg_clustersum(&acg); + u_char *mapp = cg_clustersfree(&acg); + int map = *mapp++; + int bit = 1; + int run = 0; + + for (i = 0; i < acg.cg_nclusterblks; i++) { + if ((map & bit) != 0) { + run++; + } else if (run != 0) { + if (run > sblock.fs_contigsumsize) + run = sblock.fs_contigsumsize; + sump[run]++; + run = 0; + } + if ((i & (NBBY - 1)) != (NBBY - 1)) { + bit <<= 1; + } else { + map = *mapp++; + bit = 1; + } + } + if (run != 0) { + if (run > sblock.fs_contigsumsize) + run = sblock.fs_contigsumsize; + sump[run]++; + } + } + sblock.fs_cstotal.cs_ndir += acg.cg_cs.cs_ndir; + sblock.fs_cstotal.cs_nffree += acg.cg_cs.cs_nffree; + sblock.fs_cstotal.cs_nbfree += acg.cg_cs.cs_nbfree; + sblock.fs_cstotal.cs_nifree += acg.cg_cs.cs_nifree; + *cs = acg.cg_cs; + wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), + sblock.fs_bsize, (char *)&acg); +} + +/* + * initialize the file system + */ +struct dinode node; + +#ifdef LOSTDIR +#define PREDEFDIR 3 +#else +#define PREDEFDIR 2 +#endif + +struct direct root_dir[] = { + { ROOTINO, sizeof(struct direct), DT_DIR, 1, "." }, + { ROOTINO, sizeof(struct direct), DT_DIR, 2, ".." }, +#ifdef LOSTDIR + { LOSTFOUNDINO, sizeof(struct direct), DT_DIR, 10, "lost+found" }, +#endif +}; +struct odirect { + u_long d_ino; + u_short d_reclen; + u_short d_namlen; + u_char d_name[MAXNAMLEN + 1]; +} oroot_dir[] = { + { ROOTINO, sizeof(struct direct), 1, "." }, + { ROOTINO, sizeof(struct direct), 2, ".." }, +#ifdef LOSTDIR + { LOSTFOUNDINO, sizeof(struct direct), 10, "lost+found" }, +#endif +}; +#ifdef LOSTDIR +struct direct lost_found_dir[] = { + { LOSTFOUNDINO, sizeof(struct direct), DT_DIR, 1, "." }, + { ROOTINO, sizeof(struct direct), DT_DIR, 2, ".." }, + { 0, DIRBLKSIZ, 0, 0, 0 }, +}; +struct odirect olost_found_dir[] = { + { LOSTFOUNDINO, sizeof(struct direct), 1, "." }, + { ROOTINO, sizeof(struct direct), 2, ".." }, + { 0, DIRBLKSIZ, 0, 0 }, +}; +#endif +char buf[MAXBSIZE]; + +fsinit(utime) + time_t utime; +{ + int i; + + /* + * initialize the node + */ + node.di_atime.ts_sec = utime; + node.di_mtime.ts_sec = utime; + node.di_ctime.ts_sec = utime; +#ifdef LOSTDIR + /* + * create the lost+found directory + */ + if (Oflag) { + (void)makedir((struct direct *)olost_found_dir, 2); + for (i = DIRBLKSIZ; i < sblock.fs_bsize; i += DIRBLKSIZ) + bcopy(&olost_found_dir[2], &buf[i], + DIRSIZ(0, &olost_found_dir[2])); + } else { + (void)makedir(lost_found_dir, 2); + for (i = DIRBLKSIZ; i < sblock.fs_bsize; i += DIRBLKSIZ) + bcopy(&lost_found_dir[2], &buf[i], + DIRSIZ(0, &lost_found_dir[2])); + } + node.di_mode = IFDIR | UMASK; + node.di_nlink = 2; + node.di_size = sblock.fs_bsize; + node.di_db[0] = alloc(node.di_size, node.di_mode); + node.di_blocks = btodb(fragroundup(&sblock, node.di_size)); + wtfs(fsbtodb(&sblock, node.di_db[0]), node.di_size, buf); + iput(&node, LOSTFOUNDINO); +#endif + /* + * create the root directory + */ + if (mfs) + node.di_mode = IFDIR | 01777; + else + node.di_mode = IFDIR | UMASK; + node.di_nlink = PREDEFDIR; + if (Oflag) + node.di_size = makedir((struct direct *)oroot_dir, PREDEFDIR); + else + node.di_size = makedir(root_dir, PREDEFDIR); + node.di_db[0] = alloc(sblock.fs_fsize, node.di_mode); + node.di_blocks = btodb(fragroundup(&sblock, node.di_size)); + wtfs(fsbtodb(&sblock, node.di_db[0]), sblock.fs_fsize, buf); + iput(&node, ROOTINO); +} + +/* + * construct a set of directory entries in "buf". + * return size of directory. + */ +makedir(protodir, entries) + register struct direct *protodir; + int entries; +{ + char *cp; + int i, spcleft; + + spcleft = DIRBLKSIZ; + for (cp = buf, i = 0; i < entries - 1; i++) { + protodir[i].d_reclen = DIRSIZ(0, &protodir[i]); + bcopy(&protodir[i], cp, protodir[i].d_reclen); + cp += protodir[i].d_reclen; + spcleft -= protodir[i].d_reclen; + } + protodir[i].d_reclen = spcleft; + bcopy(&protodir[i], cp, DIRSIZ(0, &protodir[i])); + return (DIRBLKSIZ); +} + +/* + * allocate a block or frag + */ +daddr_t +alloc(size, mode) + int size; + int mode; +{ + int i, frag; + daddr_t d, blkno; + + rdfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize, + (char *)&acg); + if (acg.cg_magic != CG_MAGIC) { + printf("cg 0: bad magic number\n"); + return (0); + } + if (acg.cg_cs.cs_nbfree == 0) { + printf("first cylinder group ran out of space\n"); + return (0); + } + for (d = 0; d < acg.cg_ndblk; d += sblock.fs_frag) + if (isblock(&sblock, cg_blksfree(&acg), d / sblock.fs_frag)) + goto goth; + printf("internal error: can't find block in cyl 0\n"); + return (0); +goth: + blkno = fragstoblks(&sblock, d); + clrblock(&sblock, cg_blksfree(&acg), blkno); + clrbit(cg_clustersfree(&acg), blkno); + acg.cg_cs.cs_nbfree--; + sblock.fs_cstotal.cs_nbfree--; + fscs[0].cs_nbfree--; + if (mode & IFDIR) { + acg.cg_cs.cs_ndir++; + sblock.fs_cstotal.cs_ndir++; + fscs[0].cs_ndir++; + } + cg_blktot(&acg)[cbtocylno(&sblock, d)]--; + cg_blks(&sblock, &acg, cbtocylno(&sblock, d))[cbtorpos(&sblock, d)]--; + if (size != sblock.fs_bsize) { + frag = howmany(size, sblock.fs_fsize); + fscs[0].cs_nffree += sblock.fs_frag - frag; + sblock.fs_cstotal.cs_nffree += sblock.fs_frag - frag; + acg.cg_cs.cs_nffree += sblock.fs_frag - frag; + acg.cg_frsum[sblock.fs_frag - frag]++; + for (i = frag; i < sblock.fs_frag; i++) + setbit(cg_blksfree(&acg), d + i); + } + wtfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize, + (char *)&acg); + return (d); +} + +/* + * Allocate an inode on the disk + */ +iput(ip, ino) + register struct dinode *ip; + register ino_t ino; +{ + struct dinode buf[MAXINOPB]; + daddr_t d; + int c; + + c = ino_to_cg(&sblock, ino); + rdfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize, + (char *)&acg); + if (acg.cg_magic != CG_MAGIC) { + printf("cg 0: bad magic number\n"); + exit(31); + } + acg.cg_cs.cs_nifree--; + setbit(cg_inosused(&acg), ino); + wtfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize, + (char *)&acg); + sblock.fs_cstotal.cs_nifree--; + fscs[0].cs_nifree--; + if (ino >= sblock.fs_ipg * sblock.fs_ncg) { + printf("fsinit: inode value out of range (%d).\n", ino); + exit(32); + } + d = fsbtodb(&sblock, ino_to_fsba(&sblock, ino)); + rdfs(d, sblock.fs_bsize, buf); + buf[ino_to_fsbo(&sblock, ino)] = *ip; + wtfs(d, sblock.fs_bsize, buf); +} + +/* + * Notify parent process that the filesystem has created itself successfully. + */ +void +started() +{ + + exit(0); +} + +/* + * Replace libc function with one suited to our needs. + */ +caddr_t +malloc(size) + register u_long size; +{ + char *base, *i; + static u_long pgsz; + struct rlimit rlp; + + if (pgsz == 0) { + base = sbrk(0); + pgsz = getpagesize() - 1; + i = (char *)((u_long)(base + pgsz) &~ pgsz); + base = sbrk(i - base); + if (getrlimit(RLIMIT_DATA, &rlp) < 0) + perror("getrlimit"); + rlp.rlim_cur = rlp.rlim_max; + if (setrlimit(RLIMIT_DATA, &rlp) < 0) + perror("setrlimit"); + memleft = rlp.rlim_max - (u_long)base; + } + size = (size + pgsz) &~ pgsz; + if (size > memleft) + size = memleft; + memleft -= size; + if (size == 0) + return (0); + return ((caddr_t)sbrk(size)); +} + +/* + * Replace libc function with one suited to our needs. + */ +caddr_t +realloc(ptr, size) + char *ptr; + u_long size; +{ + void *p; + + if ((p = malloc(size)) == NULL) + return (NULL); + bcopy(ptr, p, size); + free(ptr); + return (p); +} + +/* + * Replace libc function with one suited to our needs. + */ +char * +calloc(size, numelm) + u_long size, numelm; +{ + caddr_t base; + + size *= numelm; + base = malloc(size); + bzero(base, size); + return (base); +} + +/* + * Replace libc function with one suited to our needs. + */ +free(ptr) + char *ptr; +{ + + /* do not worry about it for now */ +} + +/* + * read a block from the file system + */ +rdfs(bno, size, bf) + daddr_t bno; + int size; + char *bf; +{ + int n; + + if (mfs) { + bcopy(membase + bno * sectorsize, bf, size); + return; + } + if (lseek(fsi, (off_t)bno * sectorsize, 0) < 0) { + printf("seek error: %ld\n", bno); + perror("rdfs"); + exit(33); + } + n = read(fsi, bf, size); + if (n != size) { + printf("read error: %ld\n", bno); + perror("rdfs"); + exit(34); + } +} + +/* + * write a block to the file system + */ +wtfs(bno, size, bf) + daddr_t bno; + int size; + char *bf; +{ + int n; + + if (mfs) { + bcopy(bf, membase + bno * sectorsize, size); + return; + } + if (Nflag) + return; + if (lseek(fso, (off_t)bno * sectorsize, SEEK_SET) < 0) { + printf("seek error: %ld\n", bno); + perror("wtfs"); + exit(35); + } + n = write(fso, bf, size); + if (n != size) { + printf("write error: %ld\n", bno); + perror("wtfs"); + exit(36); + } +} + +/* + * check if a block is available + */ +isblock(fs, cp, h) + struct fs *fs; + unsigned char *cp; + int h; +{ + unsigned char mask; + + switch (fs->fs_frag) { + case 8: + return (cp[h] == 0xff); + case 4: + mask = 0x0f << ((h & 0x1) << 2); + return ((cp[h >> 1] & mask) == mask); + case 2: + mask = 0x03 << ((h & 0x3) << 1); + return ((cp[h >> 2] & mask) == mask); + case 1: + mask = 0x01 << (h & 0x7); + return ((cp[h >> 3] & mask) == mask); + default: +#ifdef STANDALONE + printf("isblock bad fs_frag %d\n", fs->fs_frag); +#else + fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag); +#endif + return (0); + } +} + +/* + * take a block out of the map + */ +clrblock(fs, cp, h) + struct fs *fs; + unsigned char *cp; + int h; +{ + switch ((fs)->fs_frag) { + case 8: + cp[h] = 0; + return; + case 4: + cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2)); + return; + case 2: + cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1)); + return; + case 1: + cp[h >> 3] &= ~(0x01 << (h & 0x7)); + return; + default: +#ifdef STANDALONE + printf("clrblock bad fs_frag %d\n", fs->fs_frag); +#else + fprintf(stderr, "clrblock bad fs_frag %d\n", fs->fs_frag); +#endif + return; + } +} + +/* + * put a block into the map + */ +setblock(fs, cp, h) + struct fs *fs; + unsigned char *cp; + int h; +{ + switch (fs->fs_frag) { + case 8: + cp[h] = 0xff; + return; + case 4: + cp[h >> 1] |= (0x0f << ((h & 0x1) << 2)); + return; + case 2: + cp[h >> 2] |= (0x03 << ((h & 0x3) << 1)); + return; + case 1: + cp[h >> 3] |= (0x01 << (h & 0x7)); + return; + default: +#ifdef STANDALONE + printf("setblock bad fs_frag %d\n", fs->fs_frag); +#else + fprintf(stderr, "setblock bad fs_frag %d\n", fs->fs_frag); +#endif + return; + } +} diff --git a/sbin/newfs/newfs.8 b/sbin/newfs/newfs.8 new file mode 100644 index 0000000..0ffcfdf --- /dev/null +++ b/sbin/newfs/newfs.8 @@ -0,0 +1,282 @@ +.\" Copyright (c) 1983, 1987, 1991, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)newfs.8 8.3 (Berkeley) 3/27/94 +.\" +.Dd March 27, 1994 +.Dt NEWFS 8 +.Os BSD 4.2 +.Sh NAME +.Nm newfs , +.Nm mfs +.Nd construct a new file system +.Sh SYNOPSIS +.Nm newfs +.Op Fl NO +.Op Fl S Ar sector-size +.Op Fl a maxcontig +.Op Fl b Ar block-size +.Op Fl c Ar cylinders +.Op Fl d Ar rotdelay +.Op Fl e Ar maxbpg +.Op Fl f Ar frag-size +.Op Fl i Ar bytes +.Op Fl k Ar skew +.Op Fl l Ar interleave +.Op Fl m Ar free space +.Op Fl o Ar optimization +.Op Fl p Ar sectors +.Op Fl r Ar revolutions +.Op Fl s Ar size +.Op Fl t Ar tracks +.Op Fl u Ar sectors +.Op Fl x Ar sectors +.Ar special +.Nm mount_mfs +.Op Fl N +.Op Fl a maxcontig +.Op Fl b Ar block-size +.Op Fl c Ar cylinders +.Op Fl d Ar rotdelay +.Op Fl e Ar maxbpg +.Op Fl f Ar frag-size +.Op Fl i Ar bytes +.Op Fl m Ar free space +.Op Fl o Ar options +.Op Fl s Ar size +.Ar special node +.Sh DESCRIPTION +.Nm Newfs +replaces the more obtuse +.Xr mkfs 8 +program. +Before running +.Nm newfs +or +.Nm mount_mfs , +the disk must be labeled using +.Xr disklabel 8 . +.Nm Newfs +builds a file system on the specified special device +basing its defaults on the information in the disk label. +Typically the defaults are reasonable, however +.Nm newfs +has numerous options to allow the defaults to be selectively overridden. +.Pp +.Nm Mount_mfs +is used to build a file system in virtual memory and then mount it +on a specified node. +.Nm Mount_mfs +exits and the contents of the file system are lost +when the file system is unmounted. +If +.Nm mount_mfs +is sent a signal while running, +for example during system shutdown, +it will attempt to unmount its +corresponding file system. +The parameters to +.Nm mount_mfs +are the same as those to +.Nm newfs . +The special file is only used to read the disk label which provides +a set of configuration parameters for the memory based file system. +The special file is typically that of the primary swap area, +since that is where the file system will be backed up when +free memory gets low and the memory supporting +the file system has to be paged. +.Pp +The following options define the general layout policies. +.Bl -tag -width Fl +.It Fl N +Causes the file system parameters to be printed out +without really creating the file system. +.It Fl O +Creates a 4.3BSD format filesystem. +This options is primarily used to build root filesystems +that can be understood by older boot ROMs. +.It Fl a Ar maxcontig +This specifies the maximum number of contiguous blocks that will be +laid out before forcing a rotational delay (see the +.Fl d +option). +The default value is one. +See +.Xr tunefs 8 +for more details on how to set this option. +.It Fl b Ar block-size +The block size of the file system, in bytes. +.It Fl c Ar #cylinders/group +The number of cylinders per cylinder group in a file system. +The default value is 16. +.It Fl d Ar rotdelay +This specifies the expected time (in milliseconds) to service a transfer +completion interrupt and initiate a new transfer on the same disk. +The default is 4 milliseconds. +See +.Xr tunefs 8 +for more details on how to set this option. +.It Fl e Ar maxbpg +This indicates the maximum number of blocks any single file can +allocate out of a cylinder group before it is forced to begin +allocating blocks from another cylinder group. +The default is about one quarter of the total blocks in a cylinder group. +See +.Xr tunefs 8 +for more details on how to set this option. +.It Fl f Ar frag-size +The fragment size of the file system in bytes. +.It Fl i Ar number of bytes per inode +This specifies the density of inodes in the file system. +The default is to create an inode for each 2048 bytes of data space. +If fewer inodes are desired, a larger number should be used; +to create more inodes a smaller number should be given. +.It Fl m Ar free space \&% +The percentage of space reserved from normal users; the minimum free +space threshold. +The default value used is 10%. +See +.Xr tunefs 8 +for more details on how to set this option. +.It Fl o Ar optimization\ preference +.Pq ``space'' or ``time'' +The file system can either be instructed to try to minimize the time spent +allocating blocks, or to try to minimize the space fragmentation on the disk. +If the value of minfree (see above) is less than 10%, +the default is to optimize for space; +if the value of minfree is greater than or equal to 10%, +the default is to optimize for time. +See +.Xr tunefs 8 +for more details on how to set this option. +.It Fl s Ar size +The size of the file system in sectors. +.El +.Pp +The following options override the standard sizes for the disk geometry. +Their default values are taken from the disk label. +Changing these defaults is useful only when using +.Nm newfs +to build a file system whose raw image will eventually be used on a +different type of disk than the one on which it is initially created +(for example on a write-once disk). +Note that changing any of these values from their defaults will make +it impossible for +.Xr fsck +to find the alternate superblocks if the standard superblock is lost. +.Bl -tag -width Fl +.It Fl S Ar sector-size +The size of a sector in bytes (almost never anything but 512). +.It Fl k Ar sector \&0 skew , per track +Used to describe perturbations in the media format to compensate for +a slow controller. +Track skew is the offset of sector 0 on track N relative to sector 0 +on track N-1 on the same cylinder. +.It Fl l Ar hardware sector interleave +Used to describe perturbations in the media format to compensate for +a slow controller. +Interleave is physical sector interleave on each track, +specified as the denominator of the ratio: +.Dl sectors read/sectors passed over +Thus an interleave of 1/1 implies contiguous layout, while 1/2 implies +logical sector 0 is separated by one sector from logical sector 1. +.It Fl p Ar spare sectors per track +Spare sectors (bad sector replacements) are physical sectors that occupy +space at the end of each track. +They are not counted as part of the sectors/track +.Pq Fl u +since they are not available to the file system for data allocation. +.It Fl r Ar revolutions/minute +The speed of the disk in revolutions per minute. +.It Fl t Ar #tracks/cylinder +The number of tracks/cylinder available for data allocation by the file +system. +.It Fl u Ar sectors/track +The number of sectors per track available for data allocation by the file +system. +This does not include sectors reserved at the end of each track for bad +block replacement (see the +.Fl p +option.) +.It Fl x Ar spare sectors per cylinder +Spare sectors (bad sector replacements) are physical sectors that occupy +space at the end of the last track in the cylinder. +They are deducted from the sectors/track +.Pq Fl u +of the last track of each cylinder since they are not available to the file +system for data allocation. +.El +.Pp +The options to the +.Nm mount_mfs +command are as described for the +.Nm newfs +command, except for the +.Fl o +option. +.Pp +That option is as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Sh SEE ALSO +.Xr disktab 5 , +.Xr fs 5 , +.Xr dumpfs 8 , +.Xr disklabel 8 , +.Xr diskpart 8 , +.Xr fsck 8 , +.Xr format 8 , +.Xr mount 8 , +.Xr tunefs 8 +.Rs +.%A M. McKusick +.%A W. Joy +.%A S. Leffler +.%A R. Fabry +.%T A Fast File System for UNIX , +.%J ACM Transactions on Computer Systems 2 +.%V 3 +.%P pp 181-197 +.%D August 1984 +.%O (reprinted in the BSD System Manager's Manual) +.Re +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/newfs/newfs.c b/sbin/newfs/newfs.c new file mode 100644 index 0000000..c7748e8 --- /dev/null +++ b/sbin/newfs/newfs.c @@ -0,0 +1,678 @@ +/* + * Copyright (c) 1983, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)newfs.c 8.8 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +/* + * newfs: friendly front end to mkfs + */ +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/disklabel.h> +#include <sys/file.h> +#include <sys/mount.h> + +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> + +#include <ctype.h> +#include <errno.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_ASYNC, + { NULL }, +}; + +#if __STDC__ +void fatal(const char *fmt, ...); +#else +void fatal(); +#endif + +#define COMPAT /* allow non-labeled disks */ + +/* + * The following two constants set the default block and fragment sizes. + * Both constants must be a power of 2 and meet the following constraints: + * MINBSIZE <= DESBLKSIZE <= MAXBSIZE + * sectorsize <= DESFRAGSIZE <= DESBLKSIZE + * DESBLKSIZE / DESFRAGSIZE <= 8 + */ +#define DFL_FRAGSIZE 1024 +#define DFL_BLKSIZE 8192 + +/* + * Cylinder groups may have up to many cylinders. The actual + * number used depends upon how much information can be stored + * on a single cylinder. The default is to use 16 cylinders + * per group. + */ +#define DESCPG 16 /* desired fs_cpg */ + +/* + * ROTDELAY gives the minimum number of milliseconds to initiate + * another disk transfer on the same cylinder. It is used in + * determining the rotationally optimal layout for disk blocks + * within a file; the default of fs_rotdelay is 4ms. + */ +#define ROTDELAY 4 + +/* + * MAXBLKPG determines the maximum number of data blocks which are + * placed in a single cylinder group. The default is one indirect + * block worth of data blocks. + */ +#define MAXBLKPG(bsize) ((bsize) / sizeof(daddr_t)) + +/* + * Each file system has a number of inodes statically allocated. + * We allocate one inode slot per NFPI fragments, expecting this + * to be far more than we will ever need. + */ +#define NFPI 4 + +/* + * For each cylinder we keep track of the availability of blocks at different + * rotational positions, so that we can lay out the data to be picked + * up with minimum rotational latency. NRPOS is the default number of + * rotational positions that we distinguish. With NRPOS of 8 the resolution + * of our summary information is 2ms for a typical 3600 rpm drive. + */ +#define NRPOS 8 /* number distinct rotational positions */ + + +int mfs; /* run as the memory based filesystem */ +int Nflag; /* run without writing file system */ +int Oflag; /* format as an 4.3BSD file system */ +int fssize; /* file system size */ +int ntracks; /* # tracks/cylinder */ +int nsectors; /* # sectors/track */ +int nphyssectors; /* # sectors/track including spares */ +int secpercyl; /* sectors per cylinder */ +int trackspares = -1; /* spare sectors per track */ +int cylspares = -1; /* spare sectors per cylinder */ +int sectorsize; /* bytes/sector */ +#ifdef tahoe +int realsectorsize; /* bytes/sector in hardware */ +#endif +int rpm; /* revolutions/minute of drive */ +int interleave; /* hardware sector interleave */ +int trackskew = -1; /* sector 0 skew, per track */ +int headswitch; /* head switch time, usec */ +int trackseek; /* track-to-track seek, usec */ +int fsize = 0; /* fragment size */ +int bsize = 0; /* block size */ +int cpg = DESCPG; /* cylinders/cylinder group */ +int cpgflg; /* cylinders/cylinder group flag was given */ +int minfree = MINFREE; /* free space threshold */ +int opt = DEFAULTOPT; /* optimization preference (space or time) */ +int density; /* number of bytes per inode */ +int maxcontig = 0; /* max contiguous blocks to allocate */ +int rotdelay = ROTDELAY; /* rotational delay between blocks */ +int maxbpg; /* maximum blocks per file in a cyl group */ +int nrpos = NRPOS; /* # of distinguished rotational positions */ +int bbsize = BBSIZE; /* boot block size */ +int sbsize = SBSIZE; /* superblock size */ +int mntflags = MNT_ASYNC; /* flags to be passed to mount */ +u_long memleft; /* virtual memory available */ +caddr_t membase; /* start address of memory based filesystem */ +#ifdef COMPAT +char *disktype; +int unlabeled; +#endif + +char device[MAXPATHLEN]; +char *progname; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + register int ch; + register struct partition *pp; + register struct disklabel *lp; + struct disklabel *getdisklabel(); + struct partition oldpartition; + struct stat st; + struct statfs *mp; + int fsi, fso, len, n; + char *cp, *s1, *s2, *special, *opstring, buf[BUFSIZ]; + + if (progname = rindex(*argv, '/')) + ++progname; + else + progname = *argv; + + if (strstr(progname, "mfs")) { + mfs = 1; + Nflag++; + } + + opstring = mfs ? + "NT:a:b:c:d:e:f:i:m:o:s:" : + "NOS:T:a:b:c:d:e:f:i:k:l:m:n:o:p:r:s:t:u:x:"; + while ((ch = getopt(argc, argv, opstring)) != EOF) + switch (ch) { + case 'N': + Nflag = 1; + break; + case 'O': + Oflag = 1; + break; + case 'S': + if ((sectorsize = atoi(optarg)) <= 0) + fatal("%s: bad sector size", optarg); + break; +#ifdef COMPAT + case 'T': + disktype = optarg; + break; +#endif + case 'a': + if ((maxcontig = atoi(optarg)) <= 0) + fatal("%s: bad maximum contiguous blocks\n", + optarg); + break; + case 'b': + if ((bsize = atoi(optarg)) < MINBSIZE) + fatal("%s: bad block size", optarg); + break; + case 'c': + if ((cpg = atoi(optarg)) <= 0) + fatal("%s: bad cylinders/group", optarg); + cpgflg++; + break; + case 'd': + if ((rotdelay = atoi(optarg)) < 0) + fatal("%s: bad rotational delay\n", optarg); + break; + case 'e': + if ((maxbpg = atoi(optarg)) <= 0) + fatal("%s: bad blocks per file in a cylinder group\n", + optarg); + break; + case 'f': + if ((fsize = atoi(optarg)) <= 0) + fatal("%s: bad fragment size", optarg); + break; + case 'i': + if ((density = atoi(optarg)) <= 0) + fatal("%s: bad bytes per inode\n", optarg); + break; + case 'k': + if ((trackskew = atoi(optarg)) < 0) + fatal("%s: bad track skew", optarg); + break; + case 'l': + if ((interleave = atoi(optarg)) <= 0) + fatal("%s: bad interleave", optarg); + break; + case 'm': + if ((minfree = atoi(optarg)) < 0 || minfree > 99) + fatal("%s: bad free space %%\n", optarg); + break; + case 'n': + if ((nrpos = atoi(optarg)) <= 0) + fatal("%s: bad rotational layout count\n", + optarg); + break; + case 'o': + if (mfs) + getmntopts(optarg, mopts, &mntflags); + else { + if (strcmp(optarg, "space") == 0) + opt = FS_OPTSPACE; + else if (strcmp(optarg, "time") == 0) + opt = FS_OPTTIME; + else + fatal("%s: unknown optimization preference: use `space' or `time'."); + } + break; + case 'p': + if ((trackspares = atoi(optarg)) < 0) + fatal("%s: bad spare sectors per track", + optarg); + break; + case 'r': + if ((rpm = atoi(optarg)) <= 0) + fatal("%s: bad revolutions/minute\n", optarg); + break; + case 's': + if ((fssize = atoi(optarg)) <= 0) + fatal("%s: bad file system size", optarg); + break; + case 't': + if ((ntracks = atoi(optarg)) <= 0) + fatal("%s: bad total tracks", optarg); + break; + case 'u': + if ((nsectors = atoi(optarg)) <= 0) + fatal("%s: bad sectors/track", optarg); + break; + case 'x': + if ((cylspares = atoi(optarg)) < 0) + fatal("%s: bad spare sectors per cylinder", + optarg); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2 && (mfs || argc != 1)) + usage(); + + special = argv[0]; + cp = rindex(special, '/'); + if (cp == 0) { + /* + * No path prefix; try /dev/r%s then /dev/%s. + */ + (void)sprintf(device, "%sr%s", _PATH_DEV, special); + if (stat(device, &st) == -1) + (void)sprintf(device, "%s%s", _PATH_DEV, special); + special = device; + } + if (Nflag) { + fso = -1; + } else { + fso = open(special, O_WRONLY); + if (fso < 0) + fatal("%s: %s", special, strerror(errno)); + + /* Bail if target special is mounted */ + n = getmntinfo(&mp, MNT_NOWAIT); + if (n == 0) + fatal("%s: getmntinfo: %s", special, strerror(errno)); + + len = sizeof(_PATH_DEV) - 1; + s1 = special; + if (strncmp(_PATH_DEV, s1, len) == 0) + s1 += len; + + while (--n >= 0) { + s2 = mp->f_mntfromname; + if (strncmp(_PATH_DEV, s2, len) == 0) { + s2 += len - 1; + *s2 = 'r'; + } + if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) + fatal("%s is mounted on %s", + special, mp->f_mntonname); + ++mp; + } + } + if (mfs && disktype != NULL) { + lp = (struct disklabel *)getdiskbyname(disktype); + if (lp == NULL) + fatal("%s: unknown disk type", disktype); + pp = &lp->d_partitions[1]; + } else { + fsi = open(special, O_RDONLY); + if (fsi < 0) + fatal("%s: %s", special, strerror(errno)); + if (fstat(fsi, &st) < 0) + fatal("%s: %s", special, strerror(errno)); + if ((st.st_mode & S_IFMT) != S_IFCHR && !mfs) + printf("%s: %s: not a character-special device\n", + progname, special); + cp = index(argv[0], '\0') - 1; + if (cp == 0 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp)) + fatal("%s: can't figure out file system partition", + argv[0]); +#ifdef COMPAT + if (!mfs && disktype == NULL) + disktype = argv[1]; +#endif + lp = getdisklabel(special, fsi); + if (isdigit(*cp)) + pp = &lp->d_partitions[0]; + else + pp = &lp->d_partitions[*cp - 'a']; + if (pp->p_size == 0) + fatal("%s: `%c' partition is unavailable", + argv[0], *cp); + if (pp->p_fstype == FS_BOOT) + fatal("%s: `%c' partition overlaps boot program", + argv[0], *cp); + } + if (fssize == 0) + fssize = pp->p_size; + if (fssize > pp->p_size && !mfs) + fatal("%s: maximum file system size on the `%c' partition is %d", + argv[0], *cp, pp->p_size); + if (rpm == 0) { + rpm = lp->d_rpm; + if (rpm <= 0) + rpm = 3600; + } + if (ntracks == 0) { + ntracks = lp->d_ntracks; + if (ntracks <= 0) + fatal("%s: no default #tracks", argv[0]); + } + if (nsectors == 0) { + nsectors = lp->d_nsectors; + if (nsectors <= 0) + fatal("%s: no default #sectors/track", argv[0]); + } + if (sectorsize == 0) { + sectorsize = lp->d_secsize; + if (sectorsize <= 0) + fatal("%s: no default sector size", argv[0]); + } + if (trackskew == -1) { + trackskew = lp->d_trackskew; + if (trackskew < 0) + trackskew = 0; + } + if (interleave == 0) { + interleave = lp->d_interleave; + if (interleave <= 0) + interleave = 1; + } + if (fsize == 0) { + fsize = pp->p_fsize; + if (fsize <= 0) + fsize = MAX(DFL_FRAGSIZE, lp->d_secsize); + } + if (bsize == 0) { + bsize = pp->p_frag * pp->p_fsize; + if (bsize <= 0) + bsize = MIN(DFL_BLKSIZE, 8 * fsize); + } + /* + * Maxcontig sets the default for the maximum number of blocks + * that may be allocated sequentially. With filesystem clustering + * it is possible to allocate contiguous blocks up to the maximum + * transfer size permitted by the controller or buffering. + */ + if (maxcontig == 0) + maxcontig = MAX(1, MIN(MAXPHYS, MAXBSIZE) / bsize - 1); + if (density == 0) + density = NFPI * fsize; + if (minfree < MINFREE && opt != FS_OPTSPACE) { + fprintf(stderr, "Warning: changing optimization to space "); + fprintf(stderr, "because minfree is less than %d%%\n", MINFREE); + opt = FS_OPTSPACE; + } + if (trackspares == -1) { + trackspares = lp->d_sparespertrack; + if (trackspares < 0) + trackspares = 0; + } + nphyssectors = nsectors + trackspares; + if (cylspares == -1) { + cylspares = lp->d_sparespercyl; + if (cylspares < 0) + cylspares = 0; + } + secpercyl = nsectors * ntracks - cylspares; + if (secpercyl != lp->d_secpercyl) + fprintf(stderr, "%s (%d) %s (%lu)\n", + "Warning: calculated sectors per cylinder", secpercyl, + "disagrees with disk label", lp->d_secpercyl); + if (maxbpg == 0) + maxbpg = MAXBLKPG(bsize); + headswitch = lp->d_headswitch; + trackseek = lp->d_trkseek; +#ifdef notdef /* label may be 0 if faked up by kernel */ + bbsize = lp->d_bbsize; + sbsize = lp->d_sbsize; +#endif + oldpartition = *pp; +#ifdef tahoe + realsectorsize = sectorsize; + if (sectorsize != DEV_BSIZE) { /* XXX */ + int secperblk = DEV_BSIZE / sectorsize; + + sectorsize = DEV_BSIZE; + nsectors /= secperblk; + nphyssectors /= secperblk; + secpercyl /= secperblk; + fssize /= secperblk; + pp->p_size /= secperblk; + } +#endif + mkfs(pp, special, fsi, fso); +#ifdef tahoe + if (realsectorsize != DEV_BSIZE) + pp->p_size *= DEV_BSIZE / realsectorsize; +#endif + if (!Nflag && bcmp(pp, &oldpartition, sizeof(oldpartition))) + rewritelabel(special, fso, lp); + if (!Nflag) + close(fso); + close(fsi); +#ifdef MFS + if (mfs) { + struct mfs_args args; + + sprintf(buf, "mfs:%d", getpid()); + args.fspec = buf; + args.export.ex_root = -2; + if (mntflags & MNT_RDONLY) + args.export.ex_flags = MNT_EXRDONLY; + else + args.export.ex_flags = 0; + args.base = membase; + args.size = fssize * sectorsize; + if (mount(MOUNT_MFS, argv[1], mntflags, &args) < 0) + fatal("%s: %s", argv[1], strerror(errno)); + } +#endif + exit(0); +} + +#ifdef COMPAT +char lmsg[] = "%s: can't read disk label; disk type must be specified"; +#else +char lmsg[] = "%s: can't read disk label"; +#endif + +struct disklabel * +getdisklabel(s, fd) + char *s; + int fd; +{ + static struct disklabel lab; + + if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) { +#ifdef COMPAT + if (disktype) { + struct disklabel *lp, *getdiskbyname(); + + unlabeled++; + lp = getdiskbyname(disktype); + if (lp == NULL) + fatal("%s: unknown disk type", disktype); + return (lp); + } +#endif + warn("ioctl (GDINFO)"); + fatal(lmsg, s); + } + return (&lab); +} + +rewritelabel(s, fd, lp) + char *s; + int fd; + register struct disklabel *lp; +{ +#ifdef COMPAT + if (unlabeled) + return; +#endif + lp->d_checksum = 0; + lp->d_checksum = dkcksum(lp); + if (ioctl(fd, DIOCWDINFO, (char *)lp) < 0) { + warn("ioctl (WDINFO)"); + fatal("%s: can't rewrite disk label", s); + } +#if vax + if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) { + register i; + int cfd; + daddr_t alt; + char specname[64]; + char blk[1024]; + char *cp; + + /* + * Make name for 'c' partition. + */ + strcpy(specname, s); + cp = specname + strlen(specname) - 1; + if (!isdigit(*cp)) + *cp = 'c'; + cfd = open(specname, O_WRONLY); + if (cfd < 0) + fatal("%s: %s", specname, strerror(errno)); + bzero(blk, sizeof(blk)); + *(struct disklabel *)(blk + LABELOFFSET) = *lp; + alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors; + for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) { + if (lseek(cfd, (off_t)(alt + i) * lp->d_secsize, + L_SET) == -1) + fatal("lseek to badsector area: %s", + strerror(errno)); + if (write(cfd, blk, lp->d_secsize) < lp->d_secsize) + warn("alternate label %d write", i/2); + } + close(cfd); + } +#endif +} + +/*VARARGS*/ +void +#if __STDC__ +fatal(const char *fmt, ...) +#else +fatal(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + if (fcntl(STDERR_FILENO, F_GETFL) < 0) { + openlog(progname, LOG_CONS, LOG_DAEMON); + vsyslog(LOG_ERR, fmt, ap); + closelog(); + } else { + vwarnx(fmt, ap); + } + va_end(ap); + exit(1); + /*NOTREACHED*/ +} + +usage() +{ + if (mfs) { + fprintf(stderr, + "usage: %s [ -fsoptions ] special-device mount-point\n", + progname); + } else + fprintf(stderr, + "usage: %s [ -fsoptions ] special-device%s\n", + progname, +#ifdef COMPAT + " [device-type]"); +#else + ""); +#endif + fprintf(stderr, "where fsoptions are:\n"); + fprintf(stderr, + "\t-N do not create file system, just print out parameters\n"); + fprintf(stderr, "\t-O create a 4.3BSD format filesystem\n"); + fprintf(stderr, "\t-S sector size\n"); +#ifdef COMPAT + fprintf(stderr, "\t-T disktype\n"); +#endif + fprintf(stderr, "\t-a maximum contiguous blocks\n"); + fprintf(stderr, "\t-b block size\n"); + fprintf(stderr, "\t-c cylinders/group\n"); + fprintf(stderr, "\t-d rotational delay between contiguous blocks\n"); + fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n"); + fprintf(stderr, "\t-f frag size\n"); + fprintf(stderr, "\t-i number of bytes per inode\n"); + fprintf(stderr, "\t-k sector 0 skew, per track\n"); + fprintf(stderr, "\t-l hardware sector interleave\n"); + fprintf(stderr, "\t-m minimum free space %%\n"); + fprintf(stderr, "\t-n number of distinguished rotational positions\n"); + fprintf(stderr, "\t-o optimization preference (`space' or `time')\n"); + fprintf(stderr, "\t-p spare sectors per track\n"); + fprintf(stderr, "\t-s file system size (sectors)\n"); + fprintf(stderr, "\t-r revolutions/minute\n"); + fprintf(stderr, "\t-t tracks/cylinder\n"); + fprintf(stderr, "\t-u sectors/track\n"); + fprintf(stderr, "\t-x spare sectors per cylinder\n"); + exit(1); +} diff --git a/sbin/newlfs/Makefile b/sbin/newlfs/Makefile new file mode 100644 index 0000000..c79c83e --- /dev/null +++ b/sbin/newlfs/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 6/18/93 + +PROG= newlfs +CFLAGS+=-I/sys/ufs/lfs +SRCS= dkcksum.c lfs.c lfs_cksum.c misc.c newfs.c +MAN8= newlfs.0 +.PATH: /sys/ufs/lfs ${.CURDIR}/../disklabel + +.include <bsd.prog.mk> diff --git a/sbin/newlfs/config.h b/sbin/newlfs/config.h new file mode 100644 index 0000000..9c8b394 --- /dev/null +++ b/sbin/newlfs/config.h @@ -0,0 +1,134 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)config.h 8.2 (Berkeley) 4/22/94 + */ + +/* + * The first boot and super blocks are given in absolute disk addresses. + * The byte-offset forms are preferred, as they don't imply a sector size. + */ +#define BBSIZE 8192 +#define SBSIZE 8192 + +/* + * The following two constants set the default block and fragment sizes. + * Both constants must be a power of 2 and meet the following constraints: + * MINBSIZE <= DESBLKSIZE <= MAXBSIZE + * sectorsize <= DESFRAGSIZE <= DESBLKSIZE + * DESBLKSIZE / DESFRAGSIZE <= 8 + */ +#define DFL_FRAGSIZE 1024 +#define DFL_BLKSIZE 8192 + +/* + * Cylinder groups may have up to many cylinders. The actual + * number used depends upon how much information can be stored + * on a single cylinder. The default is to use 16 cylinders + * per group. + */ +#define DESCPG 16 /* desired fs_cpg */ + +/* + * MINFREE gives the minimum acceptable percentage of file system + * blocks which may be free. If the freelist drops below this level + * only the superuser may continue to allocate blocks. This may + * be set to 0 if no reserve of free blocks is deemed necessary, + * however throughput drops by fifty percent if the file system + * is run at between 90% and 100% full; thus the default value of + * fs_minfree is 10%. With 10% free space, fragmentation is not a + * problem, so we choose to optimize for time. + */ +#define MINFREE 10 +#define DEFAULTOPT FS_OPTTIME + +/* + * Preference for optimization. + */ +#define FS_OPTTIME 0 /* minimize allocation time */ +#define FS_OPTSPACE 1 /* minimize disk fragmentation */ + + +/* + * ROTDELAY gives the minimum number of milliseconds to initiate + * another disk transfer on the same cylinder. It is used in + * determining the rotationally optimal layout for disk blocks + * within a file; the default of fs_rotdelay is 4ms. + */ +#define ROTDELAY 4 + +/* + * MAXCONTIG sets the default for the maximum number of blocks + * that may be allocated sequentially. Since UNIX drivers are + * not capable of scheduling multi-block transfers, this defaults + * to 1 (ie no contiguous blocks are allocated). + */ +#define MAXCONTIG 1 + +/* + * MAXBLKPG determines the maximum number of data blocks which are + * placed in a single cylinder group. The default is one indirect + * block worth of data blocks. + */ +#define MAXBLKPG(bsize) ((bsize) / sizeof(daddr_t)) + +/* + * Each file system has a number of inodes statically allocated. + * We allocate one inode slot per NFPI fragments, expecting this + * to be far more than we will ever need. + */ +#define NFPI 4 + +/* + * For each cylinder we keep track of the availability of blocks at different + * rotational positions, so that we can lay out the data to be picked + * up with minimum rotational latency. NRPOS is the default number of + * rotational positions that we distinguish. With NRPOS of 8 the resolution + * of our summary information is 2ms for a typical 3600 rpm drive. + */ +#define NRPOS 8 /* number distinct rotational positions */ + +/* + * The following constants set the default block and segment size for a log + * structured file system. Both must be powers of two and the segment size + * must be a multiple of the block size. We also set minimum block and segment + * sizes. + */ +#define LFS_MINSEGSIZE (64*1024) +#define DFL_LFSSEG (1024 * 1024) +#define DFL_LFSSEG_SHIFT 20 +#define DFL_LFSSEG_MASK 0xFFFFF + +#define LFS_MINBLOCKSIZE 1024 +#define DFL_LFSBLOCK 4096 +#define DFL_LFSBLOCK_SHIFT 12 +#define DFL_LFSBLOCK_MASK 0xFFF diff --git a/sbin/newlfs/extern.h b/sbin/newlfs/extern.h new file mode 100644 index 0000000..c65cdba --- /dev/null +++ b/sbin/newlfs/extern.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/5/93 + */ + +u_long cksum __P((void *, size_t)); +u_short dkcksum __P((struct disklabel *)); +void fatal __P((const char *fmt, ...)); +u_int log2 __P((u_int)); +int make_lfs + __P((int, struct disklabel *, struct partition *, int, int, int)); +int mkfs __P((struct partition *, char *, int, int)); + +extern char *progname; +extern char *special; diff --git a/sbin/newlfs/lfs.c b/sbin/newlfs/lfs.c new file mode 100644 index 0000000..c79a0dc --- /dev/null +++ b/sbin/newlfs/lfs.c @@ -0,0 +1,673 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)lfs.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/disklabel.h> +#include <sys/time.h> +#include <sys/mount.h> + +#include <ufs/ufs/dir.h> +#include <ufs/ufs/quota.h> +#include <ufs/ufs/dinode.h> +#include <ufs/lfs/lfs.h> + +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include "config.h" +#include "extern.h" + +/* + * This table is indexed by the log base 2 of the block size. + * It returns the maximum file size allowed in a file system + * with the specified block size. For block sizes smaller than + * 8K, the size is limited by tha maximum number of blocks that + * can be reached by triply indirect blocks: + * NDADDR + INOPB(bsize) + INOPB(bsize)^2 + INOPB(bsize)^3 + * For block size of 8K or larger, the file size is limited by the + * number of blocks that can be represented in the file system. Since + * we use negative block numbers to represent indirect blocks, we can + * have a maximum of 2^31 blocks. + */ + +u_quad_t maxtable[] = { + /* 1 */ -1, + /* 2 */ -1, + /* 4 */ -1, + /* 8 */ -1, + /* 16 */ -1, + /* 32 */ -1, + /* 64 */ -1, + /* 128 */ -1, + /* 256 */ -1, + /* 512 */ NDADDR + 128 + 128 * 128 + 128 * 128 * 128, + /* 1024 */ NDADDR + 256 + 256 * 256 + 256 * 256 * 256, + /* 2048 */ NDADDR + 512 + 512 * 512 + 512 * 512 * 512, + /* 4096 */ NDADDR + 1024 + 1024 * 1024 + 1024 * 1024 * 1024, + /* 8192 */ 1 << 31, + /* 16 K */ 1 << 31, + /* 32 K */ 1 << 31, +}; + +static struct lfs lfs_default = { + /* lfs_magic */ LFS_MAGIC, + /* lfs_version */ LFS_VERSION, + /* lfs_size */ 0, + /* lfs_ssize */ DFL_LFSSEG/DFL_LFSBLOCK, + /* lfs_dsize */ 0, + /* lfs_bsize */ DFL_LFSBLOCK, + /* lfs_fsize */ DFL_LFSBLOCK, + /* lfs_frag */ 1, + /* lfs_free */ LFS_FIRST_INUM, + /* lfs_bfree */ 0, + /* lfs_nfiles */ 0, + /* lfs_avail */ 0, + /* lfs_uinodes */ 0, + /* lfs_idaddr */ 0, + /* lfs_ifile */ LFS_IFILE_INUM, + /* lfs_lastseg */ 0, + /* lfs_nextseg */ 0, + /* lfs_curseg */ 0, + /* lfs_offset */ 0, + /* lfs_lastpseg */ 0, + /* lfs_tstamp */ 0, + /* lfs_minfree */ MINFREE, + /* lfs_maxfilesize */ 0, + /* lfs_dbpseg */ DFL_LFSSEG/DEV_BSIZE, + /* lfs_inopb */ DFL_LFSBLOCK/sizeof(struct dinode), + /* lfs_ifpb */ DFL_LFSBLOCK/sizeof(IFILE), + /* lfs_sepb */ DFL_LFSBLOCK/sizeof(SEGUSE), + /* lfs_nindir */ DFL_LFSBLOCK/sizeof(daddr_t), + /* lfs_nseg */ 0, + /* lfs_nspf */ 0, + /* lfs_cleansz */ 0, + /* lfs_segtabsz */ 0, + /* lfs_segmask */ DFL_LFSSEG_MASK, + /* lfs_segshift */ DFL_LFSSEG_SHIFT, + /* lfs_bmask */ DFL_LFSBLOCK_MASK, + /* lfs_bshift */ DFL_LFSBLOCK_SHIFT, + /* lfs_ffmask */ 0, + /* lfs_ffshift */ 0, + /* lfs_fbmask */ 0, + /* lfs_fbshift */ 0, + /* lfs_fsbtodb */ 0, + /* lfs_sushift */ 0, + /* lfs_sboffs */ { 0 }, + /* lfs_sp */ NULL, + /* lfs_ivnode */ NULL, + /* lfs_seglock */ 0, + /* lfs_lockpid */ 0, + /* lfs_iocount */ 0, + /* lfs_writer */ 0, + /* lfs_dirops */ 0, + /* lfs_doifile */ 0, + /* lfs_nactive */ 0, + /* lfs_fmod */ 0, + /* lfs_clean */ 0, + /* lfs_ronly */ 0, + /* lfs_flags */ 0, + /* lfs_fsmnt */ { 0 }, + /* lfs_pad */ { 0 }, + /* lfs_cksum */ 0 +}; + + +struct direct lfs_root_dir[] = { + { ROOTINO, sizeof(struct direct), DT_DIR, 1, "."}, + { ROOTINO, sizeof(struct direct), DT_DIR, 2, ".."}, + { LFS_IFILE_INUM, sizeof(struct direct), DT_REG, 5, "ifile"}, + { LOSTFOUNDINO, sizeof(struct direct), DT_DIR, 10, "lost+found"}, +}; + +struct direct lfs_lf_dir[] = { + { LOSTFOUNDINO, sizeof(struct direct), DT_DIR, 1, "." }, + { ROOTINO, sizeof(struct direct), DT_DIR, 2, ".." }, +}; + +static daddr_t make_dinode + __P((ino_t, struct dinode *, int, daddr_t, struct lfs *)); +static void make_dir __P(( void *, struct direct *, int)); +static void put __P((int, off_t, void *, size_t)); + +int +make_lfs(fd, lp, partp, minfree, block_size, seg_size) + int fd; + struct disklabel *lp; + struct partition *partp; + int minfree; + int block_size; + int seg_size; +{ + struct dinode *dip; /* Pointer to a disk inode */ + struct dinode *dpagep; /* Pointer to page of disk inodes */ + CLEANERINFO *cleaninfo; /* Segment cleaner information table */ + FINFO file_info; /* File info structure in summary blocks */ + IFILE *ifile; /* Pointer to array of ifile structures */ + IFILE *ip; /* Pointer to array of ifile structures */ + struct lfs *lfsp; /* Superblock */ + SEGUSE *segp; /* Segment usage table */ + SEGUSE *segtable; /* Segment usage table */ + SEGSUM summary; /* Segment summary structure */ + SEGSUM *sp; /* Segment summary pointer */ + daddr_t last_sb_addr; /* Address of superblocks */ + daddr_t last_addr; /* Previous segment address */ + daddr_t sb_addr; /* Address of superblocks */ + daddr_t seg_addr; /* Address of current segment */ + void *ipagep; /* Pointer to the page we use to write stuff */ + void *sump; /* Used to copy stuff into segment buffer */ + u_long *block_array; /* Array of logical block nos to put in sum */ + u_long blocks_used; /* Number of blocks in first segment */ + u_long *dp; /* Used to computed checksum on data */ + u_long *datasump; /* Used to computed checksum on data */ + int block_array_size; /* How many entries in block array */ + int bsize; /* Block size */ + int db_per_fb; /* Disk blocks per file block */ + int i, j; + int off; /* Offset at which to write */ + int sb_interval; /* number of segs between super blocks */ + int seg_seek; /* Seek offset for a segment */ + int ssize; /* Segment size */ + int sum_size; /* Size of the summary block */ + + lfsp = &lfs_default; + + if (!(bsize = block_size)) + bsize = DFL_LFSBLOCK; + if (!(ssize = seg_size)) + ssize = DFL_LFSSEG; + + /* Modify parts of superblock overridden by command line arguments */ + if (bsize != DFL_LFSBLOCK) { + lfsp->lfs_bshift = log2(bsize); + if (1 << lfsp->lfs_bshift != bsize) + fatal("%d: block size not a power of 2", bsize); + lfsp->lfs_bsize = bsize; + lfsp->lfs_fsize = bsize; + lfsp->lfs_bmask = bsize - 1; + lfsp->lfs_inopb = bsize / sizeof(struct dinode); +/* MIS -- should I round to power of 2 */ + lfsp->lfs_ifpb = bsize / sizeof(IFILE); + lfsp->lfs_sepb = bsize / sizeof(SEGUSE); + lfsp->lfs_nindir = bsize / sizeof(daddr_t); + } + + if (ssize != DFL_LFSSEG) { + lfsp->lfs_segshift = log2(ssize); + if (1 << lfsp->lfs_segshift != ssize) + fatal("%d: segment size not power of 2", ssize); + lfsp->lfs_ssize = ssize; + lfsp->lfs_segmask = ssize - 1; + lfsp->lfs_dbpseg = ssize / DEV_BSIZE; + } + lfsp->lfs_ssize = ssize >> lfsp->lfs_bshift; + + if (minfree) + lfsp->lfs_minfree = minfree; + + /* + * Fill in parts of superblock that can be computed from file system + * size, disk geometry and current time. + */ + db_per_fb = bsize/lp->d_secsize; + lfsp->lfs_fsbtodb = log2(db_per_fb); + lfsp->lfs_sushift = log2(lfsp->lfs_sepb); + lfsp->lfs_size = partp->p_size >> lfsp->lfs_fsbtodb; + lfsp->lfs_dsize = lfsp->lfs_size - (LFS_LABELPAD >> lfsp->lfs_bshift); + lfsp->lfs_nseg = lfsp->lfs_dsize / lfsp->lfs_ssize; + lfsp->lfs_maxfilesize = maxtable[lfsp->lfs_bshift] << lfsp->lfs_bshift; + + /* + * The number of free blocks is set from the number of segments times + * the segment size - 2 (that we never write because we need to make + * sure the cleaner can run). Then we'll subtract off the room for the + * superblocks ifile entries and segment usage table. + */ + lfsp->lfs_dsize = fsbtodb(lfsp, (lfsp->lfs_nseg - 2) * lfsp->lfs_ssize); + lfsp->lfs_bfree = lfsp->lfs_dsize; + lfsp->lfs_segtabsz = SEGTABSIZE_SU(lfsp); + lfsp->lfs_cleansz = CLEANSIZE_SU(lfsp); + if ((lfsp->lfs_tstamp = time(NULL)) == -1) + fatal("time: %s", strerror(errno)); + if ((sb_interval = lfsp->lfs_nseg / LFS_MAXNUMSB) < LFS_MIN_SBINTERVAL) + sb_interval = LFS_MIN_SBINTERVAL; + + /* + * Now, lay out the file system. We need to figure out where + * the superblocks go, initialize the checkpoint information + * for the first two superblocks, initialize the segment usage + * information, put the segusage information in the ifile, create + * the first block of IFILE structures, and link all the IFILE + * structures into a free list. + */ + + /* Figure out where the superblocks are going to live */ + lfsp->lfs_sboffs[0] = LFS_LABELPAD/lp->d_secsize; + for (i = 1; i < LFS_MAXNUMSB; i++) { + sb_addr = ((i * sb_interval) << + (lfsp->lfs_segshift - lfsp->lfs_bshift + lfsp->lfs_fsbtodb)) + + lfsp->lfs_sboffs[0]; + if (sb_addr > partp->p_size) + break; + lfsp->lfs_sboffs[i] = sb_addr; + } + last_sb_addr = lfsp->lfs_sboffs[i - 1]; + lfsp->lfs_lastseg = lfsp->lfs_sboffs[0]; + lfsp->lfs_nextseg = + lfsp->lfs_sboffs[1] ? lfsp->lfs_sboffs[1] : lfsp->lfs_sboffs[0]; + lfsp->lfs_curseg = lfsp->lfs_lastseg; + + /* + * Initialize the segment usage table. The first segment will + * contain the superblock, the cleanerinfo (cleansz), the segusage + * table * (segtabsz), 1 block's worth of IFILE entries, the root + * directory, the lost+found directory and one block's worth of + * inodes (containing the ifile, root, and l+f inodes). + */ + if (!(cleaninfo = malloc(lfsp->lfs_cleansz << lfsp->lfs_bshift))) + fatal("%s", strerror(errno)); + cleaninfo->clean = lfsp->lfs_nseg - 1; + cleaninfo->dirty = 1; + + if (!(segtable = malloc(lfsp->lfs_segtabsz << lfsp->lfs_bshift))) + fatal("%s", strerror(errno)); + segp = segtable; + blocks_used = lfsp->lfs_segtabsz + lfsp->lfs_cleansz + 4; + segp->su_nbytes = ((blocks_used - 1) << lfsp->lfs_bshift) + + 3 * sizeof(struct dinode) + LFS_SUMMARY_SIZE; + segp->su_lastmod = lfsp->lfs_tstamp; + segp->su_nsums = 1; /* 1 summary blocks */ + segp->su_ninos = 1; /* 1 inode block */ + segp->su_flags = SEGUSE_SUPERBLOCK | SEGUSE_DIRTY; + lfsp->lfs_bfree -= LFS_SUMMARY_SIZE / lp->d_secsize; + lfsp->lfs_bfree -= + fsbtodb(lfsp, lfsp->lfs_cleansz + lfsp->lfs_segtabsz + 4); + + /* + * Now figure out the address of the ifile inode. The inode block + * appears immediately after the segment summary. + */ + lfsp->lfs_idaddr = (LFS_LABELPAD + LFS_SBPAD + LFS_SUMMARY_SIZE) / + lp->d_secsize; + + for (segp = segtable + 1, i = 1; i < lfsp->lfs_nseg; i++, segp++) { + if ((i % sb_interval) == 0) { + segp->su_flags = SEGUSE_SUPERBLOCK; + lfsp->lfs_bfree -= (LFS_SBPAD / lp->d_secsize); + } else + segp->su_flags = 0; + segp->su_lastmod = 0; + segp->su_nbytes = 0; + segp->su_ninos = 0; + segp->su_nsums = 0; + } + + /* + * Initialize dynamic accounting. The blocks available for + * writing are the bfree blocks minus 1 segment summary for + * each segment since you can't write any new data without + * creating a segment summary - 2 segments that the cleaner + * needs. + */ + lfsp->lfs_avail = lfsp->lfs_bfree - lfsp->lfs_nseg - + fsbtodb(lfsp, 2 * lfsp->lfs_ssize); + lfsp->lfs_uinodes = 0; + /* + * Ready to start writing segments. The first segment is different + * because it contains the segment usage table and the ifile inode + * as well as a superblock. For the rest of the segments, set the + * time stamp to be 0 so that the first segment is the most recent. + * For each segment that is supposed to contain a copy of the super + * block, initialize its first few blocks and its segment summary + * to indicate this. + */ + lfsp->lfs_nfiles = LFS_FIRST_INUM - 1; + lfsp->lfs_cksum = + cksum(lfsp, sizeof(struct lfs) - sizeof(lfsp->lfs_cksum)); + + /* Now create a block of disk inodes */ + if (!(dpagep = malloc(lfsp->lfs_bsize))) + fatal("%s", strerror(errno)); + dip = (struct dinode *)dpagep; + bzero(dip, lfsp->lfs_bsize); + + /* Create a block of IFILE structures. */ + if (!(ipagep = malloc(lfsp->lfs_bsize))) + fatal("%s", strerror(errno)); + ifile = (IFILE *)ipagep; + + /* + * Initialize IFILE. It is the next block following the + * block of inodes (whose address has been calculated in + * lfsp->lfs_idaddr; + */ + sb_addr = lfsp->lfs_idaddr + lfsp->lfs_bsize / lp->d_secsize; + sb_addr = make_dinode(LFS_IFILE_INUM, dip, + lfsp->lfs_cleansz + lfsp->lfs_segtabsz+1, sb_addr, lfsp); + dip->di_mode = IFREG|IREAD|IWRITE; + ip = &ifile[LFS_IFILE_INUM]; + ip->if_version = 1; + ip->if_daddr = lfsp->lfs_idaddr; + + /* Initialize the ROOT Directory */ + sb_addr = make_dinode(ROOTINO, ++dip, 1, sb_addr, lfsp); + dip->di_mode = IFDIR|IREAD|IWRITE|IEXEC; + dip->di_size = DIRBLKSIZ; + dip->di_nlink = 3; + ip = &ifile[ROOTINO]; + ip->if_version = 1; + ip->if_daddr = lfsp->lfs_idaddr; + + /* Initialize the lost+found Directory */ + sb_addr = make_dinode(LOSTFOUNDINO, ++dip, 1, sb_addr, lfsp); + dip->di_mode = IFDIR|IREAD|IWRITE|IEXEC; + dip->di_size = DIRBLKSIZ; + dip->di_nlink = 2; + ip = &ifile[LOSTFOUNDINO]; + ip->if_version = 1; + ip->if_daddr = lfsp->lfs_idaddr; + + /* Make all the other dinodes invalid */ + for (i = INOPB(lfsp)-3, dip++; i; i--, dip++) + dip->di_inumber = LFS_UNUSED_INUM; + + + /* Link remaining IFILE entries in free list */ + for (ip = &ifile[LFS_FIRST_INUM], i = LFS_FIRST_INUM; + i < lfsp->lfs_ifpb; ++ip) { + ip->if_version = 1; + ip->if_daddr = LFS_UNUSED_DADDR; + ip->if_nextfree = ++i; + } + ifile[lfsp->lfs_ifpb - 1].if_nextfree = LFS_UNUSED_INUM; + + /* Now, write the segment */ + + /* Compute a checksum across all the data you're writing */ + dp = datasump = malloc (blocks_used * sizeof(u_long)); + *dp++ = ((u_long *)dpagep)[0]; /* inode block */ + for (i = 0; i < lfsp->lfs_cleansz; i++) + *dp++ = ((u_long *)cleaninfo)[(i << lfsp->lfs_bshift) / + sizeof(u_long)]; /* Cleaner info */ + for (i = 0; i < lfsp->lfs_segtabsz; i++) + *dp++ = ((u_long *)segtable)[(i << lfsp->lfs_bshift) / + sizeof(u_long)]; /* Segusage table */ + *dp++ = ((u_long *)ifile)[0]; /* Ifile */ + + /* Still need the root and l+f bytes; get them later */ + + /* Write out the inode block */ + off = LFS_LABELPAD + LFS_SBPAD + LFS_SUMMARY_SIZE; + put(fd, off, dpagep, lfsp->lfs_bsize); + free(dpagep); + off += lfsp->lfs_bsize; + + /* Write out the ifile */ + + put(fd, off, cleaninfo, lfsp->lfs_cleansz << lfsp->lfs_bshift); + off += (lfsp->lfs_cleansz << lfsp->lfs_bshift); + (void)free(cleaninfo); + + put(fd, off, segtable, lfsp->lfs_segtabsz << lfsp->lfs_bshift); + off += (lfsp->lfs_segtabsz << lfsp->lfs_bshift); + (void)free(segtable); + + put(fd, off, ifile, lfsp->lfs_bsize); + off += lfsp->lfs_bsize; + + /* + * use ipagep for space for writing out other stuff. It used to + * contain the ifile, but we're done with it. + */ + + /* Write out the root and lost and found directories */ + bzero(ipagep, lfsp->lfs_bsize); + make_dir(ipagep, lfs_root_dir, + sizeof(lfs_root_dir) / sizeof(struct direct)); + *dp++ = ((u_long *)ipagep)[0]; + put(fd, off, ipagep, lfsp->lfs_bsize); + off += lfsp->lfs_bsize; + + bzero(ipagep, lfsp->lfs_bsize); + make_dir(ipagep, lfs_lf_dir, + sizeof(lfs_lf_dir) / sizeof(struct direct)); + *dp++ = ((u_long *)ipagep)[0]; + put(fd, off, ipagep, lfsp->lfs_bsize); + + /* Write Supberblock */ + lfsp->lfs_offset = (off + lfsp->lfs_bsize) / lp->d_secsize; + put(fd, LFS_LABELPAD, lfsp, sizeof(struct lfs)); + + /* + * Finally, calculate all the fields for the summary structure + * and write it. + */ + + summary.ss_next = lfsp->lfs_nextseg; + summary.ss_create = lfsp->lfs_tstamp; + summary.ss_nfinfo = 3; + summary.ss_ninos = 3; + summary.ss_datasum = cksum(datasump, sizeof(u_long) * blocks_used); + + /* + * Make sure that we don't overflow a summary block. We have to + * record: FINFO structures for ifile, root, and l+f. The number + * of blocks recorded for the ifile is determined by the size of + * the cleaner info and the segments usage table. There is room + * for one block included in sizeof(FINFO) so we don't need to add + * any extra space for the ROOT and L+F, and one block of the ifile + * is already counted. Finally, we leave room for 1 inode block + * address. + */ + sum_size = 3*sizeof(FINFO) + sizeof(SEGSUM) + sizeof(daddr_t) + + (lfsp->lfs_cleansz + lfsp->lfs_segtabsz) * sizeof(u_long); +#define SUMERR \ +"Multiple summary blocks in segment 1 not yet implemented\nsummary is %d bytes." + if (sum_size > LFS_SUMMARY_SIZE) + fatal(SUMERR, sum_size); + + block_array_size = lfsp->lfs_cleansz + lfsp->lfs_segtabsz + 1; + + if (!(block_array = malloc(block_array_size *sizeof(int)))) + fatal("%s: %s", special, strerror(errno)); + + /* fill in the array */ + for (i = 0; i < block_array_size; i++) + block_array[i] = i; + + /* copy into segment */ + sump = ipagep; + bcopy(&summary, sump, sizeof(SEGSUM)); + sump += sizeof(SEGSUM); + + /* Now, add the ifile */ + file_info.fi_nblocks = block_array_size; + file_info.fi_version = 1; + file_info.fi_ino = LFS_IFILE_INUM; + + bcopy(&file_info, sump, sizeof(FINFO) - sizeof(u_long)); + sump += sizeof(FINFO) - sizeof(u_long); + bcopy(block_array, sump, sizeof(u_long) * file_info.fi_nblocks); + sump += sizeof(u_long) * file_info.fi_nblocks; + + /* Now, add the root directory */ + file_info.fi_nblocks = 1; + file_info.fi_version = 1; + file_info.fi_ino = ROOTINO; + file_info.fi_blocks[0] = 0; + bcopy(&file_info, sump, sizeof(FINFO)); + sump += sizeof(FINFO); + + /* Now, add the lost and found */ + file_info.fi_ino = LOSTFOUNDINO; + bcopy(&file_info, sump, sizeof(FINFO)); + + ((daddr_t *)ipagep)[LFS_SUMMARY_SIZE / sizeof(daddr_t) - 1] = + lfsp->lfs_idaddr; + ((SEGSUM *)ipagep)->ss_sumsum = cksum(ipagep+sizeof(summary.ss_sumsum), + LFS_SUMMARY_SIZE - sizeof(summary.ss_sumsum)); + put(fd, LFS_LABELPAD + LFS_SBPAD, ipagep, LFS_SUMMARY_SIZE); + + sp = (SEGSUM *)ipagep; + sp->ss_create = 0; + sp->ss_nfinfo = 0; + sp->ss_ninos = 0; + sp->ss_datasum = 0; + + /* Now write the summary block for the next partial so it's invalid */ + lfsp->lfs_tstamp = 0; + off += lfsp->lfs_bsize; + sp->ss_sumsum = + cksum(&sp->ss_datasum, LFS_SUMMARY_SIZE - sizeof(sp->ss_sumsum)); + put(fd, off, sp, LFS_SUMMARY_SIZE); + + /* Now, write rest of segments containing superblocks */ + lfsp->lfs_cksum = + cksum(lfsp, sizeof(struct lfs) - sizeof(lfsp->lfs_cksum)); + for (seg_addr = last_addr = lfsp->lfs_sboffs[0], j = 1, i = 1; + i < lfsp->lfs_nseg; i++) { + + seg_addr += lfsp->lfs_ssize << lfsp->lfs_fsbtodb; + sp->ss_next = last_addr; + last_addr = seg_addr; + seg_seek = seg_addr * lp->d_secsize; + + if (seg_addr == lfsp->lfs_sboffs[j]) { + if (j < (LFS_MAXNUMSB - 2)) + j++; + put(fd, seg_seek, lfsp, sizeof(struct lfs)); + seg_seek += LFS_SBPAD; + } + + /* Summary */ + sp->ss_sumsum = cksum(&sp->ss_datasum, + LFS_SUMMARY_SIZE - sizeof(sp->ss_sumsum)); + put(fd, seg_seek, sp, LFS_SUMMARY_SIZE); + } + free(ipagep); + close(fd); + return (0); +} + +static void +put(fd, off, p, len) + int fd; + off_t off; + void *p; + size_t len; +{ + int wbytes; + + if (lseek(fd, off, SEEK_SET) < 0) + fatal("%s: %s", special, strerror(errno)); + if ((wbytes = write(fd, p, len)) < 0) + fatal("%s: %s", special, strerror(errno)); + if (wbytes != len) + fatal("%s: short write (%d, not %d)", special, wbytes, len); +} + +/* + * Create the root directory for this file system and the lost+found + * directory. + */ + + u_long d_ino; /* inode number of entry */ + u_short d_reclen; /* length of this record */ + u_short d_namlen; /* length of string in d_name */ + char d_name[MAXNAMLEN + 1]; /* name with length <= MAXNAMLEN */ +void +lfsinit() +{} + +static daddr_t +make_dinode(ino, dip, nblocks, saddr, lfsp) + ino_t ino; /* inode we're creating */ + struct dinode *dip; /* disk inode */ + int nblocks; /* number of blocks in file */ + daddr_t saddr; /* starting block address */ + struct lfs *lfsp; /* superblock */ +{ + int db_per_fb, i; + + dip->di_nlink = 1; + dip->di_blocks = nblocks << lfsp->lfs_fsbtodb; + + dip->di_size = (nblocks << lfsp->lfs_bshift); + dip->di_atime.ts_sec = dip->di_mtime.ts_sec = + dip->di_ctime.ts_sec = lfsp->lfs_tstamp; + dip->di_atime.ts_nsec = dip->di_mtime.ts_nsec = + dip->di_ctime.ts_nsec = 0; + dip->di_inumber = ino; + +#define SEGERR \ +"File requires more than the number of direct blocks; increase block or segment size." + if (NDADDR < nblocks) + fatal("%s", SEGERR); + + /* Assign the block addresses for the ifile */ + db_per_fb = 1 << lfsp->lfs_fsbtodb; + for (i = 0; i < nblocks; i++, saddr += db_per_fb) + dip->di_db[i] = saddr; + + return (saddr); +} + + +/* + * Construct a set of directory entries in "bufp". We assume that all the + * entries in protodir fir in the first DIRBLKSIZ. + */ +static void +make_dir(bufp, protodir, entries) + void *bufp; + register struct direct *protodir; + int entries; +{ + char *cp; + int i, spcleft; + + spcleft = DIRBLKSIZ; + for (cp = bufp, i = 0; i < entries - 1; i++) { + protodir[i].d_reclen = DIRSIZ(NEWDIRFMT, &protodir[i]); + bcopy(&protodir[i], cp, protodir[i].d_reclen); + cp += protodir[i].d_reclen; + if ((spcleft -= protodir[i].d_reclen) < 0) + fatal("%s: %s", special, "directory too big"); + } + protodir[i].d_reclen = spcleft; + bcopy(&protodir[i], cp, DIRSIZ(NEWDIRFMT, &protodir[i])); +} diff --git a/sbin/newlfs/misc.c b/sbin/newlfs/misc.c new file mode 100644 index 0000000..074fb71 --- /dev/null +++ b/sbin/newlfs/misc.c @@ -0,0 +1,83 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/types.h> +#include <sys/disklabel.h> +#include <stdlib.h> +#include <stdio.h> +#include "extern.h" + +u_int +log2(num) + u_int num; +{ + register u_int i, limit; + + limit = 1; + for (i = 0; limit < num; limit = limit << 1, i++); + return (i); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +fatal(const char *fmt, ...) +#else +fatal(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "%s: ", progname); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/sbin/newlfs/newfs.c b/sbin/newlfs/newfs.c new file mode 100644 index 0000000..25ac7f0 --- /dev/null +++ b/sbin/newlfs/newfs.c @@ -0,0 +1,466 @@ +/*- + * Copyright (c) 1989, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)newfs.c 8.3 (Berkeley) 4/22/94"; +#endif /* not lint */ + +/* + * newfs: friendly front end to mkfs + */ +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/disklabel.h> +#include <sys/file.h> +#include <sys/mount.h> + +#include <ufs/ufs/dir.h> +#include <ufs/ufs/dinode.h> + +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <paths.h> +#include "config.h" +#include "extern.h" + +#define COMPAT /* allow non-labeled disks */ + +int mfs; /* run as the memory based filesystem */ +int Nflag; /* run without writing file system */ +int fssize; /* file system size */ +int ntracks; /* # tracks/cylinder */ +int nsectors; /* # sectors/track */ +int nphyssectors; /* # sectors/track including spares */ +int secpercyl; /* sectors per cylinder */ +int trackspares = -1; /* spare sectors per track */ +int cylspares = -1; /* spare sectors per cylinder */ +int sectorsize; /* bytes/sector */ +#ifdef tahoe +int realsectorsize; /* bytes/sector in hardware */ +#endif +int rpm; /* revolutions/minute of drive */ +int interleave; /* hardware sector interleave */ +int trackskew = -1; /* sector 0 skew, per track */ +int headswitch; /* head switch time, usec */ +int trackseek; /* track-to-track seek, usec */ +int fsize = 0; /* fragment size */ +int bsize = 0; /* block size */ +int cpg = DESCPG; /* cylinders/cylinder group */ +int cpgflg; /* cylinders/cylinder group flag was given */ +int minfree = MINFREE; /* free space threshold */ +int opt = DEFAULTOPT; /* optimization preference (space or time) */ +int density; /* number of bytes per inode */ +int maxcontig = MAXCONTIG; /* max contiguous blocks to allocate */ +int rotdelay = ROTDELAY; /* rotational delay between blocks */ +int maxbpg; /* maximum blocks per file in a cyl group */ +int nrpos = NRPOS; /* # of distinguished rotational positions */ +int bbsize = BBSIZE; /* boot block size */ +int sbsize = SBSIZE; /* superblock size */ +int mntflags; /* flags to be passed to mount */ +u_long memleft; /* virtual memory available */ +caddr_t membase; /* start address of memory based filesystem */ +#ifdef COMPAT +char *disktype; +int unlabeled; +#endif + +char device[MAXPATHLEN]; +char *progname, *special; + +static struct disklabel *getdisklabel __P((char *, int)); +static struct disklabel *debug_readlabel __P((int)); +static void rewritelabel __P((char *, int, struct disklabel *)); +static void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register int ch; + register struct partition *pp; + register struct disklabel *lp; + struct partition oldpartition; + struct stat st; + int debug, lfs, fsi, fso, segsize; + char *cp, *opstring; + + if (progname = rindex(*argv, '/')) + ++progname; + else + progname = *argv; + + if (strstr(progname, "mfs")) { + mfs = 1; + Nflag++; + } + + /* -F is mfs only and MUST come first! */ + opstring = "F:B:DLNS:T:a:b:c:d:e:f:i:k:l:m:n:o:p:r:s:t:u:x:"; + if (!mfs) + opstring += 2; + + debug = lfs = segsize = 0; + while ((ch = getopt(argc, argv, opstring)) != EOF) + switch(ch) { + case 'B': /* LFS segment size */ + if ((segsize = atoi(optarg)) < LFS_MINSEGSIZE) + fatal("%s: bad segment size", optarg); + break; + case 'D': + debug = 1; + break; + case 'F': + if ((mntflags = atoi(optarg)) == 0) + fatal("%s: bad mount flags", optarg); + break; + case 'L': /* Create lfs */ + lfs = 1; + break; + case 'N': + Nflag++; + break; + case 'S': + if ((sectorsize = atoi(optarg)) <= 0) + fatal("%s: bad sector size", optarg); + break; +#ifdef COMPAT + case 'T': + disktype = optarg; + break; +#endif + case 'a': + if ((maxcontig = atoi(optarg)) <= 0) + fatal("%s: bad max contiguous blocks\n", + optarg); + break; + case 'b': /* used for LFS */ + if ((bsize = atoi(optarg)) < LFS_MINBLOCKSIZE) + fatal("%s: bad block size", optarg); + break; + case 'c': + if ((cpg = atoi(optarg)) <= 0) + fatal("%s: bad cylinders/group", optarg); + cpgflg++; + break; + case 'd': + if ((rotdelay = atoi(optarg)) < 0) + fatal("%s: bad rotational delay\n", optarg); + break; + case 'e': + if ((maxbpg = atoi(optarg)) <= 0) + fatal("%s: bad blocks per file in a cyl group\n", + optarg); + break; + case 'f': + if ((fsize = atoi(optarg)) <= 0) + fatal("%s: bad frag size", optarg); + break; + case 'i': + if ((density = atoi(optarg)) <= 0) + fatal("%s: bad bytes per inode\n", optarg); + break; + case 'k': + if ((trackskew = atoi(optarg)) < 0) + fatal("%s: bad track skew", optarg); + break; + case 'l': + if ((interleave = atoi(optarg)) <= 0) + fatal("%s: bad interleave", optarg); + break; + case 'm': /* used for LFS */ + if ((minfree = atoi(optarg)) < 0 || minfree > 99) + fatal("%s: bad free space %%\n", optarg); + break; + case 'n': + if ((nrpos = atoi(optarg)) <= 0) + fatal("%s: bad rotational layout count\n", + optarg); + break; + case 'o': + if (strcmp(optarg, "space") == 0) + opt = FS_OPTSPACE; + else if (strcmp(optarg, "time") == 0) + opt = FS_OPTTIME; + else + fatal("%s: bad optimization preference %s", + optarg, "(options are `space' or `time')"); + break; + case 'p': + if ((trackspares = atoi(optarg)) < 0) + fatal("%s: bad spare sectors per track", + optarg); + break; + case 'r': + if ((rpm = atoi(optarg)) <= 0) + fatal("%s: bad revs/minute\n", optarg); + break; + case 's': /* used for LFS */ + if ((fssize = atoi(optarg)) <= 0) + fatal("%s: bad file system size", optarg); + break; + case 't': + if ((ntracks = atoi(optarg)) <= 0) + fatal("%s: bad total tracks", optarg); + break; + case 'u': + if ((nsectors = atoi(optarg)) <= 0) + fatal("%s: bad sectors/track", optarg); + break; + case 'x': + if ((cylspares = atoi(optarg)) < 0) + fatal("%s: bad spare sectors per cylinder", + optarg); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2 && (mfs || argc != 1)) + usage(); + + /* + * If the -N flag isn't specified, open the output file. If no path + * prefix, try /dev/r%s and then /dev/%s. + */ + special = argv[0]; + if (index(special, '/') == NULL) { + (void)sprintf(device, "%sr%s", _PATH_DEV, special); + if (stat(device, &st) == -1) + (void)sprintf(device, "%s%s", _PATH_DEV, special); + special = device; + } + if (!Nflag) { + fso = open(special, + (debug ? O_CREAT : 0) | O_WRONLY, DEFFILEMODE); + if (fso < 0) + fatal("%s: %s", special, strerror(errno)); + } else + fso = -1; + + /* Open the input file. */ + fsi = open(special, O_RDONLY); + if (fsi < 0) + fatal("%s: %s", special, strerror(errno)); + if (fstat(fsi, &st) < 0) + fatal("%s: %s", special, strerror(errno)); + + if (!debug && !mfs && !S_ISCHR(st.st_mode)) + (void)printf("%s: %s: not a character-special device\n", + progname, special); + cp = index(argv[0], '\0') - 1; + if (!debug && (cp == 0 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp))) + fatal("%s: can't figure out file system partition", argv[0]); + +#ifdef COMPAT + if (!mfs && disktype == NULL) + disktype = argv[1]; +#endif + if (debug) + lp = debug_readlabel(fsi); + else + lp = getdisklabel(special, fsi); + + if (isdigit(*cp)) + pp = &lp->d_partitions[0]; + else + pp = &lp->d_partitions[*cp - 'a']; + if (pp->p_size == 0) + fatal("%s: `%c' partition is unavailable", argv[0], *cp); + + /* If we're making a LFS, we break out here */ + exit(make_lfs(fso, lp, pp, minfree, bsize, segsize)); +} + +#ifdef COMPAT +char lmsg[] = "%s: can't read disk label; disk type must be specified"; +#else +char lmsg[] = "%s: can't read disk label"; +#endif + +static struct disklabel * +getdisklabel(s, fd) + char *s; + int fd; +{ + static struct disklabel lab; + + if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) { +#ifdef COMPAT + if (disktype) { + struct disklabel *lp, *getdiskbyname(); + + unlabeled++; + lp = getdiskbyname(disktype); + if (lp == NULL) + fatal("%s: unknown disk type", disktype); + return (lp); + } +#endif + (void)fprintf(stderr, + "%s: ioctl (GDINFO): %s\n", progname, strerror(errno)); + fatal(lmsg, s); + } + return (&lab); +} + + +static struct disklabel * +debug_readlabel(fd) + int fd; +{ + static struct disklabel lab; + int n; + + if ((n = read(fd, &lab, sizeof(struct disklabel))) < 0) + fatal("unable to read disk label: %s", strerror(errno)); + else if (n < sizeof(struct disklabel)) + fatal("short read of disklabel: %d of %d bytes", n, + sizeof(struct disklabel)); + return(&lab); +} + +static void +rewritelabel(s, fd, lp) + char *s; + int fd; + register struct disklabel *lp; +{ +#ifdef COMPAT + if (unlabeled) + return; +#endif + lp->d_checksum = 0; + lp->d_checksum = dkcksum(lp); + if (ioctl(fd, DIOCWDINFO, (char *)lp) < 0) { + (void)fprintf(stderr, + "%s: ioctl (WDINFO): %s\n", progname, strerror(errno)); + fatal("%s: can't rewrite disk label", s); + } +#if vax + if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) { + register i; + int cfd; + daddr_t alt; + char specname[64]; + char blk[1024]; + char *cp; + + /* + * Make name for 'c' partition. + */ + strcpy(specname, s); + cp = specname + strlen(specname) - 1; + if (!isdigit(*cp)) + *cp = 'c'; + cfd = open(specname, O_WRONLY); + if (cfd < 0) + fatal("%s: %s", specname, strerror(errno)); + bzero(blk, sizeof(blk)); + *(struct disklabel *)(blk + LABELOFFSET) = *lp; + alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors; + for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) { + if (lseek(cfd, (off_t)(alt + i) * lp->d_secsize, + L_SET) == -1) + fatal("lseek to badsector area: %s", + strerror(errno)); + if (write(cfd, blk, lp->d_secsize) < lp->d_secsize) + fprintf(stderr, + "%s: alternate label %d write: %s\n", + progname, i/2, strerror(errno)); + } + close(cfd); + } +#endif +} + +void +usage() +{ + if (mfs) { + fprintf(stderr, + "usage: mfs [ -fsoptions ] special-device mount-point\n"); + } else + fprintf(stderr, + "usage: newlfs [ -fsoptions ] special-device%s\n", +#ifdef COMPAT + " [device-type]"); +#else + ""); +#endif + fprintf(stderr, "where fsoptions are:\n"); + fprintf(stderr, "\t-B LFS segment size\n"); + fprintf(stderr, "\t-D debug\n"); + fprintf(stderr, "\t-F mount flags\n"); + fprintf(stderr, "\t-L create LFS file system\n"); + fprintf(stderr, + "\t-N do not create file system, just print out parameters\n"); + fprintf(stderr, "\t-S sector size\n"); +#ifdef COMPAT + fprintf(stderr, "\t-T disktype\n"); +#endif + fprintf(stderr, "\t-a maximum contiguous blocks\n"); + fprintf(stderr, "\t-b block size\n"); + fprintf(stderr, "\t-c cylinders/group\n"); + fprintf(stderr, "\t-d rotational delay between contiguous blocks\n"); + fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n"); + fprintf(stderr, "\t-f frag size\n"); + fprintf(stderr, "\t-i number of bytes per inode\n"); + fprintf(stderr, "\t-k sector 0 skew, per track\n"); + fprintf(stderr, "\t-l hardware sector interleave\n"); + fprintf(stderr, "\t-m minimum free space %%\n"); + fprintf(stderr, "\t-n number of distinguished rotational positions\n"); + fprintf(stderr, "\t-o optimization preference (`space' or `time')\n"); + fprintf(stderr, "\t-p spare sectors per track\n"); + fprintf(stderr, "\t-r revolutions/minute\n"); + fprintf(stderr, "\t-s file system size (sectors)\n"); + fprintf(stderr, "\t-t tracks/cylinder\n"); + fprintf(stderr, "\t-u sectors/track\n"); + fprintf(stderr, "\t-x spare sectors per cylinder\n"); + exit(1); +} diff --git a/sbin/newlfs/newlfs.8 b/sbin/newlfs/newlfs.8 new file mode 100644 index 0000000..92474e5 --- /dev/null +++ b/sbin/newlfs/newlfs.8 @@ -0,0 +1,95 @@ +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)newlfs.8 8.1 (Berkeley) 6/19/93 +.\" +.Dd June 19, 1993 +.Dt NEWLFS 8 +.Os BSD 4.4 +.Sh NAME +.Nm newlfs +.Nd construct a new LFS file system +.Sh SYNOPSIS +.Nm newlfs +.Fl L +.Op Ar newlfs-options +.Ar special +.Sh DESCRIPTION +.Nm Newlfs +builds a log-structured file system on the specified special +device basing its defaults on the information in the disk label. +(Before running +.Nm newlfs +the disk must be labeled using +.Xr disklabel 8 .) +.Pp +The following options define the general layout policies. +.Bl -tag -width Fl +.It Fl B +The logical segment size of the file system in bytes. +.It Fl b Ar block-size +The block size of the file system in bytes. +.It Fl L +Create a log-structured file system (LFS). +This flag is currently required. +.It Fl m Ar free space \&% +The percentage of space reserved from normal users; the minimum +free space threshold. The default value used is 10%. +See +.Xr tunefs 8 +for more details on how to set this option. +.It Fl s Ar size +The size of the file system in sectors. +.El +.Sh SEE ALSO +.Xr disktab 5 , +.Xr fs 5 , +.Xr dumplfs 8 , +.Xr disklabel 8 , +.Xr diskpart 8 , +.Xr tunefs 8 +.Rs +.%A M. McKusick +.%A W. Joy +.%A S. Leffler +.%A R. Fabry +.%T A Fast File System for UNIX , +.%J ACM Transactions on Computer Systems 2 +.%V 3 +.%P pp 181-197 +.%D August 1984 +.%O (reprinted in the BSD System Manager's Manual) +.Re +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.4 . diff --git a/sbin/nfsd/Makefile b/sbin/nfsd/Makefile new file mode 100644 index 0000000..35d0d0e --- /dev/null +++ b/sbin/nfsd/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= nfsd +CFLAGS+=-DNFS +MAN8= nfsd.0 +DPADD= ${LIBRPC} +LDADD= -lrpc + +.include <bsd.prog.mk> diff --git a/sbin/nfsd/nfsd.8 b/sbin/nfsd/nfsd.8 new file mode 100644 index 0000000..4ee6c4b --- /dev/null +++ b/sbin/nfsd/nfsd.8 @@ -0,0 +1,114 @@ +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)nfsd.8 8.3 (Berkeley) 2/22/94 +.\" +.Dd February 22, 1994 +.Dt NFSD 8 +.Os +.Sh NAME +.Nm nfsd +.Nd remote +.Tn NFS +server +.Sh SYNOPSIS +.Nm nfsd +.Op Fl rut +.Op Fl n Ar num_servers +.Sh DESCRIPTION +.Nm Nfsd +runs on a server machine to service +.Tn NFS +requests from client machines. +At least one +.Nm nfsd +must be running for a machine to operate as a server. +.Pp +Unless otherwise specified, four servers for +.Tn UDP +transport are started. +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl r +Register the +.Tn NFS +service with +.Xr portmap 8 +without creating any servers. +This option can be used along with the +.Fl u +or +.Fl t +options to re-register NFS if the portmap server is restarted. +.It Fl n +Specifies how many servers to create. +.It Fl t +Serve +.Tn TCP NFS +clients. +.It Fl u +Serve +.Tn UDP NFS +clients. +.El +.Pp +For example, +.Dq Li "nfsd -u -t 6" +serves +.Tn UDP +and +.Tn TCP +transports using six daemons. +.Pp +A server should run enough daemons to handle +the maximum level of concurrency from its clients, +typically four to six. +.Pp +.Nm Nfsd +listens for service requests at the port indicated in the +.Tn NFS +server specification; see +.%T "Network File System Protocol Specification" , +RFC1094. +.Pp +The +.Nm nfsd +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr nfsstat 1 , +.Xr nfssvc 2 , +.Xr mountd 8 , +.Xr portmap 8 +.Sh HISTORY +The +.Nm nfsd +utility first appeared in 4.4BSD. diff --git a/sbin/nfsd/nfsd.c b/sbin/nfsd/nfsd.c new file mode 100644 index 0000000..63f4f00 --- /dev/null +++ b/sbin/nfsd/nfsd.c @@ -0,0 +1,589 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rick Macklem at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif not lint + +#ifndef lint +static char sccsid[] = "@(#)nfsd.c 8.7 (Berkeley) 2/22/94"; +#endif not lint + +#include <sys/param.h> +#include <sys/syslog.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/uio.h> +#include <sys/ucred.h> +#include <sys/mount.h> +#include <sys/socket.h> +#include <sys/socketvar.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_clnt.h> +#include <rpc/pmap_prot.h> + +#ifdef ISO +#include <netiso/iso.h> +#endif +#include <nfs/rpcv2.h> +#include <nfs/nfsv2.h> +#include <nfs/nfs.h> + +#ifdef KERBEROS +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> +#endif + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> + +/* Global defs */ +#ifdef DEBUG +#define syslog(e, s) fprintf(stderr,(s)) +int debug = 1; +#else +int debug = 0; +#endif + +struct nfsd_srvargs nsd; +char **Argv = NULL; /* pointer to argument vector */ +char *LastArg = NULL; /* end of argv */ + +#ifdef KERBEROS +char lnam[ANAME_SZ]; +KTEXT_ST kt; +AUTH_DAT auth; +char inst[INST_SZ]; +#endif + +void nonfs __P((int)); +void reapchild __P((int)); +void setproctitle __P((char *)); +void usage __P((void)); + +/* + * Nfs server daemon mostly just a user context for nfssvc() + * + * 1 - do file descriptor and signal cleanup + * 2 - fork the nfsd(s) + * 3 - create server socket(s) + * 4 - register socket with portmap + * + * For connectionless protocols, just pass the socket into the kernel via. + * nfssvc(). + * For connection based sockets, loop doing accepts. When you get a new + * socket from accept, pass the msgsock into the kernel via. nfssvc(). + * The arguments are: + * -c - support iso cltp clients + * -r - reregister with portmapper + * -t - support tcp nfs clients + * -u - support udp nfs clients + * followed by "n" which is the number of nfsds' to fork off + */ +int +main(argc, argv, envp) + int argc; + char *argv[], *envp[]; +{ + extern int optind; + struct group *grp; + struct nfsd_args nfsdargs; + struct passwd *pwd; + struct ucred *cr; + struct sockaddr_in inetaddr, inetpeer; +#ifdef ISO + struct sockaddr_iso isoaddr, isopeer; +#endif + fd_set ready, sockbits; + int ch, cltpflag, connect_type_cnt, i, len, maxsock, msgsock; + int nfsdcnt, nfssvc_flag, on, reregister, sock, tcpflag, tcpsock; + int tp4cnt, tp4flag, tp4sock, tpipcnt, tpipflag, tpipsock, udpflag; + char *cp, **cpp; + + /* Save start and extent of argv for setproctitle. */ + Argv = argv; + if (envp == 0 || *envp == 0) + envp = argv; + while (*envp) + envp++; + LastArg = envp[-1] + strlen(envp[-1]); + +#define MAXNFSDCNT 20 +#define DEFNFSDCNT 4 + nfsdcnt = DEFNFSDCNT; + cltpflag = reregister = tcpflag = tp4cnt = tp4flag = tpipcnt = 0; + tpipflag = udpflag = 0; +#ifdef ISO +#define GETOPT "cn:rtu" +#define USAGE "[-crtu] [-n num_servers]" +#else +#define GETOPT "n:rtu" +#define USAGE "[-rtu] [-n num_servers]" +#endif + while ((ch = getopt(argc, argv, GETOPT)) != EOF) + switch (ch) { + case 'n': + nfsdcnt = atoi(optarg); + if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) { + warnx("nfsd count %d; reset to %d", DEFNFSDCNT); + nfsdcnt = DEFNFSDCNT; + } + break; + case 'r': + reregister = 1; + break; + case 't': + tcpflag = 1; + break; + case 'u': + udpflag = 1; + break; +#ifdef ISO + case 'c': + cltpflag = 1; + break; +#ifdef notyet + case 'i': + tp4cnt = 1; + break; + case 'p': + tpipcnt = 1; + break; +#endif /* notyet */ +#endif /* ISO */ + default: + case '?': + usage(); + }; + argv += optind; + argc -= optind; + + /* + * XXX + * Backward compatibility, trailing number is the count of daemons. + */ + if (argc > 1) + usage(); + if (argc == 1) { + nfsdcnt = atoi(argv[0]); + if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) { + warnx("nfsd count %d; reset to %d", DEFNFSDCNT); + nfsdcnt = DEFNFSDCNT; + } + } + + if (debug == 0) { + daemon(0, 0); + (void)signal(SIGHUP, SIG_IGN); + (void)signal(SIGINT, SIG_IGN); + (void)signal(SIGQUIT, SIG_IGN); + (void)signal(SIGSYS, nonfs); + (void)signal(SIGTERM, SIG_IGN); + } + (void)signal(SIGCHLD, reapchild); + + if (reregister) { + if (udpflag && + !pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) + err(1, "can't register with portmap for UDP."); + if (tcpflag && + !pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) + err(1, "can't register with portmap for TCP."); + exit(0); + } + openlog("nfsd:", LOG_PID, LOG_DAEMON); + + for (i = 0; i < nfsdcnt; i++) { + switch (fork()) { + case -1: + syslog(LOG_ERR, "fork: %m"); + exit (1); + case 0: + break; + default: + continue; + } + + setproctitle("nfsd-srv"); + nfssvc_flag = NFSSVC_NFSD; + nsd.nsd_nfsd = NULL; +#ifdef KERBEROS + nsd.nsd_authstr = (char *)kt.dat; +#endif + while (nfssvc(nfssvc_flag, &nsd) < 0) { + if (errno != ENEEDAUTH) { + syslog(LOG_ERR, "nfssvc: %m"); + exit(1); + } + nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHINFAIL; +#ifdef KERBEROS + kt.length = nsd.nsd_authlen; + kt.mbz = 0; + (void)strcpy(inst, "*"); + if (krb_rd_req(&kt, "rcmd", + inst, nsd.nsd_haddr, &auth, "") == RD_AP_OK && + krb_kntoln(&auth, lnam) == KSUCCESS && + (pwd = getpwnam(lnam)) != NULL) { + cr = &nsd.nsd_cr; + cr->cr_uid = pwd->pw_uid; + cr->cr_groups[0] = pwd->pw_gid; + cr->cr_ngroups = 1; + setgrent(); + while ((grp = getgrent()) != NULL) { + if (grp->gr_gid == cr->cr_groups[0]) + continue; + for (cpp = grp->gr_mem; + *cpp != NULL; ++cpp) + if (!strcmp(*cpp, lnam)) + break; + if (*cpp == NULL) + continue; + cr->cr_groups[cr->cr_ngroups++] + = grp->gr_gid; + if (cr->cr_ngroups == NGROUPS) + break; + } + endgrent(); + nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHIN; + } +#endif /* KERBEROS */ + } + exit(0); + } + + /* If we are serving udp, set up the socket. */ + if (udpflag) { + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "can't create udp socket"); + exit(1); + } + inetaddr.sin_family = AF_INET; + inetaddr.sin_addr.s_addr = INADDR_ANY; + inetaddr.sin_port = htons(NFS_PORT); + inetaddr.sin_len = sizeof(inetaddr); + if (bind(sock, + (struct sockaddr *)&inetaddr, sizeof(inetaddr)) < 0) { + syslog(LOG_ERR, "can't bind udp addr"); + exit(1); + } + if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) { + syslog(LOG_ERR, "can't register with udp portmap"); + exit(1); + } + nfsdargs.sock = sock; + nfsdargs.name = NULL; + nfsdargs.namelen = 0; + if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) { + syslog(LOG_ERR, "can't Add UDP socket"); + exit(1); + } + (void)close(sock); + } + +#ifdef ISO + /* If we are serving cltp, set up the socket. */ + if (cltpflag) { + if ((sock = socket(AF_ISO, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "can't create cltp socket"); + exit(1); + } + memset(&isoaddr, 0, sizeof(isoaddr)); + isoaddr.siso_family = AF_ISO; + isoaddr.siso_tlen = 2; + cp = TSEL(&isoaddr); + *cp++ = (NFS_PORT >> 8); + *cp = (NFS_PORT & 0xff); + isoaddr.siso_len = sizeof(isoaddr); + if (bind(sock, + (struct sockaddr *)&isoaddr, sizeof(isoaddr)) < 0) { + syslog(LOG_ERR, "can't bind cltp addr"); + exit(1); + } +#ifdef notyet + /* + * XXX + * Someday this should probably use "rpcbind", the son of + * portmap. + */ + if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) { + syslog(LOG_ERR, "can't register with udp portmap"); + exit(1); + } +#endif /* notyet */ + nfsdargs.sock = sock; + nfsdargs.name = NULL; + nfsdargs.namelen = 0; + if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) { + syslog(LOG_ERR, "can't add UDP socket"); + exit(1); + } + close(sock); + } +#endif /* ISO */ + + /* Now set up the master server socket waiting for tcp connections. */ + on = 1; + FD_ZERO(&sockbits); + connect_type_cnt = 0; + if (tcpflag) { + if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + syslog(LOG_ERR, "can't create tcp socket"); + exit(1); + } + if (setsockopt(tcpsock, + SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); + inetaddr.sin_family = AF_INET; + inetaddr.sin_addr.s_addr = INADDR_ANY; + inetaddr.sin_port = htons(NFS_PORT); + inetaddr.sin_len = sizeof(inetaddr); + if (bind(tcpsock, + (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) { + syslog(LOG_ERR, "can't bind tcp addr"); + exit(1); + } + if (listen(tcpsock, 5) < 0) { + syslog(LOG_ERR, "listen failed"); + exit(1); + } + if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) { + syslog(LOG_ERR, "can't register tcp with portmap"); + exit(1); + } + FD_SET(tcpsock, &sockbits); + maxsock = tcpsock; + connect_type_cnt++; + } + +#ifdef notyet + /* Now set up the master server socket waiting for tp4 connections. */ + if (tp4flag) { + if ((tp4sock = socket(AF_ISO, SOCK_SEQPACKET, 0)) < 0) { + syslog(LOG_ERR, "can't create tp4 socket"); + exit(1); + } + if (setsockopt(tp4sock, + SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); + memset(&isoaddr, 0, sizeof(isoaddr)); + isoaddr.siso_family = AF_ISO; + isoaddr.siso_tlen = 2; + cp = TSEL(&isoaddr); + *cp++ = (NFS_PORT >> 8); + *cp = (NFS_PORT & 0xff); + isoaddr.siso_len = sizeof(isoaddr); + if (bind(tp4sock, + (struct sockaddr *)&isoaddr, sizeof (isoaddr)) < 0) { + syslog(LOG_ERR, "can't bind tp4 addr"); + exit(1); + } + if (listen(tp4sock, 5) < 0) { + syslog(LOG_ERR, "listen failed"); + exit(1); + } + /* + * XXX + * Someday this should probably use "rpcbind", the son of + * portmap. + */ + if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) { + syslog(LOG_ERR, "can't register tcp with portmap"); + exit(1); + } + FD_SET(tp4sock, &sockbits); + maxsock = tp4sock; + connect_type_cnt++; + } + + /* Now set up the master server socket waiting for tpip connections. */ + if (tpipflag) { + if ((tpipsock = socket(AF_INET, SOCK_SEQPACKET, 0)) < 0) { + syslog(LOG_ERR, "can't create tpip socket"); + exit(1); + } + if (setsockopt(tpipsock, + SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); + inetaddr.sin_family = AF_INET; + inetaddr.sin_addr.s_addr = INADDR_ANY; + inetaddr.sin_port = htons(NFS_PORT); + inetaddr.sin_len = sizeof(inetaddr); + if (bind(tpipsock, + (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) { + syslog(LOG_ERR, "can't bind tcp addr"); + exit(1); + } + if (listen(tpipsock, 5) < 0) { + syslog(LOG_ERR, "listen failed"); + exit(1); + } + /* + * XXX + * Someday this should probably use "rpcbind", the son of + * portmap. + */ + if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) { + syslog(LOG_ERR, "can't register tcp with portmap"); + exit(1); + } + FD_SET(tpipsock, &sockbits); + maxsock = tpipsock; + connect_type_cnt++; + } +#endif /* notyet */ + + if (connect_type_cnt == 0) + exit(0); + + setproctitle("nfsd-master"); + + /* + * Loop forever accepting connections and passing the sockets + * into the kernel for the mounts. + */ + for (;;) { + ready = sockbits; + if (connect_type_cnt > 1) { + if (select(maxsock + 1, + &ready, NULL, NULL, NULL) < 1) { + syslog(LOG_ERR, "select failed: %m"); + exit(1); + } + } + if (tcpflag && FD_ISSET(tcpsock, &ready)) { + len = sizeof(inetpeer); + if ((msgsock = accept(tcpsock, + (struct sockaddr *)&inetpeer, &len)) < 0) { + syslog(LOG_ERR, "accept failed: %m"); + exit(1); + } + memset(inetpeer.sin_zero, 0, sizeof(inetpeer.sin_zero)); + if (setsockopt(msgsock, SOL_SOCKET, + SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, + "setsockopt SO_KEEPALIVE: %m"); + nfsdargs.sock = msgsock; + nfsdargs.name = (caddr_t)&inetpeer; + nfsdargs.namelen = sizeof(inetpeer); + nfssvc(NFSSVC_ADDSOCK, &nfsdargs); + (void)close(msgsock); + } +#ifdef notyet + if (tp4flag && FD_ISSET(tp4sock, &ready)) { + len = sizeof(isopeer); + if ((msgsock = accept(tp4sock, + (struct sockaddr *)&isopeer, &len)) < 0) { + syslog(LOG_ERR, "accept failed: %m"); + exit(1); + } + if (setsockopt(msgsock, SOL_SOCKET, + SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, + "setsockopt SO_KEEPALIVE: %m"); + nfsdargs.sock = msgsock; + nfsdargs.name = (caddr_t)&isopeer; + nfsdargs.namelen = len; + nfssvc(NFSSVC_ADDSOCK, &nfsdargs); + (void)close(msgsock); + } + if (tpipflag && FD_ISSET(tpipsock, &ready)) { + len = sizeof(inetpeer); + if ((msgsock = accept(tpipsock, + (struct sockaddr *)&inetpeer, &len)) < 0) { + syslog(LOG_ERR, "Accept failed: %m"); + exit(1); + } + if (setsockopt(msgsock, SOL_SOCKET, + SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt SO_KEEPALIVE: %m"); + nfsdargs.sock = msgsock; + nfsdargs.name = (caddr_t)&inetpeer; + nfsdargs.namelen = len; + nfssvc(NFSSVC_ADDSOCK, &nfsdargs); + (void)close(msgsock); + } +#endif /* notyet */ + } +} + +void +usage() +{ + (void)fprintf(stderr, "nfsd %s\n", USAGE); + exit(1); +} + +void +nonfs(signo) + int signo; +{ + syslog(LOG_ERR, "missing system call: NFS not available."); +} + +void +reapchild(signo) + int signo; +{ + + while (wait3(NULL, WNOHANG, NULL)); +} + +void +setproctitle(a) + char *a; +{ + register char *cp; + char buf[80]; + + cp = Argv[0]; + (void)snprintf(buf, sizeof(buf), "%s", a); + (void)strncpy(cp, buf, LastArg - cp); + cp += strlen(cp); + while (cp < LastArg) + *cp++ = ' '; +} diff --git a/sbin/nfsiod/Makefile b/sbin/nfsiod/Makefile new file mode 100644 index 0000000..72db42b --- /dev/null +++ b/sbin/nfsiod/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= nfsiod +CFLAGS+=-DNFS +MAN8= nfsiod.0 + +.include <bsd.prog.mk> diff --git a/sbin/nfsiod/nfsiod.8 b/sbin/nfsiod/nfsiod.8 new file mode 100644 index 0000000..2acd365 --- /dev/null +++ b/sbin/nfsiod/nfsiod.8 @@ -0,0 +1,74 @@ +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)nfsiod.8 8.2 (Berkeley) 2/22/94 +.\" +.Dd February 22, 1994 +.Dt NFSIOD 8 +.Os +.Sh NAME +.Nm nfsiod +.Nd local +.Tn NFS +asynchronous I/O server +.Sh SYNOPSIS +.Nm nfsiod +.Op Fl n Ar num_servers +.Sh DESCRIPTION +.Nm Nfsiod +runs on an +.Tn NFS +client machine to service asynchronous I/O requests to its server. +It improves performance but is not required for correct operation. +.Pp +Unless otherwise specified, a single server is started. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl n +Specify how many servers are to be started. +.El +.Pp +A client should run enough daemons to handle its maximum +level of concurrency, typically four to six. +.Pp +The +.Nm nfsiod +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr nfsstat 1 , +.Xr nfssvc 2 , +.Xr mountd 8 , +.Xr portmap 8 +.Sh HISTORY +The +.Nm nfsiod +utility first appeared in 4.4BSD. diff --git a/sbin/nfsiod/nfsiod.c b/sbin/nfsiod/nfsiod.c new file mode 100644 index 0000000..b742fdc --- /dev/null +++ b/sbin/nfsiod/nfsiod.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rick Macklem at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif not lint + +#ifndef lint +static char sccsid[] = "@(#)nfsiod.c 8.3 (Berkeley) 2/22/94"; +#endif not lint + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/syslog.h> +#include <sys/ucred.h> +#include <sys/wait.h> + +#include <nfs/nfsv2.h> +#include <nfs/nfs.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +/* Global defs */ +#ifdef DEBUG +int debug = 1; +#else +int debug = 0; +#endif + +void nonfs __P((int)); +void reapchild __P((int)); +void usage __P((void)); + +/* + * Nfsiod does asynchronous buffered I/O on behalf of the NFS client. + * It does not have to be running for correct operation, but will + * improve throughput. + */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, num_servers; + +#define MAXNFSDCNT 20 +#define DEFNFSDCNT 1 + num_servers = DEFNFSDCNT; + while ((ch = getopt(argc, argv, "n:")) != EOF) + switch (ch) { + case 'n': + num_servers = atoi(optarg); + if (num_servers < 1 || num_servers > MAXNFSDCNT) { + warnx("nfsiod count %d; reset to %d", + DEFNFSDCNT); + num_servers = DEFNFSDCNT; + } + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * XXX + * Backward compatibility, trailing number is the count of daemons. + */ + if (argc > 1) + usage(); + if (argc == 1) { + num_servers = atoi(argv[0]); + if (num_servers < 1 || num_servers > MAXNFSDCNT) { + warnx("nfsiod count %d; reset to %d", DEFNFSDCNT); + num_servers = DEFNFSDCNT; + } + } + + if (debug == 0) { + daemon(0, 0); + (void)signal(SIGHUP, SIG_IGN); + (void)signal(SIGINT, SIG_IGN); + (void)signal(SIGQUIT, SIG_IGN); + (void)signal(SIGSYS, nonfs); + } + (void)signal(SIGCHLD, reapchild); + + openlog("nfsiod:", LOG_PID, LOG_DAEMON); + + while (num_servers--) + switch (fork()) { + case -1: + syslog(LOG_ERR, "fork: %m"); + exit (1); + case 0: + if (nfssvc(NFSSVC_BIOD, NULL) < 0) { + syslog(LOG_ERR, "nfssvc: %m"); + exit (1); + } + exit(0); + } + exit (0); +} + +void +nonfs(signo) + int signo; +{ + syslog(LOG_ERR, "missing system call: NFS not available."); +} + +void +reapchild(signo) + int signo; +{ + + while (wait3(NULL, WNOHANG, NULL)); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: nfsiod [-n num_servers]\n"); + exit(1); +} diff --git a/sbin/nologin/Makefile b/sbin/nologin/Makefile new file mode 100644 index 0000000..84e9f0c --- /dev/null +++ b/sbin/nologin/Makefile @@ -0,0 +1,14 @@ +# @(#)Makefile 8.2 (Berkeley) 4/22/94 + +MAN8= nologin.0 + +nologin clean depend lint tags: + +beforeinstall: + install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + ${.CURDIR}/nologin.sh ${DESTDIR}/sbin/nologin + +cleandir: + rm -f nologin.0 + +.include <bsd.prog.mk> diff --git a/sbin/nologin/nologin.8 b/sbin/nologin/nologin.8 new file mode 100644 index 0000000..32a7e73 --- /dev/null +++ b/sbin/nologin/nologin.8 @@ -0,0 +1,54 @@ +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)nologin.8 8.1 (Berkeley) 6/19/93 +.\" +.Dd June 19, 1993 +.Dt NOLOGIN 8 +.Os BSD 4.4 +.Sh NAME +.Nm nologin +.Nd politely refuse a login +.Sh SYNOPSIS +.Nm nologin +.Sh DESCRIPTION +.Nm Nologin +displays a message that an account is not available and +exits non-zero. +It is intended as a replacement shell field for accounts that +have been disabled. +.Sh SEE ALSO +.Xr login 1 +.Sh HISTORY +The +.Nm nologin +command appeared in +.Bx 4.4 . diff --git a/sbin/nologin/nologin.sh b/sbin/nologin/nologin.sh new file mode 100644 index 0000000..8ad87fb --- /dev/null +++ b/sbin/nologin/nologin.sh @@ -0,0 +1,38 @@ +#!/bin/sh - +# +# Copyright (c) 1992, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)nologin.sh 8.1 (Berkeley) 6/5/93 +# + +echo 'This account is currently not available.' +exit 1 diff --git a/sbin/ping/Makefile b/sbin/ping/Makefile new file mode 100644 index 0000000..d08f74d --- /dev/null +++ b/sbin/ping/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= ping +MAN8= ping.0 +BINOWN= root +BINMODE=4555 + +.include <bsd.prog.mk> diff --git a/sbin/ping/ping.8 b/sbin/ping/ping.8 new file mode 100644 index 0000000..e3de68a --- /dev/null +++ b/sbin/ping/ping.8 @@ -0,0 +1,327 @@ +.\" Copyright (c) 1985, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ping.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt PING 8 +.Os BSD 4.3 +.Sh NAME +.Nm ping +.Nd send +.Tn ICMP ECHO_REQUEST +packets to network hosts +.Sh SYNOPSIS +.Nm ping +.Op Fl dfnqrvR +.Op Fl c Ar count +.Op Fl i Ar wait +.Op Fl l Ar preload +.Op Fl p Ar pattern +.Op Fl s Ar packetsize +.Sh DESCRIPTION +.Nm Ping +uses the +.Tn ICMP +protocol's mandatory +.Tn ECHO_REQUEST +datagram to elicit an +.Tn ICMP ECHO_RESPONSE +from a host or gateway. +.Tn ECHO_REQUEST +datagrams (``pings'') have an IP and +.Tn ICMP +header, +followed by a +.Dq struct timeval +and then an arbitrary number of ``pad'' bytes used to fill out the +packet. +The options are as follows: +.Bl -tag -width Ds +.It Fl c Ar count +Stop after sending (and receiving) +.Ar count +.Tn ECHO_RESPONSE +packets. +.It Fl d +Set the +.Dv SO_DEBUG +option on the socket being used. +.It Fl f +Flood ping. +Outputs packets as fast as they come back or one hundred times per second, +whichever is more. +For every +.Tn ECHO_REQUEST +sent a period ``.'' is printed, while for every +.Tn ECHO_REPLY +received a backspace is printed. +This provides a rapid display of how many packets are being dropped. +Only the super-user may use this option. +.Bf -emphasis +This can be very hard on a network and should be used with caution. +.Ef +.It Fl i Ar wait +Wait +.Ar wait +seconds +.Em between sending each packet . +The default is to wait for one second between each packet. +This option is incompatible with the +.Fl f +option. +.It Fl l Ar preload +If +.Ar preload +is specified, +.Nm ping +sends that many packets as fast as possible before falling into its normal +mode of behavior. +.It Fl n +Numeric output only. +No attempt will be made to lookup symbolic names for host addresses. +.It Fl p Ar pattern +You may specify up to 16 ``pad'' bytes to fill out the packet you send. +This is useful for diagnosing data-dependent problems in a network. +For example, +.Dq Li \-p ff +will cause the sent packet to be filled with all +ones. +.It Fl q +Quiet output. +Nothing is displayed except the summary lines at startup time and +when finished. +.It Fl R +Record route. +Includes the +.Tn RECORD_ROUTE +option in the +.Tn ECHO_REQUEST +packet and displays +the route buffer on returned packets. +Note that the IP header is only large enough for nine such routes. +Many hosts ignore or discard this option. +.It Fl r +Bypass the normal routing tables and send directly to a host on an attached +network. +If the host is not on a directly-attached network, an error is returned. +This option can be used to ping a local host through an interface +that has no route through it (e.g., after the interface was dropped by +.Xr routed 8 ) . +.It Fl s Ar packetsize +Specifies the number of data bytes to be sent. +The default is 56, which translates into 64 +.Tn ICMP +data bytes when combined +with the 8 bytes of +.Tn ICMP +header data. +.It Fl v +Verbose output. +.Tn ICMP +packets other than +.Tn ECHO_RESPONSE +that are received are listed. +.El +.Pp +When using +.Nm ping +for fault isolation, it should first be run on the local host, to verify +that the local network interface is up and running. +Then, hosts and gateways further and further away should be ``pinged''. +Round-trip times and packet loss statistics are computed. +If duplicate packets are received, they are not included in the packet +loss calculation, although the round trip time of these packets is used +in calculating the minimum/average/maximum round-trip time numbers. +When the specified number of packets have been sent (and received) or +if the program is terminated with a +.Dv SIGINT , +a brief summary is displayed. +.Pp +This program is intended for use in network testing, measurement and +management. +Because of the load it can impose on the network, it is unwise to use +.Nm ping +during normal operations or from automated scripts. +.Sh ICMP PACKET DETAILS +An IP header without options is 20 bytes. +An +.Tn ICMP +.Tn ECHO_REQUEST +packet contains an additional 8 bytes worth +of +.Tn ICMP +header followed by an arbitrary amount of data. +When a +.Ar packetsize +is given, this indicated the size of this extra piece of data (the +default is 56). +Thus the amount of data received inside of an IP packet of type +.Tn ICMP +.Tn ECHO_REPLY +will always be 8 bytes more than the requested data space +(the +.Tn ICMP +header). +.Pp +If the data space is at least eight bytes large, +.Nm ping +uses the first eight bytes of this space to include a timestamp which +it uses in the computation of round trip times. +If less than eight bytes of pad are specified, no round trip times are +given. +.Sh DUPLICATE AND DAMAGED PACKETS +.Nm Ping +will report duplicate and damaged packets. +Duplicate packets should never occur, and seem to be caused by +inappropriate link-level retransmissions. +Duplicates may occur in many situations and are rarely (if ever) a +good sign, although the presence of low levels of duplicates may not +always be cause for alarm. +.Pp +Damaged packets are obviously serious cause for alarm and often +indicate broken hardware somewhere in the +.Nm ping +packet's path (in the network or in the hosts). +.Sh TRYING DIFFERENT DATA PATTERNS +The (inter)network layer should never treat packets differently depending +on the data contained in the data portion. +Unfortunately, data-dependent problems have been known to sneak into +networks and remain undetected for long periods of time. +In many cases the particular pattern that will have problems is something +that doesn't have sufficient ``transitions'', such as all ones or all +zeros, or a pattern right at the edge, such as almost all zeros. +It isn't necessarily enough to specify a data pattern of all zeros (for +example) on the command line because the pattern that is of interest is +at the data link level, and the relationship between what you type and +what the controllers transmit can be complicated. +.Pp +This means that if you have a data-dependent problem you will probably +have to do a lot of testing to find it. +If you are lucky, you may manage to find a file that either can't be sent +across your network or that takes much longer to transfer than other +similar length files. +You can then examine this file for repeated patterns that you can test +using the +.Fl p +option of +.Nm ping . +.Sh TTL DETAILS +The +.Tn TTL +value of an IP packet represents the maximum number of IP routers +that the packet can go through before being thrown away. +In current practice you can expect each router in the Internet to decrement +the +.Tn TTL +field by exactly one. +.Pp +The +.Tn TCP/IP +specification states that the +.Tn TTL +field for +.Tn TCP +packets should +be set to 60, but many systems use smaller values (4.3 +.Tn BSD +uses 30, 4.2 used +15). +.Pp +The maximum possible value of this field is 255, and most Unix systems set +the +.Tn TTL +field of +.Tn ICMP ECHO_REQUEST +packets to 255. +This is why you will find you can ``ping'' some hosts, but not reach them +with +.Xr telnet 1 +or +.Xr ftp 1 . +.Pp +In normal operation ping prints the ttl value from the packet it receives. +When a remote system receives a ping packet, it can do one of three things +with the +.Tn TTL +field in its response: +.Bl -bullet +.It +Not change it; this is what Berkeley Unix systems did before the +.Bx 4.3 tahoe +release. +In this case the +.Tn TTL +value in the received packet will be 255 minus the +number of routers in the round-trip path. +.It +Set it to 255; this is what current Berkeley Unix systems do. +In this case the +.Tn TTL +value in the received packet will be 255 minus the +number of routers in the path +.Xr from +the remote system +.Em to +the +.Nm ping Ns Em ing +host. +.It +Set it to some other value. +Some machines use the same value for +.Tn ICMP +packets that they use for +.Tn TCP +packets, for example either 30 or 60. +Others may use completely wild values. +.El +.Sh BUGS +Many Hosts and Gateways ignore the +.Tn RECORD_ROUTE +option. +.Pp +The maximum IP header length is too small for options like +.Tn RECORD_ROUTE +to +be completely useful. +There's not much that that can be done about this, however. +.Pp +Flood pinging is not recommended in general, and flood pinging the +broadcast address should only be done under very controlled conditions. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr ifconfig 8 , +.Xr routed 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c new file mode 100644 index 0000000..46db1ca --- /dev/null +++ b/sbin/ping/ping.c @@ -0,0 +1,986 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +/* + * P I N G . C + * + * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, + * measure round-trip-delays and packet loss across network paths. + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * + * Status - + * Public Domain. Distribution Unlimited. + * Bugs - + * More statistics could always be gathered. + * This program has to run SUID to ROOT to access the ICMP socket. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/signal.h> + +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <netinet/ip_var.h> +#include <netdb.h> +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <string.h> + +#define DEFDATALEN (64 - 8) /* default data length */ +#define MAXIPLEN 60 +#define MAXICMPLEN 76 +#define MAXPACKET (65536 - 60 - 8)/* max packet size */ +#define MAXWAIT 10 /* max seconds to wait for response */ +#define NROUTES 9 /* number of record route slots */ + +#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ +#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ +#define SET(bit) (A(bit) |= B(bit)) +#define CLR(bit) (A(bit) &= (~B(bit))) +#define TST(bit) (A(bit) & B(bit)) + +/* various options */ +int options; +#define F_FLOOD 0x001 +#define F_INTERVAL 0x002 +#define F_NUMERIC 0x004 +#define F_PINGFILLED 0x008 +#define F_QUIET 0x010 +#define F_RROUTE 0x020 +#define F_SO_DEBUG 0x040 +#define F_SO_DONTROUTE 0x080 +#define F_VERBOSE 0x100 + +/* + * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum + * number of received sequence numbers we can keep track of. Change 128 + * to 8192 for complete accuracy... + */ +#define MAX_DUP_CHK (8 * 128) +int mx_dup_ck = MAX_DUP_CHK; +char rcvd_tbl[MAX_DUP_CHK / 8]; + +struct sockaddr whereto; /* who to ping */ +int datalen = DEFDATALEN; +int s; /* socket file descriptor */ +u_char outpack[MAXPACKET]; +char BSPACE = '\b'; /* characters written for flood */ +char DOT = '.'; +char *hostname; +int ident; /* process id to identify our packets */ + +/* counters */ +long npackets; /* max packets to transmit */ +long nreceived; /* # of packets we got back */ +long nrepeats; /* number of duplicates */ +long ntransmitted; /* sequence # for outbound packets = #sent */ +int interval = 1; /* interval between packets */ + +/* timing */ +int timing; /* flag to do timing */ +double tmin = 999999999.0; /* minimum round trip time */ +double tmax = 0.0; /* maximum round trip time */ +double tsum = 0.0; /* sum of all times, for doing average */ + +char *pr_addr(); +void catcher(), finish(); + +main(argc, argv) + int argc; + char **argv; +{ + extern int errno, optind; + extern char *optarg; + struct timeval timeout; + struct hostent *hp; + struct sockaddr_in *to; + struct protoent *proto; + register int i; + int ch, fdmask, hold, packlen, preload; + u_char *datap, *packet; + char *target, hnamebuf[MAXHOSTNAMELEN], *malloc(); +#ifdef IP_OPTIONS + char rspace[3 + 4 * NROUTES + 1]; /* record route space */ +#endif + + preload = 0; + datap = &outpack[8 + sizeof(struct timeval)]; + while ((ch = getopt(argc, argv, "Rc:dfh:i:l:np:qrs:v")) != EOF) + switch(ch) { + case 'c': + npackets = atoi(optarg); + if (npackets <= 0) { + (void)fprintf(stderr, + "ping: bad number of packets to transmit.\n"); + exit(1); + } + break; + case 'd': + options |= F_SO_DEBUG; + break; + case 'f': + if (getuid()) { + (void)fprintf(stderr, + "ping: %s\n", strerror(EPERM)); + exit(1); + } + options |= F_FLOOD; + setbuf(stdout, (char *)NULL); + break; + case 'i': /* wait between sending packets */ + interval = atoi(optarg); + if (interval <= 0) { + (void)fprintf(stderr, + "ping: bad timing interval.\n"); + exit(1); + } + options |= F_INTERVAL; + break; + case 'l': + preload = atoi(optarg); + if (preload < 0) { + (void)fprintf(stderr, + "ping: bad preload value.\n"); + exit(1); + } + break; + case 'n': + options |= F_NUMERIC; + break; + case 'p': /* fill buffer with user pattern */ + options |= F_PINGFILLED; + fill((char *)datap, optarg); + break; + case 'q': + options |= F_QUIET; + break; + case 'R': + options |= F_RROUTE; + break; + case 'r': + options |= F_SO_DONTROUTE; + break; + case 's': /* size of packet to send */ + datalen = atoi(optarg); + if (datalen > MAXPACKET) { + (void)fprintf(stderr, + "ping: packet size too large.\n"); + exit(1); + } + if (datalen <= 0) { + (void)fprintf(stderr, + "ping: illegal packet size.\n"); + exit(1); + } + break; + case 'v': + options |= F_VERBOSE; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + target = *argv; + + bzero((char *)&whereto, sizeof(struct sockaddr)); + to = (struct sockaddr_in *)&whereto; + to->sin_family = AF_INET; + to->sin_addr.s_addr = inet_addr(target); + if (to->sin_addr.s_addr != (u_int)-1) + hostname = target; + else { + hp = gethostbyname(target); + if (!hp) { + (void)fprintf(stderr, + "ping: unknown host %s\n", target); + exit(1); + } + to->sin_family = hp->h_addrtype; + bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length); + (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1); + hostname = hnamebuf; + } + + if (options & F_FLOOD && options & F_INTERVAL) { + (void)fprintf(stderr, + "ping: -f and -i incompatible options.\n"); + exit(1); + } + + if (datalen >= sizeof(struct timeval)) /* can we time transfer */ + timing = 1; + packlen = datalen + MAXIPLEN + MAXICMPLEN; + if (!(packet = (u_char *)malloc((u_int)packlen))) { + (void)fprintf(stderr, "ping: out of memory.\n"); + exit(1); + } + if (!(options & F_PINGFILLED)) + for (i = 8; i < datalen; ++i) + *datap++ = i; + + ident = getpid() & 0xFFFF; + + if (!(proto = getprotobyname("icmp"))) { + (void)fprintf(stderr, "ping: unknown protocol icmp.\n"); + exit(1); + } + if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { + perror("ping: socket"); + exit(1); + } + hold = 1; + if (options & F_SO_DEBUG) + (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold, + sizeof(hold)); + if (options & F_SO_DONTROUTE) + (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, + sizeof(hold)); + + /* record route option */ + if (options & F_RROUTE) { +#ifdef IP_OPTIONS + rspace[IPOPT_OPTVAL] = IPOPT_RR; + rspace[IPOPT_OLEN] = sizeof(rspace)-1; + rspace[IPOPT_OFFSET] = IPOPT_MINOFF; + if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, + sizeof(rspace)) < 0) { + perror("ping: record route"); + exit(1); + } +#else + (void)fprintf(stderr, + "ping: record route not available in this implementation.\n"); + exit(1); +#endif /* IP_OPTIONS */ + } + + /* + * When pinging the broadcast address, you can get a lot of answers. + * Doing something so evil is useful if you are trying to stress the + * ethernet, or just want to fill the arp cache to get some stuff for + * /etc/ethers. + */ + hold = 48 * 1024; + (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, + sizeof(hold)); + + if (to->sin_family == AF_INET) + (void)printf("PING %s (%s): %d data bytes\n", hostname, + inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr), + datalen); + else + (void)printf("PING %s: %d data bytes\n", hostname, datalen); + + (void)signal(SIGINT, finish); + (void)signal(SIGALRM, catcher); + + while (preload--) /* fire off them quickies */ + pinger(); + + if ((options & F_FLOOD) == 0) + catcher(); /* start things going */ + + for (;;) { + struct sockaddr_in from; + register int cc; + int fromlen; + + if (options & F_FLOOD) { + pinger(); + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + fdmask = 1 << s; + if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL, + (fd_set *)NULL, &timeout) < 1) + continue; + } + fromlen = sizeof(from); + if ((cc = recvfrom(s, (char *)packet, packlen, 0, + (struct sockaddr *)&from, &fromlen)) < 0) { + if (errno == EINTR) + continue; + perror("ping: recvfrom"); + continue; + } + pr_pack((char *)packet, cc, &from); + if (npackets && nreceived >= npackets) + break; + } + finish(); + /* NOTREACHED */ +} + +/* + * catcher -- + * This routine causes another PING to be transmitted, and then + * schedules another SIGALRM for 1 second from now. + * + * bug -- + * Our sense of time will slowly skew (i.e., packets will not be + * launched exactly at 1-second intervals). This does not affect the + * quality of the delay and loss statistics. + */ +void +catcher() +{ + int waittime; + + pinger(); + (void)signal(SIGALRM, catcher); + if (!npackets || ntransmitted < npackets) + alarm((u_int)interval); + else { + if (nreceived) { + waittime = 2 * tmax / 1000; + if (!waittime) + waittime = 1; + } else + waittime = MAXWAIT; + (void)signal(SIGALRM, finish); + (void)alarm((u_int)waittime); + } +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is our UNIX process ID, + * and the sequence number is an ascending integer. The first 8 bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time. + */ +pinger() +{ + register struct icmp *icp; + register int cc; + int i; + + icp = (struct icmp *)outpack; + icp->icmp_type = ICMP_ECHO; + icp->icmp_code = 0; + icp->icmp_cksum = 0; + icp->icmp_seq = ntransmitted++; + icp->icmp_id = ident; /* ID */ + + CLR(icp->icmp_seq % mx_dup_ck); + + if (timing) + (void)gettimeofday((struct timeval *)&outpack[8], + (struct timezone *)NULL); + + cc = datalen + 8; /* skips ICMP portion */ + + /* compute ICMP checksum here */ + icp->icmp_cksum = in_cksum((u_short *)icp, cc); + + i = sendto(s, (char *)outpack, cc, 0, &whereto, + sizeof(struct sockaddr)); + + if (i < 0 || i != cc) { + if (i < 0) + perror("ping: sendto"); + (void)printf("ping: wrote %s %d chars, ret=%d\n", + hostname, cc, i); + } + if (!(options & F_QUIET) && options & F_FLOOD) + (void)write(STDOUT_FILENO, &DOT, 1); +} + +/* + * pr_pack -- + * Print out the packet, if it came from us. This logic is necessary + * because ALL readers of the ICMP socket get a copy of ALL ICMP packets + * which arrive ('tis only fair). This permits multiple copies of this + * program to be run without having intermingled output (or statistics!). + */ +pr_pack(buf, cc, from) + char *buf; + int cc; + struct sockaddr_in *from; +{ + register struct icmp *icp; + register u_long l; + register int i, j; + register u_char *cp,*dp; + static int old_rrlen; + static char old_rr[MAX_IPOPTLEN]; + struct ip *ip; + struct timeval tv, *tp; + double triptime; + int hlen, dupflag; + + (void)gettimeofday(&tv, (struct timezone *)NULL); + + /* Check the IP header */ + ip = (struct ip *)buf; + hlen = ip->ip_hl << 2; + if (cc < hlen + ICMP_MINLEN) { + if (options & F_VERBOSE) + (void)fprintf(stderr, + "ping: packet too short (%d bytes) from %s\n", cc, + inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr)); + return; + } + + /* Now the ICMP part */ + cc -= hlen; + icp = (struct icmp *)(buf + hlen); + if (icp->icmp_type == ICMP_ECHOREPLY) { + if (icp->icmp_id != ident) + return; /* 'Twas not our ECHO */ + ++nreceived; + if (timing) { +#ifndef icmp_data + tp = (struct timeval *)&icp->icmp_ip; +#else + tp = (struct timeval *)icp->icmp_data; +#endif + tvsub(&tv, tp); + triptime = ((double)tv.tv_sec) * 1000.0 + + ((double)tv.tv_usec) / 1000.0; + tsum += triptime; + if (triptime < tmin) + tmin = triptime; + if (triptime > tmax) + tmax = triptime; + } + + if (TST(icp->icmp_seq % mx_dup_ck)) { + ++nrepeats; + --nreceived; + dupflag = 1; + } else { + SET(icp->icmp_seq % mx_dup_ck); + dupflag = 0; + } + + if (options & F_QUIET) + return; + + if (options & F_FLOOD) + (void)write(STDOUT_FILENO, &BSPACE, 1); + else { + (void)printf("%d bytes from %s: icmp_seq=%u", cc, + inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), + icp->icmp_seq); + (void)printf(" ttl=%d", ip->ip_ttl); + if (timing) + (void)printf(" time=%g ms", triptime); + if (dupflag) + (void)printf(" (DUP!)"); + /* check the data */ + cp = (u_char*)&icp->icmp_data[8]; + dp = &outpack[8 + sizeof(struct timeval)]; + for (i = 8; i < datalen; ++i, ++cp, ++dp) { + if (*cp != *dp) { + (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", + i, *dp, *cp); + cp = (u_char*)&icp->icmp_data[0]; + for (i = 8; i < datalen; ++i, ++cp) { + if ((i % 32) == 8) + (void)printf("\n\t"); + (void)printf("%x ", *cp); + } + break; + } + } + } + } else { + /* We've got something other than an ECHOREPLY */ + if (!(options & F_VERBOSE)) + return; + (void)printf("%d bytes from %s: ", cc, + pr_addr(from->sin_addr.s_addr)); + pr_icmph(icp); + } + + /* Display any IP options */ + cp = (u_char *)buf + sizeof(struct ip); + + for (; hlen > (int)sizeof(struct ip); --hlen, ++cp) + switch (*cp) { + case IPOPT_EOL: + hlen = 0; + break; + case IPOPT_LSRR: + (void)printf("\nLSRR: "); + hlen -= 2; + j = *++cp; + ++cp; + if (j > IPOPT_MINOFF) + for (;;) { + l = *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + if (l == 0) + (void)printf("\t0.0.0.0"); + else + (void)printf("\t%s", pr_addr(ntohl(l))); + hlen -= 4; + j -= 4; + if (j <= IPOPT_MINOFF) + break; + (void)putchar('\n'); + } + break; + case IPOPT_RR: + j = *++cp; /* get length */ + i = *++cp; /* and pointer */ + hlen -= 2; + if (i > j) + i = j; + i -= IPOPT_MINOFF; + if (i <= 0) + continue; + if (i == old_rrlen + && cp == (u_char *)buf + sizeof(struct ip) + 2 + && !bcmp((char *)cp, old_rr, i) + && !(options & F_FLOOD)) { + (void)printf("\t(same route)"); + i = ((i + 3) / 4) * 4; + hlen -= i; + cp += i; + break; + } + old_rrlen = i; + bcopy((char *)cp, old_rr, i); + (void)printf("\nRR: "); + for (;;) { + l = *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + if (l == 0) + (void)printf("\t0.0.0.0"); + else + (void)printf("\t%s", pr_addr(ntohl(l))); + hlen -= 4; + i -= 4; + if (i <= 0) + break; + (void)putchar('\n'); + } + break; + case IPOPT_NOP: + (void)printf("\nNOP"); + break; + default: + (void)printf("\nunknown option %x", *cp); + break; + } + if (!(options & F_FLOOD)) { + (void)putchar('\n'); + (void)fflush(stdout); + } +} + +/* + * in_cksum -- + * Checksum routine for Internet Protocol family headers (C Version) + */ +in_cksum(addr, len) + u_short *addr; + int len; +{ + register int nleft = len; + register u_short *w = addr; + register int sum = 0; + u_short answer = 0; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), we add + * sequential 16 bit words to it, and at the end, fold back all the + * carry bits from the top 16 bits into the lower 16 bits. + */ + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nleft == 1) { + *(u_char *)(&answer) = *(u_char *)w ; + sum += answer; + } + + /* add back carry outs from top 16 bits to low 16 bits */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return(answer); +} + +/* + * tvsub -- + * Subtract 2 timeval structs: out = out - in. Out is assumed to + * be >= in. + */ +tvsub(out, in) + register struct timeval *out, *in; +{ + if ((out->tv_usec -= in->tv_usec) < 0) { + --out->tv_sec; + out->tv_usec += 1000000; + } + out->tv_sec -= in->tv_sec; +} + +/* + * finish -- + * Print out statistics, and give up. + */ +void +finish() +{ + register int i; + + (void)signal(SIGINT, SIG_IGN); + (void)putchar('\n'); + (void)fflush(stdout); + (void)printf("--- %s ping statistics ---\n", hostname); + (void)printf("%ld packets transmitted, ", ntransmitted); + (void)printf("%ld packets received, ", nreceived); + if (nrepeats) + (void)printf("+%ld duplicates, ", nrepeats); + if (ntransmitted) + if (nreceived > ntransmitted) + (void)printf("-- somebody's printing up packets!"); + else + (void)printf("%d%% packet loss", + (int) (((ntransmitted - nreceived) * 100) / + ntransmitted)); + (void)putchar('\n'); + if (nreceived && timing) { + /* Only display average to microseconds */ + i = 1000.0 * tsum / (nreceived + nrepeats); + (void)printf("round-trip min/avg/max = %g/%g/%g ms\n", + tmin, ((double)i) / 1000.0, tmax); + } + exit(0); +} + +#ifdef notdef +static char *ttab[] = { + "Echo Reply", /* ip + seq + udata */ + "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ + "Source Quench", /* IP */ + "Redirect", /* redirect type, gateway, + IP */ + "Echo", + "Time Exceeded", /* transit, frag reassem + IP */ + "Parameter Problem", /* pointer + IP */ + "Timestamp", /* id + seq + three timestamps */ + "Timestamp Reply", /* " */ + "Info Request", /* id + sq */ + "Info Reply" /* " */ +}; +#endif + +/* + * pr_icmph -- + * Print a descriptive string about an ICMP header. + */ +pr_icmph(icp) + struct icmp *icp; +{ + switch(icp->icmp_type) { + case ICMP_ECHOREPLY: + (void)printf("Echo Reply\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_UNREACH: + switch(icp->icmp_code) { + case ICMP_UNREACH_NET: + (void)printf("Destination Net Unreachable\n"); + break; + case ICMP_UNREACH_HOST: + (void)printf("Destination Host Unreachable\n"); + break; + case ICMP_UNREACH_PROTOCOL: + (void)printf("Destination Protocol Unreachable\n"); + break; + case ICMP_UNREACH_PORT: + (void)printf("Destination Port Unreachable\n"); + break; + case ICMP_UNREACH_NEEDFRAG: + (void)printf("frag needed and DF set\n"); + break; + case ICMP_UNREACH_SRCFAIL: + (void)printf("Source Route Failed\n"); + break; + default: + (void)printf("Dest Unreachable, Bad Code: %d\n", + icp->icmp_code); + break; + } + /* Print returned IP header information */ +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_SOURCEQUENCH: + (void)printf("Source Quench\n"); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_REDIRECT: + switch(icp->icmp_code) { + case ICMP_REDIRECT_NET: + (void)printf("Redirect Network"); + break; + case ICMP_REDIRECT_HOST: + (void)printf("Redirect Host"); + break; + case ICMP_REDIRECT_TOSNET: + (void)printf("Redirect Type of Service and Network"); + break; + case ICMP_REDIRECT_TOSHOST: + (void)printf("Redirect Type of Service and Host"); + break; + default: + (void)printf("Redirect, Bad Code: %d", icp->icmp_code); + break; + } + (void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_ECHO: + (void)printf("Echo Request\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_TIMXCEED: + switch(icp->icmp_code) { + case ICMP_TIMXCEED_INTRANS: + (void)printf("Time to live exceeded\n"); + break; + case ICMP_TIMXCEED_REASS: + (void)printf("Frag reassembly time exceeded\n"); + break; + default: + (void)printf("Time exceeded, Bad Code: %d\n", + icp->icmp_code); + break; + } +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_PARAMPROB: + (void)printf("Parameter problem: pointer = 0x%02x\n", + icp->icmp_hun.ih_pptr); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_TSTAMP: + (void)printf("Timestamp\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_TSTAMPREPLY: + (void)printf("Timestamp Reply\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_IREQ: + (void)printf("Information Request\n"); + /* XXX ID + Seq */ + break; + case ICMP_IREQREPLY: + (void)printf("Information Reply\n"); + /* XXX ID + Seq */ + break; +#ifdef ICMP_MASKREQ + case ICMP_MASKREQ: + (void)printf("Address Mask Request\n"); + break; +#endif +#ifdef ICMP_MASKREPLY + case ICMP_MASKREPLY: + (void)printf("Address Mask Reply\n"); + break; +#endif + default: + (void)printf("Bad ICMP type: %d\n", icp->icmp_type); + } +} + +/* + * pr_iph -- + * Print an IP header with options. + */ +pr_iph(ip) + struct ip *ip; +{ + int hlen; + u_char *cp; + + hlen = ip->ip_hl << 2; + cp = (u_char *)ip + 20; /* point to options */ + + (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); + (void)printf(" %1x %1x %02x %04x %04x", + ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id); + (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13, + (ip->ip_off) & 0x1fff); + (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum); + (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); + (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); + /* dump and option bytes */ + while (hlen-- > 20) { + (void)printf("%02x", *cp++); + } + (void)putchar('\n'); +} + +/* + * pr_addr -- + * Return an ascii host address as a dotted quad and optionally with + * a hostname. + */ +char * +pr_addr(l) + u_long l; +{ + struct hostent *hp; + static char buf[80]; + + if ((options & F_NUMERIC) || + !(hp = gethostbyaddr((char *)&l, 4, AF_INET))) + (void)sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&l)); + else + (void)sprintf(buf, "%s (%s)", hp->h_name, + inet_ntoa(*(struct in_addr *)&l)); + return(buf); +} + +/* + * pr_retip -- + * Dump some info on a returned (via ICMP) IP packet. + */ +pr_retip(ip) + struct ip *ip; +{ + int hlen; + u_char *cp; + + pr_iph(ip); + hlen = ip->ip_hl << 2; + cp = (u_char *)ip + hlen; + + if (ip->ip_p == 6) + (void)printf("TCP: from port %u, to port %u (decimal)\n", + (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); + else if (ip->ip_p == 17) + (void)printf("UDP: from port %u, to port %u (decimal)\n", + (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); +} + +fill(bp, patp) + char *bp, *patp; +{ + register int ii, jj, kk; + int pat[16]; + char *cp; + + for (cp = patp; *cp; cp++) + if (!isxdigit(*cp)) { + (void)fprintf(stderr, + "ping: patterns must be specified as hex digits.\n"); + exit(1); + } + ii = sscanf(patp, + "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", + &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], + &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], + &pat[13], &pat[14], &pat[15]); + + if (ii > 0) + for (kk = 0; + kk <= MAXPACKET - (8 + sizeof(struct timeval) + ii); + kk += ii) + for (jj = 0; jj < ii; ++jj) + bp[jj + kk] = pat[jj]; + if (!(options & F_QUIET)) { + (void)printf("PATTERN: 0x"); + for (jj = 0; jj < ii; ++jj) + (void)printf("%02x", bp[jj] & 0xFF); + (void)printf("\n"); + } +} + +usage() +{ + (void)fprintf(stderr, + "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n"); + exit(1); +} diff --git a/sbin/quotacheck/Makefile b/sbin/quotacheck/Makefile new file mode 100644 index 0000000..c6b8e3e --- /dev/null +++ b/sbin/quotacheck/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= quotacheck +SRCS= quotacheck.c preen.c +MAN8= quotacheck.0 +.PATH: ${.CURDIR}/../fsck + +.include <bsd.prog.mk> diff --git a/sbin/quotacheck/quotacheck.8 b/sbin/quotacheck/quotacheck.8 new file mode 100644 index 0000000..41d9fc4 --- /dev/null +++ b/sbin/quotacheck/quotacheck.8 @@ -0,0 +1,157 @@ +.\" Copyright (c) 1983, 1990, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Robert Elz at The University of Melbourne. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)quotacheck.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt QUOTACHECK 8 +.Os BSD 4.2 +.Sh NAME +.Nm quotacheck +.Nd filesystem quota consistency checker +.Sh SYNOPSIS +.Nm quotacheck +.Op Fl g +.Op Fl u +.Op Fl v +.Ar filesystem Ar ... +.Nm quotacheck +.Op Fl g +.Op Fl u +.Op Fl v +.Fl a +.Sh DESCRIPTION +.Nm Quotacheck +examines each filesystem, +builds a table of current disk usage, +and compares this table against that recorded +in the disk quota file for the filesystem. +If any inconsistencies are detected, both the +quota file and the current system copy of the +incorrect quotas are updated (the latter only +occurs if an active filesystem is checked). +By default both user and group quotas are checked. +.Pp +Available options: +.Bl -tag -width Ds +.It Fl a +If the +.Fl a +flag is supplied in place of any filesystem names, +.Nm quotacheck +will check all the filesystems indicated in +.Pa /etc/fstab +to be read-write with disk quotas. +By default only the types of quotas listed in +.Pa /etc/fstab +are checked. +.It Fl g +Only group quotas listed in +.Pa /etc/fstab +are to be checked. +.It Fl u +Only user quotas listed in +.Pa /etc/fstab +are to be checked. +.It Fl v +.Nm quotacheck +reports discrepancies between the +calculated and recorded disk quotas. +.El +.Pp +Specifying both +.Fl g +and +.Fl u +is equivalent to the default. +Parallel passes are run on the filesystems required, +using the pass numbers in +.Pa /etc/fstab +in an identical fashion to +.Xr fsck 8 . +.Pp +Normally +.Nm quotacheck +operates silently. +.Pp +.Nm Quotacheck +expects each filesystem to be checked to have a +quota files named +.Pa quota.user +and +.Pa quota.group +which are located at the root of the associated file system. +These defaults may be overridden in +.Pa /etc/fstab . +If a file is not present, +.Nm quotacheck +will create it. +.Pp +.Nm Quotacheck +is normally run at boot time from the +.Pa /etc/rc.local +file, see +.Xr rc 8 , +before enabling disk quotas with +.Xr quotaon 8 . +.Pp +.Nm Quotacheck +accesses the raw device in calculating the actual +disk usage for each user. +Thus, the filesystems +checked should be quiescent while +.Nm quotacheck +is running. +.Sh FILES +.Bl -tag -width quota.group -compact +.It Pa quota.user +at the filesystem root with user quotas +.It Pa quota.group +at the filesystem root with group quotas +.It Pa /etc/fstab +default filesystems +.El +.Sh SEE ALSO +.Xr quota 1 , +.Xr quotactl 2 , +.Xr fstab 5 , +.Xr edquota 8 , +.Xr fsck 8 , +.Xr quotaon 8 , +.Xr repquota 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/quotacheck/quotacheck.c b/sbin/quotacheck/quotacheck.c new file mode 100644 index 0000000..1ee9e87 --- /dev/null +++ b/sbin/quotacheck/quotacheck.c @@ -0,0 +1,636 @@ +/* + * Copyright (c) 1980, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Robert Elz at The University of Melbourne. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)quotacheck.c 8.3 (Berkeley) 1/29/94"; +#endif /* not lint */ + +/* + * Fix up / report on disk quotas & usage + */ +#include <sys/param.h> +#include <sys/stat.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/quota.h> +#include <ufs/ffs/fs.h> + +#include <fcntl.h> +#include <fstab.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +char *qfname = QUOTAFILENAME; +char *qfextension[] = INITQFNAMES; +char *quotagroup = QUOTAGROUP; + +union { + struct fs sblk; + char dummy[MAXBSIZE]; +} un; +#define sblock un.sblk +long dev_bsize = 1; +long maxino; + +struct quotaname { + long flags; + char grpqfname[MAXPATHLEN + 1]; + char usrqfname[MAXPATHLEN + 1]; +}; +#define HASUSR 1 +#define HASGRP 2 + +struct fileusage { + struct fileusage *fu_next; + u_long fu_curinodes; + u_long fu_curblocks; + u_long fu_id; + char fu_name[1]; + /* actually bigger */ +}; +#define FUHASH 1024 /* must be power of two */ +struct fileusage *fuhead[MAXQUOTAS][FUHASH]; + +int aflag; /* all file systems */ +int gflag; /* check group quotas */ +int uflag; /* check user quotas */ +int vflag; /* verbose */ +int fi; /* open disk file descriptor */ +u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */ + +struct fileusage * + addid __P((u_long, int, char *)); +char *blockcheck __P((char *)); +void bread __P((daddr_t, char *, long)); +int chkquota __P((char *, char *, struct quotaname *)); +void err __P((const char *, ...)); +void freeinodebuf __P((void)); +struct dinode * + getnextinode __P((ino_t)); +int getquotagid __P((void)); +int hasquota __P((struct fstab *, int, char **)); +struct fileusage * + lookup __P((u_long, int)); +void *needchk __P((struct fstab *)); +int oneof __P((char *, char*[], int)); +void resetinodebuf __P((void)); +int update __P((char *, char *, int)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct fstab *fs; + register struct passwd *pw; + register struct group *gr; + struct quotaname *auxdata; + int i, argnum, maxrun, errs; + long done = 0; + char ch, *name; + + errs = maxrun = 0; + while ((ch = getopt(argc, argv, "aguvl:")) != EOF) { + switch(ch) { + case 'a': + aflag++; + break; + case 'g': + gflag++; + break; + case 'u': + uflag++; + break; + case 'v': + vflag++; + break; + case 'l': + maxrun = atoi(optarg); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + if ((argc == 0 && !aflag) || (argc > 0 && aflag)) + usage(); + if (!gflag && !uflag) { + gflag++; + uflag++; + } + if (gflag) { + setgrent(); + while ((gr = getgrent()) != 0) + (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name); + endgrent(); + } + if (uflag) { + setpwent(); + while ((pw = getpwent()) != 0) + (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name); + endpwent(); + } + if (aflag) + exit(checkfstab(1, maxrun, needchk, chkquota)); + if (setfsent() == 0) + err("%s: can't open", FSTAB); + while ((fs = getfsent()) != NULL) { + if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || + (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) && + (auxdata = needchk(fs)) && + (name = blockcheck(fs->fs_spec))) { + done |= 1 << argnum; + errs += chkquota(name, fs->fs_file, auxdata); + } + } + endfsent(); + for (i = 0; i < argc; i++) + if ((done & (1 << i)) == 0) + fprintf(stderr, "%s not found in %s\n", + argv[i], FSTAB); + exit(errs); +} + +void +usage() +{ + (void)fprintf(stderr, "usage:\t%s\n\t%s\n", + "quotacheck -a [-guv]", + "quotacheck [-guv] filesys ..."); + exit(1); +} + +void * +needchk(fs) + register struct fstab *fs; +{ + register struct quotaname *qnp; + char *qfnp; + + if (strcmp(fs->fs_vfstype, "ufs") || + strcmp(fs->fs_type, FSTAB_RW)) + return (NULL); + if ((qnp = malloc(sizeof(*qnp))) == NULL) + err("%s", strerror(errno)); + qnp->flags = 0; + if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) { + strcpy(qnp->grpqfname, qfnp); + qnp->flags |= HASGRP; + } + if (uflag && hasquota(fs, USRQUOTA, &qfnp)) { + strcpy(qnp->usrqfname, qfnp); + qnp->flags |= HASUSR; + } + if (qnp->flags) + return (qnp); + free(qnp); + return (NULL); +} + +/* + * Scan the specified filesystem to check quota(s) present on it. + */ +int +chkquota(fsname, mntpt, qnp) + char *fsname, *mntpt; + register struct quotaname *qnp; +{ + register struct fileusage *fup; + register struct dinode *dp; + int cg, i, mode, errs = 0; + ino_t ino; + + if ((fi = open(fsname, O_RDONLY, 0)) < 0) { + perror(fsname); + return (1); + } + if (vflag) { + (void)printf("*** Checking "); + if (qnp->flags & HASUSR) + (void)printf("%s%s", qfextension[USRQUOTA], + (qnp->flags & HASGRP) ? " and " : ""); + if (qnp->flags & HASGRP) + (void)printf("%s", qfextension[GRPQUOTA]); + (void)printf(" quotas for %s (%s)\n", fsname, mntpt); + } + sync(); + bread(SBOFF, (char *)&sblock, (long)SBSIZE); + dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); + maxino = sblock.fs_ncg * sblock.fs_ipg; + resetinodebuf(); + for (ino = 0, cg = 0; cg < sblock.fs_ncg; cg++) { + for (i = 0; i < sblock.fs_ipg; i++, ino++) { + if (ino < ROOTINO) + continue; + if ((dp = getnextinode(ino)) == NULL) + continue; + if ((mode = dp->di_mode & IFMT) == 0) + continue; + if (qnp->flags & HASGRP) { + fup = addid((u_long)dp->di_gid, GRPQUOTA, + (char *)0); + fup->fu_curinodes++; + if (mode == IFREG || mode == IFDIR || + mode == IFLNK) + fup->fu_curblocks += dp->di_blocks; + } + if (qnp->flags & HASUSR) { + fup = addid((u_long)dp->di_uid, USRQUOTA, + (char *)0); + fup->fu_curinodes++; + if (mode == IFREG || mode == IFDIR || + mode == IFLNK) + fup->fu_curblocks += dp->di_blocks; + } + } + } + freeinodebuf(); + if (qnp->flags & HASUSR) + errs += update(mntpt, qnp->usrqfname, USRQUOTA); + if (qnp->flags & HASGRP) + errs += update(mntpt, qnp->grpqfname, GRPQUOTA); + close(fi); + return (errs); +} + +/* + * Update a specified quota file. + */ +int +update(fsname, quotafile, type) + char *fsname, *quotafile; + register int type; +{ + register struct fileusage *fup; + register FILE *qfi, *qfo; + register u_long id, lastid; + struct dqblk dqbuf; + static int warned = 0; + static struct dqblk zerodqbuf; + static struct fileusage zerofileusage; + + if ((qfo = fopen(quotafile, "r+")) == NULL) { + if (errno == ENOENT) + qfo = fopen(quotafile, "w+"); + if (qfo) { + (void) fprintf(stderr, + "quotacheck: creating quota file %s\n", quotafile); +#define MODE (S_IRUSR|S_IWUSR|S_IRGRP) + (void) fchown(fileno(qfo), getuid(), getquotagid()); + (void) fchmod(fileno(qfo), MODE); + } else { + (void) fprintf(stderr, + "quotacheck: %s: %s\n", quotafile, strerror(errno)); + return (1); + } + } + if ((qfi = fopen(quotafile, "r")) == NULL) { + (void) fprintf(stderr, + "quotacheck: %s: %s\n", quotafile, strerror(errno)); + (void) fclose(qfo); + return (1); + } + if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 && + errno == EOPNOTSUPP && !warned && vflag) { + warned++; + (void)printf("*** Warning: %s\n", + "Quotas are not compiled into this kernel"); + } + for (lastid = highid[type], id = 0; id <= lastid; id++) { + if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) + dqbuf = zerodqbuf; + if ((fup = lookup(id, type)) == 0) + fup = &zerofileusage; + if (dqbuf.dqb_curinodes == fup->fu_curinodes && + dqbuf.dqb_curblocks == fup->fu_curblocks) { + fup->fu_curinodes = 0; + fup->fu_curblocks = 0; + fseek(qfo, (long)sizeof(struct dqblk), 1); + continue; + } + if (vflag) { + if (aflag) + printf("%s: ", fsname); + printf("%-8s fixed:", fup->fu_name); + if (dqbuf.dqb_curinodes != fup->fu_curinodes) + (void)printf("\tinodes %d -> %d", + dqbuf.dqb_curinodes, fup->fu_curinodes); + if (dqbuf.dqb_curblocks != fup->fu_curblocks) + (void)printf("\tblocks %d -> %d", + dqbuf.dqb_curblocks, fup->fu_curblocks); + (void)printf("\n"); + } + /* + * Reset time limit if have a soft limit and were + * previously under it, but are now over it. + */ + if (dqbuf.dqb_bsoftlimit && + dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && + fup->fu_curblocks >= dqbuf.dqb_bsoftlimit) + dqbuf.dqb_btime = 0; + if (dqbuf.dqb_isoftlimit && + dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit && + fup->fu_curblocks >= dqbuf.dqb_isoftlimit) + dqbuf.dqb_itime = 0; + dqbuf.dqb_curinodes = fup->fu_curinodes; + dqbuf.dqb_curblocks = fup->fu_curblocks; + fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo); + (void) quotactl(fsname, QCMD(Q_SETUSE, type), id, + (caddr_t)&dqbuf); + fup->fu_curinodes = 0; + fup->fu_curblocks = 0; + } + fclose(qfi); + fflush(qfo); + ftruncate(fileno(qfo), + (off_t)((highid[type] + 1) * sizeof(struct dqblk))); + fclose(qfo); + return (0); +} + +/* + * Check to see if target appears in list of size cnt. + */ +int +oneof(target, list, cnt) + register char *target, *list[]; + int cnt; +{ + register int i; + + for (i = 0; i < cnt; i++) + if (strcmp(target, list[i]) == 0) + return (i); + return (-1); +} + +/* + * Determine the group identifier for quota files. + */ +int +getquotagid() +{ + struct group *gr; + + if (gr = getgrnam(quotagroup)) + return (gr->gr_gid); + return (-1); +} + +/* + * Check to see if a particular quota is to be enabled. + */ +int +hasquota(fs, type, qfnamep) + register struct fstab *fs; + int type; + char **qfnamep; +{ + register char *opt; + char *cp; + static char initname, usrname[100], grpname[100]; + static char buf[BUFSIZ]; + + if (!initname) { + (void)snprintf(usrname, sizeof(usrname), + "%s%s", qfextension[USRQUOTA], qfname); + (void)snprintf(grpname, sizeof(grpname), + "%s%s", qfextension[GRPQUOTA], qfname); + initname = 1; + } + strcpy(buf, fs->fs_mntops); + for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { + if (cp = index(opt, '=')) + *cp++ = '\0'; + if (type == USRQUOTA && strcmp(opt, usrname) == 0) + break; + if (type == GRPQUOTA && strcmp(opt, grpname) == 0) + break; + } + if (!opt) + return (0); + if (cp) + *qfnamep = cp; + else { + (void)snprintf(buf, sizeof(buf), + "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); + *qfnamep = buf; + } + return (1); +} + +/* + * Routines to manage the file usage table. + * + * Lookup an id of a specific type. + */ +struct fileusage * +lookup(id, type) + u_long id; + int type; +{ + register struct fileusage *fup; + + for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) + if (fup->fu_id == id) + return (fup); + return (NULL); +} + +/* + * Add a new file usage id if it does not already exist. + */ +struct fileusage * +addid(id, type, name) + u_long id; + int type; + char *name; +{ + struct fileusage *fup, **fhp; + int len; + + if (fup = lookup(id, type)) + return (fup); + if (name) + len = strlen(name); + else + len = 10; + if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) + err("%s", strerror(errno)); + fhp = &fuhead[type][id & (FUHASH - 1)]; + fup->fu_next = *fhp; + *fhp = fup; + fup->fu_id = id; + if (id > highid[type]) + highid[type] = id; + if (name) + bcopy(name, fup->fu_name, len + 1); + else + (void)sprintf(fup->fu_name, "%u", id); + return (fup); +} + +/* + * Special purpose version of ginode used to optimize pass + * over all the inodes in numerical order. + */ +ino_t nextino, lastinum; +long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; +struct dinode *inodebuf; +#define INOBUFSIZE 56*1024 /* size of buffer to read inodes */ + +struct dinode * +getnextinode(inumber) + ino_t inumber; +{ + long size; + daddr_t dblk; + static struct dinode *dp; + + if (inumber != nextino++ || inumber > maxino) + err("bad inode number %d to nextinode", inumber); + if (inumber >= lastinum) { + readcnt++; + dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); + if (readcnt % readpercg == 0) { + size = partialsize; + lastinum += partialcnt; + } else { + size = inobufsize; + lastinum += fullcnt; + } + bread(dblk, (char *)inodebuf, size); + dp = inodebuf; + } + return (dp++); +} + +/* + * Prepare to scan a set of inodes. + */ +void +resetinodebuf() +{ + + nextino = 0; + lastinum = 0; + readcnt = 0; + inobufsize = blkroundup(&sblock, INOBUFSIZE); + fullcnt = inobufsize / sizeof(struct dinode); + readpercg = sblock.fs_ipg / fullcnt; + partialcnt = sblock.fs_ipg % fullcnt; + partialsize = partialcnt * sizeof(struct dinode); + if (partialcnt != 0) { + readpercg++; + } else { + partialcnt = fullcnt; + partialsize = inobufsize; + } + if (inodebuf == NULL && + (inodebuf = malloc((u_int)inobufsize)) == NULL) + err("%s", strerror(errno)); + while (nextino < ROOTINO) + getnextinode(nextino); +} + +/* + * Free up data structures used to scan inodes. + */ +void +freeinodebuf() +{ + + if (inodebuf != NULL) + free(inodebuf); + inodebuf = NULL; +} + +/* + * Read specified disk blocks. + */ +void +bread(bno, buf, cnt) + daddr_t bno; + char *buf; + long cnt; +{ + + if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 || + read(fi, buf, cnt) != cnt) + err("block %ld", bno); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "quotacheck: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/sbin/reboot/Makefile b/sbin/reboot/Makefile new file mode 100644 index 0000000..f4e1a55 --- /dev/null +++ b/sbin/reboot/Makefile @@ -0,0 +1,18 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= reboot +DPADD= ${LIBUTIL} +LDADD= -lutil +MAN8= reboot.0 boot_hp300.0 boot_i386.0 boot_sparc.0 boot_tahoe.0 boot_vax.0 +MLINKS= reboot.8 halt.8 +LINKS= ${BINDIR}/reboot ${BINDIR}/halt + +afterinstall: + ${MINSTALL} boot_hp300.0 ${DESTDIR}${MANDIR}8/hp300/boot.0 + ${MINSTALL} boot_i386.0 ${DESTDIR}${MANDIR}8/i386/boot.0 + ${MINSTALL} boot_sparc.0 ${DESTDIR}${MANDIR}8/sparc/boot.0 + ${MINSTALL} boot_tahoe.0 ${DESTDIR}${MANDIR}8/tahoe/boot.0 + ${MINSTALL} boot_vax.0 ${DESTDIR}${MANDIR}8/vax/boot.0 + +.include <bsd.man.mk> +.include <bsd.prog.mk> diff --git a/sbin/reboot/boot_hp300.8 b/sbin/reboot/boot_hp300.8 new file mode 100644 index 0000000..0303542 --- /dev/null +++ b/sbin/reboot/boot_hp300.8 @@ -0,0 +1,117 @@ +.\" Copyright (c) 1990, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Systems Programming Group of the University of Utah Computer +.\" Science Department. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)boot_hp300.8 8.2 (Berkeley) 4/19/94 +.\" +.Dd April 19, 1994 +.Dt BOOT_HP300 8 hp300 +.Os +.Sh NAME +.Nm boot +.Nd +system bootstrapping procedures +.Sh DESCRIPTION +.Sy Power fail and crash recovery. +Normally, the system will reboot itself at power-up or after crashes. +An automatic consistency check of the file systems will be performed, +and unless this fails, the system will resume multi-user operations. +.Pp +.Sy Cold starts. +On an HP300, the boot procedure uses the boot ROM to load a boot program +from an +.Tn LIF +format directory at the beginning of an attached disk. +The +.Pa /usr/mdec +directory contains a disk boot programs which should be placed in a +new pack automatically by +.Xr newfs 8 +when the ``a'' partition file system on the pack is created. +.Pp +This +.Em boot +program +finds the corresponding file on the given device +.Pf ( Ar vmunix +by default), +loads that file into memory, +and starts the program at the entry address specified in the program header. +.Pp +The boot program can be interrupted by typing `^C' (ctrl-C). +This will force the boot program to interactively prompt for a system to boot. +If not interrupted, it will boot from the device from which the boot +program itself was loaded. +.Pp +The file specifications used for an interactive boot are of the form: +.Pp +.Dl device(unit, minor) +.Pp +where +.Ar device +is the type of the device to be searched, +.Ar unit +is 8 * the hpib number plus the unit number of the disk or tape, +and +.Ar minor +is the disk partition or tape file number. +Normal line editing characters can be used when typing the file specification. +Currently, ``rd'' and ``sd'' are the only valid +.Ar device +specifiers. +.Pp +For example, +to boot from the `a' file system of unit 0 on HP-IB 2, +type +.Ql rd(16, 0)vmunix +to the boot prompt. +For tapes, the minor device number gives a file offset. +.Pp +In an emergency, the bootstrap methods described in the paper +.%T Installing 4.3bsd on the HP300 +can be used to boot from a distribution tape. +.Sh FILES +.Bl -tag -width /usr/mdec/installboot -compact +.It Pa /vmunix +system code +.It Pa /usr/mdec/bootrd +.Tn LIF +format boot block +.It Pa /usr/mdec/installboot +program to install boot blocks +.El +.Sh SEE ALSO +.Xr halt 8 , +.Xr reboot 8 , +.Xr shutdown 8 diff --git a/sbin/reboot/boot_i386.8 b/sbin/reboot/boot_i386.8 new file mode 100644 index 0000000..0dc3e0b --- /dev/null +++ b/sbin/reboot/boot_i386.8 @@ -0,0 +1,126 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software written and contributed +.\" to Berkeley by William Jolitz. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)boot_i386.8 8.2 (Berkeley) 4/19/94 +.\" +.Dd April 19, 1994 +.Dt BOOT 8 i386 +.Os +.Sh NAME +.Nm boot +.Nd +system bootstrapping procedures +.Sh DESCRIPTION +.Sy Power fail and crash recovery. +Normally, the system will reboot itself at power-up or after crashes. +An automatic consistency check of the file systems will be performed, +and unless this fails, the system will resume multi-user operations. +.Pp +.Sy Cold starts. +The 386 +.Tn "PC AT" +clones attempt to boot the floppy disk drive A (otherwise known as drive +0) first, and failing that, attempt to boot the hard disk C (otherwise +known as hard disk controller 1, drive 0). +The automatic boot will attempt to load +.Pa vmunix +from partition A of either the floppy or the hard disk. +This boot may be aborted by typing any character on the keyboard repeatedly +(four or five times at least) during the operating system load phase, after +which the bootstrap will prompt for the file that you wish to load instead. +.Pp +One exception to this is the +.Ql d +key, which will not abort the load but instead silently force the +.Dv DEBUG +boot flags. +The boot flags for an autoboot are 0, and 3 for the successive boot after +an aborted autoboot sequence. +No other provison is made for setting boot flags (yet). +A specific device or bootstrap file may be used; for example, +.Pp +The file specifications used for the boostrap +when loaded with the +.Dq askme +flag +(e.g. an aborted autoboot) +are of the form: +.Pp +.Dl device unit partition: +.Pp +where +.Ar device +is the type of the device, assumed to be on the ISA bus, to be searched, +.Ar unit +is the unit number of the disk or tape, +and +.Ar partition +is the disk partition or tape file number. +Normal line editing characters can be used when typing the file specification. +The following list of supported devices may vary from installation to +installation: +.Bd -unfilled -offset indent +wd ST506, IDE, ESDI, RLL disks on a WD100[2367] or lookalike + controller +fd 5 1/4" or 3 1/2" High density floppies +.Ed +.Pp +For example, +to boot from a file system which starts at cylinder 0 +of unit 0 of an IDE disk, type +.Dq Li wd0a:vmunix +to the boot prompt; +.Dq Li fd0a:vmunix +would specify a 3 1/2" floppy drive 0 . +.Pp +In an emergency, the bootstrap methods described in the paper +.%T "Installing and Operating 4.3 BSD-Reno UNIX on the AT/386" +can be used +to boot from a distribution tape. +.Sh FILES +.Bl -tag -width /vmunixxx -compact +.It Pa /vmunix +system code +.It Pa /boot +system bootstrap +.El +.Sh SEE ALSO +.Xr halt 8 , +.Xr reboot 8 , +.Xr shutdown 8 +.Sh BUGS +The disklabel format used by this version of +.Bx +is quite +different from that of other architectures. diff --git a/sbin/reboot/boot_sparc.8 b/sbin/reboot/boot_sparc.8 new file mode 100644 index 0000000..72e57b0 --- /dev/null +++ b/sbin/reboot/boot_sparc.8 @@ -0,0 +1,89 @@ +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)boot_sparc.8 8.2 (Berkeley) 4/19/94 +.\" +.Dd April 19, 1994 +.Dt REBOOT 8 sparc +.Os +.Sh NAME +.Nm reboot +.Nd +.Tn UNIX +bootstrapping procedures +.Sh SYNOPSIS +.Nm reboot +.Op Fl n +.Op Fl q +.Sh DESCRIPTION +.Sy Power fail and crash recovery. +Normally, the system will reboot itself at power-up or after crashes. +An automatic consistency check of the file systems will be performed +as described in +.Xr fsck 8 . +and unless this fails, the system will resume multi-user operations. +.Pp +.Sy Cold starts +The SPARC system currently uses the SunOS bootstrap loaders. +This will be changed in a future version of the system. +The SunOS boot will attempt to load +.Pa vmunix +from partition A of the boot device, +which must currently be an ``sd'' disk. +.Pp +The +.Op Fl s +flag to the SunOS boot loader will being the system up in single-user mode. +The +.Op Fl d +flag to the SunOS boot loader will bring the system up in debug mode. +Here it waits for a kernel debugger connect; see +.Xr kgdb 8 . +Other flags are currently ignored. +.Sh FILES +.Bl -tag -width /vmunixxx -compact +.It Pa /vmunix +system code +.It Pa /boot +system bootstrap +.El +.Sh SEE ALSO +.Xr crash 8 , +.Xr disklabel 8 , +.Xr fsck 8 , +.Xr halt 8 , +.Xr init 8 , +.Xr rc 8 , +.Xr shutdown 8 , +.Xr syslogd 8 +.Sh BUGS +The use of Sun disk labels, without the ability to write them, +is problematic. diff --git a/sbin/reboot/boot_tahoe.8 b/sbin/reboot/boot_tahoe.8 new file mode 100644 index 0000000..01c7f81 --- /dev/null +++ b/sbin/reboot/boot_tahoe.8 @@ -0,0 +1,152 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)boot_tahoe.8 8.2 (Berkeley) 4/19/94 +.\" +.Dd April 19, 1994 +.Dt BOOT 8 tahoe +.Os +.Sh NAME +.Nm boot +.Nd +system bootstrapping procedures +.Sh DESCRIPTION +.Sy Power fail and crash recovery. +Normally, the system will reboot itself at power-up or after crashes. +An automatic consistency check of the file systems will be performed, +and unless this fails, the system will resume multi-user operations. +.Pp +.Sy Cold starts. +These are processor-type dependent. +On the +.Tn CCI +Power 6/32 and related processors, +the system will do a standard autoboot from drive 0 +upon power-up or reset. +This automatic boot may be cancelled by typing a +.Ql \&# +in the first few seconds after reset. +This enters console mode; the console prompt is +.Ql > +or +.Ql \&# . +The boot flags can be set to any hexadecimal value +.Fl n +with the command +.Pp +.Bd -filled -offset indent -compact +.Li \&#> p23 +.Ar n . +.Ed +.Pp +The default device may be examined or set; see the Diagnostics and Debugging +manual for the processor for details on device naming and syntax. +After setting the boot flags and/or device, +a bootstrap sequence can be initiated with +.Pp +.Dl #> fb +.Pp +A specific device or bootstrap file may be used; for example, +.Pp +.Dl \&#> \&fb xfd(1,0) +.Pp +would boot from the `a' partition on +.Tn XFD +drive 1. +.Pp +The file specifications used for the boostrap +when loaded with the +.Dq askme +flag +(register 23 set to 1 or 3) +are of the form: +.Pp +.Dl device(adaptor,controller,unit,minor) +.Pp +where +.Ar device +is the type of the device to be searched, +.Ar adaptor +is number of the +.Tn VERSAbus +(or +.Tn VMEbus ) +to which the device is attached, +.Ar controller +is the unit number of the controller on that buss, +.Ar unit +is the unit number of the disk or tape, +and +.Ar minor +is the disk partition or tape file number. +Leading adaptor or controller numbers default to 0. +Normal line editing characters can be used when typing the file specification. +The following list of supported devices may vary from installation to +installation: +.Pp +.Bd -unfilled -offset indent -compact +dk SMD or ESDI disks on VDDC or SMD-E +cy tape on Ciprico Tapemaster controller +.Ed +.Pp +For example, +to boot from a file system which starts at cylinder 0 +of unit 0 of an +.Tn SMD-E +disk, type +.Ql dk(0,0)vmunix +to the boot prompt; +.Ql dk(2,1,0)vmunix +would specify drive 1 on +.Tn SMD-E +controller 2. +.Pp +In an emergency, the bootstrap methods described in the paper +.%T "Installing and Operating 4.3 BSD-tahoe UNIX on the Tahoe" +can be used +to boot from a distribution tape. +.Sh FILES +.Bl -tag -width /vmunix -compact +.It Pa /vmunix +system code +.It Pa /boot +system bootstrap +.El +.Sh SEE ALSO +.Xr halt 8 , +.Xr reboot 8 , +.Xr shutdown 8 +.Sh BUGS +The disklabel format used by some versions of the console processor +is different than the format used by +.Tn UNIX +and the bootstrap. +.Sh HISTORY diff --git a/sbin/reboot/boot_vax.8 b/sbin/reboot/boot_vax.8 new file mode 100644 index 0000000..dce6b69 --- /dev/null +++ b/sbin/reboot/boot_vax.8 @@ -0,0 +1,322 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)boot_vax.8 8.2 (Berkeley) 4/19/94 +.\" +.Dd April 19, 1994 +.Dt BOOT 8 vax +.Os +.Sh NAME +.Nm boot +.Nd +system bootstrapping procedures +.Sh DESCRIPTION +.Sy Power fail and crash recovery. +Normally, the system will reboot itself at power-up or after crashes. +Provided the auto-restart is enabled on the machine front panel, +an automatic consistency check of the file systems will be performed, +and unless this fails, the system will resume multi-user operations. +.Pp +.Sy Cold starts. +These are processor-type dependent. +On an 11/780, there are two floppy files for each disk controller, +both of which cause boots from unit 0 of the root file system +of a controller located on mba0 or uba0. +One gives a single user shell, while the other invokes the multi-user +automatic reboot. +Thus these files are +.Tn HPS +and +.Tn HPM +for the single +and multi-user boot from +.Tn MASSBUS +RP06/RM03/RM05 disks, +.Tn UPS +and +.Tn UPM +for +.Tn UNIBUS +storage module controller and disks +such as the +.Tn EMULEX +SC-21 +and +.Tn AMPEX +9300 pair, +.Tn RAS +and +.Tn RAM +to boot from +.Tn MSCP +controllers and disks such as the RA81, +or +.Tn HKS +and +.Tn HKM +for RK07 disks. +There is also a script for booting from the default device, +which is normally a copy of one of the standard multi-user boot scripts, +but which may be modified to perform other actions +or to boot from a different unit. +The situation on the 8600 is similar, with scripts loaded from the console RL02. +.Pp +Giving the command +.Pp +.Dl >>>BOOT HPM +.Pp +would boot the system from (e.g.) an RP06 and run the automatic consistency +check as described in +.Xr fsck 8 . +(Note that it may +be necessary to type control-P +and halt the processor +to gain the attention of the +.Tn LSI-11 +before getting the >>> prompt.) +The command +.Pp +.Dl >>>BOOT ANY +.Pp +invokes a version of the boot program in a way which allows you to +specify any system as the system to be booted. +It reads from the console a device specification (see below) followed +immediately by a pathname. +.Pp +The scripts may be modified for local configuration if necessary. +The flags are placed in register 11 (as defined in +.Aq Pa sys/reboot.h ) . +The boot device is specified in register 10. +The encoding of this register is also defined in +.Aq Pa sys/reboot.h . +The current encoding has a historical basis, and is shown in the following +table: +.Pp +.Bd -unfilled -offset indent -compact +bits usage +0-7 boot device type (the device major number) +8-15 disk partition +16-19 drive unit +20-23 controller number +24-27 adaptor number (UNIBUS or MASSBUS as appropriate) +.Ed +.Pp +The adaptor number corresponds to the normal configuration on the 11/750, +and to the order in which adaptors are found on the 11/780 and 8600 +(generally the same as the numbers used by +.Tn UNIX ) . +.Pp +On an 11/750, the reset button will boot from the device +selected by the front panel boot device switch. In systems +with RK07's, position B normally selects the RK07 for boot. +This will boot multi-user. To boot from RK07 with boot flags you +may specify +.Pp +.Bd -unfilled -offset indent -compact +.Li \&>>>B/ Ns Fl n No DMA0 +.Ed +.Pp +where, giving a +.Ar n +of 1 causes the boot program +to ask for the name of the system to be bootstrapped, +giving a +.Ar n +of 2 causes the boot program to come up single +user, and a +.Ar n +of 3 causes both of these actions to occur. +The ``DM'' specifies RK07, the ``A'' represents the adaptor number +.Pf ( Tn UNIBUS +or +.Tn MASSBUS ) , +and the ``0'' is the drive unit number. +Other disk types which may be used are DB +.Pq Tn MASSBUS , +DD (TU58), +and DU +.Pf ( Tn UDA-50/RA +disk). +A non-zero disk partition can be used by adding (partition times 1000 hex) +to +.Ar n . +.Pp +The boot procedure on the Micro +.Tn VAX +II +is similar. +A switch on the back panel sets the power-up action +to autoboot or to halt. +When halted, the processor may be booted using the same syntax +as on the 11/750. +.Pp +The 11/750 boot procedure uses the boot roms to load block 0 off of +the specified device. The /usr/mdec directory contains a number +of bootstrap programs for the various disks which should be placed +in a new pack by +.Xr disklabel 8 . +Similarly, the Micro +.Tn VAX +II boot procedure loads a boot parameter block +from block 0 of the disk. +The +.Xr rdboot +.Dq bootstrap +contains the correct parameters for an +.Tn MSCP +disk such +as the RD53. +.Pp +On any processor, the +.Em boot +program +finds the corresponding file on the given device +.Pf ( Pa vmunix +by default), loads that file +into memory location zero, and starts the program at the entry address +specified in the program header (after clearing off the high bit +of the specified entry address). +.Pp +The file specifications used with +.Dq BOOT ANY +or +.Dq \&B/3 +are of the form: +.Pp +.Dl device(adaptor,controller,unit,minor) +.Pp +where +.Ar device +is the type of the device to be searched, +.Ar adaptor +is the +.Tn UNIBUS +or +.Tn MASSBUS +number of the adaptor to which the device is attached, +.Ar controller +is the unit number of the controller or +.Tn MASSBUS +tape formatter on that adaptor, +.Ar unit +is the unit number of the disk or transport slave unit of the tape, +and +.Ar minor +is the disk partition or tape file number. +Leading adaptor or controller numbers default to 0. +Normal line editing characters can be used when typing the file specification. +The following list of supported devices may vary from installation to +installation: +.Pp +.Bd -unfilled -offset indent -compact +hp MASSBUS disk drive +up UNIBUS storage module drive +ht TE16,TU45,TU77 on MASSBUS +kra storage module on a KDB50 +mt TU78 on MASSBUS +hk RK07 on UNIBUS +ra storage module on a MSCP-compatible UNIBUS controller +rb storage module on a 730 IDC +rl RL02 on UNIBUS +tm TM11 emulation tape drives on UNIBUS +tms TMSCP-compatible tape +ts TS11 on UNIBUS +ut UNIBUS TU45 emulator +.Ed +.Pp +For example, +to boot from a file system which starts at cylinder 0 +of unit 0 of a +.Tn MASSBUS +disk, type +.Ql hp(0,0)vmunix +to the boot prompt; +.Ql hp(2,0,1,0)vmunix +would specify drive 1 on +.Tn MASSBUS +adaptor 2; +.Ql up(0,0)vmunix +would specify a +.Tn UNIBUS +drive, +.Ql hk(0,0)vmunix +would specify +an RK07 disk drive, +.Ql ra(1,0,0,0)vmunix +would specify a +.Tn UDA50 +disk drive on a second +.Tn UNIBUS , +and +.Ql rb(0,0)vmunix +would specify a +disk on a 730 +.Tn IDC . +For tapes, the minor device number gives a file offset; +.Ql mt(1,2,3,4) +would specify the fifth file on slave 3 of the formatter +at +.Ql drive +2 on mba 1. +.Pp +On an 11/750 with patchable control store, +microcode patches will be installed by +.Em boot +if the file +.Pa psc750.bin +exists in the root of the filesystem from which the system is booted. +.Pp +In an emergency, the bootstrap methods described in the paper +.%T Installing and Operating 4.3bsd +can be used to boot from a distribution tape. +.Sh FILES +.Bl -tag -width /usr/mdec/xxboot -compact +.It Pa /vmunix +system code +.It Pa /boot +system bootstrap +.It Pa /usr/mdec/xxboot +sector-0 boot block for 750, xx is disk type +.It Pa /usr/mdec/bootxx +second-stage boot for 750, xx is disk type +.It Pa /pcs750.bin +microcode patch file on 750 +.El +.Sh SEE ALSO +.Xr arff 8 , +.Xr halt 8 , +.Xr reboot 8 , +.Xr shutdown 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.0 . diff --git a/sbin/reboot/reboot.8 b/sbin/reboot/reboot.8 new file mode 100644 index 0000000..2fd2abb --- /dev/null +++ b/sbin/reboot/reboot.8 @@ -0,0 +1,88 @@ +.\" Copyright (c) 1990, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)reboot.8 8.1 (Berkeley) 6/9/93 +.\" +.Dd June 9, 1993 +.Dt REBOOT 8 +.Os +.Sh NAME +.Nm reboot , +.Nm halt +.Nd +stopping and restarting the system +.Sh SYNOPSIS +.Nm halt +.Op Fl nq +.Nm reboot +.Op Fl nq +.Sh DESCRIPTION +The +.Nm halt +and +.Nm reboot +utilities flush the file system cache to disk, send all running processes +a SIGTERM (and subsequently a SIGKILL) and, respectively, halt or restart +the system. +The action is logged, including entering a shutdown record into the login +accounting file. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl n +If the +.Fl n +option is specified, +the file system cache is not flushed. +This option should probably not be used. +.It Fl q +If the +.Fl q +option is specified, +the system is halted or restarted quickly and ungracefully, and only +the flushing of the file system cache is performed. +This option should probably not be used. +.El +.Pp +Normally, the +.Xr shutdown 8 +utility is used when the system needs to be halted or restarted, giving +users advance warning of their impending doom. +.Sh SEE ALSO +.Xr sync 1 , +.Xr utmp 5 , +.Xr boot 8 , +.Xr shutdown 8 +.Sh HISTORY +A +.Nm reboot +command appeared in +.At v6 . diff --git a/sbin/reboot/reboot.c b/sbin/reboot/reboot.c new file mode 100644 index 0000000..df5515a --- /dev/null +++ b/sbin/reboot/reboot.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1986, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)reboot.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/reboot.h> +#include <signal.h> +#include <pwd.h> +#include <errno.h> +#include <syslog.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void err __P((const char *fmt, ...)); +void usage __P((void)); + +int dohalt; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register int i; + struct passwd *pw; + int ch, howto, lflag, nflag, qflag, sverrno; + char *p, *user; + + if (!strcmp((p = rindex(*argv, '/')) ? p + 1 : *argv, "halt")) { + dohalt = 1; + howto = RB_HALT; + } else + howto = 0; + lflag = nflag = qflag = 0; + while ((ch = getopt(argc, argv, "lnq")) != EOF) + switch(ch) { + case 'l': /* Undocumented; used by shutdown. */ + lflag = 1; + break; + case 'n': + nflag = 1; + howto |= RB_NOSYNC; + break; + case 'q': + qflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (geteuid()) + err("%s", strerror(EPERM)); + + if (qflag) { + reboot(howto); + err("%s", strerror(errno)); + } + + /* Log the reboot. */ + if (!lflag) { + if ((user = getlogin()) == NULL) + user = (pw = getpwuid(getuid())) ? + pw->pw_name : "???"; + if (dohalt) { + openlog("halt", 0, LOG_AUTH | LOG_CONS); + syslog(LOG_CRIT, "halted by %s", user); + } else { + openlog("reboot", 0, LOG_AUTH | LOG_CONS); + syslog(LOG_CRIT, "rebooted by %s", user); + } + } + logwtmp("~", "shutdown", ""); + + /* + * Do a sync early on, so disks start transfers while we're off + * killing processes. Don't worry about writes done before the + * processes die, the reboot system call syncs the disks. + */ + if (!nflag) + sync(); + + /* Just stop init -- if we fail, we'll restart it. */ + if (kill(1, SIGTSTP) == -1) + err("SIGTSTP init: %s", strerror(errno)); + + /* Ignore the SIGHUP we get when our parent shell dies. */ + (void)signal(SIGHUP, SIG_IGN); + + /* Send a SIGTERM first, a chance to save the buffers. */ + if (kill(-1, SIGTERM) == -1) + err("SIGTERM processes: %s", strerror(errno)); + + /* + * After the processes receive the signal, start the rest of the + * buffers on their way. Wait 5 seconds between the SIGTERM and + * the SIGKILL to give everybody a chance. + */ + sleep(2); + if (!nflag) + sync(); + sleep(3); + + for (i = 1;; ++i) { + if (kill(-1, SIGKILL) == -1) { + if (errno == ESRCH) + break; + goto restart; + } + if (i > 5) { + (void)fprintf(stderr, + "WARNING: some process(es) wouldn't die\n"); + break; + } + (void)sleep(2 * i); + } + + reboot(howto); + /* FALLTHROUGH */ + +restart: + sverrno = errno; + err("%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "", + strerror(sverrno)); + /* NOTREACHED */ +} + +void +usage() +{ + (void)fprintf(stderr, "usage: %s [-nq]\n", dohalt ? "halt" : "reboot"); + exit(1); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "%s: ", dohalt ? "halt" : "reboot"); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/sbin/restore/Makefile b/sbin/restore/Makefile new file mode 100644 index 0000000..6d4da1fb --- /dev/null +++ b/sbin/restore/Makefile @@ -0,0 +1,17 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= restore +LINKS= ${BINDIR}/restore ${BINDIR}/rrestore +CFLAGS+=-DRRESTORE +SRCS= main.c interactive.c restore.c dirs.c symtab.c tape.c utilities.c \ + dumprmt.c +BINOWN= root +BINGRP= tty +BINMODE=6555 +MAN8= restore.0 +MLINKS+=restore.8 rrestore.8 +.PATH: ${.CURDIR}/../dump + +all: ${PROG} ${MAN8} + +.include <bsd.prog.mk> diff --git a/sbin/restore/dirs.c b/sbin/restore/dirs.c new file mode 100644 index 0000000..5a2651c --- /dev/null +++ b/sbin/restore/dirs.c @@ -0,0 +1,747 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dirs.c 8.2 (Berkeley) 1/21/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include <ufs/ffs/fs.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <protocols/dumprestore.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pathnames.h" +#include "restore.h" +#include "extern.h" + +/* + * Symbol table of directories read from tape. + */ +#define HASHSIZE 1000 +#define INOHASH(val) (val % HASHSIZE) +struct inotab { + struct inotab *t_next; + ino_t t_ino; + long t_seekpt; + long t_size; +}; +static struct inotab *inotab[HASHSIZE]; + +/* + * Information retained about directories. + */ +struct modeinfo { + ino_t ino; + struct timeval timep[2]; + short mode; + short uid; + short gid; +}; + +/* + * Definitions for library routines operating on directories. + */ +#undef DIRBLKSIZ +#define DIRBLKSIZ 1024 +struct rstdirdesc { + int dd_fd; + long dd_loc; + long dd_size; + char dd_buf[DIRBLKSIZ]; +}; + +/* + * Global variables for this file. + */ +static long seekpt; +static FILE *df, *mf; +static RST_DIR *dirp; +static char dirfile[32] = "#"; /* No file */ +static char modefile[32] = "#"; /* No file */ +static char dot[2] = "."; /* So it can be modified */ + +/* + * Format of old style directories. + */ +#define ODIRSIZ 14 +struct odirect { + u_short d_ino; + char d_name[ODIRSIZ]; +}; + +static struct inotab *allocinotab __P((ino_t, struct dinode *, long)); +static void dcvt __P((struct odirect *, struct direct *)); +static void flushent __P((void)); +static struct inotab *inotablookup __P((ino_t)); +static RST_DIR *opendirfile __P((const char *)); +static void putdir __P((char *, long)); +static void putent __P((struct direct *)); +static void rst_seekdir __P((RST_DIR *, long, long)); +static long rst_telldir __P((RST_DIR *)); +static struct direct *searchdir __P((ino_t, char *)); + +/* + * Extract directory contents, building up a directory structure + * on disk for extraction by name. + * If genmode is requested, save mode, owner, and times for all + * directories on the tape. + */ +void +extractdirs(genmode) + int genmode; +{ + register int i; + register struct dinode *ip; + struct inotab *itp; + struct direct nulldir; + + vprintf(stdout, "Extract directories from tape\n"); + (void) sprintf(dirfile, "%s/rstdir%d", _PATH_TMP, dumpdate); + df = fopen(dirfile, "w"); + if (df == NULL) { + fprintf(stderr, + "restore: %s - cannot create directory temporary\n", + dirfile); + fprintf(stderr, "fopen: %s\n", strerror(errno)); + done(1); + } + if (genmode != 0) { + (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate); + mf = fopen(modefile, "w"); + if (mf == NULL) { + fprintf(stderr, + "restore: %s - cannot create modefile \n", + modefile); + fprintf(stderr, "fopen: %s\n", strerror(errno)); + done(1); + } + } + nulldir.d_ino = 0; + nulldir.d_type = DT_DIR; + nulldir.d_namlen = 1; + (void) strcpy(nulldir.d_name, "/"); + nulldir.d_reclen = DIRSIZ(0, &nulldir); + for (;;) { + curfile.name = "<directory file - name unknown>"; + curfile.action = USING; + ip = curfile.dip; + if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) { + (void) fclose(df); + dirp = opendirfile(dirfile); + if (dirp == NULL) + fprintf(stderr, "opendirfile: %s\n", + strerror(errno)); + if (mf != NULL) + (void) fclose(mf); + i = dirlookup(dot); + if (i == 0) + panic("Root directory is not on tape\n"); + return; + } + itp = allocinotab(curfile.ino, ip, seekpt); + getfile(putdir, xtrnull); + putent(&nulldir); + flushent(); + itp->t_size = seekpt - itp->t_seekpt; + } +} + +/* + * skip over all the directories on the tape + */ +void +skipdirs() +{ + + while ((curfile.dip->di_mode & IFMT) == IFDIR) { + skipfile(); + } +} + +/* + * Recursively find names and inumbers of all files in subtree + * pname and pass them off to be processed. + */ +void +treescan(pname, ino, todo) + char *pname; + ino_t ino; + long (*todo) __P((char *, ino_t, int)); +{ + register struct inotab *itp; + register struct direct *dp; + int namelen; + long bpt; + char locname[MAXPATHLEN + 1]; + + itp = inotablookup(ino); + if (itp == NULL) { + /* + * Pname is name of a simple file or an unchanged directory. + */ + (void) (*todo)(pname, ino, LEAF); + return; + } + /* + * Pname is a dumped directory name. + */ + if ((*todo)(pname, ino, NODE) == FAIL) + return; + /* + * begin search through the directory + * skipping over "." and ".." + */ + (void) strncpy(locname, pname, MAXPATHLEN); + (void) strncat(locname, "/", MAXPATHLEN); + namelen = strlen(locname); + rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); + dp = rst_readdir(dirp); /* "." */ + if (dp != NULL && strcmp(dp->d_name, ".") == 0) + dp = rst_readdir(dirp); /* ".." */ + else + fprintf(stderr, "Warning: `.' missing from directory %s\n", + pname); + if (dp != NULL && strcmp(dp->d_name, "..") == 0) + dp = rst_readdir(dirp); /* first real entry */ + else + fprintf(stderr, "Warning: `..' missing from directory %s\n", + pname); + bpt = rst_telldir(dirp); + /* + * a zero inode signals end of directory + */ + while (dp != NULL && dp->d_ino != 0) { + locname[namelen] = '\0'; + if (namelen + dp->d_namlen >= MAXPATHLEN) { + fprintf(stderr, "%s%s: name exceeds %d char\n", + locname, dp->d_name, MAXPATHLEN); + } else { + (void) strncat(locname, dp->d_name, (int)dp->d_namlen); + treescan(locname, dp->d_ino, todo); + rst_seekdir(dirp, bpt, itp->t_seekpt); + } + dp = rst_readdir(dirp); + bpt = rst_telldir(dirp); + } + if (dp == NULL) + fprintf(stderr, "corrupted directory: %s.\n", locname); +} + +/* + * Lookup a pathname which is always assumed to start from the ROOTINO. + */ +struct direct * +pathsearch(pathname) + const char *pathname; +{ + ino_t ino; + struct direct *dp; + char *path, *name, buffer[MAXPATHLEN]; + + strcpy(buffer, pathname); + path = buffer; + ino = ROOTINO; + while (*path == '/') + path++; + dp = NULL; + while ((name = strsep(&path, "/")) != NULL && *name != NULL) { + if ((dp = searchdir(ino, name)) == NULL) + return (NULL); + ino = dp->d_ino; + } + return (dp); +} + +/* + * Lookup the requested name in directory inum. + * Return its inode number if found, zero if it does not exist. + */ +static struct direct * +searchdir(inum, name) + ino_t inum; + char *name; +{ + register struct direct *dp; + register struct inotab *itp; + int len; + + itp = inotablookup(inum); + if (itp == NULL) + return (NULL); + rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); + len = strlen(name); + do { + dp = rst_readdir(dirp); + if (dp == NULL || dp->d_ino == 0) + return (NULL); + } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0); + return (dp); +} + +/* + * Put the directory entries in the directory file + */ +static void +putdir(buf, size) + char *buf; + long size; +{ + struct direct cvtbuf; + register struct odirect *odp; + struct odirect *eodp; + register struct direct *dp; + long loc, i; + + if (cvtflag) { + eodp = (struct odirect *)&buf[size]; + for (odp = (struct odirect *)buf; odp < eodp; odp++) + if (odp->d_ino != 0) { + dcvt(odp, &cvtbuf); + putent(&cvtbuf); + } + } else { + for (loc = 0; loc < size; ) { + dp = (struct direct *)(buf + loc); + if (oldinofmt) { + if (Bcvt) { + swabst((u_char *)"l2s", (u_char *) dp); + } + } else { + if (Bcvt) { + swabst((u_char *)"ls", (u_char *) dp); + } + } + i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); + if ((dp->d_reclen & 0x3) != 0 || + dp->d_reclen > i || + dp->d_reclen < DIRSIZ(0, dp) || + dp->d_namlen > NAME_MAX) { + vprintf(stdout, "Mangled directory: "); + if ((dp->d_reclen & 0x3) != 0) + vprintf(stdout, + "reclen not multiple of 4 "); + if (dp->d_reclen < DIRSIZ(0, dp)) + vprintf(stdout, + "reclen less than DIRSIZ (%d < %d) ", + dp->d_reclen, DIRSIZ(0, dp)); + if (dp->d_namlen > NAME_MAX) + vprintf(stdout, + "reclen name too big (%d > %d) ", + dp->d_namlen, NAME_MAX); + vprintf(stdout, "\n"); + loc += i; + continue; + } + loc += dp->d_reclen; + if (dp->d_ino != 0) { + putent(dp); + } + } + } +} + +/* + * These variables are "local" to the following two functions. + */ +char dirbuf[DIRBLKSIZ]; +long dirloc = 0; +long prev = 0; + +/* + * add a new directory entry to a file. + */ +static void +putent(dp) + struct direct *dp; +{ + dp->d_reclen = DIRSIZ(0, dp); + if (dirloc + dp->d_reclen > DIRBLKSIZ) { + ((struct direct *)(dirbuf + prev))->d_reclen = + DIRBLKSIZ - prev; + (void) fwrite(dirbuf, 1, DIRBLKSIZ, df); + dirloc = 0; + } + bcopy((char *)dp, dirbuf + dirloc, (long)dp->d_reclen); + prev = dirloc; + dirloc += dp->d_reclen; +} + +/* + * flush out a directory that is finished. + */ +static void +flushent() +{ + ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; + (void) fwrite(dirbuf, (int)dirloc, 1, df); + seekpt = ftell(df); + dirloc = 0; +} + +static void +dcvt(odp, ndp) + register struct odirect *odp; + register struct direct *ndp; +{ + + bzero((char *)ndp, (long)(sizeof *ndp)); + ndp->d_ino = odp->d_ino; + ndp->d_type = DT_UNKNOWN; + (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ); + ndp->d_namlen = strlen(ndp->d_name); + ndp->d_reclen = DIRSIZ(0, ndp); +} + +/* + * Seek to an entry in a directory. + * Only values returned by rst_telldir should be passed to rst_seekdir. + * This routine handles many directories in a single file. + * It takes the base of the directory in the file, plus + * the desired seek offset into it. + */ +static void +rst_seekdir(dirp, loc, base) + register RST_DIR *dirp; + long loc, base; +{ + + if (loc == rst_telldir(dirp)) + return; + loc -= base; + if (loc < 0) + fprintf(stderr, "bad seek pointer to rst_seekdir %d\n", loc); + (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET); + dirp->dd_loc = loc & (DIRBLKSIZ - 1); + if (dirp->dd_loc != 0) + dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); +} + +/* + * get next entry in a directory. + */ +struct direct * +rst_readdir(dirp) + register RST_DIR *dirp; +{ + register struct direct *dp; + + for (;;) { + if (dirp->dd_loc == 0) { + dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, + DIRBLKSIZ); + if (dirp->dd_size <= 0) { + dprintf(stderr, "error reading directory\n"); + return (NULL); + } + } + if (dirp->dd_loc >= dirp->dd_size) { + dirp->dd_loc = 0; + continue; + } + dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc); + if (dp->d_reclen == 0 || + dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) { + dprintf(stderr, "corrupted directory: bad reclen %d\n", + dp->d_reclen); + return (NULL); + } + dirp->dd_loc += dp->d_reclen; + if (dp->d_ino == 0 && strcmp(dp->d_name, "/") != 0) + continue; + if (dp->d_ino >= maxino) { + dprintf(stderr, "corrupted directory: bad inum %d\n", + dp->d_ino); + continue; + } + return (dp); + } +} + +/* + * Simulate the opening of a directory + */ +RST_DIR * +rst_opendir(name) + const char *name; +{ + struct inotab *itp; + RST_DIR *dirp; + ino_t ino; + + if ((ino = dirlookup(name)) > 0 && + (itp = inotablookup(ino)) != NULL) { + dirp = opendirfile(dirfile); + rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); + return (dirp); + } + return (NULL); +} + +/* + * In our case, there is nothing to do when closing a directory. + */ +void +rst_closedir(dirp) + RST_DIR *dirp; +{ + + (void)close(dirp->dd_fd); + free(dirp); + return; +} + +/* + * Simulate finding the current offset in the directory. + */ +static long +rst_telldir(dirp) + RST_DIR *dirp; +{ + return ((long)lseek(dirp->dd_fd, + (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc); +} + +/* + * Open a directory file. + */ +static RST_DIR * +opendirfile(name) + const char *name; +{ + register RST_DIR *dirp; + register int fd; + + if ((fd = open(name, O_RDONLY)) == -1) + return (NULL); + if ((dirp = malloc(sizeof(RST_DIR))) == NULL) { + (void)close(fd); + return (NULL); + } + dirp->dd_fd = fd; + dirp->dd_loc = 0; + return (dirp); +} + +/* + * Set the mode, owner, and times for all new or changed directories + */ +void +setdirmodes(flags) + int flags; +{ + FILE *mf; + struct modeinfo node; + struct entry *ep; + char *cp; + + vprintf(stdout, "Set directory mode, owner, and times.\n"); + (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate); + mf = fopen(modefile, "r"); + if (mf == NULL) { + fprintf(stderr, "fopen: %s\n", strerror(errno)); + fprintf(stderr, "cannot open mode file %s\n", modefile); + fprintf(stderr, "directory mode, owner, and times not set\n"); + return; + } + clearerr(mf); + for (;;) { + (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf); + if (feof(mf)) + break; + ep = lookupino(node.ino); + if (command == 'i' || command == 'x') { + if (ep == NULL) + continue; + if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) { + ep->e_flags &= ~NEW; + continue; + } + if (node.ino == ROOTINO && + reply("set owner/mode for '.'") == FAIL) + continue; + } + if (ep == NULL) { + panic("cannot find directory inode %d\n", node.ino); + } else { + cp = myname(ep); + (void) chown(cp, node.uid, node.gid); + (void) chmod(cp, node.mode); + utimes(cp, node.timep); + ep->e_flags &= ~NEW; + } + } + if (ferror(mf)) + panic("error setting directory modes\n"); + (void) fclose(mf); +} + +/* + * Generate a literal copy of a directory. + */ +int +genliteraldir(name, ino) + char *name; + ino_t ino; +{ + register struct inotab *itp; + int ofile, dp, i, size; + char buf[BUFSIZ]; + + itp = inotablookup(ino); + if (itp == NULL) + panic("Cannot find directory inode %d named %s\n", ino, name); + if ((ofile = creat(name, 0666)) < 0) { + fprintf(stderr, "%s: ", name); + (void) fflush(stderr); + fprintf(stderr, "cannot create file: %s\n", strerror(errno)); + return (FAIL); + } + rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); + dp = dup(dirp->dd_fd); + for (i = itp->t_size; i > 0; i -= BUFSIZ) { + size = i < BUFSIZ ? i : BUFSIZ; + if (read(dp, buf, (int) size) == -1) { + fprintf(stderr, + "write error extracting inode %d, name %s\n", + curfile.ino, curfile.name); + fprintf(stderr, "read: %s\n", strerror(errno)); + done(1); + } + if (!Nflag && write(ofile, buf, (int) size) == -1) { + fprintf(stderr, + "write error extracting inode %d, name %s\n", + curfile.ino, curfile.name); + fprintf(stderr, "write: %s\n", strerror(errno)); + done(1); + } + } + (void) close(dp); + (void) close(ofile); + return (GOOD); +} + +/* + * Determine the type of an inode + */ +int +inodetype(ino) + ino_t ino; +{ + struct inotab *itp; + + itp = inotablookup(ino); + if (itp == NULL) + return (LEAF); + return (NODE); +} + +/* + * Allocate and initialize a directory inode entry. + * If requested, save its pertinent mode, owner, and time info. + */ +static struct inotab * +allocinotab(ino, dip, seekpt) + ino_t ino; + struct dinode *dip; + long seekpt; +{ + register struct inotab *itp; + struct modeinfo node; + + itp = calloc(1, sizeof(struct inotab)); + if (itp == NULL) + panic("no memory directory table\n"); + itp->t_next = inotab[INOHASH(ino)]; + inotab[INOHASH(ino)] = itp; + itp->t_ino = ino; + itp->t_seekpt = seekpt; + if (mf == NULL) + return (itp); + node.ino = ino; + node.timep[0].tv_sec = dip->di_atime.ts_sec; + node.timep[0].tv_usec = dip->di_atime.ts_nsec / 1000; + node.timep[1].tv_sec = dip->di_mtime.ts_sec; + node.timep[1].tv_usec = dip->di_mtime.ts_nsec / 1000; + node.mode = dip->di_mode; + node.uid = dip->di_uid; + node.gid = dip->di_gid; + (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf); + return (itp); +} + +/* + * Look up an inode in the table of directories + */ +static struct inotab * +inotablookup(ino) + ino_t ino; +{ + register struct inotab *itp; + + for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) + if (itp->t_ino == ino) + return (itp); + return (NULL); +} + +/* + * Clean up and exit + */ +__dead void +done(exitcode) + int exitcode; +{ + + closemt(); + if (modefile[0] != '#') + (void) unlink(modefile); + if (dirfile[0] != '#') + (void) unlink(dirfile); + exit(exitcode); +} diff --git a/sbin/restore/extern.h b/sbin/restore/extern.h new file mode 100644 index 0000000..d82569e --- /dev/null +++ b/sbin/restore/extern.h @@ -0,0 +1,108 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.2 (Berkeley) 1/7/94 + */ + +struct entry *addentry __P((char *, ino_t, int)); +long addfile __P((char *, ino_t, int)); +void badentry __P((struct entry *, char *)); +void canon __P((char *, char *)); +void checkrestore __P((void)); +void closemt __P((void)); +void createfiles __P((void)); +void createleaves __P((char *)); +void createlinks __P((void)); +long deletefile __P((char *, ino_t, int)); +void deleteino __P((ino_t)); +ino_t dirlookup __P((const char *)); +__dead void done __P((int)); +void dumpsymtable __P((char *, long)); +void extractdirs __P((int)); +int extractfile __P((char *)); +void findunreflinks __P((void)); +char *flagvalues __P((struct entry *)); +void freeentry __P((struct entry *)); +void freename __P((char *)); +int genliteraldir __P((char *, ino_t)); +char *gentempname __P((struct entry *)); +void getfile __P((void (*)(char *, long), void (*)(char *, long))); +void getvol __P((long)); +void initsymtable __P((char *)); +int inodetype __P((ino_t)); +int linkit __P((char *, char *, int)); +struct entry *lookupino __P((ino_t)); +struct entry *lookupname __P((char *)); +long listfile __P((char *, ino_t, int)); +ino_t lowerbnd __P((ino_t)); +void mktempname __P((struct entry *)); +void moveentry __P((struct entry *, char *)); +void msg __P((const char *, ...)); +char *myname __P((struct entry *)); +void newnode __P((struct entry *)); +void newtapebuf __P((long)); +long nodeupdates __P((char *, ino_t, int)); +void onintr __P((int)); +void panic __P((const char *, ...)); +void pathcheck __P((char *)); +struct direct *pathsearch __P((const char *)); +void printdumpinfo __P((void)); +void removeleaf __P((struct entry *)); +void removenode __P((struct entry *)); +void removeoldleaves __P((void)); +void removeoldnodes __P((void)); +void renameit __P((char *, char *)); +int reply __P((char *)); +RST_DIR *rst_opendir __P((const char *)); +struct direct *rst_readdir __P((RST_DIR *)); +void rst_closedir __P((RST_DIR *dirp)); +void runcmdshell __P((void)); +char *savename __P((char *)); +void setdirmodes __P((int)); +void setinput __P((char *)); +void setup __P((void)); +void skipdirs __P((void)); +void skipfile __P((void)); +void skipmaps __P((void)); +void swabst __P((u_char *, u_char *)); +void treescan __P((char *, ino_t, long (*)(char *, ino_t, int))); +ino_t upperbnd __P((ino_t)); +long verifyfile __P((char *, ino_t, int)); +void xtrnull __P((char *, long)); + +/* From ../dump/dumprmt.c */ +void rmtclose __P((void)); +int rmthost __P((char *)); +int rmtioctl __P((int, int)); +int rmtopen __P((char *, int)); +int rmtread __P((char *, int)); +int rmtseek __P((int, int)); diff --git a/sbin/restore/interactive.c b/sbin/restore/interactive.c new file mode 100644 index 0000000..1b9616c --- /dev/null +++ b/sbin/restore/interactive.c @@ -0,0 +1,755 @@ +/* + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)interactive.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <ufs/ffs/fs.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <protocols/dumprestore.h> + +#include <setjmp.h> +#include <glob.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "restore.h" +#include "extern.h" + +#define round(a, b) (((a) + (b) - 1) / (b) * (b)) + +/* + * Things to handle interruptions. + */ +static int runshell; +static jmp_buf reset; +static char *nextarg = NULL; + +/* + * Structure and routines associated with listing directories. + */ +struct afile { + ino_t fnum; /* inode number of file */ + char *fname; /* file name */ + short len; /* name length */ + char prefix; /* prefix character */ + char postfix; /* postfix character */ +}; +struct arglist { + int freeglob; /* glob structure needs to be freed */ + int argcnt; /* next globbed argument to return */ + glob_t glob; /* globbing information */ + char *cmd; /* the current command */ +}; + +static char *copynext __P((char *, char *)); +static int fcmp __P((const void *, const void *)); +static void formatf __P((struct afile *, int)); +static void getcmd __P((char *, char *, char *, struct arglist *)); +struct dirent *glob_readdir __P((RST_DIR *dirp)); +static int glob_stat __P((const char *, struct stat *)); +static void mkentry __P((struct direct *, struct afile *)); +static void printlist __P((char *, char *)); + +/* + * Read and execute commands from the terminal. + */ +void +runcmdshell() +{ + register struct entry *np; + ino_t ino; + struct arglist arglist; + char curdir[MAXPATHLEN]; + char name[MAXPATHLEN]; + char cmd[BUFSIZ]; + + arglist.freeglob = 0; + arglist.argcnt = 0; + arglist.glob.gl_flags = GLOB_ALTDIRFUNC; + arglist.glob.gl_opendir = (void *)rst_opendir; + arglist.glob.gl_readdir = (void *)glob_readdir; + arglist.glob.gl_closedir = (void *)rst_closedir; + arglist.glob.gl_lstat = glob_stat; + arglist.glob.gl_stat = glob_stat; + canon("/", curdir); +loop: + if (setjmp(reset) != 0) { + if (arglist.freeglob != 0) { + arglist.freeglob = 0; + arglist.argcnt = 0; + globfree(&arglist.glob); + } + nextarg = NULL; + volno = 0; + } + runshell = 1; + getcmd(curdir, cmd, name, &arglist); + switch (cmd[0]) { + /* + * Add elements to the extraction list. + */ + case 'a': + if (strncmp(cmd, "add", strlen(cmd)) != 0) + goto bad; + ino = dirlookup(name); + if (ino == 0) + break; + if (mflag) + pathcheck(name); + treescan(name, ino, addfile); + break; + /* + * Change working directory. + */ + case 'c': + if (strncmp(cmd, "cd", strlen(cmd)) != 0) + goto bad; + ino = dirlookup(name); + if (ino == 0) + break; + if (inodetype(ino) == LEAF) { + fprintf(stderr, "%s: not a directory\n", name); + break; + } + (void) strcpy(curdir, name); + break; + /* + * Delete elements from the extraction list. + */ + case 'd': + if (strncmp(cmd, "delete", strlen(cmd)) != 0) + goto bad; + np = lookupname(name); + if (np == NULL || (np->e_flags & NEW) == 0) { + fprintf(stderr, "%s: not on extraction list\n", name); + break; + } + treescan(name, np->e_ino, deletefile); + break; + /* + * Extract the requested list. + */ + case 'e': + if (strncmp(cmd, "extract", strlen(cmd)) != 0) + goto bad; + createfiles(); + createlinks(); + setdirmodes(0); + if (dflag) + checkrestore(); + volno = 0; + break; + /* + * List available commands. + */ + case 'h': + if (strncmp(cmd, "help", strlen(cmd)) != 0) + goto bad; + case '?': + fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + "Available commands are:\n", + "\tls [arg] - list directory\n", + "\tcd arg - change directory\n", + "\tpwd - print current directory\n", + "\tadd [arg] - add `arg' to list of", + " files to be extracted\n", + "\tdelete [arg] - delete `arg' from", + " list of files to be extracted\n", + "\textract - extract requested files\n", + "\tsetmodes - set modes of requested directories\n", + "\tquit - immediately exit program\n", + "\twhat - list dump header information\n", + "\tverbose - toggle verbose flag", + " (useful with ``ls'')\n", + "\thelp or `?' - print this list\n", + "If no `arg' is supplied, the current", + " directory is used\n"); + break; + /* + * List a directory. + */ + case 'l': + if (strncmp(cmd, "ls", strlen(cmd)) != 0) + goto bad; + printlist(name, curdir); + break; + /* + * Print current directory. + */ + case 'p': + if (strncmp(cmd, "pwd", strlen(cmd)) != 0) + goto bad; + if (curdir[1] == '\0') + fprintf(stderr, "/\n"); + else + fprintf(stderr, "%s\n", &curdir[1]); + break; + /* + * Quit. + */ + case 'q': + if (strncmp(cmd, "quit", strlen(cmd)) != 0) + goto bad; + return; + case 'x': + if (strncmp(cmd, "xit", strlen(cmd)) != 0) + goto bad; + return; + /* + * Toggle verbose mode. + */ + case 'v': + if (strncmp(cmd, "verbose", strlen(cmd)) != 0) + goto bad; + if (vflag) { + fprintf(stderr, "verbose mode off\n"); + vflag = 0; + break; + } + fprintf(stderr, "verbose mode on\n"); + vflag++; + break; + /* + * Just restore requested directory modes. + */ + case 's': + if (strncmp(cmd, "setmodes", strlen(cmd)) != 0) + goto bad; + setdirmodes(FORCE); + break; + /* + * Print out dump header information. + */ + case 'w': + if (strncmp(cmd, "what", strlen(cmd)) != 0) + goto bad; + printdumpinfo(); + break; + /* + * Turn on debugging. + */ + case 'D': + if (strncmp(cmd, "Debug", strlen(cmd)) != 0) + goto bad; + if (dflag) { + fprintf(stderr, "debugging mode off\n"); + dflag = 0; + break; + } + fprintf(stderr, "debugging mode on\n"); + dflag++; + break; + /* + * Unknown command. + */ + default: + bad: + fprintf(stderr, "%s: unknown command; type ? for help\n", cmd); + break; + } + goto loop; +} + +/* + * Read and parse an interactive command. + * The first word on the line is assigned to "cmd". If + * there are no arguments on the command line, then "curdir" + * is returned as the argument. If there are arguments + * on the line they are returned one at a time on each + * successive call to getcmd. Each argument is first assigned + * to "name". If it does not start with "/" the pathname in + * "curdir" is prepended to it. Finally "canon" is called to + * eliminate any embedded ".." components. + */ +static void +getcmd(curdir, cmd, name, ap) + char *curdir, *cmd, *name; + struct arglist *ap; +{ + register char *cp; + static char input[BUFSIZ]; + char output[BUFSIZ]; +# define rawname input /* save space by reusing input buffer */ + + /* + * Check to see if still processing arguments. + */ + if (ap->argcnt > 0) + goto retnext; + if (nextarg != NULL) + goto getnext; + /* + * Read a command line and trim off trailing white space. + */ + do { + fprintf(stderr, "restore > "); + (void) fflush(stderr); + (void) fgets(input, BUFSIZ, terminal); + } while (!feof(terminal) && input[0] == '\n'); + if (feof(terminal)) { + (void) strcpy(cmd, "quit"); + return; + } + for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--) + /* trim off trailing white space and newline */; + *++cp = '\0'; + /* + * Copy the command into "cmd". + */ + cp = copynext(input, cmd); + ap->cmd = cmd; + /* + * If no argument, use curdir as the default. + */ + if (*cp == '\0') { + (void) strcpy(name, curdir); + return; + } + nextarg = cp; + /* + * Find the next argument. + */ +getnext: + cp = copynext(nextarg, rawname); + if (*cp == '\0') + nextarg = NULL; + else + nextarg = cp; + /* + * If it is an absolute pathname, canonicalize it and return it. + */ + if (rawname[0] == '/') { + canon(rawname, name); + } else { + /* + * For relative pathnames, prepend the current directory to + * it then canonicalize and return it. + */ + (void) strcpy(output, curdir); + (void) strcat(output, "/"); + (void) strcat(output, rawname); + canon(output, name); + } + if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0) + fprintf(stderr, "%s: out of memory\n", ap->cmd); + if (ap->glob.gl_pathc == 0) + return; + ap->freeglob = 1; + ap->argcnt = ap->glob.gl_pathc; + +retnext: + strcpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt]); + if (--ap->argcnt == 0) { + ap->freeglob = 0; + globfree(&ap->glob); + } +# undef rawname +} + +/* + * Strip off the next token of the input. + */ +static char * +copynext(input, output) + char *input, *output; +{ + register char *cp, *bp; + char quote; + + for (cp = input; *cp == ' ' || *cp == '\t'; cp++) + /* skip to argument */; + bp = output; + while (*cp != ' ' && *cp != '\t' && *cp != '\0') { + /* + * Handle back slashes. + */ + if (*cp == '\\') { + if (*++cp == '\0') { + fprintf(stderr, + "command lines cannot be continued\n"); + continue; + } + *bp++ = *cp++; + continue; + } + /* + * The usual unquoted case. + */ + if (*cp != '\'' && *cp != '"') { + *bp++ = *cp++; + continue; + } + /* + * Handle single and double quotes. + */ + quote = *cp++; + while (*cp != quote && *cp != '\0') + *bp++ = *cp++ | 0200; + if (*cp++ == '\0') { + fprintf(stderr, "missing %c\n", quote); + cp--; + continue; + } + } + *bp = '\0'; + return (cp); +} + +/* + * Canonicalize file names to always start with ``./'' and + * remove any imbedded "." and ".." components. + */ +void +canon(rawname, canonname) + char *rawname, *canonname; +{ + register char *cp, *np; + + if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0) + (void) strcpy(canonname, ""); + else if (rawname[0] == '/') + (void) strcpy(canonname, "."); + else + (void) strcpy(canonname, "./"); + (void) strcat(canonname, rawname); + /* + * Eliminate multiple and trailing '/'s + */ + for (cp = np = canonname; *np != '\0'; cp++) { + *cp = *np++; + while (*cp == '/' && *np == '/') + np++; + } + *cp = '\0'; + if (*--cp == '/') + *cp = '\0'; + /* + * Eliminate extraneous "." and ".." from pathnames. + */ + for (np = canonname; *np != '\0'; ) { + np++; + cp = np; + while (*np != '/' && *np != '\0') + np++; + if (np - cp == 1 && *cp == '.') { + cp--; + (void) strcpy(cp, np); + np = cp; + } + if (np - cp == 2 && strncmp(cp, "..", 2) == 0) { + cp--; + while (cp > &canonname[1] && *--cp != '/') + /* find beginning of name */; + (void) strcpy(cp, np); + np = cp; + } + } +} + +/* + * Do an "ls" style listing of a directory + */ +static void +printlist(name, basename) + char *name; + char *basename; +{ + register struct afile *fp, *list, *listp; + register struct direct *dp; + struct afile single; + RST_DIR *dirp; + int entries, len; + + dp = pathsearch(name); + if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)) + return; + if ((dirp = rst_opendir(name)) == NULL) { + entries = 1; + list = &single; + mkentry(dp, list); + len = strlen(basename) + 1; + if (strlen(name) - len > single.len) { + freename(single.fname); + single.fname = savename(&name[len]); + single.len = strlen(single.fname); + } + } else { + entries = 0; + while (dp = rst_readdir(dirp)) + entries++; + rst_closedir(dirp); + list = (struct afile *)malloc(entries * sizeof(struct afile)); + if (list == NULL) { + fprintf(stderr, "ls: out of memory\n"); + return; + } + if ((dirp = rst_opendir(name)) == NULL) + panic("directory reopen failed\n"); + fprintf(stderr, "%s:\n", name); + entries = 0; + listp = list; + while (dp = rst_readdir(dirp)) { + if (dp == NULL || dp->d_ino == 0) + break; + if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) + continue; + if (vflag == 0 && + (strcmp(dp->d_name, ".") == 0 || + strcmp(dp->d_name, "..") == 0)) + continue; + mkentry(dp, listp++); + entries++; + } + rst_closedir(dirp); + if (entries == 0) { + fprintf(stderr, "\n"); + free(list); + return; + } + qsort((char *)list, entries, sizeof(struct afile), fcmp); + } + formatf(list, entries); + if (dirp != NULL) { + for (fp = listp - 1; fp >= list; fp--) + freename(fp->fname); + fprintf(stderr, "\n"); + free(list); + } +} + +/* + * Read the contents of a directory. + */ +static void +mkentry(dp, fp) + struct direct *dp; + register struct afile *fp; +{ + char *cp; + struct entry *np; + + fp->fnum = dp->d_ino; + fp->fname = savename(dp->d_name); + for (cp = fp->fname; *cp; cp++) + if (!vflag && (*cp < ' ' || *cp >= 0177)) + *cp = '?'; + fp->len = cp - fp->fname; + if (dflag && TSTINO(fp->fnum, dumpmap) == 0) + fp->prefix = '^'; + else if ((np = lookupino(fp->fnum)) != NULL && (np->e_flags & NEW)) + fp->prefix = '*'; + else + fp->prefix = ' '; + switch(dp->d_type) { + + default: + fprintf(stderr, "Warning: undefined file type %d\n", + dp->d_type); + /* fall through */ + case DT_REG: + fp->postfix = ' '; + break; + + case DT_LNK: + fp->postfix = '@'; + break; + + case DT_FIFO: + case DT_SOCK: + fp->postfix = '='; + break; + + case DT_CHR: + case DT_BLK: + fp->postfix = '#'; + break; + + case DT_UNKNOWN: + case DT_DIR: + if (inodetype(dp->d_ino) == NODE) + fp->postfix = '/'; + else + fp->postfix = ' '; + break; + } + return; +} + +/* + * Print out a pretty listing of a directory + */ +static void +formatf(list, nentry) + register struct afile *list; + int nentry; +{ + register struct afile *fp, *endlist; + int width, bigino, haveprefix, havepostfix; + int i, j, w, precision, columns, lines; + + width = 0; + haveprefix = 0; + havepostfix = 0; + bigino = ROOTINO; + endlist = &list[nentry]; + for (fp = &list[0]; fp < endlist; fp++) { + if (bigino < fp->fnum) + bigino = fp->fnum; + if (width < fp->len) + width = fp->len; + if (fp->prefix != ' ') + haveprefix = 1; + if (fp->postfix != ' ') + havepostfix = 1; + } + if (haveprefix) + width++; + if (havepostfix) + width++; + if (vflag) { + for (precision = 0, i = bigino; i > 0; i /= 10) + precision++; + width += precision + 1; + } + width++; + columns = 81 / width; + if (columns == 0) + columns = 1; + lines = (nentry + columns - 1) / columns; + for (i = 0; i < lines; i++) { + for (j = 0; j < columns; j++) { + fp = &list[j * lines + i]; + if (vflag) { + fprintf(stderr, "%*d ", precision, fp->fnum); + fp->len += precision + 1; + } + if (haveprefix) { + putc(fp->prefix, stderr); + fp->len++; + } + fprintf(stderr, "%s", fp->fname); + if (havepostfix) { + putc(fp->postfix, stderr); + fp->len++; + } + if (fp + lines >= endlist) { + fprintf(stderr, "\n"); + break; + } + for (w = fp->len; w < width; w++) + putc(' ', stderr); + } + } +} + +/* + * Skip over directory entries that are not on the tape + * + * First have to get definition of a dirent. + */ +#undef DIRBLKSIZ +#include <dirent.h> +#undef d_ino + +struct dirent * +glob_readdir(dirp) + RST_DIR *dirp; +{ + struct direct *dp; + static struct dirent adirent; + + while ((dp = rst_readdir(dirp)) != NULL) { + if (dp->d_ino == 0) + continue; + if (dflag || TSTINO(dp->d_ino, dumpmap)) + break; + } + if (dp == NULL) + return (NULL); + adirent.d_fileno = dp->d_ino; + adirent.d_namlen = dp->d_namlen; + bcopy(dp->d_name, adirent.d_name, dp->d_namlen + 1); + return (&adirent); +} + +/* + * Return st_mode information in response to stat or lstat calls + */ +static int +glob_stat(name, stp) + const char *name; + struct stat *stp; +{ + register struct direct *dp; + + dp = pathsearch(name); + if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)) + return (-1); + if (inodetype(dp->d_ino) == NODE) + stp->st_mode = IFDIR; + else + stp->st_mode = IFREG; + return (0); +} + +/* + * Comparison routine for qsort. + */ +static int +fcmp(f1, f2) + register const void *f1, *f2; +{ + return (strcmp(((struct afile *)f1)->fname, + ((struct afile *)f2)->fname)); +} + +/* + * respond to interrupts + */ +void +onintr(signo) + int signo; +{ + if (command == 'i' && runshell) + longjmp(reset, 1); + if (reply("restore interrupted, continue") == FAIL) + done(1); +} diff --git a/sbin/restore/main.c b/sbin/restore/main.c new file mode 100644 index 0000000..e2d8eeb --- /dev/null +++ b/sbin/restore/main.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/7/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <ufs/ffs/fs.h> +#include <ufs/ufs/dinode.h> +#include <protocols/dumprestore.h> + +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pathnames.h" +#include "restore.h" +#include "extern.h" + +int bflag = 0, cvtflag = 0, dflag = 0, vflag = 0, yflag = 0; +int hflag = 1, mflag = 1, Nflag = 0; +char command = '\0'; +long dumpnum = 1; +long volno = 0; +long ntrec; +char *dumpmap; +char *clrimap; +ino_t maxino; +time_t dumptime; +time_t dumpdate; +FILE *terminal; + +static void obsolete __P((int *, char **[])); +static void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + ino_t ino; + char *inputdev = _PATH_DEFTAPE; + char *symtbl = "./restoresymtable"; + char *p, name[MAXPATHLEN]; + + if (argc < 2) + usage(); + + obsolete(&argc, &argv); + while ((ch = getopt(argc, argv, "b:cdf:himNRrs:tvxy")) != EOF) + switch(ch) { + case 'b': + /* Change default tape blocksize. */ + bflag = 1; + ntrec = strtol(optarg, &p, 10); + if (*p) + errx(1, "illegal blocksize -- %s", optarg); + if (ntrec <= 0) + errx(1, "block size must be greater than 0"); + break; + case 'c': + cvtflag = 1; + break; + case 'd': + dflag = 1; + break; + case 'f': + inputdev = optarg; + break; + case 'h': + hflag = 0; + break; + case 'i': + case 'R': + case 'r': + case 't': + case 'x': + if (command != '\0') + errx(1, + "%c and %c options are mutually exclusive", + ch, command); + command = ch; + break; + case 'm': + mflag = 0; + break; + case 'N': + Nflag = 1; + break; + case 's': + /* Dumpnum (skip to) for multifile dump tapes. */ + dumpnum = strtol(optarg, &p, 10); + if (*p) + errx(1, "illegal dump number -- %s", optarg); + if (dumpnum <= 0) + errx(1, "dump number must be greater than 0"); + break; + case 'v': + vflag = 1; + break; + case 'y': + yflag = 1; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (command == '\0') + errx(1, "none of i, R, r, t or x options specified"); + + if (signal(SIGINT, onintr) == SIG_IGN) + (void) signal(SIGINT, SIG_IGN); + if (signal(SIGTERM, onintr) == SIG_IGN) + (void) signal(SIGTERM, SIG_IGN); + setlinebuf(stderr); + + setinput(inputdev); + + if (argc == 0) { + argc = 1; + *--argv = "."; + } + + switch (command) { + /* + * Interactive mode. + */ + case 'i': + setup(); + extractdirs(1); + initsymtable(NULL); + runcmdshell(); + break; + /* + * Incremental restoration of a file system. + */ + case 'r': + setup(); + if (dumptime > 0) { + /* + * This is an incremental dump tape. + */ + vprintf(stdout, "Begin incremental restore\n"); + initsymtable(symtbl); + extractdirs(1); + removeoldleaves(); + vprintf(stdout, "Calculate node updates.\n"); + treescan(".", ROOTINO, nodeupdates); + findunreflinks(); + removeoldnodes(); + } else { + /* + * This is a level zero dump tape. + */ + vprintf(stdout, "Begin level 0 restore\n"); + initsymtable((char *)0); + extractdirs(1); + vprintf(stdout, "Calculate extraction list.\n"); + treescan(".", ROOTINO, nodeupdates); + } + createleaves(symtbl); + createlinks(); + setdirmodes(FORCE); + checkrestore(); + if (dflag) { + vprintf(stdout, "Verify the directory structure\n"); + treescan(".", ROOTINO, verifyfile); + } + dumpsymtable(symtbl, (long)1); + break; + /* + * Resume an incremental file system restoration. + */ + case 'R': + initsymtable(symtbl); + skipmaps(); + skipdirs(); + createleaves(symtbl); + createlinks(); + setdirmodes(FORCE); + checkrestore(); + dumpsymtable(symtbl, (long)1); + break; + /* + * List contents of tape. + */ + case 't': + setup(); + extractdirs(0); + initsymtable((char *)0); + while (argc--) { + canon(*argv++, name); + ino = dirlookup(name); + if (ino == 0) + continue; + treescan(name, ino, listfile); + } + break; + /* + * Batch extraction of tape contents. + */ + case 'x': + setup(); + extractdirs(1); + initsymtable((char *)0); + while (argc--) { + canon(*argv++, name); + ino = dirlookup(name); + if (ino == 0) + continue; + if (mflag) + pathcheck(name); + treescan(name, ino, addfile); + } + createfiles(); + createlinks(); + setdirmodes(0); + if (dflag) + checkrestore(); + break; + } + done(0); + /* NOTREACHED */ +} + +static void +usage() +{ + (void)fprintf(stderr, "usage:\t%s%s%s%s%s", + "restore tfhsvy [file ...]\n", + "\trestore xfhmsvy [file ...]\n", + "\trestore ifhmsvy\n", + "\trestore rfsvy\n", + "\trestore Rfsvy\n"); + done(1); +} + +/* + * obsolete -- + * Change set of key letters and ordered arguments into something + * getopt(3) will like. + */ +static void +obsolete(argcp, argvp) + int *argcp; + char **argvp[]; +{ + int argc, flags; + char *ap, **argv, *flagsp, **nargv, *p; + + /* Setup. */ + argv = *argvp; + argc = *argcp; + + /* Return if no arguments or first argument has leading dash. */ + ap = argv[1]; + if (argc == 1 || *ap == '-') + return; + + /* Allocate space for new arguments. */ + if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL || + (p = flagsp = malloc(strlen(ap) + 2)) == NULL) + err(1, NULL); + + *nargv++ = *argv; + argv += 2; + + for (flags = 0; *ap; ++ap) { + switch(*ap) { + case 'b': + case 'f': + case 's': + if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL) + err(1, NULL); + nargv[0][0] = '-'; + nargv[0][1] = *ap; + (void)strcpy(&nargv[0][2], *argv); + if (*argv != NULL) + ++argv; + ++nargv; + break; + default: + if (!flags) { + *p++ = '-'; + flags = 1; + } + *p++ = *ap; + break; + } + } + + /* Terminate flags. */ + if (flags) { + *p = '\0'; + *nargv++ = flagsp; + } + + /* Copy remaining arguments. */ + while (*nargv++ = *argv++); +} diff --git a/sbin/restore/pathnames.h b/sbin/restore/pathnames.h new file mode 100644 index 0000000..b9541f9 --- /dev/null +++ b/sbin/restore/pathnames.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.2 (Berkeley) 1/21/94 + */ + +#include <paths.h> + +#define _PATH_DEFTAPE "/dev/rmt8" diff --git a/sbin/restore/restore.8 b/sbin/restore/restore.8 new file mode 100644 index 0000000..dfe7d68 --- /dev/null +++ b/sbin/restore/restore.8 @@ -0,0 +1,405 @@ +.\" Copyright (c) 1985, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)restore.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt RESTORE 8 +.Os BSD 4 +.Sh NAME +.Nm restore +.Nd "restore files or file systems from backups made with dump" +.Sh SYNOPSIS +.Nm restore +.Ar key +.Op Ar name Ar ... +.Sh DESCRIPTION +The +.Nm restore +command performs the inverse function of +.Xr dump 8 . +A full backup of a file system may be restored and +subsequent incremental backups layered on top of it. +Single files and +directory subtrees may be restored from full or partial +backups. +.Nm Restore +works across a network; +to do this see the +.Fl f +flag described below. +The actions +of +.Nm restore +are controlled by the given +.Cm key , +which +is a string of characters containing +at most one function letter and possibly +one or more function modifiers. +Other arguments to the command are file or directory +names specifying the files that are to be restored. +Unless the +.Cm h +key is specified (see below), +the appearance of a directory name refers to +the files and (recursively) subdirectories of that directory. +.Pp +The function portion of +the key is specified by one of the following letters: +.Bl -tag -width Ds +.It Cm r +Restore (rebuild a file system). +The target file system should be made pristine with +.Xr newfs 8 , +mounted and the +user +.Xr cd Ns 'd +into the pristine file system +before starting the restoration of the initial level 0 backup. If the +level 0 restores successfully, the +.Cm r +key may be used to restore +any necessary incremental backups on top of the level 0. +The +.Cm r +key precludes an interactive file extraction and can be +detrimental to one's health if not used carefully (not to mention +the disk). An example: +.Bd -literal -offset indent +newfs /dev/rrp0g eagle +mount /dev/rp0g /mnt +cd /mnt + +restore rf /dev/rst8 +.Ed +.Pp +Note that +.Nm restore +leaves a file +.Pa restoresymtable +in the root directory to pass information between incremental +restore passes. +This file should be removed when the last incremental has been +restored. +.Pp +.Nm Restore , +in conjunction with +.Xr newfs 8 +and +.Xr dump 8 , +may be used to modify file system parameters +such as size or block size. +.It Cm R +.Nm Restore +requests a particular tape of a multi volume set on which to restart +a full restore +(see the +.Cm r +key above). +This is useful if the restore has been interrupted. +.It Cm x +The named files are read from the given media. +If a named file matches a directory whose contents +are on the backup +and the +.Cm h +key is not specified, +the directory is recursively extracted. +The owner, modification time, +and mode are restored (if possible). +If no file argument is given, +then the root directory is extracted, +which results in the entire content of the +backup being extracted, +unless the +.Cm h +key has been specified. +.It Cm t +The names of the specified files are listed if they occur +on the backup. +If no file argument is given, +then the root directory is listed, +which results in the entire content of the +backup being listed, +unless the +.Cm h +key has been specified. +Note that the +.Cm t +key replaces the function of the old +.Xr dumpdir 8 +program. +.It Cm i +This mode allows interactive restoration of files from a dump. +After reading in the directory information from the dump, +.Nm restore +provides a shell like interface that allows the user to move +around the directory tree selecting files to be extracted. +The available commands are given below; +for those commands that require an argument, +the default is the current directory. +.Bl -tag -width Fl +.It Ic add Op Ar arg +The current directory or specified argument is added to the list of +files to be extracted. +If a directory is specified, then it and all its descendents are +added to the extraction list +(unless the +.Cm h +key is specified on the command line). +Files that are on the extraction list are prepended with a ``*'' +when they are listed by +.Ic ls . +.It Ic \&cd Ar arg +Change the current working directory to the specified argument. +.It Ic delete Op Ar arg +The current directory or specified argument is deleted from the list of +files to be extracted. +If a directory is specified, then it and all its descendents are +deleted from the extraction list +(unless the +.Cm h +key is specified on the command line). +The most expedient way to extract most of the files from a directory +is to add the directory to the extraction list and then delete +those files that are not needed. +.It Ic extract +All the files that are on the extraction list are extracted +from the dump. +.Nm Restore +will ask which volume the user wishes to mount. +The fastest way to extract a few files is to +start with the last volume, and work towards the first volume. +.It Ic help +List a summary of the available commands. +.It Ic \&ls Op Ar arg +List the current or specified directory. +Entries that are directories are appended with a ``/''. +Entries that have been marked for extraction are prepended with a ``*''. +If the verbose key is set the inode number of each entry is also listed. +.It Ic pwd +Print the full pathname of the current working directory. +.It Ic quit +Restore immediately exits, +even if the extraction list is not empty. +.It Ic setmodes +All the directories that have been added to the extraction list +have their owner, modes, and times set; +nothing is extracted from the dump. +This is useful for cleaning up after a restore has been prematurely aborted. +.It Ic verbose +The sense of the +.Cm v +key is toggled. +When set, the verbose key causes the +.Ic ls +command to list the inode numbers of all entries. +It also causes +.Nm restore +to print out information about each file as it is extracted. +.El +.El +.Pp +The following characters may be used in addition to the letter +that selects the function desired. +.Bl -tag -width Ds +.It Cm b +The next argument to +.Nm restore +is used as the block size of the media (in kilobytes). +If the +.Fl b +option is not specified, +.Nm restore +tries to determine the media block size dynamically. +.It Cm f +The next argument to +.Nm restore +is used as the name of the archive instead +of +.Pa /dev/rmt? . +If the name of the file is of the form +.Dq host:file , +.Nm restore +reads from the named file on the remote host using +.Xr rmt 8 . +If the name of the file is +.Ql Fl , +.Nm restore +reads from standard input. +Thus, +.Xr dump 8 +and +.Nm restore +can be used in a pipeline to dump and restore a file system +with the command +.Bd -literal -offset indent +dump 0f - /usr | (cd /mnt; restore xf -) +.Ed +.Pp +.It Cm h +.Nm Restore +extracts the actual directory, +rather than the files that it references. +This prevents hierarchical restoration of complete subtrees +from the dump. +.It Cm m +.Nm Restore +will extract by inode numbers rather than by file name. +This is useful if only a few files are being extracted, +and one wants to avoid regenerating the complete pathname +to the file. +.It Cm s +The next argument to +.Nm restore +is a number which +selects the file on a multi-file dump tape. File numbering +starts at 1. +.It Cm v +Normally +.Nm restore +does its work silently. +The +.Cm v +(verbose) +key causes it to type the name of each file it treats +preceded by its file type. +.It Cm y +.Nm Restore +will not ask whether it should abort the restore if it gets an error. +It will always try to skip over the bad block(s) and continue as +best it can. +.El +.Sh DIAGNOSTICS +Complaints about bad key characters. +.Pp +Complaints if it gets a read error. +If +.Cm y +has been specified, or the user responds +.Ql y , +.Nm restore +will attempt to continue the restore. +.Pp +If a backup was made using more than one tape volume, +.Nm restore +will notify the user when it is time to mount the next volume. +If the +.Cm x +or +.Cm i +key has been specified, +.Nm restore +will also ask which volume the user wishes to mount. +The fastest way to extract a few files is to +start with the last volume, and work towards the first volume. +.Pp +There are numerous consistency checks that can be listed by +.Nm restore . +Most checks are self-explanatory or can ``never happen''. +Common errors are given below. +.Pp +.Bl -tag -width Ds -compact +.It Converting to new file system format. +A dump tape created from the old file system has been loaded. +It is automatically converted to the new file system format. +.Pp +.It <filename>: not found on tape +The specified file name was listed in the tape directory, +but was not found on the tape. +This is caused by tape read errors while looking for the file, +and from using a dump tape created on an active file system. +.Pp +.It expected next file <inumber>, got <inumber> +A file that was not listed in the directory showed up. +This can occur when using a dump created on an active file system. +.Pp +.It Incremental dump too low +When doing incremental restore, +a dump that was written before the previous incremental dump, +or that has too low an incremental level has been loaded. +.Pp +.It Incremental dump too high +When doing incremental restore, +a dump that does not begin its coverage where the previous incremental +dump left off, +or that has too high an incremental level has been loaded. +.Pp +.It Tape read error while restoring <filename> +.It Tape read error while skipping over inode <inumber> +.It Tape read error while trying to resynchronize +A tape (or other media) read error has occurred. +If a file name is specified, +then its contents are probably partially wrong. +If an inode is being skipped or the tape is trying to resynchronize, +then no extracted files have been corrupted, +though files may not be found on the tape. +.Pp +.It resync restore, skipped <num> blocks +After a dump read error, +.Nm restore +may have to resynchronize itself. +This message lists the number of blocks that were skipped over. +.El +.Sh FILES +.Bl -tag -width "./restoresymtable" -compact +.It Pa /dev/rmt? +the default tape drive +.It Pa /tmp/rstdir* +file containing directories on the tape. +.It Pa /tmp/rstmode* +owner, mode, and time stamps for directories. +.It Pa \&./restoresymtable +information passed between incremental restores. +.El +.Sh SEE ALSO +.Xr dump 8 , +.Xr newfs 8 , +.Xr mount 8 , +.Xr mkfs 8 , +.Xr rmt 8 +.Sh BUGS +.Nm Restore +can get confused when doing incremental restores from +dump that were made on active file systems. +.Pp +A level zero dump must be done after a full restore. +Because restore runs in user code, +it has no control over inode allocation; +thus a full restore must be done to get a new set of directories +reflecting the new inode numbering, +even though the contents of the files is unchanged. +.Sh HISTORY +The +.Nm restore +command appeared in +.Bx 4.2 . diff --git a/sbin/restore/restore.c b/sbin/restore/restore.c new file mode 100644 index 0000000..dea964c --- /dev/null +++ b/sbin/restore/restore.c @@ -0,0 +1,819 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)restore.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ufs/ufs/dinode.h> + +#include <stdio.h> +#include <string.h> + +#include "restore.h" +#include "extern.h" + +static char *keyval __P((int)); + +/* + * This implements the 't' option. + * List entries on the tape. + */ +long +listfile(name, ino, type) + char *name; + ino_t ino; + int type; +{ + long descend = hflag ? GOOD : FAIL; + + if (TSTINO(ino, dumpmap) == 0) + return (descend); + vprintf(stdout, "%s", type == LEAF ? "leaf" : "dir "); + fprintf(stdout, "%10d\t%s\n", ino, name); + return (descend); +} + +/* + * This implements the 'x' option. + * Request that new entries be extracted. + */ +long +addfile(name, ino, type) + char *name; + ino_t ino; + int type; +{ + register struct entry *ep; + long descend = hflag ? GOOD : FAIL; + char buf[100]; + + if (TSTINO(ino, dumpmap) == 0) { + dprintf(stdout, "%s: not on the tape\n", name); + return (descend); + } + if (!mflag) { + (void) sprintf(buf, "./%u", ino); + name = buf; + if (type == NODE) { + (void) genliteraldir(name, ino); + return (descend); + } + } + ep = lookupino(ino); + if (ep != NULL) { + if (strcmp(name, myname(ep)) == 0) { + ep->e_flags |= NEW; + return (descend); + } + type |= LINK; + } + ep = addentry(name, ino, type); + if (type == NODE) + newnode(ep); + ep->e_flags |= NEW; + return (descend); +} + +/* + * This is used by the 'i' option to undo previous requests made by addfile. + * Delete entries from the request queue. + */ +/* ARGSUSED */ +long +deletefile(name, ino, type) + char *name; + ino_t ino; + int type; +{ + long descend = hflag ? GOOD : FAIL; + struct entry *ep; + + if (TSTINO(ino, dumpmap) == 0) + return (descend); + ep = lookupino(ino); + if (ep != NULL) + ep->e_flags &= ~NEW; + return (descend); +} + +/* + * The following four routines implement the incremental + * restore algorithm. The first removes old entries, the second + * does renames and calculates the extraction list, the third + * cleans up link names missed by the first two, and the final + * one deletes old directories. + * + * Directories cannot be immediately deleted, as they may have + * other files in them which need to be moved out first. As + * directories to be deleted are found, they are put on the + * following deletion list. After all deletions and renames + * are done, this list is actually deleted. + */ +static struct entry *removelist; + +/* + * Remove unneeded leaves from the old tree. + * Remove directories from the lookup chains. + */ +void +removeoldleaves() +{ + register struct entry *ep; + register ino_t i; + + vprintf(stdout, "Mark entries to be removed.\n"); + for (i = ROOTINO + 1; i < maxino; i++) { + ep = lookupino(i); + if (ep == NULL) + continue; + if (TSTINO(i, clrimap)) + continue; + for ( ; ep != NULL; ep = ep->e_links) { + dprintf(stdout, "%s: REMOVE\n", myname(ep)); + if (ep->e_type == LEAF) { + removeleaf(ep); + freeentry(ep); + } else { + mktempname(ep); + deleteino(ep->e_ino); + ep->e_next = removelist; + removelist = ep; + } + } + } +} + +/* + * For each directory entry on the incremental tape, determine which + * category it falls into as follows: + * KEEP - entries that are to be left alone. + * NEW - new entries to be added. + * EXTRACT - files that must be updated with new contents. + * LINK - new links to be added. + * Renames are done at the same time. + */ +long +nodeupdates(name, ino, type) + char *name; + ino_t ino; + int type; +{ + register struct entry *ep, *np, *ip; + long descend = GOOD; + int lookuptype = 0; + int key = 0; + /* key values */ +# define ONTAPE 0x1 /* inode is on the tape */ +# define INOFND 0x2 /* inode already exists */ +# define NAMEFND 0x4 /* name already exists */ +# define MODECHG 0x8 /* mode of inode changed */ + + /* + * This routine is called once for each element in the + * directory hierarchy, with a full path name. + * The "type" value is incorrectly specified as LEAF for + * directories that are not on the dump tape. + * + * Check to see if the file is on the tape. + */ + if (TSTINO(ino, dumpmap)) + key |= ONTAPE; + /* + * Check to see if the name exists, and if the name is a link. + */ + np = lookupname(name); + if (np != NULL) { + key |= NAMEFND; + ip = lookupino(np->e_ino); + if (ip == NULL) + panic("corrupted symbol table\n"); + if (ip != np) + lookuptype = LINK; + } + /* + * Check to see if the inode exists, and if one of its links + * corresponds to the name (if one was found). + */ + ip = lookupino(ino); + if (ip != NULL) { + key |= INOFND; + for (ep = ip->e_links; ep != NULL; ep = ep->e_links) { + if (ep == np) { + ip = ep; + break; + } + } + } + /* + * If both a name and an inode are found, but they do not + * correspond to the same file, then both the inode that has + * been found and the inode corresponding to the name that + * has been found need to be renamed. The current pathname + * is the new name for the inode that has been found. Since + * all files to be deleted have already been removed, the + * named file is either a now unneeded link, or it must live + * under a new name in this dump level. If it is a link, it + * can be removed. If it is not a link, it is given a + * temporary name in anticipation that it will be renamed + * when it is later found by inode number. + */ + if (((key & (INOFND|NAMEFND)) == (INOFND|NAMEFND)) && ip != np) { + if (lookuptype == LINK) { + removeleaf(np); + freeentry(np); + } else { + dprintf(stdout, "name/inode conflict, mktempname %s\n", + myname(np)); + mktempname(np); + } + np = NULL; + key &= ~NAMEFND; + } + if ((key & ONTAPE) && + (((key & INOFND) && ip->e_type != type) || + ((key & NAMEFND) && np->e_type != type))) + key |= MODECHG; + + /* + * Decide on the disposition of the file based on its flags. + * Note that we have already handled the case in which + * a name and inode are found that correspond to different files. + * Thus if both NAMEFND and INOFND are set then ip == np. + */ + switch (key) { + + /* + * A previously existing file has been found. + * Mark it as KEEP so that other links to the inode can be + * detected, and so that it will not be reclaimed by the search + * for unreferenced names. + */ + case INOFND|NAMEFND: + ip->e_flags |= KEEP; + dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, + flagvalues(ip)); + break; + + /* + * A file on the tape has a name which is the same as a name + * corresponding to a different file in the previous dump. + * Since all files to be deleted have already been removed, + * this file is either a now unneeded link, or it must live + * under a new name in this dump level. If it is a link, it + * can simply be removed. If it is not a link, it is given a + * temporary name in anticipation that it will be renamed + * when it is later found by inode number (see INOFND case + * below). The entry is then treated as a new file. + */ + case ONTAPE|NAMEFND: + case ONTAPE|NAMEFND|MODECHG: + if (lookuptype == LINK) { + removeleaf(np); + freeentry(np); + } else { + mktempname(np); + } + /* fall through */ + + /* + * A previously non-existent file. + * Add it to the file system, and request its extraction. + * If it is a directory, create it immediately. + * (Since the name is unused there can be no conflict) + */ + case ONTAPE: + ep = addentry(name, ino, type); + if (type == NODE) + newnode(ep); + ep->e_flags |= NEW|KEEP; + dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, + flagvalues(ep)); + break; + + /* + * A file with the same inode number, but a different + * name has been found. If the other name has not already + * been found (indicated by the KEEP flag, see above) then + * this must be a new name for the file, and it is renamed. + * If the other name has been found then this must be a + * link to the file. Hard links to directories are not + * permitted, and are either deleted or converted to + * symbolic links. Finally, if the file is on the tape, + * a request is made to extract it. + */ + case ONTAPE|INOFND: + if (type == LEAF && (ip->e_flags & KEEP) == 0) + ip->e_flags |= EXTRACT; + /* fall through */ + case INOFND: + if ((ip->e_flags & KEEP) == 0) { + renameit(myname(ip), name); + moveentry(ip, name); + ip->e_flags |= KEEP; + dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, + flagvalues(ip)); + break; + } + if (ip->e_type == NODE) { + descend = FAIL; + fprintf(stderr, + "deleted hard link %s to directory %s\n", + name, myname(ip)); + break; + } + ep = addentry(name, ino, type|LINK); + ep->e_flags |= NEW; + dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name, + flagvalues(ep)); + break; + + /* + * A previously known file which is to be updated. If it is a link, + * then all names referring to the previous file must be removed + * so that the subset of them that remain can be recreated. + */ + case ONTAPE|INOFND|NAMEFND: + if (lookuptype == LINK) { + removeleaf(np); + freeentry(np); + ep = addentry(name, ino, type|LINK); + if (type == NODE) + newnode(ep); + ep->e_flags |= NEW|KEEP; + dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name, + flagvalues(ep)); + break; + } + if (type == LEAF && lookuptype != LINK) + np->e_flags |= EXTRACT; + np->e_flags |= KEEP; + dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, + flagvalues(np)); + break; + + /* + * An inode is being reused in a completely different way. + * Normally an extract can simply do an "unlink" followed + * by a "creat". Here we must do effectively the same + * thing. The complications arise because we cannot really + * delete a directory since it may still contain files + * that we need to rename, so we delete it from the symbol + * table, and put it on the list to be deleted eventually. + * Conversely if a directory is to be created, it must be + * done immediately, rather than waiting until the + * extraction phase. + */ + case ONTAPE|INOFND|MODECHG: + case ONTAPE|INOFND|NAMEFND|MODECHG: + if (ip->e_flags & KEEP) { + badentry(ip, "cannot KEEP and change modes"); + break; + } + if (ip->e_type == LEAF) { + /* changing from leaf to node */ + removeleaf(ip); + freeentry(ip); + ip = addentry(name, ino, type); + newnode(ip); + } else { + /* changing from node to leaf */ + if ((ip->e_flags & TMPNAME) == 0) + mktempname(ip); + deleteino(ip->e_ino); + ip->e_next = removelist; + removelist = ip; + ip = addentry(name, ino, type); + } + ip->e_flags |= NEW|KEEP; + dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, + flagvalues(ip)); + break; + + /* + * A hard link to a diirectory that has been removed. + * Ignore it. + */ + case NAMEFND: + dprintf(stdout, "[%s] %s: Extraneous name\n", keyval(key), + name); + descend = FAIL; + break; + + /* + * If we find a directory entry for a file that is not on + * the tape, then we must have found a file that was created + * while the dump was in progress. Since we have no contents + * for it, we discard the name knowing that it will be on the + * next incremental tape. + */ + case NULL: + fprintf(stderr, "%s: (inode %d) not found on tape\n", + name, ino); + break; + + /* + * If any of these arise, something is grievously wrong with + * the current state of the symbol table. + */ + case INOFND|NAMEFND|MODECHG: + case NAMEFND|MODECHG: + case INOFND|MODECHG: + fprintf(stderr, "[%s] %s: inconsistent state\n", keyval(key), + name); + break; + + /* + * These states "cannot" arise for any state of the symbol table. + */ + case ONTAPE|MODECHG: + case MODECHG: + default: + panic("[%s] %s: impossible state\n", keyval(key), name); + break; + } + return (descend); +} + +/* + * Calculate the active flags in a key. + */ +static char * +keyval(key) + int key; +{ + static char keybuf[32]; + + (void) strcpy(keybuf, "|NIL"); + keybuf[0] = '\0'; + if (key & ONTAPE) + (void) strcat(keybuf, "|ONTAPE"); + if (key & INOFND) + (void) strcat(keybuf, "|INOFND"); + if (key & NAMEFND) + (void) strcat(keybuf, "|NAMEFND"); + if (key & MODECHG) + (void) strcat(keybuf, "|MODECHG"); + return (&keybuf[1]); +} + +/* + * Find unreferenced link names. + */ +void +findunreflinks() +{ + register struct entry *ep, *np; + register ino_t i; + + vprintf(stdout, "Find unreferenced names.\n"); + for (i = ROOTINO; i < maxino; i++) { + ep = lookupino(i); + if (ep == NULL || ep->e_type == LEAF || TSTINO(i, dumpmap) == 0) + continue; + for (np = ep->e_entries; np != NULL; np = np->e_sibling) { + if (np->e_flags == 0) { + dprintf(stdout, + "%s: remove unreferenced name\n", + myname(np)); + removeleaf(np); + freeentry(np); + } + } + } + /* + * Any leaves remaining in removed directories is unreferenced. + */ + for (ep = removelist; ep != NULL; ep = ep->e_next) { + for (np = ep->e_entries; np != NULL; np = np->e_sibling) { + if (np->e_type == LEAF) { + if (np->e_flags != 0) + badentry(np, "unreferenced with flags"); + dprintf(stdout, + "%s: remove unreferenced name\n", + myname(np)); + removeleaf(np); + freeentry(np); + } + } + } +} + +/* + * Remove old nodes (directories). + * Note that this routine runs in O(N*D) where: + * N is the number of directory entries to be removed. + * D is the maximum depth of the tree. + * If N == D this can be quite slow. If the list were + * topologically sorted, the deletion could be done in + * time O(N). + */ +void +removeoldnodes() +{ + register struct entry *ep, **prev; + long change; + + vprintf(stdout, "Remove old nodes (directories).\n"); + do { + change = 0; + prev = &removelist; + for (ep = removelist; ep != NULL; ep = *prev) { + if (ep->e_entries != NULL) { + prev = &ep->e_next; + continue; + } + *prev = ep->e_next; + removenode(ep); + freeentry(ep); + change++; + } + } while (change); + for (ep = removelist; ep != NULL; ep = ep->e_next) + badentry(ep, "cannot remove, non-empty"); +} + +/* + * This is the routine used to extract files for the 'r' command. + * Extract new leaves. + */ +void +createleaves(symtabfile) + char *symtabfile; +{ + register struct entry *ep; + ino_t first; + long curvol; + + if (command == 'R') { + vprintf(stdout, "Continue extraction of new leaves\n"); + } else { + vprintf(stdout, "Extract new leaves.\n"); + dumpsymtable(symtabfile, volno); + } + first = lowerbnd(ROOTINO); + curvol = volno; + while (curfile.ino < maxino) { + first = lowerbnd(first); + /* + * If the next available file is not the one which we + * expect then we have missed one or more files. Since + * we do not request files that were not on the tape, + * the lost files must have been due to a tape read error, + * or a file that was removed while the dump was in progress. + */ + while (first < curfile.ino) { + ep = lookupino(first); + if (ep == NULL) + panic("%d: bad first\n", first); + fprintf(stderr, "%s: not found on tape\n", myname(ep)); + ep->e_flags &= ~(NEW|EXTRACT); + first = lowerbnd(first); + } + /* + * If we find files on the tape that have no corresponding + * directory entries, then we must have found a file that + * was created while the dump was in progress. Since we have + * no name for it, we discard it knowing that it will be + * on the next incremental tape. + */ + if (first != curfile.ino) { + fprintf(stderr, "expected next file %d, got %d\n", + first, curfile.ino); + skipfile(); + goto next; + } + ep = lookupino(curfile.ino); + if (ep == NULL) + panic("unknown file on tape\n"); + if ((ep->e_flags & (NEW|EXTRACT)) == 0) + badentry(ep, "unexpected file on tape"); + /* + * If the file is to be extracted, then the old file must + * be removed since its type may change from one leaf type + * to another (eg "file" to "character special"). + */ + if ((ep->e_flags & EXTRACT) != 0) { + removeleaf(ep); + ep->e_flags &= ~REMOVED; + } + (void) extractfile(myname(ep)); + ep->e_flags &= ~(NEW|EXTRACT); + /* + * We checkpoint the restore after every tape reel, so + * as to simplify the amount of work re quired by the + * 'R' command. + */ + next: + if (curvol != volno) { + dumpsymtable(symtabfile, volno); + skipmaps(); + curvol = volno; + } + } +} + +/* + * This is the routine used to extract files for the 'x' and 'i' commands. + * Efficiently extract a subset of the files on a tape. + */ +void +createfiles() +{ + register ino_t first, next, last; + register struct entry *ep; + long curvol; + + vprintf(stdout, "Extract requested files\n"); + curfile.action = SKIP; + getvol((long)1); + skipmaps(); + skipdirs(); + first = lowerbnd(ROOTINO); + last = upperbnd(maxino - 1); + for (;;) { + first = lowerbnd(first); + last = upperbnd(last); + /* + * Check to see if any files remain to be extracted + */ + if (first > last) + return; + /* + * Reject any volumes with inodes greater + * than the last one needed + */ + while (curfile.ino > last) { + curfile.action = SKIP; + getvol((long)0); + skipmaps(); + skipdirs(); + } + /* + * Decide on the next inode needed. + * Skip across the inodes until it is found + * or an out of order volume change is encountered + */ + next = lowerbnd(curfile.ino); + do { + curvol = volno; + while (next > curfile.ino && volno == curvol) + skipfile(); + skipmaps(); + skipdirs(); + } while (volno == curvol + 1); + /* + * If volume change out of order occurred the + * current state must be recalculated + */ + if (volno != curvol) + continue; + /* + * If the current inode is greater than the one we were + * looking for then we missed the one we were looking for. + * Since we only attempt to extract files listed in the + * dump map, the lost files must have been due to a tape + * read error, or a file that was removed while the dump + * was in progress. Thus we report all requested files + * between the one we were looking for, and the one we + * found as missing, and delete their request flags. + */ + while (next < curfile.ino) { + ep = lookupino(next); + if (ep == NULL) + panic("corrupted symbol table\n"); + fprintf(stderr, "%s: not found on tape\n", myname(ep)); + ep->e_flags &= ~NEW; + next = lowerbnd(next); + } + /* + * The current inode is the one that we are looking for, + * so extract it per its requested name. + */ + if (next == curfile.ino && next <= last) { + ep = lookupino(next); + if (ep == NULL) + panic("corrupted symbol table\n"); + (void) extractfile(myname(ep)); + ep->e_flags &= ~NEW; + if (volno != curvol) + skipmaps(); + } + } +} + +/* + * Add links. + */ +void +createlinks() +{ + register struct entry *np, *ep; + register ino_t i; + char name[BUFSIZ]; + + vprintf(stdout, "Add links\n"); + for (i = ROOTINO; i < maxino; i++) { + ep = lookupino(i); + if (ep == NULL) + continue; + for (np = ep->e_links; np != NULL; np = np->e_links) { + if ((np->e_flags & NEW) == 0) + continue; + (void) strcpy(name, myname(ep)); + if (ep->e_type == NODE) { + (void) linkit(name, myname(np), SYMLINK); + } else { + (void) linkit(name, myname(np), HARDLINK); + } + np->e_flags &= ~NEW; + } + } +} + +/* + * Check the symbol table. + * We do this to insure that all the requested work was done, and + * that no temporary names remain. + */ +void +checkrestore() +{ + register struct entry *ep; + register ino_t i; + + vprintf(stdout, "Check the symbol table.\n"); + for (i = ROOTINO; i < maxino; i++) { + for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { + ep->e_flags &= ~KEEP; + if (ep->e_type == NODE) + ep->e_flags &= ~(NEW|EXISTED); + if (ep->e_flags != NULL) + badentry(ep, "incomplete operations"); + } + } +} + +/* + * Compare with the directory structure on the tape + * A paranoid check that things are as they should be. + */ +long +verifyfile(name, ino, type) + char *name; + ino_t ino; + int type; +{ + struct entry *np, *ep; + long descend = GOOD; + + ep = lookupname(name); + if (ep == NULL) { + fprintf(stderr, "Warning: missing name %s\n", name); + return (FAIL); + } + np = lookupino(ino); + if (np != ep) + descend = FAIL; + for ( ; np != NULL; np = np->e_links) + if (np == ep) + break; + if (np == NULL) + panic("missing inumber %d\n", ino); + if (ep->e_type == LEAF && type != LEAF) + badentry(ep, "type should be LEAF"); + return (descend); +} diff --git a/sbin/restore/restore.h b/sbin/restore/restore.h new file mode 100644 index 0000000..2202ccd --- /dev/null +++ b/sbin/restore/restore.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)restore.h 8.2 (Berkeley) 1/21/94 + */ + +/* + * Flags + */ +extern int cvtflag; /* convert from old to new tape format */ +extern int bflag; /* set input block size */ +extern int dflag; /* print out debugging info */ +extern int hflag; /* restore heirarchies */ +extern int mflag; /* restore by name instead of inode number */ +extern int Nflag; /* do not write the disk */ +extern int vflag; /* print out actions taken */ +extern int yflag; /* always try to recover from tape errors */ +/* + * Global variables + */ +extern char *dumpmap; /* map of inodes on this dump tape */ +extern char *clrimap; /* map of inodes to be deleted */ +extern ino_t maxino; /* highest numbered inode in this file system */ +extern long dumpnum; /* location of the dump on this tape */ +extern long volno; /* current volume being read */ +extern long ntrec; /* number of TP_BSIZE records per tape block */ +extern time_t dumptime; /* time that this dump begins */ +extern time_t dumpdate; /* time that this dump was made */ +extern char command; /* opration being performed */ +extern FILE *terminal; /* file descriptor for the terminal input */ +extern int oldinofmt; /* reading tape with old format inodes */ +extern int Bcvt; /* need byte swapping on inodes and dirs */ + +/* + * Each file in the file system is described by one of these entries + */ +struct entry { + char *e_name; /* the current name of this entry */ + u_char e_namlen; /* length of this name */ + char e_type; /* type of this entry, see below */ + short e_flags; /* status flags, see below */ + ino_t e_ino; /* inode number in previous file sys */ + long e_index; /* unique index (for dumpped table) */ + struct entry *e_parent; /* pointer to parent directory (..) */ + struct entry *e_sibling; /* next element in this directory (.) */ + struct entry *e_links; /* hard links to this inode */ + struct entry *e_entries; /* for directories, their entries */ + struct entry *e_next; /* hash chain list */ +}; +/* types */ +#define LEAF 1 /* non-directory entry */ +#define NODE 2 /* directory entry */ +#define LINK 4 /* synthesized type, stripped by addentry */ +/* flags */ +#define EXTRACT 0x0001 /* entry is to be replaced from the tape */ +#define NEW 0x0002 /* a new entry to be extracted */ +#define KEEP 0x0004 /* entry is not to change */ +#define REMOVED 0x0010 /* entry has been removed */ +#define TMPNAME 0x0020 /* entry has been given a temporary name */ +#define EXISTED 0x0040 /* directory already existed during extract */ + +/* + * Constants associated with entry structs + */ +#define HARDLINK 1 +#define SYMLINK 2 +#define TMPHDR "RSTTMP" + +/* + * The entry describes the next file available on the tape + */ +struct context { + char *name; /* name of file */ + ino_t ino; /* inumber of file */ + struct dinode *dip; /* pointer to inode */ + char action; /* action being taken on this file */ +} curfile; +/* actions */ +#define USING 1 /* extracting from the tape */ +#define SKIP 2 /* skipping */ +#define UNKNOWN 3 /* disposition or starting point is unknown */ + +/* + * Definitions for library routines operating on directories. + */ +typedef struct rstdirdesc RST_DIR; + +/* + * Flags to setdirmodes. + */ +#define FORCE 0x0001 + +/* + * Useful macros + */ +#define TSTINO(ino, map) \ + (map[(u_int)((ino) - 1) / NBBY] & (1 << ((u_int)((ino) - 1) % NBBY))) + +#define dprintf if (dflag) fprintf +#define vprintf if (vflag) fprintf + +#define GOOD 1 +#define FAIL 0 diff --git a/sbin/restore/symtab.c b/sbin/restore/symtab.c new file mode 100644 index 0000000..3895ec0 --- /dev/null +++ b/sbin/restore/symtab.c @@ -0,0 +1,629 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)symtab.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +/* + * These routines maintain the symbol table which tracks the state + * of the file system being restored. They provide lookup by either + * name or inode number. They also provide for creation, deletion, + * and renaming of entries. Because of the dynamic nature of pathnames, + * names should not be saved, but always constructed just before they + * are needed, by calling "myname". + */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ufs/ufs/dinode.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "restore.h" +#include "extern.h" + +/* + * The following variables define the inode symbol table. + * The primary hash table is dynamically allocated based on + * the number of inodes in the file system (maxino), scaled by + * HASHFACTOR. The variable "entry" points to the hash table; + * the variable "entrytblsize" indicates its size (in entries). + */ +#define HASHFACTOR 5 +static struct entry **entry; +static long entrytblsize; + +static void addino __P((ino_t, struct entry *)); +static struct entry *lookupparent __P((char *)); +static void removeentry __P((struct entry *)); + +/* + * Look up an entry by inode number + */ +struct entry * +lookupino(inum) + ino_t inum; +{ + register struct entry *ep; + + if (inum < ROOTINO || inum >= maxino) + return (NULL); + for (ep = entry[inum % entrytblsize]; ep != NULL; ep = ep->e_next) + if (ep->e_ino == inum) + return (ep); + return (NULL); +} + +/* + * Add an entry into the entry table + */ +static void +addino(inum, np) + ino_t inum; + struct entry *np; +{ + struct entry **epp; + + if (inum < ROOTINO || inum >= maxino) + panic("addino: out of range %d\n", inum); + epp = &entry[inum % entrytblsize]; + np->e_ino = inum; + np->e_next = *epp; + *epp = np; + if (dflag) + for (np = np->e_next; np != NULL; np = np->e_next) + if (np->e_ino == inum) + badentry(np, "duplicate inum"); +} + +/* + * Delete an entry from the entry table + */ +void +deleteino(inum) + ino_t inum; +{ + register struct entry *next; + struct entry **prev; + + if (inum < ROOTINO || inum >= maxino) + panic("deleteino: out of range %d\n", inum); + prev = &entry[inum % entrytblsize]; + for (next = *prev; next != NULL; next = next->e_next) { + if (next->e_ino == inum) { + next->e_ino = 0; + *prev = next->e_next; + return; + } + prev = &next->e_next; + } + panic("deleteino: %d not found\n", inum); +} + +/* + * Look up an entry by name + */ +struct entry * +lookupname(name) + char *name; +{ + register struct entry *ep; + register char *np, *cp; + char buf[MAXPATHLEN]; + + cp = name; + for (ep = lookupino(ROOTINO); ep != NULL; ep = ep->e_entries) { + for (np = buf; *cp != '/' && *cp != '\0'; ) + *np++ = *cp++; + *np = '\0'; + for ( ; ep != NULL; ep = ep->e_sibling) + if (strcmp(ep->e_name, buf) == 0) + break; + if (ep == NULL) + break; + if (*cp++ == '\0') + return (ep); + } + return (NULL); +} + +/* + * Look up the parent of a pathname + */ +static struct entry * +lookupparent(name) + char *name; +{ + struct entry *ep; + char *tailindex; + + tailindex = rindex(name, '/'); + if (tailindex == NULL) + return (NULL); + *tailindex = '\0'; + ep = lookupname(name); + *tailindex = '/'; + if (ep == NULL) + return (NULL); + if (ep->e_type != NODE) + panic("%s is not a directory\n", name); + return (ep); +} + +/* + * Determine the current pathname of a node or leaf + */ +char * +myname(ep) + register struct entry *ep; +{ + register char *cp; + static char namebuf[MAXPATHLEN]; + + for (cp = &namebuf[MAXPATHLEN - 2]; cp > &namebuf[ep->e_namlen]; ) { + cp -= ep->e_namlen; + bcopy(ep->e_name, cp, (long)ep->e_namlen); + if (ep == lookupino(ROOTINO)) + return (cp); + *(--cp) = '/'; + ep = ep->e_parent; + } + panic("%s: pathname too long\n", cp); + return(cp); +} + +/* + * Unused symbol table entries are linked together on a freelist + * headed by the following pointer. + */ +static struct entry *freelist = NULL; + +/* + * add an entry to the symbol table + */ +struct entry * +addentry(name, inum, type) + char *name; + ino_t inum; + int type; +{ + register struct entry *np, *ep; + + if (freelist != NULL) { + np = freelist; + freelist = np->e_next; + bzero((char *)np, (long)sizeof(struct entry)); + } else { + np = (struct entry *)calloc(1, sizeof(struct entry)); + if (np == NULL) + panic("no memory to extend symbol table\n"); + } + np->e_type = type & ~LINK; + ep = lookupparent(name); + if (ep == NULL) { + if (inum != ROOTINO || lookupino(ROOTINO) != NULL) + panic("bad name to addentry %s\n", name); + np->e_name = savename(name); + np->e_namlen = strlen(name); + np->e_parent = np; + addino(ROOTINO, np); + return (np); + } + np->e_name = savename(rindex(name, '/') + 1); + np->e_namlen = strlen(np->e_name); + np->e_parent = ep; + np->e_sibling = ep->e_entries; + ep->e_entries = np; + if (type & LINK) { + ep = lookupino(inum); + if (ep == NULL) + panic("link to non-existant name\n"); + np->e_ino = inum; + np->e_links = ep->e_links; + ep->e_links = np; + } else if (inum != 0) { + if (lookupino(inum) != NULL) + panic("duplicate entry\n"); + addino(inum, np); + } + return (np); +} + +/* + * delete an entry from the symbol table + */ +void +freeentry(ep) + register struct entry *ep; +{ + register struct entry *np; + ino_t inum; + + if (ep->e_flags != REMOVED) + badentry(ep, "not marked REMOVED"); + if (ep->e_type == NODE) { + if (ep->e_links != NULL) + badentry(ep, "freeing referenced directory"); + if (ep->e_entries != NULL) + badentry(ep, "freeing non-empty directory"); + } + if (ep->e_ino != 0) { + np = lookupino(ep->e_ino); + if (np == NULL) + badentry(ep, "lookupino failed"); + if (np == ep) { + inum = ep->e_ino; + deleteino(inum); + if (ep->e_links != NULL) + addino(inum, ep->e_links); + } else { + for (; np != NULL; np = np->e_links) { + if (np->e_links == ep) { + np->e_links = ep->e_links; + break; + } + } + if (np == NULL) + badentry(ep, "link not found"); + } + } + removeentry(ep); + freename(ep->e_name); + ep->e_next = freelist; + freelist = ep; +} + +/* + * Relocate an entry in the tree structure + */ +void +moveentry(ep, newname) + register struct entry *ep; + char *newname; +{ + struct entry *np; + char *cp; + + np = lookupparent(newname); + if (np == NULL) + badentry(ep, "cannot move ROOT"); + if (np != ep->e_parent) { + removeentry(ep); + ep->e_parent = np; + ep->e_sibling = np->e_entries; + np->e_entries = ep; + } + cp = rindex(newname, '/') + 1; + freename(ep->e_name); + ep->e_name = savename(cp); + ep->e_namlen = strlen(cp); + if (strcmp(gentempname(ep), ep->e_name) == 0) + ep->e_flags |= TMPNAME; + else + ep->e_flags &= ~TMPNAME; +} + +/* + * Remove an entry in the tree structure + */ +static void +removeentry(ep) + register struct entry *ep; +{ + register struct entry *np; + + np = ep->e_parent; + if (np->e_entries == ep) { + np->e_entries = ep->e_sibling; + } else { + for (np = np->e_entries; np != NULL; np = np->e_sibling) { + if (np->e_sibling == ep) { + np->e_sibling = ep->e_sibling; + break; + } + } + if (np == NULL) + badentry(ep, "cannot find entry in parent list"); + } +} + +/* + * Table of unused string entries, sorted by length. + * + * Entries are allocated in STRTBLINCR sized pieces so that names + * of similar lengths can use the same entry. The value of STRTBLINCR + * is chosen so that every entry has at least enough space to hold + * a "struct strtbl" header. Thus every entry can be linked onto an + * apprpriate free list. + * + * NB. The macro "allocsize" below assumes that "struct strhdr" + * has a size that is a power of two. + */ +struct strhdr { + struct strhdr *next; +}; + +#define STRTBLINCR (sizeof(struct strhdr)) +#define allocsize(size) (((size) + 1 + STRTBLINCR - 1) & ~(STRTBLINCR - 1)) + +static struct strhdr strtblhdr[allocsize(NAME_MAX) / STRTBLINCR]; + +/* + * Allocate space for a name. It first looks to see if it already + * has an appropriate sized entry, and if not allocates a new one. + */ +char * +savename(name) + char *name; +{ + struct strhdr *np; + long len; + char *cp; + + if (name == NULL) + panic("bad name\n"); + len = strlen(name); + np = strtblhdr[len / STRTBLINCR].next; + if (np != NULL) { + strtblhdr[len / STRTBLINCR].next = np->next; + cp = (char *)np; + } else { + cp = malloc((unsigned)allocsize(len)); + if (cp == NULL) + panic("no space for string table\n"); + } + (void) strcpy(cp, name); + return (cp); +} + +/* + * Free space for a name. The resulting entry is linked onto the + * appropriate free list. + */ +void +freename(name) + char *name; +{ + struct strhdr *tp, *np; + + tp = &strtblhdr[strlen(name) / STRTBLINCR]; + np = (struct strhdr *)name; + np->next = tp->next; + tp->next = np; +} + +/* + * Useful quantities placed at the end of a dumped symbol table. + */ +struct symtableheader { + long volno; + long stringsize; + long entrytblsize; + time_t dumptime; + time_t dumpdate; + ino_t maxino; + long ntrec; +}; + +/* + * dump a snapshot of the symbol table + */ +void +dumpsymtable(filename, checkpt) + char *filename; + long checkpt; +{ + register struct entry *ep, *tep; + register ino_t i; + struct entry temp, *tentry; + long mynum = 1, stroff = 0; + FILE *fd; + struct symtableheader hdr; + + vprintf(stdout, "Check pointing the restore\n"); + if (Nflag) + return; + if ((fd = fopen(filename, "w")) == NULL) { + fprintf(stderr, "fopen: %s\n", strerror(errno)); + panic("cannot create save file %s for symbol table\n", + filename); + } + clearerr(fd); + /* + * Assign indicies to each entry + * Write out the string entries + */ + for (i = ROOTINO; i < maxino; i++) { + for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { + ep->e_index = mynum++; + (void) fwrite(ep->e_name, sizeof(char), + (int)allocsize(ep->e_namlen), fd); + } + } + /* + * Convert pointers to indexes, and output + */ + tep = &temp; + stroff = 0; + for (i = ROOTINO; i < maxino; i++) { + for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { + bcopy((char *)ep, (char *)tep, + (long)sizeof(struct entry)); + tep->e_name = (char *)stroff; + stroff += allocsize(ep->e_namlen); + tep->e_parent = (struct entry *)ep->e_parent->e_index; + if (ep->e_links != NULL) + tep->e_links = + (struct entry *)ep->e_links->e_index; + if (ep->e_sibling != NULL) + tep->e_sibling = + (struct entry *)ep->e_sibling->e_index; + if (ep->e_entries != NULL) + tep->e_entries = + (struct entry *)ep->e_entries->e_index; + if (ep->e_next != NULL) + tep->e_next = + (struct entry *)ep->e_next->e_index; + (void) fwrite((char *)tep, sizeof(struct entry), 1, fd); + } + } + /* + * Convert entry pointers to indexes, and output + */ + for (i = 0; i < entrytblsize; i++) { + if (entry[i] == NULL) + tentry = NULL; + else + tentry = (struct entry *)entry[i]->e_index; + (void) fwrite((char *)&tentry, sizeof(struct entry *), 1, fd); + } + hdr.volno = checkpt; + hdr.maxino = maxino; + hdr.entrytblsize = entrytblsize; + hdr.stringsize = stroff; + hdr.dumptime = dumptime; + hdr.dumpdate = dumpdate; + hdr.ntrec = ntrec; + (void) fwrite((char *)&hdr, sizeof(struct symtableheader), 1, fd); + if (ferror(fd)) { + fprintf(stderr, "fwrite: %s\n", strerror(errno)); + panic("output error to file %s writing symbol table\n", + filename); + } + (void) fclose(fd); +} + +/* + * Initialize a symbol table from a file + */ +void +initsymtable(filename) + char *filename; +{ + char *base; + long tblsize; + register struct entry *ep; + struct entry *baseep, *lep; + struct symtableheader hdr; + struct stat stbuf; + register long i; + int fd; + + vprintf(stdout, "Initialize symbol table.\n"); + if (filename == NULL) { + entrytblsize = maxino / HASHFACTOR; + entry = (struct entry **) + calloc((unsigned)entrytblsize, sizeof(struct entry *)); + if (entry == (struct entry **)NULL) + panic("no memory for entry table\n"); + ep = addentry(".", ROOTINO, NODE); + ep->e_flags |= NEW; + return; + } + if ((fd = open(filename, O_RDONLY, 0)) < 0) { + fprintf(stderr, "open: %s\n", strerror(errno)); + panic("cannot open symbol table file %s\n", filename); + } + if (fstat(fd, &stbuf) < 0) { + fprintf(stderr, "stat: %s\n", strerror(errno)); + panic("cannot stat symbol table file %s\n", filename); + } + tblsize = stbuf.st_size - sizeof(struct symtableheader); + base = calloc(sizeof(char), (unsigned)tblsize); + if (base == NULL) + panic("cannot allocate space for symbol table\n"); + if (read(fd, base, (int)tblsize) < 0 || + read(fd, (char *)&hdr, sizeof(struct symtableheader)) < 0) { + fprintf(stderr, "read: %s\n", strerror(errno)); + panic("cannot read symbol table file %s\n", filename); + } + switch (command) { + case 'r': + /* + * For normal continuation, insure that we are using + * the next incremental tape + */ + if (hdr.dumpdate != dumptime) { + if (hdr.dumpdate < dumptime) + fprintf(stderr, "Incremental tape too low\n"); + else + fprintf(stderr, "Incremental tape too high\n"); + done(1); + } + break; + case 'R': + /* + * For restart, insure that we are using the same tape + */ + curfile.action = SKIP; + dumptime = hdr.dumptime; + dumpdate = hdr.dumpdate; + if (!bflag) + newtapebuf(hdr.ntrec); + getvol(hdr.volno); + break; + default: + panic("initsymtable called from command %c\n", command); + break; + } + maxino = hdr.maxino; + entrytblsize = hdr.entrytblsize; + entry = (struct entry **) + (base + tblsize - (entrytblsize * sizeof(struct entry *))); + baseep = (struct entry *)(base + hdr.stringsize - sizeof(struct entry)); + lep = (struct entry *)entry; + for (i = 0; i < entrytblsize; i++) { + if (entry[i] == NULL) + continue; + entry[i] = &baseep[(long)entry[i]]; + } + for (ep = &baseep[1]; ep < lep; ep++) { + ep->e_name = base + (long)ep->e_name; + ep->e_parent = &baseep[(long)ep->e_parent]; + if (ep->e_sibling != NULL) + ep->e_sibling = &baseep[(long)ep->e_sibling]; + if (ep->e_links != NULL) + ep->e_links = &baseep[(long)ep->e_links]; + if (ep->e_entries != NULL) + ep->e_entries = &baseep[(long)ep->e_entries]; + if (ep->e_next != NULL) + ep->e_next = &baseep[(long)ep->e_next]; + } +} diff --git a/sbin/restore/tape.c b/sbin/restore/tape.c new file mode 100644 index 0000000..368c59c --- /dev/null +++ b/sbin/restore/tape.c @@ -0,0 +1,1348 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)tape.c 8.3 (Berkeley) 4/1/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/mtio.h> +#include <sys/stat.h> + +#include <ufs/ufs/dinode.h> +#include <protocols/dumprestore.h> + +#include <errno.h> +#include <setjmp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "restore.h" +#include "extern.h" +#include "pathnames.h" + +static long fssize = MAXBSIZE; +static int mt = -1; +static int pipein = 0; +static char magtape[BUFSIZ]; +static int blkcnt; +static int numtrec; +static char *tapebuf; +static union u_spcl endoftapemark; +static long blksread; /* blocks read since last header */ +static long tpblksread = 0; /* TP_BSIZE blocks read */ +static long tapesread; +static jmp_buf restart; +static int gettingfile = 0; /* restart has a valid frame */ +static char *host = NULL; + +static int ofile; +static char *map; +static char lnkbuf[MAXPATHLEN + 1]; +static int pathlen; + +int oldinofmt; /* old inode format conversion required */ +int Bcvt; /* Swap Bytes (for CCI or sun) */ +static int Qcvt; /* Swap quads (for sun) */ + +#define FLUSHTAPEBUF() blkcnt = ntrec + 1 + +static void accthdr __P((struct s_spcl *)); +static int checksum __P((int *)); +static void findinode __P((struct s_spcl *)); +static void findtapeblksize __P((void)); +static int gethead __P((struct s_spcl *)); +static void readtape __P((char *)); +static void setdumpnum __P((void)); +static u_long swabl __P((u_long)); +static u_char *swablong __P((u_char *, int)); +static u_char *swabshort __P((u_char *, int)); +static void terminateinput __P((void)); +static void xtrfile __P((char *, long)); +static void xtrlnkfile __P((char *, long)); +static void xtrlnkskip __P((char *, long)); +static void xtrmap __P((char *, long)); +static void xtrmapskip __P((char *, long)); +static void xtrskip __P((char *, long)); + +/* + * Set up an input source + */ +void +setinput(source) + char *source; +{ + FLUSHTAPEBUF(); + if (bflag) + newtapebuf(ntrec); + else + newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC); + terminal = stdin; + +#ifdef RRESTORE + if (index(source, ':')) { + host = source; + source = index(host, ':'); + *source++ = '\0'; + if (rmthost(host) == 0) + done(1); + } else +#endif + if (strcmp(source, "-") == 0) { + /* + * Since input is coming from a pipe we must establish + * our own connection to the terminal. + */ + terminal = fopen(_PATH_TTY, "r"); + if (terminal == NULL) { + (void)fprintf(stderr, "cannot open %s: %s\n", + _PATH_TTY, strerror(errno)); + terminal = fopen(_PATH_DEVNULL, "r"); + if (terminal == NULL) { + (void)fprintf(stderr, "cannot open %s: %s\n", + _PATH_DEVNULL, strerror(errno)); + done(1); + } + } + pipein++; + } + setuid(getuid()); /* no longer need or want root privileges */ + (void) strcpy(magtape, source); +} + +void +newtapebuf(size) + long size; +{ + static tapebufsize = -1; + + ntrec = size; + if (size <= tapebufsize) + return; + if (tapebuf != NULL) + free(tapebuf); + tapebuf = malloc(size * TP_BSIZE); + if (tapebuf == NULL) { + fprintf(stderr, "Cannot allocate space for tape buffer\n"); + done(1); + } + tapebufsize = size; +} + +/* + * Verify that the tape drive can be accessed and + * that it actually is a dump tape. + */ +void +setup() +{ + int i, j, *ip; + struct stat stbuf; + + vprintf(stdout, "Verify tape and initialize maps\n"); +#ifdef RRESTORE + if (host) + mt = rmtopen(magtape, 0); + else +#endif + if (pipein) + mt = 0; + else + mt = open(magtape, O_RDONLY, 0); + if (mt < 0) { + fprintf(stderr, "%s: %s\n", magtape, strerror(errno)); + done(1); + } + volno = 1; + setdumpnum(); + FLUSHTAPEBUF(); + if (!pipein && !bflag) + findtapeblksize(); + if (gethead(&spcl) == FAIL) { + blkcnt--; /* push back this block */ + blksread--; + tpblksread--; + cvtflag++; + if (gethead(&spcl) == FAIL) { + fprintf(stderr, "Tape is not a dump tape\n"); + done(1); + } + fprintf(stderr, "Converting to new file system format.\n"); + } + if (pipein) { + endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : NFS_MAGIC; + endoftapemark.s_spcl.c_type = TS_END; + ip = (int *)&endoftapemark; + j = sizeof(union u_spcl) / sizeof(int); + i = 0; + do + i += *ip++; + while (--j); + endoftapemark.s_spcl.c_checksum = CHECKSUM - i; + } + if (vflag || command == 't') + printdumpinfo(); + dumptime = spcl.c_ddate; + dumpdate = spcl.c_date; + if (stat(".", &stbuf) < 0) { + fprintf(stderr, "cannot stat .: %s\n", strerror(errno)); + done(1); + } + if (stbuf.st_blksize > 0 && stbuf.st_blksize <= MAXBSIZE) + fssize = stbuf.st_blksize; + if (((fssize - 1) & fssize) != 0) { + fprintf(stderr, "bad block size %d\n", fssize); + done(1); + } + if (spcl.c_volume != 1) { + fprintf(stderr, "Tape is not volume 1 of the dump\n"); + done(1); + } + if (gethead(&spcl) == FAIL) { + dprintf(stdout, "header read failed at %d blocks\n", blksread); + panic("no header after volume mark!\n"); + } + findinode(&spcl); + if (spcl.c_type != TS_CLRI) { + fprintf(stderr, "Cannot find file removal list\n"); + done(1); + } + maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1; + dprintf(stdout, "maxino = %d\n", maxino); + map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY)); + if (map == NULL) + panic("no memory for file removal list\n"); + clrimap = map; + curfile.action = USING; + getfile(xtrmap, xtrmapskip); + if (spcl.c_type != TS_BITS) { + fprintf(stderr, "Cannot find file dump list\n"); + done(1); + } + map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY)); + if (map == (char *)NULL) + panic("no memory for file dump list\n"); + dumpmap = map; + curfile.action = USING; + getfile(xtrmap, xtrmapskip); +} + +/* + * Prompt user to load a new dump volume. + * "Nextvol" is the next suggested volume to use. + * This suggested volume is enforced when doing full + * or incremental restores, but can be overrridden by + * the user when only extracting a subset of the files. + */ +void +getvol(nextvol) + long nextvol; +{ + long newvol, savecnt, wantnext, i; + union u_spcl tmpspcl; +# define tmpbuf tmpspcl.s_spcl + char buf[TP_BSIZE]; + + if (nextvol == 1) { + tapesread = 0; + gettingfile = 0; + } + if (pipein) { + if (nextvol != 1) + panic("Changing volumes on pipe input?\n"); + if (volno == 1) + return; + goto gethdr; + } + savecnt = blksread; +again: + if (pipein) + done(1); /* pipes do not get a second chance */ + if (command == 'R' || command == 'r' || curfile.action != SKIP) { + newvol = nextvol; + wantnext = 1; + } else { + newvol = 0; + wantnext = 0; + } + while (newvol <= 0) { + if (tapesread == 0) { + fprintf(stderr, "%s%s%s%s%s", + "You have not read any tapes yet.\n", + "Unless you know which volume your", + " file(s) are on you should start\n", + "with the last volume and work", + " towards towards the first.\n"); + } else { + fprintf(stderr, "You have read volumes"); + strcpy(buf, ": "); + for (i = 1; i < 32; i++) + if (tapesread & (1 << i)) { + fprintf(stderr, "%s%d", buf, i); + strcpy(buf, ", "); + } + fprintf(stderr, "\n"); + } + do { + fprintf(stderr, "Specify next volume #: "); + (void) fflush(stderr); + (void) fgets(buf, BUFSIZ, terminal); + } while (!feof(terminal) && buf[0] == '\n'); + if (feof(terminal)) + done(1); + newvol = atoi(buf); + if (newvol <= 0) { + fprintf(stderr, + "Volume numbers are positive numerics\n"); + } + } + if (newvol == volno) { + tapesread |= 1 << volno; + return; + } + closemt(); + fprintf(stderr, "Mount tape volume %d\n", newvol); + fprintf(stderr, "Enter ``none'' if there are no more tapes\n"); + fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape); + (void) fflush(stderr); + (void) fgets(buf, BUFSIZ, terminal); + if (feof(terminal)) + done(1); + if (!strcmp(buf, "none\n")) { + terminateinput(); + return; + } + if (buf[0] != '\n') { + (void) strcpy(magtape, buf); + magtape[strlen(magtape) - 1] = '\0'; + } +#ifdef RRESTORE + if (host) + mt = rmtopen(magtape, 0); + else +#endif + mt = open(magtape, O_RDONLY, 0); + + if (mt == -1) { + fprintf(stderr, "Cannot open %s\n", magtape); + volno = -1; + goto again; + } +gethdr: + volno = newvol; + setdumpnum(); + FLUSHTAPEBUF(); + if (gethead(&tmpbuf) == FAIL) { + dprintf(stdout, "header read failed at %d blocks\n", blksread); + fprintf(stderr, "tape is not dump tape\n"); + volno = 0; + goto again; + } + if (tmpbuf.c_volume != volno) { + fprintf(stderr, "Wrong volume (%d)\n", tmpbuf.c_volume); + volno = 0; + goto again; + } + if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) { + fprintf(stderr, "Wrong dump date\n\tgot: %s", + ctime(&tmpbuf.c_date)); + fprintf(stderr, "\twanted: %s", ctime(&dumpdate)); + volno = 0; + goto again; + } + tapesread |= 1 << volno; + blksread = savecnt; + /* + * If continuing from the previous volume, skip over any + * blocks read already at the end of the previous volume. + * + * If coming to this volume at random, skip to the beginning + * of the next record. + */ + dprintf(stdout, "read %ld recs, tape starts with %ld\n", + tpblksread, tmpbuf.c_firstrec); + if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) { + if (!wantnext) { + tpblksread = tmpbuf.c_firstrec; + for (i = tmpbuf.c_count; i > 0; i--) + readtape(buf); + } else if (tmpbuf.c_firstrec > 0 && + tmpbuf.c_firstrec < tpblksread - 1) { + /* + * -1 since we've read the volume header + */ + i = tpblksread - tmpbuf.c_firstrec - 1; + dprintf(stderr, "Skipping %d duplicate record%s.\n", + i, i > 1 ? "s" : ""); + while (--i >= 0) + readtape(buf); + } + } + if (curfile.action == USING) { + if (volno == 1) + panic("active file into volume 1\n"); + return; + } + /* + * Skip up to the beginning of the next record + */ + if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) + for (i = tmpbuf.c_count; i > 0; i--) + readtape(buf); + (void) gethead(&spcl); + findinode(&spcl); + if (gettingfile) { + gettingfile = 0; + longjmp(restart, 1); + } +} + +/* + * Handle unexpected EOF. + */ +static void +terminateinput() +{ + + if (gettingfile && curfile.action == USING) { + printf("Warning: %s %s\n", + "End-of-input encountered while extracting", curfile.name); + } + curfile.name = "<name unknown>"; + curfile.action = UNKNOWN; + curfile.dip = NULL; + curfile.ino = maxino; + if (gettingfile) { + gettingfile = 0; + longjmp(restart, 1); + } +} + +/* + * handle multiple dumps per tape by skipping forward to the + * appropriate one. + */ +static void +setdumpnum() +{ + struct mtop tcom; + + if (dumpnum == 1 || volno != 1) + return; + if (pipein) { + fprintf(stderr, "Cannot have multiple dumps on pipe input\n"); + done(1); + } + tcom.mt_op = MTFSF; + tcom.mt_count = dumpnum - 1; +#ifdef RRESTORE + if (host) + rmtioctl(MTFSF, dumpnum - 1); + else +#endif + if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0) + fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno)); +} + +void +printdumpinfo() +{ + fprintf(stdout, "Dump date: %s", ctime(&spcl.c_date)); + fprintf(stdout, "Dumped from: %s", + (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&spcl.c_ddate)); + if (spcl.c_host[0] == '\0') + return; + fprintf(stderr, "Level %d dump of %s on %s:%s\n", + spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev); + fprintf(stderr, "Label: %s\n", spcl.c_label); +} + +int +extractfile(name) + char *name; +{ + int mode; + struct timeval timep[2]; + struct entry *ep; + + curfile.name = name; + curfile.action = USING; + timep[0].tv_sec = curfile.dip->di_atime.ts_sec; + timep[0].tv_usec = curfile.dip->di_atime.ts_nsec / 1000; + timep[1].tv_sec = curfile.dip->di_mtime.ts_sec; + timep[1].tv_usec = curfile.dip->di_mtime.ts_nsec / 1000; + mode = curfile.dip->di_mode; + switch (mode & IFMT) { + + default: + fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode); + skipfile(); + return (FAIL); + + case IFSOCK: + vprintf(stdout, "skipped socket %s\n", name); + skipfile(); + return (GOOD); + + case IFDIR: + if (mflag) { + ep = lookupname(name); + if (ep == NULL || ep->e_flags & EXTRACT) + panic("unextracted directory %s\n", name); + skipfile(); + return (GOOD); + } + vprintf(stdout, "extract file %s\n", name); + return (genliteraldir(name, curfile.ino)); + + case IFLNK: + lnkbuf[0] = '\0'; + pathlen = 0; + getfile(xtrlnkfile, xtrlnkskip); + if (pathlen == 0) { + vprintf(stdout, + "%s: zero length symbolic link (ignored)\n", name); + return (GOOD); + } + return (linkit(lnkbuf, name, SYMLINK)); + + case IFCHR: + case IFBLK: + vprintf(stdout, "extract special file %s\n", name); + if (Nflag) { + skipfile(); + return (GOOD); + } + if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) { + fprintf(stderr, "%s: cannot create special file: %s\n", + name, strerror(errno)); + skipfile(); + return (FAIL); + } + (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid); + (void) chmod(name, mode); + skipfile(); + utimes(name, timep); + return (GOOD); + + case IFREG: + vprintf(stdout, "extract file %s\n", name); + if (Nflag) { + skipfile(); + return (GOOD); + } + if ((ofile = creat(name, 0666)) < 0) { + fprintf(stderr, "%s: cannot create file: %s\n", + name, strerror(errno)); + skipfile(); + return (FAIL); + } + (void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid); + (void) fchmod(ofile, mode); + getfile(xtrfile, xtrskip); + (void) close(ofile); + utimes(name, timep); + return (GOOD); + } + /* NOTREACHED */ +} + +/* + * skip over bit maps on the tape + */ +void +skipmaps() +{ + + while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI) + skipfile(); +} + +/* + * skip over a file on the tape + */ +void +skipfile() +{ + + curfile.action = SKIP; + getfile(xtrnull, xtrnull); +} + +/* + * Extract a file from the tape. + * When an allocated block is found it is passed to the fill function; + * when an unallocated block (hole) is found, a zeroed buffer is passed + * to the skip function. + */ +void +getfile(fill, skip) + void (*fill) __P((char *, long)); + void (*skip) __P((char *, long)); +{ + register int i; + int curblk = 0; + long size = spcl.c_dinode.di_size; + static char clearedbuf[MAXBSIZE]; + char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE]; + char junk[TP_BSIZE]; + + if (spcl.c_type == TS_END) + panic("ran off end of tape\n"); + if (spcl.c_magic != NFS_MAGIC) + panic("not at beginning of a file\n"); + if (!gettingfile && setjmp(restart) != 0) + return; + gettingfile++; +loop: + for (i = 0; i < spcl.c_count; i++) { + if (spcl.c_addr[i]) { + readtape(&buf[curblk++][0]); + if (curblk == fssize / TP_BSIZE) { + (*fill)((char *)buf, size > TP_BSIZE ? + (long) (fssize) : + (curblk - 1) * TP_BSIZE + size); + curblk = 0; + } + } else { + if (curblk > 0) { + (*fill)((char *)buf, size > TP_BSIZE ? + (long) (curblk * TP_BSIZE) : + (curblk - 1) * TP_BSIZE + size); + curblk = 0; + } + (*skip)(clearedbuf, size > TP_BSIZE ? + (long) TP_BSIZE : size); + } + if ((size -= TP_BSIZE) <= 0) { + for (i++; i < spcl.c_count; i++) + if (spcl.c_addr[i]) + readtape(junk); + break; + } + } + if (gethead(&spcl) == GOOD && size > 0) { + if (spcl.c_type == TS_ADDR) + goto loop; + dprintf(stdout, + "Missing address (header) block for %s at %d blocks\n", + curfile.name, blksread); + } + if (curblk > 0) + (*fill)((char *)buf, (curblk * TP_BSIZE) + size); + findinode(&spcl); + gettingfile = 0; +} + +/* + * Write out the next block of a file. + */ +static void +xtrfile(buf, size) + char *buf; + long size; +{ + + if (Nflag) + return; + if (write(ofile, buf, (int) size) == -1) { + fprintf(stderr, + "write error extracting inode %d, name %s\nwrite: %s\n", + curfile.ino, curfile.name, strerror(errno)); + done(1); + } +} + +/* + * Skip over a hole in a file. + */ +/* ARGSUSED */ +static void +xtrskip(buf, size) + char *buf; + long size; +{ + + if (lseek(ofile, size, SEEK_CUR) == -1) { + fprintf(stderr, + "seek error extracting inode %d, name %s\nlseek: %s\n", + curfile.ino, curfile.name, strerror(errno)); + done(1); + } +} + +/* + * Collect the next block of a symbolic link. + */ +static void +xtrlnkfile(buf, size) + char *buf; + long size; +{ + + pathlen += size; + if (pathlen > MAXPATHLEN) { + fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n", + curfile.name, lnkbuf, buf, pathlen); + done(1); + } + (void) strcat(lnkbuf, buf); +} + +/* + * Skip over a hole in a symbolic link (should never happen). + */ +/* ARGSUSED */ +static void +xtrlnkskip(buf, size) + char *buf; + long size; +{ + + fprintf(stderr, "unallocated block in symbolic link %s\n", + curfile.name); + done(1); +} + +/* + * Collect the next block of a bit map. + */ +static void +xtrmap(buf, size) + char *buf; + long size; +{ + + bcopy(buf, map, size); + map += size; +} + +/* + * Skip over a hole in a bit map (should never happen). + */ +/* ARGSUSED */ +static void +xtrmapskip(buf, size) + char *buf; + long size; +{ + + panic("hole in map\n"); + map += size; +} + +/* + * Noop, when an extraction function is not needed. + */ +/* ARGSUSED */ +void +xtrnull(buf, size) + char *buf; + long size; +{ + + return; +} + +/* + * Read TP_BSIZE blocks from the input. + * Handle read errors, and end of media. + */ +static void +readtape(buf) + char *buf; +{ + long rd, newvol, i; + int cnt, seek_failed; + + if (blkcnt < numtrec) { + bcopy(&tapebuf[(blkcnt++ * TP_BSIZE)], buf, (long)TP_BSIZE); + blksread++; + tpblksread++; + return; + } + for (i = 0; i < ntrec; i++) + ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; + if (numtrec == 0) + numtrec = ntrec; + cnt = ntrec * TP_BSIZE; + rd = 0; +getmore: +#ifdef RRESTORE + if (host) + i = rmtread(&tapebuf[rd], cnt); + else +#endif + i = read(mt, &tapebuf[rd], cnt); + /* + * Check for mid-tape short read error. + * If found, skip rest of buffer and start with the next. + */ + if (!pipein && numtrec < ntrec && i > 0) { + dprintf(stdout, "mid-media short read error.\n"); + numtrec = ntrec; + } + /* + * Handle partial block read. + */ + if (pipein && i == 0 && rd > 0) + i = rd; + else if (i > 0 && i != ntrec * TP_BSIZE) { + if (pipein) { + rd += i; + cnt -= i; + if (cnt > 0) + goto getmore; + i = rd; + } else { + /* + * Short read. Process the blocks read. + */ + if (i % TP_BSIZE != 0) + vprintf(stdout, + "partial block read: %d should be %d\n", + i, ntrec * TP_BSIZE); + numtrec = i / TP_BSIZE; + } + } + /* + * Handle read error. + */ + if (i < 0) { + fprintf(stderr, "Tape read error while "); + switch (curfile.action) { + default: + fprintf(stderr, "trying to set up tape\n"); + break; + case UNKNOWN: + fprintf(stderr, "trying to resynchronize\n"); + break; + case USING: + fprintf(stderr, "restoring %s\n", curfile.name); + break; + case SKIP: + fprintf(stderr, "skipping over inode %d\n", + curfile.ino); + break; + } + if (!yflag && !reply("continue")) + done(1); + i = ntrec * TP_BSIZE; + bzero(tapebuf, i); +#ifdef RRESTORE + if (host) + seek_failed = (rmtseek(i, 1) < 0); + else +#endif + seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1); + + if (seek_failed) { + fprintf(stderr, + "continuation failed: %s\n", strerror(errno)); + done(1); + } + } + /* + * Handle end of tape. + */ + if (i == 0) { + vprintf(stdout, "End-of-tape encountered\n"); + if (!pipein) { + newvol = volno + 1; + volno = 0; + numtrec = 0; + getvol(newvol); + readtape(buf); + return; + } + if (rd % TP_BSIZE != 0) + panic("partial block read: %d should be %d\n", + rd, ntrec * TP_BSIZE); + terminateinput(); + bcopy((char *)&endoftapemark, &tapebuf[rd], (long)TP_BSIZE); + } + blkcnt = 0; + bcopy(&tapebuf[(blkcnt++ * TP_BSIZE)], buf, (long)TP_BSIZE); + blksread++; + tpblksread++; +} + +static void +findtapeblksize() +{ + register long i; + + for (i = 0; i < ntrec; i++) + ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; + blkcnt = 0; +#ifdef RRESTORE + if (host) + i = rmtread(tapebuf, ntrec * TP_BSIZE); + else +#endif + i = read(mt, tapebuf, ntrec * TP_BSIZE); + + if (i <= 0) { + fprintf(stderr, "tape read error: %s\n", strerror(errno)); + done(1); + } + if (i % TP_BSIZE != 0) { + fprintf(stderr, "Tape block size (%d) %s (%d)\n", + i, "is not a multiple of dump block size", TP_BSIZE); + done(1); + } + ntrec = i / TP_BSIZE; + numtrec = ntrec; + vprintf(stdout, "Tape block size is %d\n", ntrec); +} + +void +closemt() +{ + + if (mt < 0) + return; +#ifdef RRESTORE + if (host) + rmtclose(); + else +#endif + (void) close(mt); +} + +/* + * Read the next block from the tape. + * Check to see if it is one of several vintage headers. + * If it is an old style header, convert it to a new style header. + * If it is not any valid header, return an error. + */ +static int +gethead(buf) + struct s_spcl *buf; +{ + long i; + union { + quad_t qval; + long val[2]; + } qcvt; + union u_ospcl { + char dummy[TP_BSIZE]; + struct s_ospcl { + long c_type; + long c_date; + long c_ddate; + long c_volume; + long c_tapea; + u_short c_inumber; + long c_magic; + long c_checksum; + struct odinode { + unsigned short odi_mode; + u_short odi_nlink; + u_short odi_uid; + u_short odi_gid; + long odi_size; + long odi_rdev; + char odi_addr[36]; + long odi_atime; + long odi_mtime; + long odi_ctime; + } c_dinode; + long c_count; + char c_addr[256]; + } s_ospcl; + } u_ospcl; + + if (!cvtflag) { + readtape((char *)buf); + if (buf->c_magic != NFS_MAGIC) { + if (swabl(buf->c_magic) != NFS_MAGIC) + return (FAIL); + if (!Bcvt) { + vprintf(stdout, "Note: Doing Byte swapping\n"); + Bcvt = 1; + } + } + if (checksum((int *)buf) == FAIL) + return (FAIL); + if (Bcvt) + swabst((u_char *)"8l4s31l", (u_char *)buf); + goto good; + } + readtape((char *)(&u_ospcl.s_ospcl)); + bzero((char *)buf, (long)TP_BSIZE); + buf->c_type = u_ospcl.s_ospcl.c_type; + buf->c_date = u_ospcl.s_ospcl.c_date; + buf->c_ddate = u_ospcl.s_ospcl.c_ddate; + buf->c_volume = u_ospcl.s_ospcl.c_volume; + buf->c_tapea = u_ospcl.s_ospcl.c_tapea; + buf->c_inumber = u_ospcl.s_ospcl.c_inumber; + buf->c_checksum = u_ospcl.s_ospcl.c_checksum; + buf->c_magic = u_ospcl.s_ospcl.c_magic; + buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode; + buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink; + buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid; + buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid; + buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size; + buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev; + buf->c_dinode.di_atime.ts_sec = u_ospcl.s_ospcl.c_dinode.odi_atime; + buf->c_dinode.di_mtime.ts_sec = u_ospcl.s_ospcl.c_dinode.odi_mtime; + buf->c_dinode.di_ctime.ts_sec = u_ospcl.s_ospcl.c_dinode.odi_ctime; + buf->c_count = u_ospcl.s_ospcl.c_count; + bcopy(u_ospcl.s_ospcl.c_addr, buf->c_addr, (long)256); + if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC || + checksum((int *)(&u_ospcl.s_ospcl)) == FAIL) + return(FAIL); + buf->c_magic = NFS_MAGIC; + +good: + if ((buf->c_dinode.di_size == 0 || buf->c_dinode.di_size > 0xfffffff) && + (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) { + qcvt.qval = buf->c_dinode.di_size; + if (qcvt.val[0] || qcvt.val[1]) { + printf("Note: Doing Quad swapping\n"); + Qcvt = 1; + } + } + if (Qcvt) { + qcvt.qval = buf->c_dinode.di_size; + i = qcvt.val[1]; + qcvt.val[1] = qcvt.val[0]; + qcvt.val[0] = i; + buf->c_dinode.di_size = qcvt.qval; + } + + switch (buf->c_type) { + + case TS_CLRI: + case TS_BITS: + /* + * Have to patch up missing information in bit map headers + */ + buf->c_inumber = 0; + buf->c_dinode.di_size = buf->c_count * TP_BSIZE; + for (i = 0; i < buf->c_count; i++) + buf->c_addr[i]++; + break; + + case TS_TAPE: + if ((buf->c_flags & DR_NEWINODEFMT) == 0) + oldinofmt = 1; + /* fall through */ + case TS_END: + buf->c_inumber = 0; + break; + + case TS_INODE: + case TS_ADDR: + break; + + default: + panic("gethead: unknown inode type %d\n", buf->c_type); + break; + } + /* + * If we are restoring a filesystem with old format inodes, + * copy the uid/gid to the new location. + */ + if (oldinofmt) { + buf->c_dinode.di_uid = buf->c_dinode.di_ouid; + buf->c_dinode.di_gid = buf->c_dinode.di_ogid; + } + if (dflag) + accthdr(buf); + return(GOOD); +} + +/* + * Check that a header is where it belongs and predict the next header + */ +static void +accthdr(header) + struct s_spcl *header; +{ + static ino_t previno = 0x7fffffff; + static int prevtype; + static long predict; + long blks, i; + + if (header->c_type == TS_TAPE) { + fprintf(stderr, "Volume header (%s inode format) ", + oldinofmt ? "old" : "new"); + if (header->c_firstrec) + fprintf(stderr, "begins with record %d", + header->c_firstrec); + fprintf(stderr, "\n"); + previno = 0x7fffffff; + return; + } + if (previno == 0x7fffffff) + goto newcalc; + switch (prevtype) { + case TS_BITS: + fprintf(stderr, "Dump mask header"); + break; + case TS_CLRI: + fprintf(stderr, "Remove mask header"); + break; + case TS_INODE: + fprintf(stderr, "File header, ino %d", previno); + break; + case TS_ADDR: + fprintf(stderr, "File continuation header, ino %d", previno); + break; + case TS_END: + fprintf(stderr, "End of tape header"); + break; + } + if (predict != blksread - 1) + fprintf(stderr, "; predicted %d blocks, got %d blocks", + predict, blksread - 1); + fprintf(stderr, "\n"); +newcalc: + blks = 0; + if (header->c_type != TS_END) + for (i = 0; i < header->c_count; i++) + if (header->c_addr[i] != 0) + blks++; + predict = blks; + blksread = 0; + prevtype = header->c_type; + previno = header->c_inumber; +} + +/* + * Find an inode header. + * Complain if had to skip, and complain is set. + */ +static void +findinode(header) + struct s_spcl *header; +{ + static long skipcnt = 0; + long i; + char buf[TP_BSIZE]; + + curfile.name = "<name unknown>"; + curfile.action = UNKNOWN; + curfile.dip = NULL; + curfile.ino = 0; + do { + if (header->c_magic != NFS_MAGIC) { + skipcnt++; + while (gethead(header) == FAIL || + header->c_date != dumpdate) + skipcnt++; + } + switch (header->c_type) { + + case TS_ADDR: + /* + * Skip up to the beginning of the next record + */ + for (i = 0; i < header->c_count; i++) + if (header->c_addr[i]) + readtape(buf); + while (gethead(header) == FAIL || + header->c_date != dumpdate) + skipcnt++; + break; + + case TS_INODE: + curfile.dip = &header->c_dinode; + curfile.ino = header->c_inumber; + break; + + case TS_END: + curfile.ino = maxino; + break; + + case TS_CLRI: + curfile.name = "<file removal list>"; + break; + + case TS_BITS: + curfile.name = "<file dump list>"; + break; + + case TS_TAPE: + panic("unexpected tape header\n"); + /* NOTREACHED */ + + default: + panic("unknown tape header type %d\n", spcl.c_type); + /* NOTREACHED */ + + } + } while (header->c_type == TS_ADDR); + if (skipcnt > 0) + fprintf(stderr, "resync restore, skipped %d blocks\n", skipcnt); + skipcnt = 0; +} + +static int +checksum(buf) + register int *buf; +{ + register int i, j; + + j = sizeof(union u_spcl) / sizeof(int); + i = 0; + if(!Bcvt) { + do + i += *buf++; + while (--j); + } else { + /* What happens if we want to read restore tapes + for a 16bit int machine??? */ + do + i += swabl(*buf++); + while (--j); + } + + if (i != CHECKSUM) { + fprintf(stderr, "Checksum error %o, inode %d file %s\n", i, + curfile.ino, curfile.name); + return(FAIL); + } + return(GOOD); +} + +#ifdef RRESTORE +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +msg(const char *fmt, ...) +#else +msg(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)vfprintf(stderr, fmt, ap); + va_end(ap); +} +#endif /* RRESTORE */ + +static u_char * +swabshort(sp, n) + register u_char *sp; + register int n; +{ + char c; + + while (--n >= 0) { + c = sp[0]; sp[0] = sp[1]; sp[1] = c; + sp += 2; + } + return (sp); +} + +static u_char * +swablong(sp, n) + register u_char *sp; + register int n; +{ + char c; + + while (--n >= 0) { + c = sp[0]; sp[0] = sp[3]; sp[3] = c; + c = sp[2]; sp[2] = sp[1]; sp[1] = c; + sp += 4; + } + return (sp); +} + +void +swabst(cp, sp) + register u_char *cp, *sp; +{ + int n = 0; + + while (*cp) { + switch (*cp) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = (n * 10) + (*cp++ - '0'); + continue; + + case 's': case 'w': case 'h': + if (n == 0) + n = 1; + sp = swabshort(sp, n); + break; + + case 'l': + if (n == 0) + n = 1; + sp = swablong(sp, n); + break; + + default: /* Any other character, like 'b' counts as byte. */ + if (n == 0) + n = 1; + sp += n; + break; + } + cp++; + n = 0; + } +} + +static u_long +swabl(x) + u_long x; +{ + swabst((u_char *)"l", (u_char *)&x); + return (x); +} diff --git a/sbin/restore/utilities.c b/sbin/restore/utilities.c new file mode 100644 index 0000000..31fd11c --- /dev/null +++ b/sbin/restore/utilities.c @@ -0,0 +1,395 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)utilities.c 8.2 (Berkeley) 3/25/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "restore.h" +#include "extern.h" + +/* + * Insure that all the components of a pathname exist. + */ +void +pathcheck(name) + char *name; +{ + register char *cp; + struct entry *ep; + char *start; + + start = index(name, '/'); + if (start == 0) + return; + for (cp = start; *cp != '\0'; cp++) { + if (*cp != '/') + continue; + *cp = '\0'; + ep = lookupname(name); + if (ep == NULL) { + /* Safe; we know the pathname exists in the dump. */ + ep = addentry(name, pathsearch(name)->d_ino, NODE); + newnode(ep); + } + ep->e_flags |= NEW|KEEP; + *cp = '/'; + } +} + +/* + * Change a name to a unique temporary name. + */ +void +mktempname(ep) + register struct entry *ep; +{ + char oldname[MAXPATHLEN]; + + if (ep->e_flags & TMPNAME) + badentry(ep, "mktempname: called with TMPNAME"); + ep->e_flags |= TMPNAME; + (void) strcpy(oldname, myname(ep)); + freename(ep->e_name); + ep->e_name = savename(gentempname(ep)); + ep->e_namlen = strlen(ep->e_name); + renameit(oldname, myname(ep)); +} + +/* + * Generate a temporary name for an entry. + */ +char * +gentempname(ep) + struct entry *ep; +{ + static char name[MAXPATHLEN]; + struct entry *np; + long i = 0; + + for (np = lookupino(ep->e_ino); + np != NULL && np != ep; np = np->e_links) + i++; + if (np == NULL) + badentry(ep, "not on ino list"); + (void) sprintf(name, "%s%d%d", TMPHDR, i, ep->e_ino); + return (name); +} + +/* + * Rename a file or directory. + */ +void +renameit(from, to) + char *from, *to; +{ + if (!Nflag && rename(from, to) < 0) { + fprintf(stderr, "warning: cannot rename %s to %s: %s\n", + from, to, strerror(errno)); + return; + } + vprintf(stdout, "rename %s to %s\n", from, to); +} + +/* + * Create a new node (directory). + */ +void +newnode(np) + struct entry *np; +{ + char *cp; + + if (np->e_type != NODE) + badentry(np, "newnode: not a node"); + cp = myname(np); + if (!Nflag && mkdir(cp, 0777) < 0) { + np->e_flags |= EXISTED; + fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno)); + return; + } + vprintf(stdout, "Make node %s\n", cp); +} + +/* + * Remove an old node (directory). + */ +void +removenode(ep) + register struct entry *ep; +{ + char *cp; + + if (ep->e_type != NODE) + badentry(ep, "removenode: not a node"); + if (ep->e_entries != NULL) + badentry(ep, "removenode: non-empty directory"); + ep->e_flags |= REMOVED; + ep->e_flags &= ~TMPNAME; + cp = myname(ep); + if (!Nflag && rmdir(cp) < 0) { + fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno)); + return; + } + vprintf(stdout, "Remove node %s\n", cp); +} + +/* + * Remove a leaf. + */ +void +removeleaf(ep) + register struct entry *ep; +{ + char *cp; + + if (ep->e_type != LEAF) + badentry(ep, "removeleaf: not a leaf"); + ep->e_flags |= REMOVED; + ep->e_flags &= ~TMPNAME; + cp = myname(ep); + if (!Nflag && unlink(cp) < 0) { + fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno)); + return; + } + vprintf(stdout, "Remove leaf %s\n", cp); +} + +/* + * Create a link. + */ +int +linkit(existing, new, type) + char *existing, *new; + int type; +{ + + if (type == SYMLINK) { + if (!Nflag && symlink(existing, new) < 0) { + fprintf(stderr, + "warning: cannot create symbolic link %s->%s: %s\n", + new, existing, strerror(errno)); + return (FAIL); + } + } else if (type == HARDLINK) { + if (!Nflag && link(existing, new) < 0) { + fprintf(stderr, + "warning: cannot create hard link %s->%s: %s\n", + new, existing, strerror(errno)); + return (FAIL); + } + } else { + panic("linkit: unknown type %d\n", type); + return (FAIL); + } + vprintf(stdout, "Create %s link %s->%s\n", + type == SYMLINK ? "symbolic" : "hard", new, existing); + return (GOOD); +} + +/* + * find lowest number file (above "start") that needs to be extracted + */ +ino_t +lowerbnd(start) + ino_t start; +{ + register struct entry *ep; + + for ( ; start < maxino; start++) { + ep = lookupino(start); + if (ep == NULL || ep->e_type == NODE) + continue; + if (ep->e_flags & (NEW|EXTRACT)) + return (start); + } + return (start); +} + +/* + * find highest number file (below "start") that needs to be extracted + */ +ino_t +upperbnd(start) + ino_t start; +{ + register struct entry *ep; + + for ( ; start > ROOTINO; start--) { + ep = lookupino(start); + if (ep == NULL || ep->e_type == NODE) + continue; + if (ep->e_flags & (NEW|EXTRACT)) + return (start); + } + return (start); +} + +/* + * report on a badly formed entry + */ +void +badentry(ep, msg) + register struct entry *ep; + char *msg; +{ + + fprintf(stderr, "bad entry: %s\n", msg); + fprintf(stderr, "name: %s\n", myname(ep)); + fprintf(stderr, "parent name %s\n", myname(ep->e_parent)); + if (ep->e_sibling != NULL) + fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling)); + if (ep->e_entries != NULL) + fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries)); + if (ep->e_links != NULL) + fprintf(stderr, "next link name: %s\n", myname(ep->e_links)); + if (ep->e_next != NULL) + fprintf(stderr, + "next hashchain name: %s\n", myname(ep->e_next)); + fprintf(stderr, "entry type: %s\n", + ep->e_type == NODE ? "NODE" : "LEAF"); + fprintf(stderr, "inode number: %ld\n", ep->e_ino); + panic("flags: %s\n", flagvalues(ep)); +} + +/* + * Construct a string indicating the active flag bits of an entry. + */ +char * +flagvalues(ep) + register struct entry *ep; +{ + static char flagbuf[BUFSIZ]; + + (void) strcpy(flagbuf, "|NIL"); + flagbuf[0] = '\0'; + if (ep->e_flags & REMOVED) + (void) strcat(flagbuf, "|REMOVED"); + if (ep->e_flags & TMPNAME) + (void) strcat(flagbuf, "|TMPNAME"); + if (ep->e_flags & EXTRACT) + (void) strcat(flagbuf, "|EXTRACT"); + if (ep->e_flags & NEW) + (void) strcat(flagbuf, "|NEW"); + if (ep->e_flags & KEEP) + (void) strcat(flagbuf, "|KEEP"); + if (ep->e_flags & EXISTED) + (void) strcat(flagbuf, "|EXISTED"); + return (&flagbuf[1]); +} + +/* + * Check to see if a name is on a dump tape. + */ +ino_t +dirlookup(name) + const char *name; +{ + struct direct *dp; + ino_t ino; + + ino = ((dp = pathsearch(name)) == NULL) ? 0 : dp->d_ino; + + if (ino == 0 || TSTINO(ino, dumpmap) == 0) + fprintf(stderr, "%s is not on the tape\n", name); + return (ino); +} + +/* + * Elicit a reply. + */ +int +reply(question) + char *question; +{ + char c; + + do { + fprintf(stderr, "%s? [yn] ", question); + (void) fflush(stderr); + c = getc(terminal); + while (c != '\n' && getc(terminal) != '\n') + if (feof(terminal)) + return (FAIL); + } while (c != 'y' && c != 'n'); + if (c == 'y') + return (GOOD); + return (FAIL); +} + +/* + * handle unexpected inconsistencies + */ +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +panic(const char *fmt, ...) +#else +panic(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + + vfprintf(stderr, fmt, ap); + if (yflag) + return; + if (reply("abort") == GOOD) { + if (reply("dump core") == GOOD) + abort(); + done(1); + } +} diff --git a/sbin/route/Makefile b/sbin/route/Makefile new file mode 100644 index 0000000..f7de878 --- /dev/null +++ b/sbin/route/Makefile @@ -0,0 +1,25 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= route +MAN8= route.0 +SRCS= route.c ccitt_addr.c +CFLAGS+=-I. +CLEANFILES+=keywords.h +BINOWN= root +BINMODE=4555 + +all: route ${MAN8} + +keywords.h: keywords + sed -e '/^#/d' -e '/^$$/d' ${.CURDIR}/keywords > _keywords.tmp + tr a-z A-Z < _keywords.tmp | paste _keywords.tmp - | \ + awk '{ \ + if (NF > 1) \ + printf "#define\tK_%s\t%d\n\t{\"%s\", K_%s},\n", \ + $$2, NR, $$1, $$2 }' \ + > ${.TARGET} + rm -f _keywords.tmp + +.include <bsd.prog.mk> + +route .depend lint tags: keywords.h diff --git a/sbin/route/ccitt_addr.c b/sbin/route/ccitt_addr.c new file mode 100644 index 0000000..868f6fc --- /dev/null +++ b/sbin/route/ccitt_addr.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ccitt_addr.c 8.1 (Berkeley) 6/5/93 + */ +/* + * parse CCITT addresses + * + * Addresses must have the format: [hpr],x121address[,userdata][,protocol] + * items enclosed with square brackets are optional + * 'h' or 'p' means hi priority (packet size = 128; specific to Datapac + * and necessary only for X.25(76) and non-negotiating X.25(80) DTE's) + * 'r' means reverse charge (remote DTE pays for call). + * The x121address consists of an optional netid and dot, followed + * by a dte address. + * + * Frank Pronk + * The University of British Columbia + * Laboratory for Computational Vision + * Copyright (c) 1984 + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netccitt/x25.h> + +static char *copychar (); + +ccitt_addr (addr, xp) +char *addr; +register struct sockaddr_x25 *xp; +{ + register char *p, *ap, *limit; + int havenet = 0; + + bzero ((char *)xp, sizeof (*xp)); + xp->x25_family = AF_CCITT; + xp->x25_len = sizeof(*xp); + p = addr; + + /* + * process optional priority and reverse charging flags + */ + + if (*p == 'p' || *p == 'r' || *p == 'h') { + while (*p == 'p' || *p == 'r' || *p == 'h') { + if (*p == 'p' || *p == 'h') + xp->x25_opts.op_psize = X25_PS128; + else if (*p == 'r') + xp->x25_opts.op_flags |= X25_REVERSE_CHARGE; + p++; + } + if (*p != ',') + return (0); + p++; + } + if (*p == '\0') + return (0); + + /* + * [network id:]X.121 address + */ + + ap = xp->x25_addr; + limit = ap + sizeof (xp->x25_addr) - 1; + while (*p) { + if (*p == ',') + break; + if (*p == '.' || *p == ':') { + if (havenet) + return (0); + havenet++; + xp->x25_net = atoi (xp->x25_addr); + p++; + ap = xp->x25_addr; + *ap = '\0'; + } + if (*p < '0' || *p > '9') + return (0); + if (ap >= limit) + return (0); + *ap++ = *p++; + } + if (*p == '\0') + return (1); + + /* + * optional user data, bytes 4 to 16 + */ + + p++; + ap = xp->x25_udata + 4; /* first four bytes are protocol id */ + limit = ap + sizeof (xp->x25_udata) - 4; + xp->x25_udlen = 4; + while (*p) { + if (*p == ',') + break; + if (ap >= limit) + return (0); + p = copychar (p, ap++); + xp->x25_udlen++; + } + if (xp->x25_udlen == 4) + xp->x25_udlen = 0; + if (*p == '\0') + return (1); + + p++; + ap = xp->x25_udata; /* protocol id */ + limit = ap + (xp->x25_udlen ? 4 : sizeof(xp->x25_udata)); + while (*p) { + if (*p == ',') + return (0); + if (ap >= limit) + return (0); + p = copychar (p, ap++); + } + if (xp->x25_udlen == 0) + xp->x25_udlen = ap - xp->x25_udata; + return (1); +} + +static char * +copychar (from, to) +register char *from, *to; +{ + register int n; + + if (*from != '\\' || from[1] < '0' || from[1] > '7') { + *to = *from++; + return (from); + } + n = *++from - '0'; + from++; + if (*from >= '0' && *from <= '7') { + register int n1; + + n = n*8 + *from++ - '0'; + if (*from >= '0' && *from <= '7' && (n1 = n*8 + *from-'0') < 256) { + n = n1; + from++; + } + } + *to = n; + return (from); +} diff --git a/sbin/route/keywords b/sbin/route/keywords new file mode 100644 index 0000000..e486042 --- /dev/null +++ b/sbin/route/keywords @@ -0,0 +1,44 @@ +# @(#)keywords 8.2 (Berkeley) 3/19/94 + +add +blackhole +change +cloning +delete +dst +expire +flush +gateway +genmask +get +host +hopcount +iface +interface +ifa +ifp +inet +iso +link +lock +lockrest +mask +monitor +mtu +net +netmask +nostatic +osi +proto1 +proto2 +recvpipe +reject +rtt +rttvar +sa +sendpipe +ssthresh +static +x25 +xns +xresolve diff --git a/sbin/route/route.8 b/sbin/route/route.8 new file mode 100644 index 0000000..906f8c2 --- /dev/null +++ b/sbin/route/route.8 @@ -0,0 +1,324 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)route.8 8.3 (Berkeley) 3/19/94 +.\" +.Dd March 19, 1994 +.Dt ROUTE 8 +.Os BSD 4.4 +.Sh NAME +.Nm route +.Nd manually manipulate the routing tables. +.Sh SYNOPSIS +.Nm route +.Op Fl nqv +.Ar command +.Oo +.Op Ar modifiers +.Ar args +.Oc +.Sh DESCRIPTION +.Nm Route +is a utility used to manually manipulate the network +routing tables. It normally is not needed, as a +system routing table management daemon such as +.Xr routed 8 , +should tend to this task. +.Pp +The +.Nm route : +utility supports a limited number of general options, +but a rich command language, enabling the user to specify +any arbitrary request that could be delivered via the +programmatic interface discussed in +.Xr route 4 . +.Pp +.Bl -tag -width Ds +.It Fl n +Bypasses attempts to print host and network names symbolically +when reporting actions. (The process of translating between symbolic +names and numerical equivalents can be quite time consuming, and +may require correct operation of the network; thus it may be expedient +to forgo this, especially when attempting to repair networking operations), +.It Fl v +(verbose) Print additional details. +.It Fl q +Suppress all output. +.El +.Pp +The +.Nm route : +utility provides six commands: +.Pp +.Bl -tag -width Fl -compact +.It Cm add +Add a route. +.It Cm flush +Remove all routes. +.It Cm delete +Delete a specific route. +.It Cm change +Change aspects of a route (such as its gateway). +.It Cm get +Lookup and display the route for a destination. +.It Cm monitor +Continuously report any changes to the routing information base, +routing lookup misses, or suspected network partitionings. +.El +.Pp +The monitor command has the syntax +.Pp +.Bd -filled -offset indent -compact +.Nm route Op Fl n +.Cm monitor +.Ed +.Pp +The flush command has the syntax +.Pp +.Bd -filled -offset indent -compact +.Nm route Op Fl n +.Cm flush +.Op Ar family +.Ed +.Pp +If the +.Cm flush +command is specified, +.Nm route +will ``flush'' the routing tables of all gateway entries. +When the address family may is specified by any of the +.Fl osi , +.Fl xns , +or +.Fl inet +modifiers, only routes having destinations with addresses in the +delineated family will be deleted. +.Pp +The other commands have the following syntax: +.Pp +.Bd -filled -offset indent -compact +.Nm route Op Fl n +.Ar command +.Op Fl net No \&| Fl host +.Ar destination gateway +.Ed +.Pp +where +.Ar destination +is the destination host or network, +.Ar gateway +is the next-hop intermediary via which packets should be routed. +Routes to a particular host may be distinguished from those to +a network by interpreting the Internet address specified as the +.Ar destination argument. +The optional modifiers +.Fl net +and +.Fl host +force the destination to be interpreted as a network or a host, respectively. +Otherwise, if the +.Ar destination +has a ``local address part'' of +INADDR_ANY , +or if the +.Ar destination +is the symbolic name of a network, then the route is +assumed to be to a network; otherwise, it is presumed to be a +route to a host. +.Pp +For example, +.Li 128.32 +is interpreted as +.Fl host Li 128.0.0.32 ; +.Li 128.32.130 +is interpreted as +.Fl host Li 128.32.0.130 ; +.Fl net Li 128.32 +is interpreted as +.Li 128.32.0.0; +and +.Fl net Li 128.32.130 +is interpreted as +.Li 128.32.130.0 . +.Pp +If the destination is directly reachable +via an interface requiring +no intermediary system to act as a gateway, the +.Fl interface +modifier should be specified; +the gateway given is the address of this host on the common network, +indicating the interface to be used for transmission. +.Pp +The optional modifiers +.Fl xns , +.Fl osi , +and +.Fl link +specify that all subsequent addresses are in the +.Tn XNS +.Tn OSI +address families, +or are specified as link-level addresses, +and the names must be numeric specifications rather than +symbolic names. +.Pp +The optional +.Fl netmask +qualifier is intended +to achieve the effect of an +.Tn OSI +.Tn ESIS +redirect with the netmask option, +or to manually add subnet routes with +netmasks different from that of the implied network interface +(as would otherwise be communicated using the OSPF or ISIS routing protocols). +One specifies an additional ensuing address parameter +(to be interpreted as a network mask). +The implicit network mask generated in the AF_INET case +can be overridden by making sure this option follows the destination parameter. +.Pp +Routes have associated flags which influence operation of the protocols +when sending to destinations matched by the routes. +These flags may be set (or sometimes cleared) +by indicating the following corresponding modifiers: +.Bd -literal +-cloning RTF_CLONING - generates a new route on use +-xresolve RTF_XRESOLVE - emit mesg on use (for external lookup) +-iface ~RTF_GATEWAY - destination is directly reachable +-static RTF_STATIC - manually added route +-nostatic ~RTF_STATIC - pretend route added by kernel or daemon +-reject RTF_REJECT - emit an ICMP unreachable when matched +-blackhole RTF_BLACKHOLE - silently discard pkts (during updates) +-proto1 RTF_PROTO1 - set protocol specific routing flag #1 +-proto2 RTF_PROTO2 - set protocol specific routing flag #2 +-llinfo RTF_LLINFO - validly translates proto addr to link addr +.Ed +.Pp +The optional modifiers +.Fl rtt , +.Fl rttvar , +.Fl sendpipe , +.Fl recvpipe , +.Fl mtu , +.Fl hopcount , +.Fl expire , +and +.Fl ssthresh +provide initial values to quantities maintained in the routing entry +by transport level protocols, such as TCP or TP4. +These may be individually locked by preceding each such modifier to +be locked by +the +.Fl lock +meta-modifier, or one can +specify that all ensuing metrics may be locked by the +.Fl lockrest +meta-modifier. +.Pp +In a +.Cm change +or +.Cm add +command where the destination and gateway are not sufficient to specify +the route (as in the +.Tn ISO +case where several interfaces may have the +same address), the +.Fl ifp +or +.Fl ifa +modifiers may be used to determine the interface or interface address. +.Pp +All symbolic names specified for a +.Ar destination +or +.Ar gateway +are looked up first as a host name using +.Xr gethostbyname 3 . +If this lookup fails, +.Xr getnetbyname 3 +is then used to interpret the name as that of a network. +.Pp +.Nm Route +uses a routing socket and the new message types +RTM_ADD, +RTM_DELETE, +RTM_GET, +and +RTM_CHANGE. +As such, only the super-user may modify +the routing tables. +.Sh DIAGNOSTICS +.Bl -tag -width Ds +.It Sy "add [host \&| network ] %s: gateway %s flags %x" +The specified route is being added to the tables. The +values printed are from the routing table entry supplied +in the +.Xr ioctl 2 +call. +If the gateway address used was not the primary address of the gateway +(the first one returned by +.Xr gethostbyname 3 ) , +the gateway address is printed numerically as well as symbolically. +.It Sy "delete [ host &| network ] %s: gateway %s flags %x" +As above, but when deleting an entry. +.It Sy "%s %s done" +When the +.Cm flush +command is specified, each routing table entry deleted +is indicated with a message of this form. +.It Sy "Network is unreachable" +An attempt to add a route failed because the gateway listed was not +on a directly-connected network. +The next-hop gateway must be given. +.It Sy "not in table" +A delete operation was attempted for an entry which +wasn't present in the tables. +.It Sy "routing table overflow" +An add operation was attempted, but the system was +low on resources and was unable to allocate memory +to create the new entry. +.El +.Sh SEE ALSO +.Xr netintro 4 , +.Xr route 4 , +.Xr esis 4 , +.Xr routed 8 , +.Xr XNSrouted 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +.Sh BUGS +The first paragraph may have slightly exaggerated +.Xr routed Ns 's +abilities. diff --git a/sbin/route/route.c b/sbin/route/route.c new file mode 100644 index 0000000..893996c --- /dev/null +++ b/sbin/route/route.c @@ -0,0 +1,1415 @@ +/* + * + * Copyright (c) 1983, 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1989, 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)route.c 8.3 (Berkeley) 3/19/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/mbuf.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/if_dl.h> +#include <netinet/in.h> +#include <netns/ns.h> +#include <netiso/iso.h> +#include <netccitt/x25.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <paths.h> + +struct keytab { + char *kt_cp; + int kt_i; +} keywords[] = { +#include "keywords.h" + {0, 0} +}; + +struct ortentry route; +union sockunion { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_ns sns; + struct sockaddr_iso siso; + struct sockaddr_dl sdl; + struct sockaddr_x25 sx25; +} so_dst, so_gate, so_mask, so_genmask, so_ifa, so_ifp; + +typedef union sockunion *sup; +int pid, rtm_addrs, uid; +int s; +int forcehost, forcenet, doflush, nflag, af, qflag, tflag, keyword(); +int iflag, verbose, aflen = sizeof (struct sockaddr_in); +int locking, lockrest, debugonly; +struct rt_metrics rt_metrics; +u_long rtm_inits; +struct in_addr inet_makeaddr(); +char *routename(), *netname(); +void flushroutes(), newroute(), monitor(), sockaddr(), sodump(), bprintf(); +void print_getmsg(), print_rtmsg(), pmsg_common(), pmsg_addrs(), mask_addr(); +int getaddr(), rtmsg(), x25_makemask(); +extern char *inet_ntoa(), *iso_ntoa(), *link_ntoa(); + +__dead void +usage(cp) + char *cp; +{ + if (cp) + (void) fprintf(stderr, "route: botched keyword: %s\n", cp); + (void) fprintf(stderr, + "usage: route [ -nqv ] cmd [[ -<qualifers> ] args ]\n"); + exit(1); + /* NOTREACHED */ +} + +void +quit(s) + char *s; +{ + int sverrno = errno; + + (void) fprintf(stderr, "route: "); + if (s) + (void) fprintf(stderr, "%s: ", s); + (void) fprintf(stderr, "%s\n", strerror(sverrno)); + exit(1); + /* NOTREACHED */ +} + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +int +main(argc, argv) + int argc; + char **argv; +{ + extern int optind; + int ch; + + if (argc < 2) + usage((char *)NULL); + + while ((ch = getopt(argc, argv, "nqdtv")) != EOF) + switch(ch) { + case 'n': + nflag = 1; + break; + case 'q': + qflag = 1; + break; + case 'v': + verbose = 1; + break; + case 't': + tflag = 1; + break; + case 'd': + debugonly = 1; + break; + case '?': + default: + usage((char *)NULL); + } + argc -= optind; + argv += optind; + + pid = getpid(); + uid = getuid(); + if (tflag) + s = open("/dev/null", O_WRONLY, 0); + else + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) + quit("socket"); + if (*argv) + switch (keyword(*argv)) { + case K_GET: + uid = 0; + /* FALLTHROUGH */ + + case K_CHANGE: + case K_ADD: + case K_DELETE: + newroute(argc, argv); + exit(0); + /* NOTREACHED */ + + case K_MONITOR: + monitor(); + /* NOTREACHED */ + + case K_FLUSH: + flushroutes(argc, argv); + exit(0); + /* NOTREACHED */ + } + usage(*argv); + /* NOTREACHED */ +} + +/* + * Purge all entries in the routing tables not + * associated with network interfaces. + */ +void +flushroutes(argc, argv) + int argc; + char *argv[]; +{ + size_t needed; + int mib[6], rlen, seqno; + char *buf, *next, *lim; + register struct rt_msghdr *rtm; + + if (uid) { + errno = EACCES; + quit("must be root to alter routing table"); + } + shutdown(s, 0); /* Don't want to read back our messages */ + if (argc > 1) { + argv++; + if (argc == 2 && **argv == '-') + switch (keyword(*argv + 1)) { + case K_INET: + af = AF_INET; + break; + case K_XNS: + af = AF_NS; + break; + case K_LINK: + af = AF_LINK; + break; + case K_ISO: + case K_OSI: + af = AF_ISO; + break; + case K_X25: + af = AF_CCITT; + default: + goto bad; + } else +bad: usage(*argv); + } + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_DUMP; + mib[5] = 0; /* no flags */ + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + quit("route-sysctl-estimate"); + if ((buf = malloc(needed)) == NULL) + quit("malloc"); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + quit("actual retrieval of routing table"); + lim = buf + needed; + if (verbose) + (void) printf("Examining routing table from sysctl\n"); + seqno = 0; /* ??? */ + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (verbose) + print_rtmsg(rtm, rtm->rtm_msglen); + if ((rtm->rtm_flags & RTF_GATEWAY) == 0) + continue; + if (af) { + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + + if (sa->sa_family != af) + continue; + } + if (debugonly) + continue; + rtm->rtm_type = RTM_DELETE; + rtm->rtm_seq = seqno; + rlen = write(s, next, rtm->rtm_msglen); + if (rlen < (int)rtm->rtm_msglen) { + (void) fprintf(stderr, + "route: write to routing socket: %s\n", + strerror(errno)); + (void) printf("got only %d for rlen\n", rlen); + break; + } + seqno++; + if (qflag) + continue; + if (verbose) + print_rtmsg(rtm, rlen); + else { + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + (void) printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ? + routename(sa) : netname(sa)); + sa = (struct sockaddr *)(sa->sa_len + (char *)sa); + (void) printf("%-20.20s ", routename(sa)); + (void) printf("done\n"); + } + } +} + +char * +routename(sa) + struct sockaddr *sa; +{ + register char *cp; + static char line[50]; + struct hostent *hp; + static char domain[MAXHOSTNAMELEN + 1]; + static int first = 1; + char *ns_print(); + + if (first) { + first = 0; + if (gethostname(domain, MAXHOSTNAMELEN) == 0 && + (cp = index(domain, '.'))) + (void) strcpy(domain, cp + 1); + else + domain[0] = 0; + } + + if (sa->sa_len == 0) + strcpy(line, "default"); + else switch (sa->sa_family) { + + case AF_INET: + { struct in_addr in; + in = ((struct sockaddr_in *)sa)->sin_addr; + + cp = 0; + if (in.s_addr == INADDR_ANY || sa->sa_len < 4) + cp = "default"; + if (cp == 0 && !nflag) { + hp = gethostbyaddr((char *)&in, sizeof (struct in_addr), + AF_INET); + if (hp) { + if ((cp = index(hp->h_name, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + cp = hp->h_name; + } + } + if (cp) + strcpy(line, cp); + else { +#define C(x) ((x) & 0xff) + in.s_addr = ntohl(in.s_addr); + (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); + } + break; + } + + case AF_NS: + return (ns_print((struct sockaddr_ns *)sa)); + + case AF_LINK: + return (link_ntoa((struct sockaddr_dl *)sa)); + + case AF_ISO: + (void) sprintf(line, "iso %s", + iso_ntoa(&((struct sockaddr_iso *)sa)->siso_addr)); + break; + + default: + { u_short *s = (u_short *)sa; + u_short *slim = s + ((sa->sa_len + 1) >> 1); + char *cp = line + sprintf(line, "(%d)", sa->sa_family); + + while (++s < slim) /* start with sa->sa_data */ + cp += sprintf(cp, " %x", *s); + break; + } + } + return (line); +} + +/* + * Return the name of the network whose address is given. + * The address is assumed to be that of a net or subnet, not a host. + */ +char * +netname(sa) + struct sockaddr *sa; +{ + char *cp = 0; + static char line[50]; + struct netent *np = 0; + u_long net, mask; + register u_long i; + int subnetshift; + char *ns_print(); + + switch (sa->sa_family) { + + case AF_INET: + { struct in_addr in; + in = ((struct sockaddr_in *)sa)->sin_addr; + + i = in.s_addr = ntohl(in.s_addr); + if (in.s_addr == 0) + cp = "default"; + else if (!nflag) { + if (IN_CLASSA(i)) { + mask = IN_CLASSA_NET; + subnetshift = 8; + } else if (IN_CLASSB(i)) { + mask = IN_CLASSB_NET; + subnetshift = 8; + } else { + mask = IN_CLASSC_NET; + subnetshift = 4; + } + /* + * If there are more bits than the standard mask + * would suggest, subnets must be in use. + * Guess at the subnet mask, assuming reasonable + * width subnet fields. + */ + while (in.s_addr &~ mask) + mask = (long)mask >> subnetshift; + net = in.s_addr & mask; + while ((mask & 1) == 0) + mask >>= 1, net >>= 1; + np = getnetbyaddr(net, AF_INET); + if (np) + cp = np->n_name; + } + if (cp) + strcpy(line, cp); + else if ((in.s_addr & 0xffffff) == 0) + (void) sprintf(line, "%u", C(in.s_addr >> 24)); + else if ((in.s_addr & 0xffff) == 0) + (void) sprintf(line, "%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16)); + else if ((in.s_addr & 0xff) == 0) + (void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8)); + else + (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8), + C(in.s_addr)); + break; + } + + case AF_NS: + return (ns_print((struct sockaddr_ns *)sa)); + break; + + case AF_LINK: + return (link_ntoa((struct sockaddr_dl *)sa)); + + case AF_ISO: + (void) sprintf(line, "iso %s", + iso_ntoa(&((struct sockaddr_iso *)sa)->siso_addr)); + break; + + default: + { u_short *s = (u_short *)sa->sa_data; + u_short *slim = s + ((sa->sa_len + 1)>>1); + char *cp = line + sprintf(line, "af %d:", sa->sa_family); + + while (s < slim) + cp += sprintf(cp, " %x", *s++); + break; + } + } + return (line); +} + +void +set_metric(value, key) + char *value; + int key; +{ + int flag = 0; + u_long noval, *valp = &noval; + + switch (key) { +#define caseof(x, y, z) case x: valp = &rt_metrics.z; flag = y; break + caseof(K_MTU, RTV_MTU, rmx_mtu); + caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount); + caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire); + caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe); + caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe); + caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh); + caseof(K_RTT, RTV_RTT, rmx_rtt); + caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar); + } + rtm_inits |= flag; + if (lockrest || locking) + rt_metrics.rmx_locks |= flag; + if (locking) + locking = 0; + *valp = atoi(value); +} + +void +newroute(argc, argv) + int argc; + register char **argv; +{ + char *cmd, *dest = "", *gateway = "", *err; + int ishost = 0, ret, attempts, oerrno, flags = RTF_STATIC; + int key; + struct hostent *hp = 0; + + if (uid) { + errno = EACCES; + quit("must be root to alter routing table"); + } + cmd = argv[0]; + if (*cmd != 'g') + shutdown(s, 0); /* Don't want to read back our messages */ + while (--argc > 0) { + if (**(++argv)== '-') { + switch (key = keyword(1 + *argv)) { + case K_LINK: + af = AF_LINK; + aflen = sizeof(struct sockaddr_dl); + break; + case K_OSI: + case K_ISO: + af = AF_ISO; + aflen = sizeof(struct sockaddr_iso); + break; + case K_INET: + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + break; + case K_X25: + af = AF_CCITT; + aflen = sizeof(struct sockaddr_x25); + break; + case K_SA: + af = PF_ROUTE; + aflen = sizeof(union sockunion); + break; + case K_XNS: + af = AF_NS; + aflen = sizeof(struct sockaddr_ns); + break; + case K_IFACE: + case K_INTERFACE: + iflag++; + case K_NOSTATIC: + flags &= ~RTF_STATIC; + break; + case K_LOCK: + locking = 1; + break; + case K_LOCKREST: + lockrest = 1; + break; + case K_HOST: + forcehost++; + break; + case K_REJECT: + flags |= RTF_REJECT; + break; + case K_BLACKHOLE: + flags |= RTF_BLACKHOLE; + break; + case K_PROTO1: + flags |= RTF_PROTO1; + break; + case K_PROTO2: + flags |= RTF_PROTO2; + break; + case K_CLONING: + flags |= RTF_CLONING; + break; + case K_XRESOLVE: + flags |= RTF_XRESOLVE; + break; + case K_STATIC: + flags |= RTF_STATIC; + break; + case K_IFA: + argc--; + (void) getaddr(RTA_IFA, *++argv, 0); + break; + case K_IFP: + argc--; + (void) getaddr(RTA_IFP, *++argv, 0); + break; + case K_GENMASK: + argc--; + (void) getaddr(RTA_GENMASK, *++argv, 0); + break; + case K_GATEWAY: + argc--; + (void) getaddr(RTA_GATEWAY, *++argv, 0); + break; + case K_DST: + argc--; + ishost = getaddr(RTA_DST, *++argv, &hp); + dest = *argv; + break; + case K_NETMASK: + argc--; + (void) getaddr(RTA_NETMASK, *++argv, 0); + /* FALLTHROUGH */ + case K_NET: + forcenet++; + break; + case K_MTU: + case K_HOPCOUNT: + case K_EXPIRE: + case K_RECVPIPE: + case K_SENDPIPE: + case K_SSTHRESH: + case K_RTT: + case K_RTTVAR: + argc--; + set_metric(*++argv, key); + break; + default: + usage(1+*argv); + } + } else { + if ((rtm_addrs & RTA_DST) == 0) { + dest = *argv; + ishost = getaddr(RTA_DST, *argv, &hp); + } else if ((rtm_addrs & RTA_GATEWAY) == 0) { + gateway = *argv; + (void) getaddr(RTA_GATEWAY, *argv, &hp); + } else { + int ret = atoi(*argv); + + if (ret == 0) { + if (strcmp(*argv, "0") == 0) + printf("%s,%s", + "old usage of trailing 0", + "assuming route to if\n"); + else + usage((char *)NULL); + iflag = 1; + continue; + } else if (ret > 0 && ret < 10) { + printf("old usage of trailing digit, "); + printf("assuming route via gateway\n"); + iflag = 0; + continue; + } + (void) getaddr(RTA_NETMASK, *argv, 0); + } + } + } + if (forcehost) + ishost = 1; + if (forcenet) + ishost = 0; + flags |= RTF_UP; + if (ishost) + flags |= RTF_HOST; + if (iflag == 0) + flags |= RTF_GATEWAY; + for (attempts = 1; ; attempts++) { + errno = 0; + if ((ret = rtmsg(*cmd, flags)) == 0) + break; + if (errno != ENETUNREACH && errno != ESRCH) + break; + if (af == AF_INET && *gateway && hp && hp->h_addr_list[1]) { + hp->h_addr_list++; + bcopy(hp->h_addr_list[0], &so_gate.sin.sin_addr, + hp->h_length); + } else + break; + } + if (*cmd == 'g') + exit(0); + oerrno = errno; + (void) printf("%s %s %s", cmd, ishost? "host" : "net", dest); + if (*gateway) { + (void) printf(": gateway %s", gateway); + if (attempts > 1 && ret == 0 && af == AF_INET) + (void) printf(" (%s)", + inet_ntoa(((struct sockaddr_in *)&route.rt_gateway)->sin_addr)); + } + if (ret == 0) + (void) printf("\n"); + else { + switch (oerrno) { + case ESRCH: + err = "not in table"; + break; + case EBUSY: + err = "entry in use"; + break; + case ENOBUFS: + err = "routing table overflow"; + break; + default: + err = strerror(oerrno); + break; + } + (void) printf(": %s\n", err); + } +} + +void +inet_makenetandmask(net, sin) + u_long net; + register struct sockaddr_in *sin; +{ + u_long addr, mask = 0; + register char *cp; + + rtm_addrs |= RTA_NETMASK; + if (net == 0) + mask = addr = 0; + else if (net < 128) { + addr = net << IN_CLASSA_NSHIFT; + mask = IN_CLASSA_NET; + } else if (net < 65536) { + addr = net << IN_CLASSB_NSHIFT; + mask = IN_CLASSB_NET; + } else if (net < 16777216L) { + addr = net << IN_CLASSC_NSHIFT; + mask = IN_CLASSC_NET; + } else { + addr = net; + if ((addr & IN_CLASSA_HOST) == 0) + mask = IN_CLASSA_NET; + else if ((addr & IN_CLASSB_HOST) == 0) + mask = IN_CLASSB_NET; + else if ((addr & IN_CLASSC_HOST) == 0) + mask = IN_CLASSC_NET; + else + mask = -1; + } + sin->sin_addr.s_addr = htonl(addr); + sin = &so_mask.sin; + sin->sin_addr.s_addr = htonl(mask); + sin->sin_len = 0; + sin->sin_family = 0; + cp = (char *)(&sin->sin_addr + 1); + while (*--cp == 0 && cp > (char *)sin) + ; + sin->sin_len = 1 + cp - (char *)sin; +} + +/* + * Interpret an argument as a network address of some kind, + * returning 1 if a host address, 0 if a network address. + */ +int +getaddr(which, s, hpp) + int which; + char *s; + struct hostent **hpp; +{ + register sup su; + struct ns_addr ns_addr(); + struct iso_addr *iso_addr(); + struct hostent *hp; + struct netent *np; + u_long val; + + if (af == 0) { + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + } + rtm_addrs |= which; + switch (which) { + case RTA_DST: + su = &so_dst; + su->sa.sa_family = af; + break; + case RTA_GATEWAY: + su = &so_gate; + su->sa.sa_family = af; + break; + case RTA_NETMASK: + su = &so_mask; + break; + case RTA_GENMASK: + su = &so_genmask; + break; + case RTA_IFP: + su = &so_ifp; + su->sa.sa_family = af; + break; + case RTA_IFA: + su = &so_ifa; + su->sa.sa_family = af; + break; + default: + usage("Internal Error"); + /*NOTREACHED*/ + } + su->sa.sa_len = aflen; + if (strcmp(s, "default") == 0) { + switch (which) { + case RTA_DST: + forcenet++; + (void) getaddr(RTA_NETMASK, s, 0); + break; + case RTA_NETMASK: + case RTA_GENMASK: + su->sa.sa_len = 0; + } + return (0); + } + switch (af) { + case AF_NS: + if (which == RTA_DST) { + extern short ns_bh[3]; + struct sockaddr_ns *sms = &(so_mask.sns); + bzero((char *)sms, sizeof(*sms)); + sms->sns_family = 0; + sms->sns_len = 6; + sms->sns_addr.x_net = *(union ns_net *)ns_bh; + rtm_addrs |= RTA_NETMASK; + } + su->sns.sns_addr = ns_addr(s); + return (!ns_nullhost(su->sns.sns_addr)); + + case AF_OSI: + su->siso.siso_addr = *iso_addr(s); + if (which == RTA_NETMASK || which == RTA_GENMASK) { + register char *cp = (char *)TSEL(&su->siso); + su->siso.siso_nlen = 0; + do {--cp ;} while ((cp > (char *)su) && (*cp == 0)); + su->siso.siso_len = 1 + cp - (char *)su; + } + return (1); + + case AF_LINK: + link_addr(s, &su->sdl); + return (1); + + case AF_CCITT: + ccitt_addr(s, &su->sx25); + return (which == RTA_DST ? x25_makemask() : 1); + + case PF_ROUTE: + su->sa.sa_len = sizeof(*su); + sockaddr(s, &su->sa); + return (1); + + case AF_INET: + default: + break; + } + + if (hpp == NULL) + hpp = &hp; + *hpp = NULL; + if (((val = inet_addr(s)) != -1) && + (which != RTA_DST || forcenet == 0)) { + su->sin.sin_addr.s_addr = val; + if (inet_lnaof(su->sin.sin_addr) != INADDR_ANY) + return (1); + else { + val = ntohl(val); + goto netdone; + } + } + if ((val = inet_network(s)) != -1 || + ((np = getnetbyname(s)) != NULL && (val = np->n_net) != 0)) { +netdone: + if (which == RTA_DST) + inet_makenetandmask(val, &su->sin); + return (0); + } + hp = gethostbyname(s); + if (hp) { + *hpp = hp; + su->sin.sin_family = hp->h_addrtype; + bcopy(hp->h_addr, (char *)&su->sin.sin_addr, hp->h_length); + return (1); + } + (void) fprintf(stderr, "%s: bad value\n", s); + exit(1); +} + +int +x25_makemask() +{ + register char *cp; + + if ((rtm_addrs & RTA_NETMASK) == 0) { + rtm_addrs |= RTA_NETMASK; + for (cp = (char *)&so_mask.sx25.x25_net; + cp < &so_mask.sx25.x25_opts.op_flags; cp++) + *cp = -1; + so_mask.sx25.x25_len = (u_char)&(((sup)0)->sx25.x25_opts); + } + return 0; +} + +short ns_nullh[] = {0,0,0}; +short ns_bh[] = {-1,-1,-1}; + +char * +ns_print(sns) + struct sockaddr_ns *sns; +{ + struct ns_addr work; + union { union ns_net net_e; u_long long_e; } net; + u_short port; + static char mybuf[50], cport[10], chost[25]; + char *host = ""; + register char *p; + register u_char *q; + + work = sns->sns_addr; + port = ntohs(work.x_port); + work.x_port = 0; + net.net_e = work.x_net; + if (ns_nullhost(work) && net.long_e == 0) { + if (!port) + return ("*.*"); + (void) sprintf(mybuf, "*.%XH", port); + return (mybuf); + } + + if (bcmp((char *)ns_bh, (char *)work.x_host.c_host, 6) == 0) + host = "any"; + else if (bcmp((char *)ns_nullh, (char *)work.x_host.c_host, 6) == 0) + host = "*"; + else { + q = work.x_host.c_host; + (void) sprintf(chost, "%02X%02X%02X%02X%02X%02XH", + q[0], q[1], q[2], q[3], q[4], q[5]); + for (p = chost; *p == '0' && p < chost + 12; p++) + /* void */; + host = p; + } + if (port) + (void) sprintf(cport, ".%XH", htons(port)); + else + *cport = 0; + + (void) sprintf(mybuf,"%XH.%s%s", ntohl(net.long_e), host, cport); + return (mybuf); +} + +void +interfaces() +{ + size_t needed; + int mib[6]; + char *buf, *lim, *next; + register struct rt_msghdr *rtm; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_IFLIST; + mib[5] = 0; /* no flags */ + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + quit("route-sysctl-estimate"); + if ((buf = malloc(needed)) == NULL) + quit("malloc"); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + quit("actual retrieval of interface table"); + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + print_rtmsg(rtm, rtm->rtm_msglen); + } +} + +void +monitor() +{ + int n; + char msg[2048]; + + verbose = 1; + if (debugonly) { + interfaces(); + exit(0); + } + for(;;) { + n = read(s, msg, 2048); + (void) printf("got message of size %d\n", n); + print_rtmsg((struct rt_msghdr *)msg, n); + } +} + +struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +int +rtmsg(cmd, flags) + int cmd, flags; +{ + static int seq; + int rlen; + register char *cp = m_rtmsg.m_space; + register int l; + +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) {\ + l = ROUNDUP(u.sa.sa_len); bcopy((char *)&(u), cp, l); cp += l;\ + if (verbose) sodump(&(u),"u");\ + } + + errno = 0; + bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); + if (cmd == 'a') + cmd = RTM_ADD; + else if (cmd == 'c') + cmd = RTM_CHANGE; + else if (cmd == 'g') { + cmd = RTM_GET; + if (so_ifp.sa.sa_family == 0) { + so_ifp.sa.sa_family == AF_LINK; + so_ifp.sa.sa_len == sizeof(struct sockaddr_dl); + rtm_addrs |= RTA_IFP; + } + } else + cmd = RTM_DELETE; +#define rtm m_rtmsg.m_rtm + rtm.rtm_type = cmd; + rtm.rtm_flags = flags; + rtm.rtm_version = RTM_VERSION; + rtm.rtm_seq = ++seq; + rtm.rtm_addrs = rtm_addrs; + rtm.rtm_rmx = rt_metrics; + rtm.rtm_inits = rtm_inits; + + if (rtm_addrs & RTA_NETMASK) + mask_addr(); + NEXTADDR(RTA_DST, so_dst); + NEXTADDR(RTA_GATEWAY, so_gate); + NEXTADDR(RTA_NETMASK, so_mask); + NEXTADDR(RTA_GENMASK, so_genmask); + NEXTADDR(RTA_IFP, so_ifp); + NEXTADDR(RTA_IFA, so_ifa); + rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; + if (verbose) + print_rtmsg(&rtm, l); + if (debugonly) + return (0); + if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { + perror("writing to routing socket"); + return (-1); + } + if (cmd == RTM_GET) { + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); + if (l < 0) + (void) fprintf(stderr, + "route: read from routing socket: %s\n", + strerror(errno)); + else + print_getmsg(&rtm, l); + } +#undef rtm + return (0); +} + +void +mask_addr() +{ + int olen = so_mask.sa.sa_len; + register char *cp1 = olen + (char *)&so_mask, *cp2; + + for (so_mask.sa.sa_len = 0; cp1 > (char *)&so_mask; ) + if (*--cp1 != 0) { + so_mask.sa.sa_len = 1 + cp1 - (char *)&so_mask; + break; + } + if ((rtm_addrs & RTA_DST) == 0) + return; + switch (so_dst.sa.sa_family) { + case AF_NS: + case AF_INET: + case AF_CCITT: + case 0: + return; + case AF_ISO: + olen = MIN(so_dst.siso.siso_nlen, + MAX(so_mask.sa.sa_len - 6, 0)); + break; + } + cp1 = so_mask.sa.sa_len + 1 + (char *)&so_dst; + cp2 = so_dst.sa.sa_len + 1 + (char *)&so_dst; + while (cp2 > cp1) + *--cp2 = 0; + cp2 = so_mask.sa.sa_len + 1 + (char *)&so_mask; + while (cp1 > so_dst.sa.sa_data) + *--cp1 &= *--cp2; + switch (so_dst.sa.sa_family) { + case AF_ISO: + so_dst.siso.siso_nlen = olen; + break; + } +} + +char *msgtypes[] = { + "", + "RTM_ADD: Add Route", + "RTM_DELETE: Delete Route", + "RTM_CHANGE: Change Metrics or flags", + "RTM_GET: Report Metrics", + "RTM_LOSING: Kernel Suspects Partitioning", + "RTM_REDIRECT: Told to use different route", + "RTM_MISS: Lookup failed on this address", + "RTM_LOCK: fix specified metrics", + "RTM_OLDADD: caused by SIOCADDRT", + "RTM_OLDDEL: caused by SIOCDELRT", + "RTM_RESOLVE: Route created by cloning", + "RTM_NEWADDR: address being added to iface", + "RTM_DELADDR: address being removed from iface", + "RTM_IFINFO: iface status change", + 0, +}; + +char metricnames[] = +"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount\1mtu"; +char routeflags[] = +"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT\011CLONING\012XRESOLVE\013LLINFO\014STATIC\017PROTO2\020PROTO1"; +char ifnetflags[] = +"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1\017LINK2\020MULTICAST"; +char addrnames[] = +"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; + +void +print_rtmsg(rtm, msglen) + register struct rt_msghdr *rtm; + int msglen; +{ + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; + + if (verbose == 0) + return; + if (rtm->rtm_version != RTM_VERSION) { + (void) printf("routing message version %d not understood\n", + rtm->rtm_version); + return; + } + (void)printf("%s: len %d, ", msgtypes[rtm->rtm_type], rtm->rtm_msglen); + switch (rtm->rtm_type) { + case RTM_IFINFO: + ifm = (struct if_msghdr *)rtm; + (void) printf("if# %d, flags:", ifm->ifm_index); + bprintf(stdout, ifm->ifm_flags, ifnetflags); + pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + ifam = (struct ifa_msghdr *)rtm; + (void) printf("metric %d, flags:", ifam->ifam_metric); + bprintf(stdout, ifam->ifam_flags, routeflags); + pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs); + break; + default: + (void) printf("pid: %d, seq %d, errno %d, flags:", + rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno); + bprintf(stdout, rtm->rtm_flags, routeflags); + pmsg_common(rtm); + } +} + +void +print_getmsg(rtm, msglen) + register struct rt_msghdr *rtm; + int msglen; +{ + struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL; + struct sockaddr_dl *ifp = NULL; + register struct sockaddr *sa; + register char *cp; + register int i; + + (void) printf(" route to: %s\n", routename(&so_dst)); + if (rtm->rtm_version != RTM_VERSION) { + (void)fprintf(stderr, + "routing message version %d not understood\n", + rtm->rtm_version); + return; + } + if (rtm->rtm_msglen > msglen) { + (void)fprintf(stderr, + "message length mismatch, in packet %d, returned %d\n", + rtm->rtm_msglen, msglen); + } + if (rtm->rtm_errno) { + (void) fprintf(stderr, "RTM_GET: %s (errno %d)\n", + strerror(rtm->rtm_errno), rtm->rtm_errno); + return; + } + cp = ((char *)(rtm + 1)); + if (rtm->rtm_addrs) + for (i = 1; i; i <<= 1) + if (i & rtm->rtm_addrs) { + sa = (struct sockaddr *)cp; + switch (i) { + case RTA_DST: + dst = sa; + break; + case RTA_GATEWAY: + gate = sa; + break; + case RTA_NETMASK: + mask = sa; + break; + case RTA_IFP: + if (sa->sa_family == AF_LINK && + ((struct sockaddr_dl *)sa)->sdl_nlen) + ifp = (struct sockaddr_dl *)sa; + break; + } + ADVANCE(cp, sa); + } + if (dst && mask) + mask->sa_family = dst->sa_family; /* XXX */ + if (dst) + (void)printf("destination: %s\n", routename(dst)); + if (mask) { + int savenflag = nflag; + + nflag = 1; + (void)printf(" mask: %s\n", routename(mask)); + nflag = savenflag; + } + if (gate && rtm->rtm_flags & RTF_GATEWAY) + (void)printf(" gateway: %s\n", routename(gate)); + if (ifp) + (void)printf(" interface: %.*s\n", + ifp->sdl_nlen, ifp->sdl_data); + (void)printf(" flags: "); + bprintf(stdout, rtm->rtm_flags, routeflags); + +#define lock(f) ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ') +#define msec(u) (((u) + 500) / 1000) /* usec to msec */ + + (void) printf("\n%s\n", "\ + recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire"); + printf("%8d%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE)); + printf("%8d%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE)); + printf("%8d%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH)); + printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT)); + printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR)); + printf("%8d%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT)); + printf("%8d%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU)); + if (rtm->rtm_rmx.rmx_expire) + rtm->rtm_rmx.rmx_expire -= time(0); + printf("%8d%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE)); +#undef lock +#undef msec +#define RTA_IGN (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD) + if (verbose) + pmsg_common(rtm); + else if (rtm->rtm_addrs &~ RTA_IGN) { + (void) printf("sockaddrs: "); + bprintf(stdout, rtm->rtm_addrs, addrnames); + putchar('\n'); + } +#undef RTA_IGN +} + +void +pmsg_common(rtm) + register struct rt_msghdr *rtm; +{ + (void) printf("\nlocks: "); + bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames); + (void) printf(" inits: "); + bprintf(stdout, rtm->rtm_inits, metricnames); + pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs); +} + +void +pmsg_addrs(cp, addrs) + char *cp; + int addrs; +{ + register struct sockaddr *sa; + int i; + + if (addrs == 0) + return; + (void) printf("\nsockaddrs: "); + bprintf(stdout, addrs, addrnames); + (void) putchar('\n'); + for (i = 1; i; i <<= 1) + if (i & addrs) { + sa = (struct sockaddr *)cp; + (void) printf(" %s", routename(sa)); + ADVANCE(cp, sa); + } + (void) putchar('\n'); + (void) fflush(stdout); +} + +void +bprintf(fp, b, s) + register FILE *fp; + register int b; + register u_char *s; +{ + register int i; + int gotsome = 0; + + if (b == 0) + return; + while (i = *s++) { + if (b & (1 << (i-1))) { + if (gotsome == 0) + i = '<'; + else + i = ','; + (void) putc(i, fp); + gotsome = 1; + for (; (i = *s) > 32; s++) + (void) putc(i, fp); + } else + while (*s > 32) + s++; + } + if (gotsome) + (void) putc('>', fp); +} + +int +keyword(cp) + char *cp; +{ + register struct keytab *kt = keywords; + + while (kt->kt_cp && strcmp(kt->kt_cp, cp)) + kt++; + return kt->kt_i; +} + +void +sodump(su, which) + register sup su; + char *which; +{ + switch (su->sa.sa_family) { + case AF_LINK: + (void) printf("%s: link %s; ", + which, link_ntoa(&su->sdl)); + break; + case AF_ISO: + (void) printf("%s: iso %s; ", + which, iso_ntoa(&su->siso.siso_addr)); + break; + case AF_INET: + (void) printf("%s: inet %s; ", + which, inet_ntoa(su->sin.sin_addr)); + break; + case AF_NS: + (void) printf("%s: xns %s; ", + which, ns_ntoa(su->sns.sns_addr)); + break; + } + (void) fflush(stdout); +} + +/* States*/ +#define VIRGIN 0 +#define GOTONE 1 +#define GOTTWO 2 +/* Inputs */ +#define DIGIT (4*0) +#define END (4*1) +#define DELIM (4*2) + +void +sockaddr(addr, sa) + register char *addr; + register struct sockaddr *sa; +{ + register char *cp = (char *)sa; + int size = sa->sa_len; + char *cplim = cp + size; + register int byte = 0, state = VIRGIN, new; + + bzero(cp, size); + cp++; + do { + if ((*addr >= '0') && (*addr <= '9')) { + new = *addr - '0'; + } else if ((*addr >= 'a') && (*addr <= 'f')) { + new = *addr - 'a' + 10; + } else if ((*addr >= 'A') && (*addr <= 'F')) { + new = *addr - 'A' + 10; + } else if (*addr == 0) + state |= END; + else + state |= DELIM; + addr++; + switch (state /* | INPUT */) { + case GOTTWO | DIGIT: + *cp++ = byte; /*FALLTHROUGH*/ + case VIRGIN | DIGIT: + state = GOTONE; byte = new; continue; + case GOTONE | DIGIT: + state = GOTTWO; byte = new + (byte << 4); continue; + default: /* | DELIM */ + state = VIRGIN; *cp++ = byte; byte = 0; continue; + case GOTONE | END: + case GOTTWO | END: + *cp++ = byte; /* FALLTHROUGH */ + case VIRGIN | END: + break; + } + break; + } while (cp < cplim); + sa->sa_len = cp - (char *)sa; +} diff --git a/sbin/savecore/Makefile b/sbin/savecore/Makefile new file mode 100644 index 0000000..965c52c --- /dev/null +++ b/sbin/savecore/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.2 (Berkeley) 4/17/94 + +PROG= savecore +SRCS= savecore.c zopen.c +MAN8= savecore.0 +.PATH: ${.CURDIR}/../../usr.bin/compress + +.include <bsd.prog.mk> diff --git a/sbin/savecore/savecore.8 b/sbin/savecore/savecore.8 new file mode 100644 index 0000000..aabee53 --- /dev/null +++ b/sbin/savecore/savecore.8 @@ -0,0 +1,124 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)savecore.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt SAVECORE 8 +.Os BSD 4 +.Sh NAME +.Nm savecore +.Nd "save a core dump of the operating system" +.Sh SYNOPSIS +.Nm savecore +.Fl c +.Nm savecore +.Op Fl fvz +.Op Fl N Ar system +.Ar directory +.Sh DESCRIPTION +.Nm Savecore +copies the currently running kernel and its associated core dump into +.Fa directory , +and enters a reboot message and information about the core dump into +the system log. +.Pp +The options are as follows: +.Bl -tag -width directory +.It Fl c +Clears the dump, so that future invocations of +.Nm savecore +will ignore it. +.It Fl f +Forces a dump to be taken even if the dump doesn't appear correct or there +is insufficient disk space. +.It Fl N +Use +.Ar system +as the kernel instead of the default ``/vmunix''. +.It Fl v +Prints out some additional debugging information. +.It Fl z +Compresses the core dump and kernel (see +.Xr compress 1 ). +.El +.Pp +.Nm Savecore +checks the core dump in various ways to make sure that it is current and +that it corresponds to the currently running system. +If it passes these checks, it saves the core image in +.Ar directory Ns Pa /vmcore.# +and the system in +.Ar directory Ns Pa /vmunix.# +The ``#'' is the number from the first line of the file +.Ar directory Ns Pa /bounds , +and it is incremented and stored back into the file each time +.Nm savecore +successfully runs. +.Pp +.Nm Savecore +also checks the available disk space before attempting to make the copies. +If there is insufficient disk space in the filesystem containing +.Ar directory , +or if the file +.Ar directory Ns Pa /minfree +exists and the number of free kilobytes (for non-superusers) in the +filesystem after the copies were made would be less than the number +in the first line of this file, the copies are not attempted. +.Pp +If +.Nm savecore +successfully copies the kernel and the core dump, the core dump is cleared +so that future invocations of +.Nm savecore +will ignore it. +.Pp +.Nm Savecore +is meant to be called near the end of the initialization file +.Pa /etc/rc +(see +.Xr rc 8 ) . +.Sh FILES +.Bl -tag -width /vmunixxx -compact +.It Pa /vmunix +current +.Tn UNIX +.El +.Sh BUGS +The minfree code does not consider the effect of compression. +.Sh SEE ALSO +.Xr compress 1 , +.Xr syslogd 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.1 . diff --git a/sbin/savecore/savecore.c b/sbin/savecore/savecore.c new file mode 100644 index 0000000..6e34924be --- /dev/null +++ b/sbin/savecore/savecore.c @@ -0,0 +1,649 @@ +/*- + * Copyright (c) 1986, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1986, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)savecore.c 8.3 (Berkeley) 1/2/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/syslog.h> +#include <sys/time.h> + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <nlist.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <tzfile.h> +#include <unistd.h> + +#define ok(number) ((number) - KERNBASE) + +struct nlist current_nl[] = { /* Namelist for currently running system. */ +#define X_DUMPDEV 0 + { "_dumpdev" }, +#define X_DUMPLO 1 + { "_dumplo" }, +#define X_TIME 2 + { "_time" }, +#define X_DUMPSIZE 3 + { "_dumpsize" }, +#define X_VERSION 4 + { "_version" }, +#define X_PANICSTR 5 + { "_panicstr" }, +#define X_DUMPMAG 6 + { "_dumpmag" }, + { "" }, +}; +int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; +int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 }; + +struct nlist dump_nl[] = { /* Name list for dumped system. */ + { "_dumpdev" }, /* Entries MUST be the same as */ + { "_dumplo" }, /* those in current_nl[]. */ + { "_time" }, + { "_dumpsize" }, + { "_version" }, + { "_panicstr" }, + { "_dumpmag" }, + { "" }, +}; + +/* Types match kernel declarations. */ +long dumplo; /* where dump starts on dumpdev */ +int dumpmag; /* magic number in dump */ +int dumpsize; /* amount of memory dumped */ + +char *vmunix; +char *dirname; /* directory to save dumps in */ +char *ddname; /* name of dump device */ +dev_t dumpdev; /* dump device */ +int dumpfd; /* read/write descriptor on block dev */ +time_t now; /* current date */ +char panic_mesg[1024]; +int panicstr; +char vers[1024]; + +int clear, compress, force, verbose; /* flags */ + +void check_kmem __P((void)); +int check_space __P((void)); +void clear_dump __P((void)); +int Create __P((char *, int)); +int dump_exists __P((void)); +char *find_dev __P((dev_t, int)); +int get_crashtime __P((void)); +void kmem_setup __P((void)); +void log __P((int, char *, ...)); +void Lseek __P((int, off_t, int)); +int Open __P((char *, int rw)); +int Read __P((int, void *, int)); +char *rawname __P((char *s)); +void save_core __P((void)); +void usage __P((void)); +void Write __P((int, void *, int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + + openlog("savecore", LOG_PERROR, LOG_DAEMON); + + while ((ch = getopt(argc, argv, "cdfNvz")) != EOF) + switch(ch) { + case 'c': + clear = 1; + break; + case 'd': /* Not documented. */ + case 'v': + verbose = 1; + break; + case 'f': + force = 1; + break; + case 'N': + vmunix = optarg; + break; + case 'z': + compress = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!clear) { + if (argc != 1 && argc != 2) + usage(); + dirname = argv[0]; + } + if (argc == 2) + vmunix = argv[1]; + + (void)time(&now); + kmem_setup(); + + if (clear) { + clear_dump(); + exit(0); + } + + if (!dump_exists() && !force) + exit(1); + + check_kmem(); + + if (panicstr) + syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg); + else + syslog(LOG_ALERT, "reboot"); + + if ((!get_crashtime() || !check_space()) && !force) + exit(1); + + save_core(); + + clear_dump(); + exit(0); +} + +void +kmem_setup() +{ + FILE *fp; + int kmem, i; + char *dump_sys; + + /* + * Some names we need for the currently running system, others for + * the system that was running when the dump was made. The values + * obtained from the current system are used to look for things in + * /dev/kmem that cannot be found in the dump_sys namelist, but are + * presumed to be the same (since the disk partitions are probably + * the same!) + */ + if ((nlist(_PATH_UNIX, current_nl)) == -1) + syslog(LOG_ERR, "%s: nlist: %s", _PATH_UNIX, strerror(errno)); + for (i = 0; cursyms[i] != -1; i++) + if (current_nl[cursyms[i]].n_value == 0) { + syslog(LOG_ERR, "%s: %s not in namelist", + _PATH_UNIX, current_nl[cursyms[i]].n_name); + exit(1); + } + + dump_sys = vmunix ? vmunix : _PATH_UNIX; + if ((nlist(dump_sys, dump_nl)) == -1) + syslog(LOG_ERR, "%s: nlist: %s", dump_sys, strerror(errno)); + for (i = 0; dumpsyms[i] != -1; i++) + if (dump_nl[dumpsyms[i]].n_value == 0) { + syslog(LOG_ERR, "%s: %s not in namelist", + dump_sys, dump_nl[dumpsyms[i]].n_name); + exit(1); + } + + kmem = Open(_PATH_KMEM, O_RDONLY); + Lseek(kmem, (off_t)current_nl[X_DUMPDEV].n_value, L_SET); + (void)Read(kmem, &dumpdev, sizeof(dumpdev)); + if (dumpdev == NODEV) { + syslog(LOG_WARNING, "no core dump (no dumpdev)"); + exit(1); + } + Lseek(kmem, (off_t)current_nl[X_DUMPLO].n_value, L_SET); + (void)Read(kmem, &dumplo, sizeof(dumplo)); + if (verbose) + (void)printf("dumplo = %d (%d * %d)\n", + dumplo, dumplo/DEV_BSIZE, DEV_BSIZE); + Lseek(kmem, (off_t)current_nl[X_DUMPMAG].n_value, L_SET); + (void)Read(kmem, &dumpmag, sizeof(dumpmag)); + dumplo *= DEV_BSIZE; + ddname = find_dev(dumpdev, S_IFBLK); + dumpfd = Open(ddname, O_RDWR); + fp = fdopen(kmem, "r"); + if (fp == NULL) { + syslog(LOG_ERR, "%s: fdopen: %m", _PATH_KMEM); + exit(1); + } + if (vmunix) + return; + (void)fseek(fp, (off_t)current_nl[X_VERSION].n_value, L_SET); + (void)fgets(vers, sizeof(vers), fp); + + /* Don't fclose(fp), we use dumpfd later. */ +} + +void +check_kmem() +{ + register char *cp; + FILE *fp; + char core_vers[1024]; + + fp = fdopen(dumpfd, "r"); + if (fp == NULL) { + syslog(LOG_ERR, "%s: fdopen: %m", ddname); + exit(1); + } + fseek(fp, (off_t)(dumplo + ok(dump_nl[X_VERSION].n_value)), L_SET); + fgets(core_vers, sizeof(core_vers), fp); + if (strcmp(vers, core_vers) && vmunix == 0) + syslog(LOG_WARNING, + "warning: %s version mismatch:\n\t%s\nand\t%s\n", + _PATH_UNIX, vers, core_vers); + (void)fseek(fp, + (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET); + (void)fread(&panicstr, sizeof(panicstr), 1, fp); + if (panicstr) { + (void)fseek(fp, dumplo + ok(panicstr), L_SET); + cp = panic_mesg; + do + *cp = getc(fp); + while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)]); + } + /* Don't fclose(fp), we use dumpfd later. */ +} + +void +clear_dump() +{ + long newdumplo; + + newdumplo = 0; + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); + Write(dumpfd, &newdumplo, sizeof(newdumplo)); +} + +int +dump_exists() +{ + int newdumpmag; + + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); + (void)Read(dumpfd, &newdumpmag, sizeof(newdumpmag)); + if (newdumpmag != dumpmag) { + if (verbose) + syslog(LOG_WARNING, "magic number mismatch (%x != %x)", + newdumpmag, dumpmag); + syslog(LOG_WARNING, "no core dump"); + return (0); + } + return (1); +} + +char buf[1024 * 1024]; + +void +save_core() +{ + register FILE *fp; + register int bounds, ifd, nr, nw, ofd; + char *rawp, path[MAXPATHLEN]; + + /* + * Get the current number and update the bounds file. Do the update + * now, because may fail later and don't want to overwrite anything. + */ + (void)snprintf(path, sizeof(path), "%s/bounds", dirname); + if ((fp = fopen(path, "r")) == NULL) + goto err1; + if (fgets(buf, sizeof(buf), fp) == NULL) { + if (ferror(fp)) +err1: syslog(LOG_WARNING, "%s: %s", path, strerror(errno)); + bounds = 0; + } else + bounds = atoi(buf); + if (fp != NULL) + (void)fclose(fp); + if ((fp = fopen(path, "w")) == NULL) + syslog(LOG_ERR, "%s: %m", path); + else { + (void)fprintf(fp, "%d\n", bounds + 1); + (void)fclose(fp); + } + (void)fclose(fp); + + /* Create the core file. */ + (void)snprintf(path, sizeof(path), "%s/vmcore.%d%s", + dirname, bounds, compress ? ".Z" : ""); + if (compress) { + if ((fp = zopen(path, "w", 0)) == NULL) { + syslog(LOG_ERR, "%s: %s", path, strerror(errno)); + exit(1); + } + } else + ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + /* Open the raw device. */ + rawp = rawname(ddname); + if ((ifd = open(rawp, O_RDONLY)) == -1) { + syslog(LOG_WARNING, "%s: %m; using block device", rawp); + ifd = dumpfd; + } + + /* Read the dump size. */ + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET); + (void)Read(dumpfd, &dumpsize, sizeof(dumpsize)); + + /* Seek to the start of the core. */ + Lseek(ifd, (off_t)dumplo, L_SET); + + /* Copy the core file. */ + dumpsize *= NBPG; + syslog(LOG_NOTICE, "writing %score to %s", + compress ? "compressed " : "", path); + for (; dumpsize > 0; dumpsize -= nr) { + (void)printf("%6dK\r", dumpsize / 1024); + (void)fflush(stdout); + nr = read(ifd, buf, MIN(dumpsize, sizeof(buf))); + if (nr <= 0) { + if (nr == 0) + syslog(LOG_WARNING, + "WARNING: EOF on dump device"); + else + syslog(LOG_ERR, "%s: %m", rawp); + goto err2; + } + if (compress) + nw = fwrite(buf, 1, nr, fp); + else + nw = write(ofd, buf, nr); + if (nw != nr) { + syslog(LOG_ERR, "%s: %s", + path, strerror(nw == 0 ? EIO : errno)); +err2: syslog(LOG_WARNING, + "WARNING: vmcore may be incomplete"); + (void)printf("\n"); + exit(1); + } + } + (void)printf("\n"); + (void)close(ifd); + if (compress) + (void)fclose(fp); + else + (void)close(ofd); + + /* Copy the kernel. */ + ifd = Open(vmunix ? vmunix : _PATH_UNIX, O_RDONLY); + (void)snprintf(path, sizeof(path), "%s/vmunix.%d%s", + dirname, bounds, compress ? ".Z" : ""); + if (compress) { + if ((fp = zopen(path, "w", 0)) == NULL) { + syslog(LOG_ERR, "%s: %s", path, strerror(errno)); + exit(1); + } + } else + ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + syslog(LOG_NOTICE, "writing %skernel to %s", + compress ? "compressed " : "", path); + while ((nr = read(ifd, buf, sizeof(buf))) > 0) { + if (compress) + nw = fwrite(buf, 1, nr, fp); + else + nw = write(ofd, buf, nr); + if (nw != nr) { + syslog(LOG_ERR, "%s: %s", + path, strerror(nw == 0 ? EIO : errno)); + syslog(LOG_WARNING, + "WARNING: vmunix may be incomplete"); + exit(1); + } + } + if (nr < 0) { + syslog(LOG_ERR, "%s: %s", + vmunix ? vmunix : _PATH_UNIX, strerror(errno)); + syslog(LOG_WARNING, + "WARNING: vmunix may be incomplete"); + exit(1); + } + if (compress) + (void)fclose(fp); + else + (void)close(ofd); +} + +char * +find_dev(dev, type) + register dev_t dev; + register int type; +{ + register DIR *dfd; + struct dirent *dir; + struct stat sb; + char *dp, devname[MAXPATHLEN + 1]; + + if ((dfd = opendir(_PATH_DEV)) == NULL) { + syslog(LOG_ERR, "%s: %s", _PATH_DEV, strerror(errno)); + exit(1); + } + (void)strcpy(devname, _PATH_DEV); + while ((dir = readdir(dfd))) { + (void)strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name); + if (lstat(devname, &sb)) { + syslog(LOG_ERR, "%s: %s", devname, strerror(errno)); + continue; + } + if ((sb.st_mode & S_IFMT) != type) + continue; + if (dev == sb.st_rdev) { + closedir(dfd); + if ((dp = strdup(devname)) == NULL) { + syslog(LOG_ERR, "%s", strerror(errno)); + exit(1); + } + return (dp); + } + } + closedir(dfd); + syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev)); + exit(1); +} + +char * +rawname(s) + char *s; +{ + char *sl, name[MAXPATHLEN]; + + if ((sl = rindex(s, '/')) == NULL || sl[1] == '0') { + syslog(LOG_ERR, + "can't make raw dump device name from %s", s); + return (s); + } + (void)snprintf(name, sizeof(name), "%.*s/r%s", sl - s, s, sl + 1); + if ((sl = strdup(name)) == NULL) { + syslog(LOG_ERR, "%s", strerror(errno)); + exit(1); + } + return (sl); +} + +int +get_crashtime() +{ + time_t dumptime; /* Time the dump was taken. */ + + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET); + (void)Read(dumpfd, &dumptime, sizeof(dumptime)); + if (dumptime == 0) { + if (verbose) + syslog(LOG_ERR, "dump time is zero"); + return (0); + } + (void)printf("savecore: system went down at %s", ctime(&dumptime)); +#define LEEWAY (7 * SECSPERDAY) + if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { + (void)printf("dump time is unreasonable\n"); + return (0); + } + return (1); +} + +int +check_space() +{ + register FILE *fp; + char *tvmunix; + off_t minfree, spacefree, vmunixsize, needed; + struct stat st; + struct statfs fsbuf; + char buf[100], path[MAXPATHLEN]; + + tvmunix = vmunix ? vmunix : _PATH_UNIX; + if (stat(tvmunix, &st) < 0) { + syslog(LOG_ERR, "%s: %m", tvmunix); + exit(1); + } + vmunixsize = st.st_blocks * S_BLKSIZE; + if (statfs(dirname, &fsbuf) < 0) { + syslog(LOG_ERR, "%s: %m", dirname); + exit(1); + } + spacefree = (fsbuf.f_bavail * fsbuf.f_bsize) / 1024; + + (void)snprintf(path, sizeof(path), "%s/minfree", dirname); + if ((fp = fopen(path, "r")) == NULL) + minfree = 0; + else { + if (fgets(buf, sizeof(buf), fp) == NULL) + minfree = 0; + else + minfree = atoi(buf); + (void)fclose(fp); + } + + needed = (dumpsize + vmunixsize) / 1024; + if (minfree > 0 && spacefree - needed < minfree) { + syslog(LOG_WARNING, + "no dump, not enough free space on device"); + return (0); + } + if (spacefree - needed < minfree) + syslog(LOG_WARNING, + "dump performed, but free space threshold crossed"); + return (1); +} + +int +Open(name, rw) + char *name; + int rw; +{ + int fd; + + if ((fd = open(name, rw, 0)) < 0) { + syslog(LOG_ERR, "%s: %m", name); + exit(1); + } + return (fd); +} + +int +Read(fd, bp, size) + int fd, size; + void *bp; +{ + int nr; + + nr = read(fd, bp, size); + if (nr != size) { + syslog(LOG_ERR, "read: %m"); + exit(1); + } + return (nr); +} + +void +Lseek(fd, off, flag) + int fd, flag; + off_t off; +{ + off_t ret; + + ret = lseek(fd, off, flag); + if (ret == -1) { + syslog(LOG_ERR, "lseek: %m"); + exit(1); + } +} + +int +Create(file, mode) + char *file; + int mode; +{ + register int fd; + + fd = creat(file, mode); + if (fd < 0) { + syslog(LOG_ERR, "%s: %m", file); + exit(1); + } + return (fd); +} + +void +Write(fd, bp, size) + int fd, size; + void *bp; +{ + int n; + + if ((n = write(fd, bp, size)) < size) { + syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO)); + exit(1); + } +} + +void +usage() +{ + (void)syslog(LOG_ERR, "usage: savecore [-cfvz] [-N system] directory"); + exit(1); +} diff --git a/sbin/scsiformat/Makefile b/sbin/scsiformat/Makefile new file mode 100644 index 0000000..e46ee4c --- /dev/null +++ b/sbin/scsiformat/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 5.3 (Berkeley) 6/5/93 + +PROG= scsiformat +MAN8= scsiformat.0 +CFLAGS+=-I/sys + +.include <bsd.prog.mk> diff --git a/sbin/scsiformat/scsiformat.8 b/sbin/scsiformat/scsiformat.8 new file mode 100644 index 0000000..ff054bd --- /dev/null +++ b/sbin/scsiformat/scsiformat.8 @@ -0,0 +1,82 @@ +.\" Copyright (c) 1993 Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)scsiformat.8 5.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt SCSIFORMAT 8 +.Os BSD 4 +.Sh NAME +.Nm scsiformat +.Nd format SCSI disks and show SCSI parameters +.Sh SYNOPSIS +.Nm scsiformat +.Op Fl r +.Op Fl p Ar page-control +.Ar raw-device-name +.Sh DESCRIPTION +The +.Nm scsiformat +utility can be used to format SCSI disks +on systems that support on-line SCSI formatting +(currently this is limited to the HP300 and SPARC ports). +It will also show the various parameters set in the SCSI controller. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl p +Modify how the SCSI mode sense page query is constructed. +By default, +.Nm scsiformat +asks for the current settings. +The page-control flag is one of: +.sp +.Bl -tag -width XXX -compact +.It Li c +gets current settings. +.It Li d +gets default settings (those provided by the disk's manufacturer). +.It Li s +gets saved settings (those that persist across power cycles). +.It Li v +shows which parameters are variable. +.El +.It Fl r +Don't format, instead display the disk parameters. +.El +.Sh DIAGNOSTICS +These should mostly be self-explanatory. +A copy of the SCSI standard (or a more readable derivative) +may be useful, however. +.Sh HISTORY +The +.Nm scsiformat +utility first appeared in 4.4BSD. diff --git a/sbin/scsiformat/scsiformat.c b/sbin/scsiformat/scsiformat.c new file mode 100644 index 0000000..a185a8e --- /dev/null +++ b/sbin/scsiformat/scsiformat.c @@ -0,0 +1,664 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scsiformat.c 5.5 (Berkeley) 4/2/94 + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)scsiformat.c 5.5 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> + +#include <dev/scsi/scsi.h> +#include <dev/scsi/disk.h> +#include <dev/scsi/disktape.h> +#include <dev/scsi/scsi_ioctl.h> + +#define COMPAT_HPSCSI + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int fd; +char *device; + +void scsi_str __P((char *, char *, int)); +void do_command __P((int, struct scsi_cdb *, void *, int)); +void do_format __P((void)); +void print_capacity __P((void)); +void print_inquiry __P((void)); +void prflags __P((int, const char *)); +u_char *print_mode_page __P((u_char *)); +void print_mode_sense __P((void)); +void usage __P((void)); + +#define N2(c, d) (((c) << 8) | (d)) +#define N3(b, c, d) (((b) << 16) | N2(c, d)) +#define N4(a, b, c, d) (((a) << 24) | N3(b, c, d)) + +int sense_pctl; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + int ch, readonly; + + readonly = 0; + sense_pctl = SCSI_MSENSE_PCTL_CUR; + while ((ch = getopt(argc, argv, "rp:")) != EOF) { + switch(ch) { + case 'r': + readonly = 1; + break; + case 'p': /* mode sense page control */ + switch (*optarg) { + case 'c': + sense_pctl = SCSI_MSENSE_PCTL_CUR; + break; + case 'd': + sense_pctl = SCSI_MSENSE_PCTL_DFLT; + break; + case 's': + sense_pctl = SCSI_MSENSE_PCTL_SAVED; + break; + case 'v': + (void)printf( + "*** note: for variable parameters, 1-bit means ``can write here''\n"); + sense_pctl = SCSI_MSENSE_PCTL_VAR; + break; + } + /* FALLTHROUGH */ + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + device = *argv; + fd = open(device, readonly ? O_RDONLY : O_RDWR, 0); + if (fd < 0) { + (void)fprintf(stderr, + "scsiformat: %s: %s\n", device, strerror(errno)); + exit(1); + } + print_inquiry(); + print_capacity(); + print_mode_sense(); + + if (!readonly) + do_format(); + exit(0); +} + +/* + * Copy a counted string, trimming trailing blanks, and turning the + * result into a C-style string. + */ +void +scsi_str(src, dst, len) + register char *src, *dst; + register int len; +{ + + while (src[len - 1] == ' ') { + if (--len == 0) { + *dst = 0; + return; + } + } + bcopy(src, dst, len); + dst[len] = 0; +} + +void +print_inquiry() +{ + register struct scsi_inq_ansi *si; + int ver; + struct scsi_inquiry inqbuf; + char vendor[10], product[17], rev[5]; + static struct scsi_cdb inq = { + CMD_INQUIRY, 0, 0, 0, sizeof(inqbuf), 0 + }; + + do_command(fd, &inq, &inqbuf, sizeof(inqbuf)); + (void)printf("%s: ", device); + + ver = (inqbuf.si_version >> VER_ANSI_SHIFT) & VER_ANSI_MASK; + if (ver != 1 && ver != 2) { + (void)printf("type 0x%x, qual 0x%x, ver 0x%x (ansi %d)\n", + inqbuf.si_type, inqbuf.si_qual, inqbuf.si_version, ver); + return; + } + si = (struct scsi_inq_ansi *)&inqbuf; + switch (si->si_type & TYPE_TYPE_MASK) { + + case TYPE_DAD: + (void)printf("(disk)"); + break; + + case TYPE_WORM: + (void)printf("(WORM)"); + break; + + case TYPE_ROM: + (void)printf("(CD-ROM)"); + break; + + case TYPE_MO: + (void)printf("(MO-DISK)"); + break; + + case TYPE_JUKEBOX: + (void)printf("(jukebox)"); + break; + + default: + (void)printf("(??)"); + break; + } + scsi_str(si->si_vendor, vendor, sizeof(si->si_vendor)); + scsi_str(si->si_product, product, sizeof(si->si_product)); + scsi_str(si->si_rev, rev, sizeof(si->si_rev)); + (void)printf(" %s %s rev %s:", vendor, product, rev); +} + +void +print_capacity() +{ + struct scsi_rc rc; /* for READ CAPACITY */ + static struct scsi_cdb cap = { CMD_READ_CAPACITY }; + + do_command(fd, &cap, &rc, sizeof(rc)); + (void)printf(" %d blocks of %d bytes each\n", + N4(rc.rc_lbah, rc.rc_lbahm, rc.rc_lbalm, rc.rc_lbal) + 1, + N4(rc.rc_blh, rc.rc_blhm, rc.rc_bllm, rc.rc_bll)); +} + +void +print_mode_sense() +{ + register u_char *cp, *ep; + register struct scsi_ms_bd *bd; + register int n, i, l, len, bdlen; +#ifdef TEN_BYTE_SENSE + struct { + struct scsi_ms10 ms; + u_char p[1023 - sizeof(struct scsi_ms10)]; + } msbuf; + static struct scsi_cdb modesense = { + CMD_MODE_SENSE10, SCSI_MSENSE_DBD, 0, 0, 0, 0, 0, + sizeof(msbuf) >> 8, sizeof (msbuf), 0 + }; + + CDB10(&modesense)->cdb_lbam = sense_pctl | SCSI_MS_PC_ALL; + do_command(fd, &modesense, &msbuf, sizeof(msbuf)); + len = N2(msbuf.ms.ms_lenh, msbuf.ms.ms_lenl); + bdlen = N2(msbuf.ms.ms_bdlh, msbuf.ms.ms_bdll); +#else + struct { + struct scsi_ms6 ms; + u_char p[255 - sizeof(struct scsi_ms6)]; + } msbuf; + static struct scsi_cdb modesense = { + CMD_MODE_SENSE6, 0, 0, 0, sizeof(msbuf), 0 + }; + + CDB6(&modesense)->cdb_lbam = sense_pctl | SCSI_MS_PC_ALL; + do_command(fd, &modesense, &msbuf, sizeof(msbuf)); + len = msbuf.ms.ms_len; + bdlen = msbuf.ms.ms_bdl; +#endif + (void)printf("\n%d bytes of mode sense data. ", len); + (void)printf("medium type 0x%x, %swrite protected\n", + msbuf.ms.ms_mt, msbuf.ms.ms_dsp & SCSI_MS_DSP_WP ? "" : "not "); + if ((n = bdlen) != 0) { + bd = (struct scsi_ms_bd *)msbuf.p; + for (n /= sizeof(*bd); --n >= 0; bd++) { + (void)printf("\tdensity code 0x%x, ", bd->bd_dc); + i = N3(bd->bd_nbh, bd->bd_nbm, bd->bd_nbl); + l = N3(bd->bd_blh, bd->bd_blm, bd->bd_bll); + if (i) + (void)printf("%d blocks of length %d\n", i, l); + else + (void)printf("all blocks of length %d\n", l); + } + } + /* + * Sense header lengths includes the sense header, while mode page + * lengths do not ... let's hear it for consistency! + */ + cp = msbuf.p + bdlen; + ep = msbuf.p + len - sizeof(msbuf.ms); + while (cp < ep) + cp = print_mode_page(cp); +} + +void +prflags(v, cp) + int v; + register const char *cp; +{ + register const char *np; + char f, sep; + + for (sep = '<'; (f = *cp++) != 0; cp = np) { + for (np = cp; *np >= ' ';) + np++; + if ((v & (1 << (f - 1))) == 0) + continue; + printf("%c%.*s", sep, np - cp, cp); + sep = ','; + } + if (sep != '<') + putchar('>'); +} + +static char * +cache_policy(x) + int x; +{ + static char rsvd[30]; + + switch (x) { + + case SCSI_CACHE_DEFAULT: + return ("default"); + + case SCSI_CACHE_KEEPPF: + return ("toss cmd data, save prefetch"); + + case SCSI_CACHE_KEEPCMD: + return ("toss prefetch data, save cmd"); + + default: + (void)sprintf(rsvd, "reserved %d", x); + return (rsvd); + } + /* NOTREACHED */ +} + +u_char * +print_mode_page(cp) + u_char *cp; +{ + register struct scsi_ms_page_hdr *mp; + int len, code, i; + u_char *tp; + const char *s; + + mp = (struct scsi_ms_page_hdr *)cp; + code = mp->mp_psc & SCSI_MS_PC_MASK; + len = mp->mp_len; + (void)printf("\npage type %d%s (%d bytes): ", + code, mp->mp_psc & SCSI_MS_MP_SAVEABLE ? " (saveable)" : "", len); + switch (code) { + + case SCSI_MS_PC_RWERRREC: +#define rw ((struct scsi_page_rwerrrec *)(mp + 1)) + (void)printf("Read/Write Error Recovery parameters.\n"); + (void)printf("\tflags = 0x%x", rw->rw_flags); + prflags(rw->rw_flags, + "\10AWRE\7ARRE\6TB\5RC\4EER\3PER\2DTE\1DCR"); + (void)printf(",\n\t%d read retries, %d correction span bits,\n", + rw->rw_read_retry, rw->rw_corr_span); + (void)printf("\t%d head offsets, %d data strobe offsets%s\n", + rw->rw_hd_off, rw->rw_ds_off, len > 6 ? "," : "."); + if (len <= 6) + break; + (void)printf("\t%d write retries, ", rw->rw_write_retry); + i = N2(rw->rw_rtlh, rw->rw_rtll); + if (i != 0xffff) + (void)printf("%d", i); + else + (void)printf("no"); + (void)printf(" recovery time limit.\n"); + break; +#undef rw + + case SCSI_MS_PC_DR: +#define dr ((struct scsi_page_dr *)(mp + 1)) + (void)printf("Disconnect/Reconnect control.\n"); + (void)printf("\tbuffer full ratio %d, buffer empty ratio %d,\n", + dr->dr_full, dr->dr_empty); + (void)printf("\ttime limits: %d bus inactivity, ", + N2(dr->dr_inacth, dr->dr_inactl)); + (void)printf("%d disconnect, %d connect.\n", + N2(dr->dr_disconh, dr->dr_disconl), + N2(dr->dr_conh, dr->dr_conl)); + (void)printf("\tmaximum burst size %d,\n", + N2(dr->dr_bursth, dr->dr_burstl)); + switch (dr->dr_dtdc & SCSI_DR_DTDC_MASK) { + case SCSI_DR_DTDC_NONE: + s = "never"; + break; + case SCSI_DR_DTDC_NOTDATA: + s = "during data transfer"; + break; + case SCSI_DR_DTDC_RSVD: + s = "???"; + break; + case SCSI_DR_DTDC_NOTD2: + s = "during and after data transfer"; + break; + } + (void)printf("\tsuppress disconnect %s.\n", s); + break; +#undef dr + + case SCSI_MS_PC_FMT: +#define fmt ((struct scsi_page_fmt *)(mp + 1)) + (void)printf("Format parameters.\n"); + (void)printf("\t%d tracks/zone, %d alt.sect./zone, ", + N2(fmt->fmt_tpzh, fmt->fmt_tpzl), + N2(fmt->fmt_aspzh, fmt->fmt_aspzl)); + (void)printf("%d alt.tracks/zone,\n\t%d alt.tracks/vol., ", + N2(fmt->fmt_atpzh, fmt->fmt_atpzl), + N2(fmt->fmt_atpvh, fmt->fmt_atpvl)); + (void)printf("%d sectors/track, %d bytes/phys.sector,\n", + N2(fmt->fmt_spth, fmt->fmt_sptl), + N2(fmt->fmt_dbppsh, fmt->fmt_dbppsl)); + (void)printf("\tinterleave %d, track skew %d, cyl.skew %d,\n", + N2(fmt->fmt_ilh, fmt->fmt_ill), + N2(fmt->fmt_tsfh, fmt->fmt_tsfl), + N2(fmt->fmt_csfh, fmt->fmt_csfl)); + (void)printf("\tdrive flags 0x%x", fmt->fmt_flags); + prflags(fmt->fmt_flags, "\10SSEC\7HSEC\6RMB\5SURF"); + (void)printf(".\n"); + break; +#undef fmt + + case SCSI_MS_PC_RDGEOM: +#define rd ((struct scsi_page_rdgeom *)(mp + 1)) + (void)printf("Disk Geometry parameters.\n"); + (void)printf("\t%d cylinders, %d heads,\n", + N3(rd->rd_ncylh, rd->rd_ncylm, rd->rd_ncyll), + rd->rd_nheads); + (void)printf("\tstart write precompensation at cyl %d,\n", + N3(rd->rd_wpcylh, rd->rd_wpcylm, rd->rd_wpcyll)); + (void)printf("\tstart reduced write current at cyl %d,\n", + N3(rd->rd_rwcylh, rd->rd_rwcylm, rd->rd_rwcyll)); + (void)printf("\tseek step rate %f us, landing zone cyl %d,\n", + N2(rd->rd_steph, rd->rd_stepl) * 0.1, + N3(rd->rd_lcylh, rd->rd_lcylm, rd->rd_lcyll)); + switch (rd->rd_rpl & SCSI_RD_RPL_MASK) { + case SCSI_RD_RPL_NONE: + s = "disabled or unsupported"; + break; + case SCSI_RD_RPL_SLAVE: + s = "slave"; + break; + case SCSI_RD_RPL_MASTER: + s = "master"; + break; + case SCSI_RD_RPL_MCONTROL: + s = "master control"; + break; + } + (void)printf("\trotational synch %s, offset %d/256%s\n", + s, rd->rd_roff, len > 18 ? "," : "."); + if (len > 18) + (void)printf("\trotation %d rpm.\n", + N2(rd->rd_rpmh, rd->rd_rpml)); + break; +#undef rd + + case SCSI_MS_PC_VERRREC: +#define v ((struct scsi_page_verrrec *)(mp + 1)) + (void)printf("Verify Error Recovery parameters.\n"); + (void)printf("\tflags = 0x%x", v->v_flags); + prflags(v->v_flags, "\4EER\3PER\2DTE\1DCR"); + (void)printf(",\n\t%d verify retries, %d %s span bits,\n\t", + v->v_verify_retry, v->v_corr_span, "correction"); + (void)printf("%d recovery time limit.\n", + N2(v->v_rtlh, v->v_rtll)); + break; +#undef v + + case SCSI_MS_PC_CACHE: +#define cache ((struct scsi_page_cache *)(mp + 1)) + (void)printf("Caching Page.\n"); + (void)printf("\tflags = 0x%x", cache->cache_flags); + prflags(cache->cache_flags, "\3WCE\2MF\1RCD"); + (void)printf( + ",\n\tread retention = %s, write retention = %s,\n", + cache_policy(SCSI_CACHE_RDPOLICY(cache->cache_reten)), + cache_policy(SCSI_CACHE_WRPOLICY(cache->cache_reten))); + (void)printf("\tdisable prefetch transfer length = %d,\n", + N2(cache->cache_dptlh, cache->cache_dptll)); + (void)printf("\tmin prefetch = %d, max prefetch = %d, ", + N2(cache->cache_minpfh, cache->cache_minpfl), + N2(cache->cache_maxpfh, cache->cache_maxpfl)); + (void)printf("max prefetch ceiling = %d.\n", + N2(cache->cache_mpch, cache->cache_mpcl)); + break; +#undef cache + + case SCSI_MS_PC_CTLMODE: +#define cm ((struct scsi_page_ctlmode *)(mp + 1)) + (void)printf("Control Mode Page.\n"); + (void)printf("\t%s report log-activity error conditions,\n", + cm->cm_rlec & SCSI_CM_RLEC ? "do" : "do not"); + (void)printf("\tqueue algorithm modifier = %d, flags = 0x%x", + SCSI_CM_QMOD(cm->cm_qctl), + cm->cm_qctl & (SCSI_CM_QERR|SCSI_CM_DQUE)); + prflags(cm->cm_qctl, "\2QERR\1DQUE"); + (void)printf(",\n\tECA/AEN flags = 0x%x", cm->cm_ecaaen); + prflags(cm->cm_ecaaen, "\10ECA\3RAENP\2UUAENP\1EAENP"); + (void)printf(", AEN holdoff period = %d ms.\n", + N2(cm->cm_aenholdh, cm->cm_aenholdl)); + break; +#undef cm + + /* + * Vendor Unique, but what the heck. + */ + case SCSI_MS_PC_CDCCACHECTL: +#define ccm ((struct scsi_page_CDCcachectlmode *)(mp + 1)) + (void)printf("CDC-specific Cache Control Mode Page.\n"); + (void)printf("\tflags = 0x%x", ccm->ccm_flags); + prflags(ccm->ccm_flags, "\7WIE\5ENABLE"); + (void)printf(", table size = %d, prefetch threshold = %d\n", + SCSI_CDC_CCM_TBLSZ(ccm->ccm_flags), + ccm->ccm_pfthresh); + (void)printf("\tmaximum %s = %d, maximum %s = %d,\n", + "threshold", ccm->ccm_maxthresh, + "prefetch multiplier", ccm->ccm_maxpfmult); + (void)printf("\tminimum %s = %d, minimum %s = %d.\n", + "threshold", ccm->ccm_minthresh, + "prefetch multiplier", ccm->ccm_minpfmult); + break; +#undef ccm + + default: + (void)printf("Unknown page type."); + for (tp = cp + sizeof(*mp), i = 0; i < len; ++i) { + if ((i & 7) == 0) + (void)printf("\n\t%2d: ", i); + (void)printf(" %02x", *tp++); + } + (void)printf(".\n"); + break; + } + return (cp + sizeof(*mp) + len); +} + +void +pr_sense(fd) + int fd; +{ + static struct scsi_fmt_sense s; + register struct scsi_sense *sn; + + if (ioctl(fd, SDIOCSENSE, &s) < 0) + (void)fprintf(stderr, + "scsiformat: SDIOCSENSE: %s\n", strerror(errno)); + + (void)printf("scsi status 0x%x", s.status); + if (s.status & STS_CHECKCOND) { + sn = (struct scsi_sense *)s.sense; + + (void)printf(" sense class %d, code %d", + SENSE_ECLASS(sn), SENSE_ECODE(sn)); + if (SENSE_ISXSENSE(sn)) { + (void)printf(", key %d", XSENSE_KEY(sn)); + if (XSENSE_IVALID(sn)) + (void)printf(", blk %d", XSENSE_INFO(sn)); + } + } + (void)printf("\n"); +} + +void +do_format() +{ + struct { + struct scsi_ms6 ms; /* mode select header */ + struct scsi_ms_bd bd; /* block descriptor */ + struct scsi_ms_page_hdr mp; /* ctl mode page hdr */ + struct scsi_page_ctlmode cm; /* ctl mode page */ + u_char pad[4]; /* ??? */ + } msel; + u_char fmtbuf[128]; + static struct scsi_cdb modeselect = { + CMD_MODE_SELECT6, + SCSI_MSEL_SCSI2_DATA | SCSI_MSEL_SAVEPAGES, 0, 0, + sizeof(msel), 0 + }; + static struct scsi_cdb format = { CMD_FORMAT_UNIT }; + + /* want mostly 0s; set them all zero here */ + bzero(&msel, sizeof(msel)); + + /* one block descriptor */ + msel.ms.ms_bdl = sizeof(struct scsi_ms_bd); + + /* block length = 512 bytes */ + msel.bd.bd_blm = 512 / 256; + msel.bd.bd_bll = 512 % 256; + + /* + * In the following, the mystery pad region is copied from + * the original driver. I have no idea what it is for. + * (Anyone got SCSI-2 documents?) + */ + + /* mode page parameters: report log-activity exception conditions */ + msel.mp.mp_psc = SCSI_MS_PC_CTLMODE; + msel.mp.mp_len = sizeof(msel.cm) + sizeof(msel.pad); + msel.cm.cm_rlec = SCSI_CM_RLEC; + + do_command(fd, &modeselect, &msel, sizeof(msel)); + + bzero(fmtbuf, sizeof(fmtbuf)); + do_command(fd, &format, fmtbuf, sizeof(fmtbuf)); +} + +void +do_command(fd, cdb, buf, len) + int fd; + struct scsi_cdb *cdb; + void *buf; + int len; +{ + static int on = 1, off = 0; + int user, ret; + + bzero(buf, len); + if (ioctl(fd, SDIOCSFORMAT, &on) < 0) { + (void)fprintf(stderr, + "scsiformat: SDIOCSFORMAT (on): %s\n", strerror(errno)); + if (ioctl(fd, SDIOCGFORMAT, &user) == 0 && user != 0) + (void)fprintf(stderr, "scsiformat: pid %d has it\n", + user); + return; + } + ret = ioctl(fd, SDIOCSCSICOMMAND, cdb); +#ifdef COMPAT_HPSCSI + if (ret < 0) { + static const char scsicmdlen[8] = { 6, 10, 0, 0, 0, 12, 0, 0 }; +#define SCSICMDLEN(cmd) scsicmdlen[(cmd) >> 5] + struct scsi_fmt_cdb { + int len; + u_char cdb[28]; + } sc; +#define OSDIOCSCSICOMMAND _IOW('S', 0x3, struct scsi_fmt_cdb) + + sc.len = SCSICMDLEN(cdb->cdb_bytes[0]); + bcopy(cdb->cdb_bytes, sc.cdb, sc.len); + ret = ioctl(fd, OSDIOCSCSICOMMAND, &sc); + } +#endif + if (ret < 0) + (void)fprintf(stderr, + "scsiformat: SDIOCSCSICOMMAND: %s\n", strerror(errno)); + else if (read(fd, buf, len) < 0) { + (void)fprintf(stderr, + "scsiformat: read: %s\n", strerror(errno)); + pr_sense(fd); + } + + if (ioctl(fd, SDIOCSFORMAT, &off) < 0) + (void)fprintf(stderr, + "scsiformat: SDIOCSFORMAT (off): %s\n", strerror(errno)); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: scsiformat [-r] [-p c|d|s|v] device\n"); + exit(1); +} diff --git a/sbin/shutdown/Makefile b/sbin/shutdown/Makefile new file mode 100644 index 0000000..19122f2 --- /dev/null +++ b/sbin/shutdown/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= shutdown +MAN8= shutdown.0 +BINOWN= root +BINGRP= operator +BINMODE=4550 + +.include <bsd.prog.mk> diff --git a/sbin/shutdown/pathnames.h b/sbin/shutdown/pathnames.h new file mode 100644 index 0000000..9d05838 --- /dev/null +++ b/sbin/shutdown/pathnames.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + */ + +#include <paths.h> + +#define _PATH_FASTBOOT "/fastboot" +#define _PATH_HALT "/sbin/halt" +#define _PATH_REBOOT "/sbin/reboot" +#define _PATH_WALL "/usr/bin/wall" diff --git a/sbin/shutdown/shutdown.8 b/sbin/shutdown/shutdown.8 new file mode 100644 index 0000000..d582569 --- /dev/null +++ b/sbin/shutdown/shutdown.8 @@ -0,0 +1,162 @@ +.\" Copyright (c) 1988, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)shutdown.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt SHUTDOWN 8 +.Os BSD 4 +.Sh NAME +.Nm shutdown +.Nd "close down the system at a given time" +.Sh SYNOPSIS +.Nm shutdown +.Op Fl +.Op Fl fhkrn +.Ar time +.Op Ar warning-message ... +.Sh DESCRIPTION +.Nm Shutdown +provides an automated shutdown procedure for super-users +to nicely notify users when the system is shutting down, +saving them from system administrators, hackers, and gurus, who +would otherwise not bother with such niceties. +.Pp +Available friendlinesses: +.Bl -tag -width time +.It Fl f +.Nm Shutdown +arranges, in the manner of +.Xr fastboot 8 , +for the file systems +.Em not to be +checked on reboot. +.It Fl h +The system is halted at the specified +.Ar time +when +.Nm shutdown +execs +.Xr halt 8 . +.It Fl k +Kick every body off. +The +.Fl k +option +does not actually halt the system, but leaves the +system multi-user with logins disabled (for all but super-user). +.It Fl n +Prevent the normal +.Xr sync 2 +before stopping. +.It Fl r +.Nm Shutdown +execs +.Xr reboot 8 +at the specified +.Ar time . +.It Ar time +.Ar Time +is the time at which +.Nm shutdown +will bring the system down and +may be the word +.Ar now +(indicating an immediate shutdown) or +specify a future time in one of two formats: +.Ar +number , +or +.Ar yymmddhhmm , +where the year, month, and day may be defaulted +to the current system values. The first form brings the system down in +.Ar number +minutes and the second at the absolute time specified. +.It Ar warning-message +Any other arguments comprise the warning message that is broadcast +to users currently logged into the system. +.It Fl +If +.Ql Fl +is supplied as an option, the warning message is read from the standard +input. +.El +.Pp +At intervals, becoming more frequent as apocalypse approaches +and starting at ten hours before shutdown, warning messages are displayed +on the terminals of all users logged in. Five minutes before +shutdown, or immediately if shutdown is in less than 5 minutes, +logins are disabled by creating +.Pa /etc/nologin +and copying the +warning message there. If this file exists when a user attempts to +log in, +.Xr login 1 +prints its contents and exits. The file is +removed just before +.Nm shutdown +exits. +.Pp +At shutdown time a message is written in the system log, containing the +time of shutdown, who initiated the shutdown and the reason. +A terminate +signal is then sent to +.Xr init +to bring the system down to single-user state (depending on above +options). +The time of the shutdown and the warning message +are placed in +.Pa /etc/nologin +and should be used to +inform the users about when the system will be back up +and why it is going down (or anything else). +.Sh FILES +.Bl -tag -width /etc/nologin -compact +.It Pa /etc/nologin +tells login not to let anyone log in +.It Pa /fastboot +tells +.Xr rc 8 +not to run fsck when rebooting +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr wall 1 , +.Xr fastboot 8 , +.Xr halt 8 , +.Xr reboot 8 +.Sh BACKWARD COMPATIBILITY +The hours and minutes in the second time format may be separated by +a colon (``:'') for backward compatibility. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.0 . diff --git a/sbin/shutdown/shutdown.c b/sbin/shutdown/shutdown.c new file mode 100644 index 0000000..c63ba65 --- /dev/null +++ b/sbin/shutdown/shutdown.c @@ -0,0 +1,492 @@ +/* + * Copyright (c) 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1988, 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)shutdown.c 8.2 (Berkeley) 2/16/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/syslog.h> + +#include <ctype.h> +#include <fcntl.h> +#include <pwd.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <tzfile.h> +#include <unistd.h> + +#include "pathnames.h" + +#ifdef DEBUG +#undef _PATH_NOLOGIN +#define _PATH_NOLOGIN "./nologin" +#undef _PATH_FASTBOOT +#define _PATH_FASTBOOT "./fastboot" +#endif + +#define H *60*60 +#define M *60 +#define S *1 +#define NOLOG_TIME 5*60 +struct interval { + int timeleft, timetowait; +} tlist[] = { + 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, + 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, + 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, + 0, 0, +}; +#undef H +#undef M +#undef S + +static time_t offset, shuttime; +static int dofast, dohalt, doreboot, killflg, mbuflen; +static char *nosync, *whom, mbuf[BUFSIZ]; + +void badtime __P((void)); +void die_you_gravy_sucking_pig_dog __P((void)); +void doitfast __P((void)); +void finish __P((int)); +void getoffset __P((char *)); +void loop __P((void)); +void nolog __P((void)); +void timeout __P((int)); +void timewarn __P((int)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + register char *p, *endp; + struct passwd *pw; + int arglen, ch, len, readstdin; + +#ifndef DEBUG + if (geteuid()) { + (void)fprintf(stderr, "shutdown: NOT super-user\n"); + exit(1); + } +#endif + nosync = NULL; + readstdin = 0; + while ((ch = getopt(argc, argv, "-fhknr")) != EOF) + switch (ch) { + case '-': + readstdin = 1; + break; + case 'f': + dofast = 1; + break; + case 'h': + dohalt = 1; + break; + case 'k': + killflg = 1; + break; + case 'n': + nosync = "-n"; + break; + case 'r': + doreboot = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + if (dofast && nosync) { + (void)fprintf(stderr, + "shutdown: incompatible switches -f and -n.\n"); + usage(); + } + if (doreboot && dohalt) { + (void)fprintf(stderr, + "shutdown: incompatible switches -h and -r.\n"); + usage(); + } + getoffset(*argv++); + + if (*argv) { + for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { + arglen = strlen(*argv); + if ((len -= arglen) <= 2) + break; + if (p != mbuf) + *p++ = ' '; + bcopy(*argv, p, arglen); + p += arglen; + } + *p = '\n'; + *++p = '\0'; + } + + if (readstdin) { + p = mbuf; + endp = mbuf + sizeof(mbuf) - 2; + for (;;) { + if (!fgets(p, endp - p + 1, stdin)) + break; + for (; *p && p < endp; ++p); + if (p == endp) { + *p = '\n'; + *++p = '\0'; + break; + } + } + } + mbuflen = strlen(mbuf); + + if (offset) + (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); + else + (void)printf("Shutdown NOW!\n"); + + if (!(whom = getlogin())) + whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; + +#ifdef DEBUG + (void)putc('\n', stdout); +#else + (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); + { + int forkpid; + + forkpid = fork(); + if (forkpid == -1) { + perror("shutdown: fork"); + exit(1); + } + if (forkpid) { + (void)printf("shutdown: [pid %d]\n", forkpid); + exit(0); + } + } +#endif + openlog("shutdown", LOG_CONS, LOG_AUTH); + loop(); + /* NOTREACHED */ +} + +void +loop() +{ + struct interval *tp; + u_int sltime; + int logged; + + if (offset <= NOLOG_TIME) { + logged = 1; + nolog(); + } + else + logged = 0; + tp = tlist; + if (tp->timeleft < offset) + (void)sleep((u_int)(offset - tp->timeleft)); + else { + while (offset < tp->timeleft) + ++tp; + /* + * Warn now, if going to sleep more than a fifth of + * the next wait time. + */ + if (sltime = offset - tp->timeleft) { + if (sltime > tp->timetowait / 5) + timewarn(offset); + (void)sleep(sltime); + } + } + for (;; ++tp) { + timewarn(tp->timeleft); + if (!logged && tp->timeleft <= NOLOG_TIME) { + logged = 1; + nolog(); + } + (void)sleep((u_int)tp->timetowait); + if (!tp->timeleft) + break; + } + die_you_gravy_sucking_pig_dog(); +} + +static jmp_buf alarmbuf; + +void +timewarn(timeleft) + int timeleft; +{ + static int first; + static char hostname[MAXHOSTNAMELEN + 1]; + FILE *pf; + char wcmd[MAXPATHLEN + 4]; + + if (!first++) + (void)gethostname(hostname, sizeof(hostname)); + + /* undoc -n option to wall suppresses normal wall banner */ + (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); + if (!(pf = popen(wcmd, "w"))) { + syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); + return; + } + + (void)fprintf(pf, + "\007*** %sSystem shutdown message from %s@%s ***\007\n", + timeleft ? "": "FINAL ", whom, hostname); + + if (timeleft > 10*60) + (void)fprintf(pf, "System going down at %5.5s\n\n", + ctime(&shuttime) + 11); + else if (timeleft > 59) + (void)fprintf(pf, "System going down in %d minute%s\n\n", + timeleft / 60, (timeleft > 60) ? "s" : ""); + else if (timeleft) + (void)fprintf(pf, "System going down in 30 seconds\n\n"); + else + (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); + + if (mbuflen) + (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); + + /* + * play some games, just in case wall doesn't come back + * probably unecessary, given that wall is careful. + */ + if (!setjmp(alarmbuf)) { + (void)signal(SIGALRM, timeout); + (void)alarm((u_int)30); + (void)pclose(pf); + (void)alarm((u_int)0); + (void)signal(SIGALRM, SIG_DFL); + } +} + +void +timeout(signo) + int signo; +{ + longjmp(alarmbuf, 1); +} + +void +die_you_gravy_sucking_pig_dog() +{ + + syslog(LOG_NOTICE, "%s by %s: %s", + doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); + (void)sleep(2); + + (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); + if (killflg) { + (void)printf("\rbut you'll have to do it yourself\r\n"); + finish(0); + } + if (dofast) + doitfast(); +#ifdef DEBUG + if (doreboot) + (void)printf("reboot"); + else if (dohalt) + (void)printf("halt"); + if (nosync) + (void)printf(" no sync"); + if (dofast) + (void)printf(" no fsck"); + (void)printf("\nkill -HUP 1\n"); +#else + if (doreboot) { + execle(_PATH_REBOOT, "reboot", "-l", nosync, 0); + syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); + perror("shutdown"); + } + else if (dohalt) { + execle(_PATH_HALT, "halt", "-l", nosync, 0); + syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); + perror("shutdown"); + } + (void)kill(1, SIGTERM); /* to single user */ +#endif + finish(0); +} + +#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; + +void +getoffset(timearg) + register char *timearg; +{ + register struct tm *lt; + register char *p; + time_t now; + + if (!strcasecmp(timearg, "now")) { /* now */ + offset = 0; + return; + } + + (void)time(&now); + if (*timearg == '+') { /* +minutes */ + if (!isdigit(*++timearg)) + badtime(); + offset = atoi(timearg) * 60; + shuttime = now + offset; + return; + } + + /* handle hh:mm by getting rid of the colon */ + for (p = timearg; *p; ++p) + if (!isascii(*p) || !isdigit(*p)) + if (*p == ':' && strlen(p) == 3) { + p[0] = p[1]; + p[1] = p[2]; + p[2] = '\0'; + } + else + badtime(); + + unsetenv("TZ"); /* OUR timezone */ + lt = localtime(&now); /* current time val */ + + switch(strlen(timearg)) { + case 10: + lt->tm_year = ATOI2(timearg); + /* FALLTHROUGH */ + case 8: + lt->tm_mon = ATOI2(timearg); + if (--lt->tm_mon < 0 || lt->tm_mon > 11) + badtime(); + /* FALLTHROUGH */ + case 6: + lt->tm_mday = ATOI2(timearg); + if (lt->tm_mday < 1 || lt->tm_mday > 31) + badtime(); + /* FALLTHROUGH */ + case 4: + lt->tm_hour = ATOI2(timearg); + if (lt->tm_hour < 0 || lt->tm_hour > 23) + badtime(); + lt->tm_min = ATOI2(timearg); + if (lt->tm_min < 0 || lt->tm_min > 59) + badtime(); + lt->tm_sec = 0; + if ((shuttime = mktime(lt)) == -1) + badtime(); + if ((offset = shuttime - now) < 0) { + (void)fprintf(stderr, + "shutdown: that time is already past.\n"); + exit(1); + } + break; + default: + badtime(); + } +} + +#define FSMSG "fastboot file for fsck\n" +void +doitfast() +{ + int fastfd; + + if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, + 0664)) >= 0) { + (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); + (void)close(fastfd); + } +} + +#define NOMSG "\n\nNO LOGINS: System going down at " +void +nolog() +{ + int logfd; + char *ct; + + (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ + (void)signal(SIGINT, finish); + (void)signal(SIGHUP, finish); + (void)signal(SIGQUIT, finish); + (void)signal(SIGTERM, finish); + if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, + 0664)) >= 0) { + (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); + ct = ctime(&shuttime); + (void)write(logfd, ct + 11, 5); + (void)write(logfd, "\n\n", 2); + (void)write(logfd, mbuf, strlen(mbuf)); + (void)close(logfd); + } +} + +void +finish(signo) + int signo; +{ + (void)unlink(_PATH_NOLOGIN); + exit(0); +} + +void +badtime() +{ + (void)fprintf(stderr, "shutdown: bad time format.\n"); + exit(1); +} + +void +usage() +{ + fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n"); + exit(1); +} diff --git a/sbin/slattach/Makefile b/sbin/slattach/Makefile new file mode 100644 index 0000000..0a1e759 --- /dev/null +++ b/sbin/slattach/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= slattach +MAN8= slattach.0 +MLINKS= slattach.8 slip.8 + +.include <bsd.prog.mk> diff --git a/sbin/slattach/slattach.8 b/sbin/slattach/slattach.8 new file mode 100644 index 0000000..217c481 --- /dev/null +++ b/sbin/slattach/slattach.8 @@ -0,0 +1,90 @@ +.\" Copyright (c) 1986, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)slattach.8 8.2 (Berkeley) 4/1/94 +.\" +.Dd April 1, 1994 +.Dt SLATTACH 8 +.Os BSD 4.3 +.Sh NAME +.Nm slattach +.Nd attach serial lines as network interfaces +.Sh SYNOPSIS +.Nm Slattach +.Ar ttyname Op Ar baudrate +.Sh DESCRIPTION +.Nm Slattach +is used to assign a tty line to a network interface, +and to define the network source and destination addresses. +The following operands are supported by +.Nm slattach : +.Bl -tag -width Ar +.It Ar ttyname +Specifies the name of the tty device. +.Ar Ttyname +should be a string of the form +.Ql ttyXX , +or +.Ql /dev/ttyXX . +.It Ar baudrate +Specifies the speed of the connection. If not specified, the +default of 9600 is used. +.El +.Pp +Only the super-user may attach a network interface. +.Pp +To detach the interface, use +.Dq Li ifconfig interface-name down +after killing off the +.Nm slattach +process. +.Ar Interface-name +is the name that is shown by +.Xr netstat 1 +.Sh EXAMPLES +.Bd -literal -offset indent -compact +slattach ttyh8 +slattach /dev/tty01 4800 +.Ed +.Sh DIAGNOSTICS +Messages indicating the specified interface does not exit, the +requested address is unknown, the user is not privileged and +tried to alter an interface's configuration. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr netintro 4 , +.Xr ifconfig 8 , +.Xr rc 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/sbin/slattach/slattach.c b/sbin/slattach/slattach.c new file mode 100644 index 0000000..ac81d33 --- /dev/null +++ b/sbin/slattach/slattach.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rick Adams. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)slattach.c 8.2 (Berkeley) 1/7/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sgtty.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/if.h> +#include <netdb.h> +#include <fcntl.h> +#include <stdio.h> +#include <paths.h> + +#define DEFAULT_BAUD 9600 +int slipdisc = SLIPDISC; + +char devname[32]; +char hostname[MAXHOSTNAMELEN]; + +main(argc, argv) + int argc; + char *argv[]; +{ + register int fd; + register char *dev = argv[1]; + struct sgttyb sgtty; + int speed; + + if (argc < 2 || argc > 3) { + fprintf(stderr, "usage: %s ttyname [baudrate]\n", argv[0]); + exit(1); + } + speed = argc == 3 ? findspeed(atoi(argv[2])) : findspeed(DEFAULT_BAUD); + if (speed == 0) { + fprintf(stderr, "unknown speed %s", argv[2]); + exit(1); + } + if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) { + (void)snprintf(devname, sizeof(devname), + "%s%s", _PATH_DEV, dev); + dev = devname; + } + if ((fd = open(dev, O_RDWR | O_NDELAY)) < 0) { + perror(dev); + exit(1); + } + sgtty.sg_flags = RAW | ANYP; + sgtty.sg_ispeed = sgtty.sg_ospeed = speed; + if (ioctl(fd, TIOCSETP, &sgtty) < 0) { + perror("ioctl(TIOCSETP)"); + exit(1); + } + if (ioctl(fd, TIOCSETD, &slipdisc) < 0) { + perror("ioctl(TIOCSETD)"); + exit(1); + } + + if (fork() > 0) + exit(0); + for (;;) + sigpause(0L); +} + +struct sg_spds { + int sp_val, sp_name; +} spds[] = { +#ifdef B50 + { 50, B50 }, +#endif +#ifdef B75 + { 75, B75 }, +#endif +#ifdef B110 + { 110, B110 }, +#endif +#ifdef B150 + { 150, B150 }, +#endif +#ifdef B200 + { 200, B200 }, +#endif +#ifdef B300 + { 300, B300 }, +#endif +#ifdef B600 + { 600, B600 }, +#endif +#ifdef B1200 + { 1200, B1200 }, +#endif +#ifdef B1800 + { 1800, B1800 }, +#endif +#ifdef B2000 + { 2000, B2000 }, +#endif +#ifdef B2400 + { 2400, B2400 }, +#endif +#ifdef B3600 + { 3600, B3600 }, +#endif +#ifdef B4800 + { 4800, B4800 }, +#endif +#ifdef B7200 + { 7200, B7200 }, +#endif +#ifdef B9600 + { 9600, B9600 }, +#endif +#ifdef EXTA + { 19200, EXTA }, +#endif +#ifdef EXTB + { 38400, EXTB }, +#endif + { 0, 0 } +}; + +findspeed(speed) + register int speed; +{ + register struct sg_spds *sp; + + sp = spds; + while (sp->sp_val && sp->sp_val != speed) + sp++; + return (sp->sp_name); +} diff --git a/sbin/startslip/Makefile b/sbin/startslip/Makefile new file mode 100644 index 0000000..14c72bc --- /dev/null +++ b/sbin/startslip/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= startslip +MAN8= startslip.0 + +.include <bsd.prog.mk> diff --git a/sbin/startslip/startslip.1 b/sbin/startslip/startslip.1 new file mode 100644 index 0000000..f12feab --- /dev/null +++ b/sbin/startslip/startslip.1 @@ -0,0 +1,105 @@ +.\" Copyright (c) 1990, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)startslip.1 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt STARTSLIP 1 +.Os BSD 4.4 +.Sh NAME +.Nm startslip +.Nd dial up and login to a slip server +.Sh SYNOPSIS +.Nm startslip +.Op Fl d +.Op Fl s Ar string +.Op Fl A Ar annexname +.Op Fl F Ar flowcontrol +.Ar device user passwd +.Sh DESCRIPTION +.Nm Startslip +opens the specified +.Ar device . +.Pp +Once carrier is asserted +.Nm startslip +attempts to login as the specified +.Ar user +with the given +.Ar password . +If successful, it puts the device into the slip line discipline. +If carrier drops and a +.Dv SIGHUP +is sent to +.Nm startslip , +it closes the device and attempts to repeat the dialup and login sequence. +.Pp +Available options: +.Bl -tag -width Ar +.It Fl d +.Nm Startslip +prints out debugging information about what it is trying to do. +.It Fl s Ar string +The optional +.Ar string +is written to +.Ar device . +For a dialup modem, +the string is used to specify a dial sequence. +.It Fl A Ar annexname +.Nm Startslip +assumes it is connecting to a Xylogics Annex box and engages in an +appropriate dialog using the +.Ar user +and +.Ar passwd +arguments. +The +.Ar annexname +argument is a string that is used to match against the Annex prompt +to determine when a connection has been established. +.It Fl F Ar flowcontrol +Determines the type of flow control used on +.Ar device . +Choices for +.Ar flowcontrol +are +``none'' for no flow control (the default), +``hw'' for hardware RTS/CTS flow control and +``sw'' for software XON/XOFF flow control. +.El +.Sh SEE ALSO +.Xr sliplogin 8 +.Sh HISTORY +The +.Nm startslip +appeared in +.Bx 4.4 . diff --git a/sbin/startslip/startslip.c b/sbin/startslip/startslip.c new file mode 100644 index 0000000..d68b247 --- /dev/null +++ b/sbin/startslip/startslip.c @@ -0,0 +1,452 @@ + +/*- + * Copyright (c) 1990, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1990, 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)startslip.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#if BSD >= 199006 +#define POSIX +#endif +#ifdef POSIX +#include <sys/termios.h> +#include <sys/ioctl.h> +#else +#include <sgtty.h> +#endif +#include <sys/socket.h> +#include <sys/syslog.h> +#include <netinet/in.h> +#include <net/if.h> +#include <net/if_slvar.h> +#include <netdb.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <signal.h> + +#define DEFAULT_BAUD B9600 +int speed = DEFAULT_BAUD; +#define FC_NONE 0 /* flow control: none */ +#define FC_SW 1 /* flow control: software (XON/XOFF) */ +#define FC_HW 2 /* flow control: hardware (RTS/CTS) */ +int flowcontrol = FC_NONE; +char *annex; +int hup; +int logged_in; +int wait_time = 60; /* then back off */ +#define MAXTRIES 6 /* w/60 sec and doubling, takes an hour */ +#define PIDFILE "/var/run/startslip.pid" + +#ifdef DEBUG +int debug = 1; +#undef LOG_ERR +#undef LOG_INFO +#define syslog fprintf +#define LOG_ERR stderr +#define LOG_INFO stderr +#else +int debug = 0; +#endif +#define printd if (debug) printf + +main(argc, argv) + int argc; + char **argv; +{ + extern char *optarg; + extern int optind; + char *cp, **ap; + int ch, disc; + int fd = -1; + void sighup(); + FILE *wfd = NULL, *pfd; + char *dialerstring = 0, buf[BUFSIZ]; + int first = 1, tries = 0; + int pausefirst = 0; + int pid; +#ifdef POSIX + struct termios t; +#else + struct sgttyb sgtty; +#endif + + while ((ch = getopt(argc, argv, "db:s:p:A:F:")) != EOF) + switch (ch) { + case 'd': + debug = 1; + break; +#ifdef POSIX + case 'b': + speed = atoi(optarg); + break; +#endif + case 'p': + pausefirst = atoi(optarg); + break; + case 's': + dialerstring = optarg; + break; + case 'A': + annex = optarg; + break; + case 'F': +#ifdef POSIX + if (strcmp(optarg, "none") == 0) + flowcontrol = FC_NONE; + else if (strcmp(optarg, "sw") == 0) + flowcontrol = FC_SW; + else if (strcmp(optarg, "hw") == 0) + flowcontrol = FC_HW; + else { + (void)fprintf(stderr, + "flow control: none, sw, hw\n"); + exit(1); + } + break; +#else + (void)fprintf(stderr, "flow control not supported\n"); + exit(1); +#endif + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 3) + usage(); + + openlog("startslip", LOG_PID, LOG_DAEMON); + +#if BSD <= 43 + if (debug == 0 && (fd = open("/dev/tty", 0)) >= 0) { + ioctl(fd, TIOCNOTTY, 0); + close(fd); + fd = -1; + } +#endif + + if (debug) + setbuf(stdout, NULL); + + if (pfd = fopen(PIDFILE, "r")) { + pid = 0; + fscanf(pfd, "%d", &pid); + if (pid > 0) + kill(pid, SIGUSR1); + fclose(pfd); + } +restart: + logged_in = 0; + if (++tries > MAXTRIES) { + syslog(LOG_ERR, "exiting after %d tries\n", tries); + /* ??? + if (first) + */ + exit(1); + } + + /* + * We may get a HUP below, when the parent (session leader/ + * controlling process) exits; ignore HUP until into new session. + */ + signal(SIGHUP, SIG_IGN); + hup = 0; + if (fork() > 0) { + if (pausefirst) + sleep(pausefirst); + if (first) + printd("parent exit\n"); + exit(0); + } + pausefirst = 0; +#ifdef POSIX + if (setsid() == -1) + perror("setsid"); +#endif + pid = getpid(); + printd("restart: pid %d: ", pid); + if (pfd = fopen(PIDFILE, "w")) { + fprintf(pfd, "%d\n", pid); + fclose(pfd); + } + if (wfd) { + printd("fclose, "); + fclose(wfd); + wfd == NULL; + } + if (fd >= 0) { + printd("close, "); + close(fd); + sleep(5); + } + printd("open"); + if ((fd = open(argv[0], O_RDWR)) < 0) { + perror(argv[0]); + syslog(LOG_ERR, "open %s: %m\n", argv[0]); + if (first) + exit(1); + else { + sleep(wait_time * tries); + goto restart; + } + } + printd(" %d", fd); +#ifdef TIOCSCTTY + if (ioctl(fd, TIOCSCTTY, 0) < 0) + perror("ioctl (TIOCSCTTY)"); +#endif + signal(SIGHUP, sighup); + if (debug) { + if (ioctl(fd, TIOCGETD, &disc) < 0) + perror("ioctl(TIOCSETD)"); + printf(" (disc was %d)", disc); + } + disc = TTYDISC; + if (ioctl(fd, TIOCSETD, &disc) < 0) { + perror("ioctl(TIOCSETD)"); + syslog(LOG_ERR, "%s: ioctl (TIOCSETD 0): %m\n", + argv[0]); + } + printd(", ioctl"); +#ifdef POSIX + if (tcgetattr(fd, &t) < 0) { + perror("tcgetattr"); + syslog(LOG_ERR, "%s: tcgetattr: %m\n", argv[0]); + exit(2); + } + cfmakeraw(&t); + t.c_iflag &= ~IMAXBEL; + switch (flowcontrol) { + case FC_HW: + t.c_cflag |= (CRTS_IFLOW|CCTS_OFLOW); + break; + case FC_SW: + t.c_iflag |= (IXON|IXOFF); + break; + case FC_NONE: + t.c_cflag &= ~(CRTS_IFLOW|CCTS_OFLOW); + t.c_iflag &= ~(IXON|IXOFF); + break; + } + cfsetspeed(&t, speed); + if (tcsetattr(fd, TCSAFLUSH, &t) < 0) { + perror("tcsetattr"); + syslog(LOG_ERR, "%s: tcsetattr: %m\n", argv[0]); + if (first) + exit(2); + else { + sleep(wait_time * tries); + goto restart; + } + } +#else + if (ioctl(fd, TIOCGETP, &sgtty) < 0) { + perror("ioctl (TIOCGETP)"); + syslog(LOG_ERR, "%s: ioctl (TIOCGETP): %m\n", + argv[0]); + exit(2); + } + sgtty.sg_flags = RAW | ANYP; + sgtty.sg_erase = sgtty.sg_kill = 0377; + sgtty.sg_ispeed = sgtty.sg_ospeed = speed; + if (ioctl(fd, TIOCSETP, &sgtty) < 0) { + perror("ioctl (TIOCSETP)"); + syslog(LOG_ERR, "%s: ioctl (TIOCSETP): %m\n", + argv[0]); + if (first) + exit(2); + else { + sleep(wait_time * tries); + goto restart; + } + } +#endif + sleep(2); /* wait for flakey line to settle */ + if (hup) + goto restart; + + wfd = fdopen(fd, "w+"); + if (wfd == NULL) { + syslog(LOG_ERR, "can't fdopen slip line\n"); + exit(10); + } + setbuf(wfd, (char *)0); + if (dialerstring) { + printd(", send dialstring"); + fprintf(wfd, "%s\r", dialerstring); + } else + putc('\r', wfd); + printd("\n"); + + /* + * Log in + */ + printd("look for login: "); + for (;;) { + if (getline(buf, BUFSIZ, fd) == 0 || hup) { + sleep(wait_time * tries); + goto restart; + } + if (annex) { + if (bcmp(buf, annex, strlen(annex)) == 0) { + fprintf(wfd, "slip\r"); + printd("Sent \"slip\"\n"); + continue; + } + if (bcmp(&buf[1], "sername:", 8) == 0) { + fprintf(wfd, "%s\r", argv[1]); + printd("Sent login: %s\n", argv[1]); + continue; + } + if (bcmp(&buf[1], "assword:", 8) == 0) { + fprintf(wfd, "%s\r", argv[2]); + printd("Sent password: %s\n", argv[2]); + break; + } + } else { + if (bcmp(&buf[1], "ogin:", 5) == 0) { + fprintf(wfd, "%s\r", argv[1]); + printd("Sent login: %s\n", argv[1]); + continue; + } + if (bcmp(&buf[1], "assword:", 8) == 0) { + fprintf(wfd, "%s\r", argv[2]); + printd("Sent password: %s\n", argv[2]); + break; + } + } + } + + /* + * Security hack. Do not want private information such as the + * password and possible phone number to be left around. + * So we clobber the arguments. + */ + for (ap = argv - optind + 1; ap < argv + 3; ap++) + for (cp = *ap; *cp != 0; cp++) + *cp = '\0'; + + /* + * Attach + */ + printd("setd"); + disc = SLIPDISC; + if (ioctl(fd, TIOCSETD, &disc) < 0) { + perror("ioctl(TIOCSETD)"); + syslog(LOG_ERR, "%s: ioctl (TIOCSETD SLIP): %m\n", + argv[0]); + exit(1); + } + if (first && debug == 0) { + close(0); + close(1); + close(2); + (void) open("/dev/null", O_RDWR); + (void) dup2(0, 1); + (void) dup2(0, 2); + } + (void) system("ifconfig sl0 up"); + printd(", ready\n"); + if (!first) + syslog(LOG_INFO, "reconnected (%d tries).\n", tries); + first = 0; + tries = 0; + logged_in = 1; + while (hup == 0) { + sigpause(0L); + printd("sigpause return\n"); + } + goto restart; +} + +void +sighup() +{ + + printd("hup\n"); + if (hup == 0 && logged_in) + syslog(LOG_INFO, "hangup signal\n"); + hup = 1; +} + +getline(buf, size, fd) + char *buf; + int size, fd; +{ + register int i; + int ret; + + size--; + for (i = 0; i < size; i++) { + if (hup) + return (0); + if ((ret = read(fd, &buf[i], 1)) == 1) { + buf[i] &= 0177; + if (buf[i] == '\r' || buf[i] == '\0') + buf[i] = '\n'; + if (buf[i] != '\n' && buf[i] != ':') + continue; + buf[i + 1] = '\0'; + printd("Got %d: \"%s\"\n", i + 1, buf); + return (i+1); + } + if (ret <= 0) { + if (ret < 0) + perror("getline: read"); + else + fprintf(stderr, "read returned 0\n"); + buf[i] = '\0'; + printd("returning 0 after %d: \"%s\"\n", i, buf); + return (0); + } + } + return (0); +} + +usage() +{ + (void)fprintf(stderr, + "usage: startslip [-d] [-b speed] [-s string] [-A annexname] [-F flowcontrol] dev user passwd\n"); + exit(1); +} diff --git a/sbin/swapon/Makefile b/sbin/swapon/Makefile new file mode 100644 index 0000000..23836a1 --- /dev/null +++ b/sbin/swapon/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= swapon +MAN8= swapon.0 + +.include <bsd.prog.mk> diff --git a/sbin/swapon/swapon.8 b/sbin/swapon/swapon.8 new file mode 100644 index 0000000..973ebc2 --- /dev/null +++ b/sbin/swapon/swapon.8 @@ -0,0 +1,90 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)swapon.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt SWAPON 8 +.Os BSD 4 +.Sh NAME +.Nm swapon +.Nd "specify additional device for paging and swapping" +.Sh SYNOPSIS +.Nm swapon +.Fl a +.Nm swapon +.Ar special_file ... +.Sh DESCRIPTION +.Nm Swapon +is used to specify additional devices on which paging and swapping +are to take place. +The system begins by swapping and paging on only a single device +so that only one disk is required at bootstrap time. +Calls to +.Nm swapon +normally occur in the system multi-user initialization file +.Pa /etc/rc +making all swap devices available, so that the paging and swapping +activity is interleaved across several devices. +.Pp +Normally, the first form is used: +.Bl -tag -width Ds +.It Fl a +All devices marked as ``sw'' +swap devices in +.Pa /etc/fstab +are made available. +.El +.Pp +The second form gives individual block devices as given +in the system swap configuration table. The call makes only this space +available to the system for swap allocation. +.Sh SEE ALSO +.Xr swapon 2 , +.Xr fstab 8 +.Xr init 8 +.Xr rc 8 +.Sh FILES +.Bl -tag -width /dev/[ru][pk]?b -compact +.It Pa /dev/[ru][pk]?b +standard paging devices +.It Pa /etc/fstab +ascii filesystem description table +.El +.Sh BUGS +There is no way to stop paging and swapping on a device. +It is therefore not possible to make use of devices which may be +dismounted during system operation. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.0 . diff --git a/sbin/swapon/swapon.c b/sbin/swapon/swapon.c new file mode 100644 index 0000000..577219d --- /dev/null +++ b/sbin/swapon/swapon.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)swapon.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <fstab.h> +#include <errno.h> +#include <stdio.h> + +main(argc, argv) + int argc; + char **argv; +{ + extern char *optarg; + extern int optind; + register struct fstab *fsp; + register int stat; + int ch, doall; + + doall = 0; + while ((ch = getopt(argc, argv, "a")) != EOF) + switch((char)ch) { + case 'a': + doall = 1; + break; + case '?': + default: + usage(); + } + argv += optind; + + stat = 0; + if (doall) + while (fsp = getfsent()) { + if (strcmp(fsp->fs_type, FSTAB_SW)) + continue; + if (add(fsp->fs_spec, 1)) + stat = 1; + else + printf("swapon: adding %s as swap device\n", + fsp->fs_spec); + } + else if (!*argv) + usage(); + for (; *argv; ++argv) + stat |= add(*argv, 0); + exit(stat); +} + +add(name, ignoreebusy) + char *name; + int ignoreebusy; +{ + extern int errno; + + if (swapon(name) == -1) { + switch (errno) { + case EINVAL: + fprintf(stderr, "swapon: %s: device not configured\n", + name); + break; + case EBUSY: + if (!ignoreebusy) + fprintf(stderr, + "swapon: %s: device already in use\n", + name); + break; + default: + fprintf(stderr, "swapon: %s: ", name); + perror((char *)NULL); + break; + } + return(1); + } + return(0); +} + +usage() +{ + fprintf(stderr, "usage: swapon [-a] [special_file ...]\n"); + exit(1); +} diff --git a/sbin/tunefs/Makefile b/sbin/tunefs/Makefile new file mode 100644 index 0000000..92f94d7 --- /dev/null +++ b/sbin/tunefs/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= tunefs +MAN8= tunefs.0 + +.include <bsd.prog.mk> diff --git a/sbin/tunefs/tunefs.8 b/sbin/tunefs/tunefs.8 new file mode 100644 index 0000000..bfe4cb1 --- /dev/null +++ b/sbin/tunefs/tunefs.8 @@ -0,0 +1,138 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tunefs.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt TUNEFS 8 +.Os BSD 4.2 +.Sh NAME +.Nm tunefs +.Nd tune up an existing file system +.Sh SYNOPSIS +.Nm tunefs +.Op Fl a Ar maxcontig +.Op Fl d Ar rotdelay +.Op Fl e Ar maxbpg +.Op Fl m Ar minfree +.Bk -words +.Op Fl o Ar optimize_preference +.Ek +.Op Ar special | Ar filesys +.Sh DESCRIPTION +.Nm Tunefs +is designed to change the dynamic parameters of a file system +which affect the layout policies. +The parameters which are to be changed are indicated by the flags +given below: +.Bl -tag -width Ds +.It Fl a Ar maxcontig +This specifies the maximum number of contiguous blocks that will +be laid out before forcing a rotational delay (see +.Fl d +below). +The default value is one, since most device drivers require +an interrupt per disk transfer. +Device drivers that can chain several buffers together in a single +transfer should set this to the maximum chain length. +.It Fl d Ar rotdelay +This specifies the expected time (in milliseconds) +to service a transfer completion +interrupt and initiate a new transfer on the same disk. +It is used to decide how much rotational spacing to place between +successive blocks in a file. +.It Fl e Ar maxbpg +This indicates the maximum number of blocks any single file can +allocate out of a cylinder group before it is forced to begin +allocating blocks from another cylinder group. +Typically this value is set to about one quarter of the total blocks +in a cylinder group. +The intent is to prevent any single file from using up all the +blocks in a single cylinder group, +thus degrading access times for all files subsequently allocated +in that cylinder group. +The effect of this limit is to cause big files to do long seeks +more frequently than if they were allowed to allocate all the blocks +in a cylinder group before seeking elsewhere. +For file systems with exclusively large files, +this parameter should be set higher. +.It Fl m Ar minfree +This value specifies the percentage of space held back +from normal users; the minimum free space threshold. +The default value used is 10%. +This value can be set to zero, however up to a factor of three +in throughput will be lost over the performance obtained at a 10% +threshold. +Note that if the value is raised above the current usage level, +users will be unable to allocate files until enough files have +been deleted to get under the higher threshold. +.It Fl o Ar optimize_preference +The file system can either try to minimize the time spent +allocating blocks, or it can attempt to minimize the space +fragmentation on the disk. +If the value of minfree (see above) is less than 10%, +then the file system should optimize for space to avoid +running out of full sized blocks. +For values of minfree greater than or equal to 10%, +fragmentation is unlikely to be problematical, and +the file system can be optimized for time. +.El +.Sh SEE ALSO +.Xr fs 5 , +.Xr dumpfs 8 , +.Xr newfs 8 , +.Xr mkfs 8 +.Rs +.%A M. McKusick +.%A W. Joy +.%A S. Leffler +.%A R. Fabry +.%T "A Fast File System for UNIX" +.%J "ACM Transactions on Computer Systems 2" +.%N 3 +.%P pp 181-197 +.%D August 1984 +.%O "(reprinted in the BSD System Manager's Manual, SMM:5)" +.Re +.Sh BUGS +This program should work on mounted and active file systems. +Because the super-block is not kept in the buffer cache, +the changes will only take effect if the program +is run on dismounted file systems. +To change the root file system, the system must be rebooted +after the file system is tuned. +.Pp +You can tune a file system, but you can't tune a fish. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/tunefs/tunefs.c b/sbin/tunefs/tunefs.c new file mode 100644 index 0000000..583dbff --- /dev/null +++ b/sbin/tunefs/tunefs.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tunefs.c 8.2 (Berkeley) 4/19/94"; +#endif /* not lint */ + +/* + * tunefs: change layout parameters to an existing file system. + */ +#include <sys/param.h> +#include <sys/stat.h> + +#include <ufs/ffs/fs.h> + +#include <errno.h> +#include <err.h> +#include <fcntl.h> +#include <fstab.h> +#include <stdio.h> +#include <paths.h> +#include <stdlib.h> +#include <unistd.h> + +/* the optimization warning string template */ +#define OPTWARN "should optimize for %s with minfree %s %d%%" + +union { + struct fs sb; + char pad[MAXBSIZE]; +} sbun; +#define sblock sbun.sb + +int fi; +long dev_bsize = 1; + +void bwrite(daddr_t, char *, int); +int bread(daddr_t, char *, int); +void getsb(struct fs *, char *); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + char *cp, *special, *name; + struct stat st; + int i; + int Aflag = 0; + struct fstab *fs; + char *chg[2], device[MAXPATHLEN]; + + argc--, argv++; + if (argc < 2) + usage(); + special = argv[argc - 1]; + fs = getfsfile(special); + if (fs) + special = fs->fs_spec; +again: + if (stat(special, &st) < 0) { + if (*special != '/') { + if (*special == 'r') + special++; + (void)sprintf(device, "%s/%s", _PATH_DEV, special); + special = device; + goto again; + } + err(1, "%s", special); + } + if ((st.st_mode & S_IFMT) != S_IFBLK && + (st.st_mode & S_IFMT) != S_IFCHR) + errx(10, "%s: not a block or character device", special); + getsb(&sblock, special); + for (; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + for (cp = &argv[0][1]; *cp; cp++) + switch (*cp) { + + case 'A': + Aflag++; + continue; + + case 'a': + name = "maximum contiguous block count"; + if (argc < 1) + errx(10, "-a: missing %s", name); + argc--, argv++; + i = atoi(*argv); + if (i < 1) + errx(10, "%s must be >= 1 (was %s)", + name, *argv); + warnx("%s changes from %d to %d", + name, sblock.fs_maxcontig, i); + sblock.fs_maxcontig = i; + continue; + + case 'd': + name = + "rotational delay between contiguous blocks"; + if (argc < 1) + errx(10, "-d: missing %s", name); + argc--, argv++; + i = atoi(*argv); + warnx("%s changes from %dms to %dms", + name, sblock.fs_rotdelay, i); + sblock.fs_rotdelay = i; + continue; + + case 'e': + name = + "maximum blocks per file in a cylinder group"; + if (argc < 1) + errx(10, "-e: missing %s", name); + argc--, argv++; + i = atoi(*argv); + if (i < 1) + errx(10, "%s must be >= 1 (was %s)", + name, *argv); + warnx("%s changes from %d to %d", + name, sblock.fs_maxbpg, i); + sblock.fs_maxbpg = i; + continue; + + case 'm': + name = "minimum percentage of free space"; + if (argc < 1) + errx(10, "-m: missing %s", name); + argc--, argv++; + i = atoi(*argv); + if (i < 0 || i > 99) + errx(10, "bad %s (%s)", name, *argv); + warnx("%s changes from %d%% to %d%%", + name, sblock.fs_minfree, i); + sblock.fs_minfree = i; + if (i >= MINFREE && + sblock.fs_optim == FS_OPTSPACE) + warnx(OPTWARN, "time", ">=", MINFREE); + if (i < MINFREE && + sblock.fs_optim == FS_OPTTIME) + warnx(OPTWARN, "space", "<", MINFREE); + continue; + + case 'o': + name = "optimization preference"; + if (argc < 1) + errx(10, "-o: missing %s", name); + argc--, argv++; + chg[FS_OPTSPACE] = "space"; + chg[FS_OPTTIME] = "time"; + if (strcmp(*argv, chg[FS_OPTSPACE]) == 0) + i = FS_OPTSPACE; + else if (strcmp(*argv, chg[FS_OPTTIME]) == 0) + i = FS_OPTTIME; + else + errx(10, "bad %s (options are `space' or `time')", + name); + if (sblock.fs_optim == i) { + warnx("%s remains unchanged as %s", + name, chg[i]); + continue; + } + warnx("%s changes from %s to %s", + name, chg[sblock.fs_optim], chg[i]); + sblock.fs_optim = i; + if (sblock.fs_minfree >= MINFREE && + i == FS_OPTSPACE) + warnx(OPTWARN, "time", ">=", MINFREE); + if (sblock.fs_minfree < MINFREE && + i == FS_OPTTIME) + warnx(OPTWARN, "space", "<", MINFREE); + continue; + + default: + usage(); + } + } + if (argc != 1) + usage(); + bwrite((daddr_t)SBOFF / dev_bsize, (char *)&sblock, SBSIZE); + if (Aflag) + for (i = 0; i < sblock.fs_ncg; i++) + bwrite(fsbtodb(&sblock, cgsblock(&sblock, i)), + (char *)&sblock, SBSIZE); + close(fi); + exit(0); +} + +void +usage() +{ + + fprintf(stderr, "Usage: tunefs tuneup-options special-device\n"); + fprintf(stderr, "where tuneup-options are:\n"); + fprintf(stderr, "\t-a maximum contiguous blocks\n"); + fprintf(stderr, "\t-d rotational delay between contiguous blocks\n"); + fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n"); + fprintf(stderr, "\t-m minimum percentage of free space\n"); + fprintf(stderr, "\t-o optimization preference (`space' or `time')\n"); + exit(2); +} + +void +getsb(fs, file) + register struct fs *fs; + char *file; +{ + + fi = open(file, 2); + if (fi < 0) + err(3, "cannot open %s", file); + if (bread((daddr_t)SBOFF, (char *)fs, SBSIZE)) + err(4, "%s: bad super block", file); + if (fs->fs_magic != FS_MAGIC) + err(5, "%s: bad magic number", file); + dev_bsize = fs->fs_fsize / fsbtodb(fs, 1); +} + +void +bwrite(blk, buf, size) + daddr_t blk; + char *buf; + int size; +{ + + if (lseek(fi, (off_t)blk * dev_bsize, SEEK_SET) < 0) + err(6, "FS SEEK"); + if (write(fi, buf, size) != size) + err(7, "FS WRITE"); +} + +int +bread(bno, buf, cnt) + daddr_t bno; + char *buf; + int cnt; +{ + int i; + + if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0) + return(1); + if ((i = read(fi, buf, cnt)) != cnt) { + for(i=0; i<sblock.fs_bsize; i++) + buf[i] = 0; + return (1); + } + return (0); +} diff --git a/sbin/umount/Makefile b/sbin/umount/Makefile new file mode 100644 index 0000000..9665a59 --- /dev/null +++ b/sbin/umount/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.2 (Berkeley) 2/20/94 + +PROG= umount +DPADD= ${LIBRPC} +LDADD= -lrpc + +.include <bsd.prog.mk> diff --git a/sbin/umount/umount.8 b/sbin/umount/umount.8 new file mode 100644 index 0000000..234eb8f --- /dev/null +++ b/sbin/umount/umount.8 @@ -0,0 +1,123 @@ +.\" Copyright (c) 1980, 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)umount.8 8.1 (Berkeley) 2/20/94 +.\" +.Dd February 20, 1994 +.Dt UMOUNT 8 +.Os BSD 4 +.Sh NAME +.Nm umount +.Nd unmount file systems +.Sh SYNOPSIS +.Nm umount +.Op Fl fv +.Ar special | node +.Nm umount +.Fl a +.Op Fl fv +.Op Fl h Ar host +.Op Fl t Ar ufs | lfs | external_type +.Sh DESCRIPTION +The +.Nm umount +command +calls the +.Xr unmount 2 +system call to remove a +.Ar "special device" +or the remote node (rhost:path) from the file system tree at the point +.Ar node . +If either +.Ar special +or +.Ar node +are not provided, the appropriate information is taken from the +.Xr fstab 5 +file. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl a +All of the file systems described in +.Xr fstab 5 +are unmounted. +.It Fl f +The file system is forcibly unmounted. +Active special devices continue to work, +but all other files return errors if further accesses are attempted. +The root file system cannot be forcibly unmounted. +.It Fl h Ar host +Only filesystems mounted from the specified host will be +unmounted. +This option is implies the +.Fl a +option and, unless otherwise specified with the +.Fl t +option, will only unmount NFS filesystems. +.It Fl t Ar "ufs \\*(Ba lfs \\*(Ba external type" +Is used to indicate the actions should only be taken on +filesystems of the specified type. +More than one type may be specified in a comma separated list. +The list of filesystem types can be prefixed with +.Dq no +to specify the filesystem types for which action should +.Em not +be taken. +For example, the +.Nm umount +command: +.Bd -literal -offset indent +umount -a -t nfs,mfs +.Ed +.Pp +umounts all filesystems of the type +.Tn NFS +and +.Tn MFS . +.It Fl v +Verbose, additional information is printed out as each file system +is unmounted. +.El +.Sh FILES +.Bl -tag -width /etc/fstab -compact +.It Pa /etc/fstab +file system table +.El +.Sh SEE ALSO +.Xr unmount 2 , +.Xr fstab 5 , +.Xr mount 8 +.Sh HISTORY +A +.Nm umount +command appeared in +.At v6 . diff --git a/sbin/umount/umount.c b/sbin/umount/umount.c new file mode 100644 index 0000000..56660de --- /dev/null +++ b/sbin/umount/umount.c @@ -0,0 +1,421 @@ +/*- + * Copyright (c) 1980, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)umount.c 8.3 (Berkeley) 2/20/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/socketvar.h> + +#include <netdb.h> +#include <rpc/rpc.h> +#include <rpc/pmap_clnt.h> +#include <rpc/pmap_prot.h> +#include <nfs/rpcv2.h> + +#include <err.h> +#include <fstab.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +typedef enum { MNTON, MNTFROM } mntwhat; + +int fake, fflag, vflag, *typelist; +char *nfshost; + +int fsnametotype __P((char *)); +char *getmntname __P((char *, mntwhat, int *)); +void maketypelist __P((char *)); +int selected __P((int)); +int namematch __P((struct hostent *)); +int umountall __P((void)); +int umountfs __P((char *)); +void usage __P((void)); +int xdr_dir __P((XDR *, char *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int all, ch, errs; + + /* Start disks transferring immediately. */ + sync(); + + all = 0; + while ((ch = getopt(argc, argv, "aFfh:t:v")) != EOF) + switch (ch) { + case 'a': + all = 1; + break; + case 'F': + fake = 1; + break; + case 'f': + fflag = MNT_FORCE; + break; + case 'h': /* -h implies -a. */ + all = 1; + nfshost = optarg; + break; + case 't': + maketypelist(optarg); + break; + case 'v': + vflag = 1; + break; + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + + if (argc == 0 && !all || argc != 0 && all) + usage(); + + /* -h implies "-t nfs" if no -t flag. */ + if ((nfshost != NULL) && (typelist == NULL)) + maketypelist("nfs"); + + if (all) { + if (setfsent() == 0) + err(1, "%s", _PATH_FSTAB); + errs = umountall(); + } else + for (errs = 0; *argv != NULL; ++argv) + if (umountfs(*argv) == 0) + errs = 1; + exit(errs); +} + +int +umountall() +{ + struct fstab *fs; + int rval, type; + char *cp; + + while ((fs = getfsent()) != NULL) { + /* Ignore the root. */ + if (strcmp(fs->fs_file, "/") == 0) + continue; + /* + * !!! + * Historic practice: ignore unknown FSTAB_* fields. + */ + if (strcmp(fs->fs_type, FSTAB_RW) && + strcmp(fs->fs_type, FSTAB_RO) && + strcmp(fs->fs_type, FSTAB_RQ)) + continue; + /* If an unknown file system type, complain. */ + if ((type = fsnametotype(fs->fs_vfstype)) == MOUNT_NONE) { + warnx("%s: unknown mount type", fs->fs_vfstype); + continue; + } + if (!selected(type)) + continue; + + /* + * We want to unmount the file systems in the reverse order + * that they were mounted. So, we save off the file name + * in some allocated memory, and then call recursively. + */ + if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL) + err(1, NULL); + (void)strcpy(cp, fs->fs_file); + rval = umountall(); + return (umountfs(cp) || rval); + } + return (0); +} + +int +umountfs(name) + char *name; +{ + enum clnt_stat clnt_stat; + struct hostent *hp; + struct sockaddr_in saddr; + struct stat sb; + struct timeval pertry, try; + CLIENT *clp; + int so, type; + char *delimp, *hostp, *mntpt, rname[MAXPATHLEN]; + + if (realpath(name, rname) == NULL) { + warn("%s", rname); + return (0); + } + + name = rname; + + if (stat(name, &sb) < 0) { + if (((mntpt = getmntname(name, MNTFROM, &type)) == NULL) && + ((mntpt = getmntname(name, MNTON, &type)) == NULL)) { + warnx("%s: not currently mounted", name); + return (1); + } + } else if (S_ISBLK(sb.st_mode)) { + if ((mntpt = getmntname(name, MNTON, &type)) == NULL) { + warnx("%s: not currently mounted", name); + return (1); + } + } else if (S_ISDIR(sb.st_mode)) { + mntpt = name; + if ((name = getmntname(mntpt, MNTFROM, &type)) == NULL) { + warnx("%s: not currently mounted", mntpt); + return (1); + } + } else { + warnx("%s: not a directory or special device", name); + return (1); + } + + if (!selected(type)) + return (0); + + if ((delimp = strchr(name, '@')) != NULL) { + hostp = delimp + 1; + *delimp = '\0'; + hp = gethostbyname(hostp); + *delimp = '@'; + } else if ((delimp = strchr(name, ':')) != NULL) { + *delimp = '\0'; + hostp = name; + hp = gethostbyname(hostp); + name = delimp + 1; + *delimp = ':'; + } else + hp = NULL; + if (!namematch(hp)) + return (0); + + if (vflag) + (void)printf("%s: unmount from %s\n", name, mntpt); + if (fake) + return (0); + + if (unmount(mntpt, fflag) < 0) { + warn("%s", mntpt); + return (1); + } + + if ((hp != NULL) && !(fflag & MNT_FORCE)) { + *delimp = '\0'; + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + memmove(&saddr.sin_addr, hp->h_addr, hp->h_length); + pertry.tv_sec = 3; + pertry.tv_usec = 0; + so = RPC_ANYSOCK; + if ((clp = clntudp_create(&saddr, + RPCPROG_MNT, RPCMNT_VER1, pertry, &so)) == NULL) { + clnt_pcreateerror("Cannot MNT PRC"); + return (1); + } + clp->cl_auth = authunix_create_default(); + try.tv_sec = 20; + try.tv_usec = 0; + clnt_stat = clnt_call(clp, + RPCMNT_UMOUNT, xdr_dir, name, xdr_void, (caddr_t)0, try); + if (clnt_stat != RPC_SUCCESS) { + clnt_perror(clp, "Bad MNT RPC"); + return (1); + } + auth_destroy(clp->cl_auth); + clnt_destroy(clp); + } + return (0); +} + +char * +getmntname(name, what, type) + char *name; + mntwhat what; + int *type; +{ + struct statfs *mntbuf; + int i, mntsize; + + if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { + warn("getmntinfo"); + return (NULL); + } + for (i = 0; i < mntsize; i++) { + if ((what == MNTON) && !strcmp(mntbuf[i].f_mntfromname, name)) { + if (type) + *type = mntbuf[i].f_type; + return (mntbuf[i].f_mntonname); + } + if ((what == MNTFROM) && !strcmp(mntbuf[i].f_mntonname, name)) { + if (type) + *type = mntbuf[i].f_type; + return (mntbuf[i].f_mntfromname); + } + } + return (NULL); +} + +static enum { IN_LIST, NOT_IN_LIST } which; + +int +selected(type) + int type; +{ + /* If no type specified, it's always selected. */ + if (typelist == NULL) + return (1); + for (; *typelist != MOUNT_NONE; ++typelist) + if (type == *typelist) + return (which == IN_LIST ? 1 : 0); + return (which == IN_LIST ? 0 : 1); +} + +void +maketypelist(fslist) + char *fslist; +{ + int *av, i; + char *nextcp; + + if ((fslist == NULL) || (fslist[0] == '\0')) + errx(1, "empty type list"); + + /* + * XXX + * Note: the syntax is "noxxx,yyy" for no xxx's and + * no yyy's, not the more intuitive "noyyy,noyyy". + */ + if (fslist[0] == 'n' && fslist[1] == 'o') { + fslist += 2; + which = NOT_IN_LIST; + } else + which = IN_LIST; + + /* Count the number of types. */ + for (i = 0, nextcp = fslist; *nextcp != NULL; ++nextcp) + if (*nextcp == ',') + i++; + + /* Build an array of that many types. */ + if ((av = typelist = malloc((i + 2) * sizeof(int))) == NULL) + err(1, NULL); + for (i = 0; fslist != NULL; fslist = nextcp, ++i) { + if ((nextcp = strchr(fslist, ',')) != NULL) + *nextcp++ = '\0'; + av[i] = fsnametotype(fslist); + if (av[i] == MOUNT_NONE) + errx(1, "%s: unknown mount type", fslist); + } + /* Terminate the array. */ + av[i++] = MOUNT_NONE; +} + +int +fsnametotype(name) + char *name; +{ + static char const *namelist[] = INITMOUNTNAMES; + char const **cp; + + for (cp = namelist; *cp; ++cp) + if (strcmp(name, *cp) == 0) + return (cp - namelist); + return (MOUNT_NONE); +} + +int +namematch(hp) + struct hostent *hp; +{ + char *cp, **np; + + if ((hp == NULL) || (nfshost == NULL)) + return (1); + + if (strcasecmp(nfshost, hp->h_name) == 0) + return (1); + + if ((cp = strchr(hp->h_name, '.')) != NULL) { + *cp = '\0'; + if (strcasecmp(nfshost, hp->h_name) == 0) + return (1); + } + for (np = hp->h_aliases; *np; np++) { + if (strcasecmp(nfshost, *np) == 0) + return (1); + if ((cp = strchr(*np, '.')) != NULL) { + *cp = '\0'; + if (strcasecmp(nfshost, *np) == 0) + return (1); + } + } + return (0); +} + +/* + * xdr routines for mount rpc's + */ +int +xdr_dir(xdrsp, dirp) + XDR *xdrsp; + char *dirp; +{ + return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: %s\n %s\n", + "umount [-fv] [-t fstypelist] special | node", + "umount -a[fv] [-h host] [-t fstypelist]"); + exit(1); +} |