diff options
65 files changed, 23758 insertions, 0 deletions
diff --git a/sbin/dump/dump.8 b/sbin/dump/dump.8 new file mode 100644 index 000000000000..1f06bcbfd92f --- /dev/null +++ b/sbin/dump/dump.8 @@ -0,0 +1,354 @@ +.\" 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.3 (Berkeley) 5/1/95 +.\" +.Dd May 1, 1995 +.Dt DUMP 8 +.Os BSD 4 +.Sh NAME +.Nm dump +.Nd filesystem backup +.Sh SYNOPSIS +.Nm dump +.Op Fl 0123456789cnu +.Op Fl B Ar records +.Op Fl b Ar blocksize +.Op Fl d Ar density +.Op Fl f Ar file +.Op Fl h Ar level +.Op Fl s Ar feet +.Op Fl T Ar date +.Ar filesystem +.Nm dump +.Op Fl W Li \&| Fl w +.Pp +.in -\\n(iSu +(The +.Bx 4.3 +option syntax is implemented for backward compatibility, but +is not documented here.) +.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 +.Fl 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 Ds +.It Fl 0\-9 +Dump levels. +A level 0, full backup, +guarantees the entire file system is copied +(but see also the +.Fl 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 Fl B Ar records +The number of dump records per volume. +This option overrides the calculation of tape size +based on length and density. +.It Fl b Ar blocksize +The number of kilobytes per dump record. +.It Fl c +Modify the calculation of the default density and tape size to be more +appropriate for cartridge tapes. +.It Fl d Ar density +Set tape density to +.Ar density . +The default is 1600BPI. +.It Fl 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 Fl 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 Fl n +Whenever +.Nm dump +requires operator attention, +notify all operators in the group +.Dq operator +by means similar to a +.Xr wall 1 . +.It Fl 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. +.ne 1i +.It Fl 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 +.Fl T +option is mutually exclusive from the +.Fl u +option. +.It Fl 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 Fl 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 +.Fl 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 +.Fl W +option is set, all other options are ignored, and +.Nm dump +exits immediately. +.It Fl 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 +.Fl 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 +/sbin/dump -0u -f /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 +Fewer than 32 read errors on the filesystem are ignored. +.Pp +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 +.Fl W +or +.Fl 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 000000000000..842c45f99fbe --- /dev/null +++ b/sbin/dump/dump.h @@ -0,0 +1,213 @@ +/*- + * 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.2 (Berkeley) 4/28/95 + */ + +#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> +#define strchr(a,b) index(a,b) +#define strrchr(a,b) rindex(a,b) +extern char *strdup(), *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 000000000000..9c5273e2a0bc --- /dev/null +++ b/sbin/dump/dumprmt.c @@ -0,0 +1,374 @@ +/*- + * 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.3 (Berkeley) 4/28/95"; +#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 <err.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() +{ + + errx(1, "Lost connection to remote host."); +} + +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) + errx(1, "shell/tcp: unknown service"); + pwd = getpwuid(getuid()); + if (pwd == NULL) + errx(1, "who are you?"); + } + if ((cp = strchr(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 == '-')) { + warnx("invalid user name: %s", 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/main.c b/sbin/dump/main.c new file mode 100644 index 000000000000..7b3e1393f889 --- /dev/null +++ b/sbin/dump/main.c @@ -0,0 +1,586 @@ +/*- + * 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.6 (Berkeley) 5/1/95"; +#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/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#endif + +#include <protocols/dumprestore.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fstab.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#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((char *, long, long)); +static void obsolete __P((int *, char **[])); +static void usage __P((void)); + +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 int ch; + 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 < 2) + usage(); + + obsolete(&argc, &argv); + while ((ch = getopt(argc, argv, "0123456789B:b:cd:f:h:ns:T:uWw")) != -1) + switch (ch) { + /* dump level */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + level = ch; + break; + + case 'B': /* blocks per output file */ + blocksperfile = numarg("blocks per file", 1L, 0L); + break; + + case 'b': /* blocks per tape write */ + ntrec = numarg("blocks per write", 1L, 1000L); + break; + + case 'c': /* Tape is cart. not 9-track */ + cartridge = 1; + break; + + case 'd': /* density, in bits per inch */ + density = numarg("density", 10L, 327670L) / 10; + if (density >= 625 && !bflag) + ntrec = HIGHDENSITYTREC; + break; + + case 'f': /* output file */ + tape = optarg; + break; + + case 'h': + honorlevel = numarg("honor level", 0L, 10L); + break; + + case 'n': /* notify operators */ + notify = 1; + break; + + case 's': /* tape size, feet */ + tsize = numarg("tape size", 1L, 0L) * 12 * 10; + break; + + case 'T': /* time of last dump */ + spcl.c_ddate = unctime(optarg); + if (spcl.c_ddate < 0) { + (void)fprintf(stderr, "bad time \"%s\"\n", + optarg); + exit(X_ABORT); + } + Tflag = 1; + lastlevel = '?'; + break; + + case 'u': /* update /etc/dumpdates */ + uflag = 1; + break; + + case 'W': /* what to do */ + case 'w': + lastdump(ch); + exit(0); /* do nothing else */ + + default: + usage(); + } + argc -= optind; + argv += optind; + + 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 (strchr(tape, ':')) { + host = tape; + tape = strchr(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 */ +} + +static void +usage() +{ + + (void)fprintf(stderr, "usage: dump [-0123456789cnu] [-B records] [-b blocksize] [-d density] [-f file]\n [-h level] [-s feet] [-T date] filesystem\n"); + (void)fprintf(stderr, " dump [-W | -w]\n"); + exit(1); +} + +/* + * 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(meaning, vmin, vmax) + char *meaning; + long vmin, vmax; +{ + char *p; + long val; + + val = strtol(optarg, &p, 10); + if (*p) + errx(1, "illegal %s -- %s", meaning, optarg); + if (val < vmin || (vmax && val > vmax)) + errx(1, "%s must be between %ld and %ld", meaning, vmin, vmax); + return (val); +} + +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 = strrchr(cp, '/'); + + if (dp == NULL) + return (NULL); + *dp = '\0'; + (void)strcpy(rawbuf, cp); + *dp = '/'; + (void)strcat(rawbuf, "/r"); + (void)strcat(rawbuf, dp + 1); + return (rawbuf); +} + +/* + * 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 'b': + case 'd': + case 'f': + case 'h': + case 's': + case 'T': + if (*argv == NULL) { + warnx("option requires an argument -- %c", *ap); + usage(); + } + 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); + ++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++); + + /* Update argument count. */ + *argcp = nargv - *argvp - 1; +} diff --git a/sbin/dump/tape.c b/sbin/dump/tape.c new file mode 100644 index 000000000000..db5c24a26ae6 --- /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.4 (Berkeley) 5/1/95"; +#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/ufs/dinode.h> +#include <ufs/ffs/fs.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 || strchr(tape, ',')) { + if (nexttape && *nexttape) + tape = nexttape; + if ((p = strchr(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 000000000000..8eaabf3477f3 --- /dev/null +++ b/sbin/dump/traverse.c @@ -0,0 +1,607 @@ +/*- + * 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.7 (Berkeley) 6/15/95"; +#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/ufs/dir.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.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. */ +#define CHANGEDSINCE(dp, t) \ + ((dp)->di_mtime >= (t) || (dp)->di_ctime >= (t)) + +/* 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); + memmove(buf, dp->di_shortlink, (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 + memset(idblk, 0, (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) == -1) + 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. + */ + memset(buf, 0, size); + for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) { + if ((int)lseek(diskfd, ((off_t)blkno << dev_bshift), 0) == -1) + 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 000000000000..bacb469aa789 --- /dev/null +++ b/sbin/dump/unctime.c @@ -0,0 +1,106 @@ +/*- + * 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.2 (Berkeley) 6/14/94"; +#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 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; + then.tm_isdst = -1; + return(mktime(&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); +} diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c new file mode 100644 index 000000000000..ed68f5cc71cd --- /dev/null +++ b/sbin/dumpfs/dumpfs.c @@ -0,0 +1,322 @@ +/* + * 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.5 (Berkeley) 4/29/95"; +#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 <fstab.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.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, "")) != -1) + 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_magic != FS_MAGIC) { + warnx("%s: superblock has bad magic number, skipped", name); + (void)close(fd); + return (1); + } + + 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\tclean\t%d\n", + afs.fs_cgrotor, afs.fs_fmod, afs.fs_ronly, afs.fs_clean); + if (afs.fs_cpc != 0) + printf("blocks available in each of %d rotational positions", + afs.fs_nrpos); + else + printf("(no rotational position table)\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); + warn("%s", name); + 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) { + warnx("%s: error reading cg", 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/fsck/Makefile b/sbin/fsck/Makefile new file mode 100644 index 000000000000..718656034575 --- /dev/null +++ b/sbin/fsck/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.2 (Berkeley) 4/27/95 + +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 +CFLAGS+=-W +.PATH: ${.CURDIR}/../../sys/ufs/ffs + +.include <bsd.prog.mk> diff --git a/sbin/fsck/dir.c b/sbin/fsck/dir.c new file mode 100644 index 000000000000..f9b8b63b68a4 --- /dev/null +++ b/sbin/fsck/dir.c @@ -0,0 +1,725 @@ +/* + * 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.8 (Berkeley) 4/28/95"; +#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 <err.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, ".." +}; + +static int chgino __P((struct inodesc *)); +static int dircheck __P((struct inodesc *, struct direct *)); +static int expanddir __P((struct dinode *dp, char *name)); +static void freedir __P((ino_t ino, ino_t parent)); +static struct direct *fsck_readdir __P((struct inodesc *)); +static struct bufarea *getdirblk __P((ufs_daddr_t blkno, long size)); +static int lftempname __P((char *bufp, ino_t ino)); +static int mkentry __P((struct inodesc *)); + +/* + * Propagate connected state through the tree. + */ +void +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. + */ +int +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) + errx(EEXIT, "wrong type to dirscan %d", 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; + memmove(dbuf, dp, (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); + memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, + (size_t)dsize); + dirty(bp); + sbdirty(); + } + if (n & STOP) + return (n); + } + return (idesc->id_filesize > 0 ? KEEPON : STOP); +} + +/* + * get next entry in a directory. + */ +static 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; + if (idesc->id_fix == IGNORE) + return (0); + 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; + if (idesc->id_fix == IGNORE) + return (0); + 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. + */ +static int +dircheck(idesc, dp) + struct inodesc *idesc; + register struct direct *dp; +{ + register int size; + register char *cp; + u_char namlen, type; + int spaceleft; + + spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); + if (dp->d_ino >= maxino || + dp->d_reclen == 0 || + dp->d_reclen > spaceleft || + (dp->d_reclen & 0x3) != 0) + return (0); + if (dp->d_ino == 0) + return (1); + size = DIRSIZ(!newinofmt, dp); +# 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_reclen < size || + idesc->id_filesize < size || + namlen > MAXNAMLEN || + type > 15) + return (0); + for (cp = dp->d_name, size = 0; size < namlen; size++) + if (*cp == '\0' || (*cp++ == '/')) + return (0); + if (*cp != '\0') + return (0); + return (1); +} + +void +direrror(ino, errmesg) + ino_t ino; + char *errmesg; +{ + + fileerror(ino, ino, errmesg); +} + +void +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); +} + +void +adjust(idesc, lcnt) + register struct inodesc *idesc; + int 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(); + } + } +} + +static int +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 */ + dirp->d_reclen = newent.d_reclen; + if (newinofmt) + dirp->d_type = typemap[idesc->id_parent]; + else + dirp->d_type = 0; + dirp->d_namlen = newent.d_namlen; + memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1); +# if (BYTE_ORDER == LITTLE_ENDIAN) + /* + * If the entry was split, dirscan() will only reverse the byte + * order of the original entry, and not the new one, before + * writing it back out. So, we reverse the byte order here if + * necessary. + */ + if (oldlen != 0 && !newinofmt && !doinglevel2) { + u_char tmp; + + tmp = dirp->d_namlen; + dirp->d_namlen = dirp->d_type; + dirp->d_type = tmp; + } +# endif + return (ALTERED|STOP); +} + +static int +chgino(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (memcmp(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); +} + +int +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(); + + memset(&idesc, 0, 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. + */ +int +changeino(dir, name, newnum) + ino_t dir; + char *name; + ino_t newnum; +{ + struct inodesc idesc; + + memset(&idesc, 0, 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 + */ +int +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); + memset(&idesc, 0, 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 + */ +static int +expanddir(dp, name) + register struct dinode *dp; + char *name; +{ + ufs_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; + memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ); + bp = getdirblk(newblk, sblock.fs_bsize); + if (bp->b_errs) + goto bad; + memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ); + for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; + cp < &bp->b_un.b_buf[sblock.fs_bsize]; + cp += DIRBLKSIZ) + memmove(cp, &emptydir, sizeof emptydir); + dirty(bp); + bp = getdirblk(dp->di_db[lastbn + 1], + (long)dblksize(&sblock, dp, lastbn + 1)); + if (bp->b_errs) + goto bad; + memmove(bp->b_un.b_buf, &emptydir, 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 + */ +ino_t +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); + } + memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate)); + for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; + cp < &bp->b_un.b_buf[sblock.fs_fsize]; + cp += DIRBLKSIZ) + memmove(cp, &emptydir, 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 + */ +static void +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. + */ +static int +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. + */ +static struct bufarea * +getdirblk(blkno, size) + ufs_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 000000000000..fd3ff07762ed --- /dev/null +++ b/sbin/fsck/fsck.8 @@ -0,0 +1,301 @@ +.\" 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.4 (Berkeley) 5/9/95 +.\" +.Dd May 9, 1995 +.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. +In preening mode, +filesystems that are marked clean are skipped. +Filesystems are marked clean when they are unmounted, +when they have been mounted read-only, or when +.Nm fsck +runs on them successfully. +.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 four 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. +.It 3 +If maxcontig is greater than one, +build the free segment maps to aid in finding contiguous sets of blocks. +If maxcontig is equal to one, delete any existing segment maps. +.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 000000000000..21b36588b560 --- /dev/null +++ b/sbin/fsck/fsck.h @@ -0,0 +1,278 @@ +/* + * 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.4 (Berkeley) 5/9/95 + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#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 */ + ufs_daddr_t b_bno; + int b_size; + int b_errs; + int b_flags; + union { + char *b_buf; /* buffer space */ + ufs_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 */ + +#define dirty(bp) (bp)->b_dirty = 1 +#define initbarea(bp) \ + (bp)->b_dirty = 0; \ + (bp)->b_bno = (ufs_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 */ + ufs_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; + ufs_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 */ + ufs_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 */ + +ufs_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 */ +u_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 */ + +ufs_daddr_t n_blks; /* number of blocks in use */ +ufs_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 + +#define EEXIT 8 /* Standard error exit. */ + +struct fstab; + +void adjust __P((struct inodesc *, int lcnt)); +ufs_daddr_t allocblk __P((long frags)); +ino_t allocdir __P((ino_t parent, ino_t request, int mode)); +ino_t allocino __P((ino_t request, int type)); +void blkerror __P((ino_t ino, char *type, ufs_daddr_t blk)); +char *blockcheck __P((char *name)); +int bread __P((int fd, char *buf, ufs_daddr_t blk, long size)); +void bufinit __P((void)); +void bwrite __P((int fd, char *buf, ufs_daddr_t blk, long size)); +void cacheino __P((struct dinode *dp, ino_t inumber)); +void catch __P((int)); +void catchquit __P((int)); +int changeino __P((ino_t dir, char *name, ino_t newnum)); +int checkfstab __P((int preen, int maxrun, + int (*docheck)(struct fstab *), + int (*chkit)(char *, char *, long, int))); +int chkrange __P((ufs_daddr_t blk, int cnt)); +void ckfini __P((int markclean)); +int ckinode __P((struct dinode *dp, struct inodesc *)); +void clri __P((struct inodesc *, char *type, int flag)); +void direrror __P((ino_t ino, char *errmesg)); +int dirscan __P((struct inodesc *)); +int dofix __P((struct inodesc *, char *msg)); +void ffs_clrblock __P((struct fs *, u_char *, ufs_daddr_t)); +void ffs_fragacct __P((struct fs *, int, int32_t [], int)); +int ffs_isblock __P((struct fs *, u_char *, ufs_daddr_t)); +void ffs_setblock __P((struct fs *, u_char *, ufs_daddr_t)); +void fileerror __P((ino_t cwd, ino_t ino, char *errmesg)); +int findino __P((struct inodesc *)); +int findname __P((struct inodesc *)); +void flush __P((int fd, struct bufarea *bp)); +void freeblk __P((ufs_daddr_t blkno, long frags)); +void freeino __P((ino_t ino)); +void freeinodebuf __P((void)); +int ftypeok __P((struct dinode *dp)); +void getblk __P((struct bufarea *bp, ufs_daddr_t blk, long size)); +struct bufarea *getdatablk __P((ufs_daddr_t blkno, long size)); +struct inoinfo *getinoinfo __P((ino_t inumber)); +struct dinode *getnextinode __P((ino_t inumber)); +void getpathname __P((char *namebuf, ino_t curdir, ino_t ino)); +struct dinode *ginode __P((ino_t inumber)); +void inocleanup __P((void)); +void inodirty __P((void)); +int linkup __P((ino_t orphan, ino_t parentdir)); +int makeentry __P((ino_t parent, ino_t ino, char *name)); +void panic __P((const char *fmt, ...)); +void pass1 __P((void)); +void pass1b __P((void)); +int pass1check __P((struct inodesc *)); +void pass2 __P((void)); +void pass3 __P((void)); +void pass4 __P((void)); +int pass4check __P((struct inodesc *)); +void pass5 __P((void)); +void pfatal __P((const char *fmt, ...)); +void pinode __P((ino_t ino)); +void propagate __P((void)); +void pwarn __P((const char *fmt, ...)); +int reply __P((char *question)); +void resetinodebuf __P((void)); +int setup __P((char *dev)); +void voidquit __P((int)); diff --git a/sbin/fsck/inode.c b/sbin/fsck/inode.c new file mode 100644 index 000000000000..1d40761cd72f --- /dev/null +++ b/sbin/fsck/inode.c @@ -0,0 +1,562 @@ +/* + * 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.8 (Berkeley) 4/28/95"; +#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 <err.h> +#include <pwd.h> +#include <string.h> + +#include "fsck.h" + +static ino_t startinum; + +static int iblock __P((struct inodesc *, long ilevel, quad_t isize)); + +int +ckinode(dp, idesc) + struct dinode *dp; + register struct inodesc *idesc; +{ + ufs_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); +} + +static int +iblock(idesc, ilevel, isize) + struct inodesc *idesc; + long ilevel; + quad_t isize; +{ + ufs_daddr_t *ap; + ufs_daddr_t *aplim; + struct bufarea *bp; + int i, n, (*func)(), nif; + quad_t sizepb; + char buf[BUFSIZ]; + + 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. + */ +int +chkrange(blk, cnt) + ufs_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; +{ + ufs_daddr_t iblk; + + if (inumber < ROOTINO || inumber > maxino) + errx(EEXIT, "bad inode number %d to ginode", 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; + ufs_daddr_t dblk; + static struct dinode *dp; + + if (inumber != nextino++ || inumber > maxino) + errx(EEXIT, "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; + } + (void)bread(fsreadfd, (char *)inodebuf, dblk, size); /* ??? */ + dp = inodebuf; + } + return (dp++); +} + +void +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) + errx(EEXIT, "Cannot allocate space for inode buffer"); + while (nextino < ROOTINO) + (void)getnextinode(nextino); +} + +void +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. + */ +void +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(ufs_daddr_t)); + if (inp == NULL) + return; + inpp = &inphead[inumber % numdirs]; + inp->i_nexthash = *inpp; + *inpp = inp; + if (inumber == ROOTINO) + inp->i_parent = ROOTINO; + else + 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(ufs_daddr_t); + memmove(&inp->i_blks[0], &dp->di_db[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) + errx(EEXIT, "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); + } + errx(EEXIT, "cannot find inode %d", inumber); + return ((struct inoinfo *)0); +} + +/* + * Clean up all the inode cache structure. + */ +void +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; +} + +void +inodirty() +{ + + dirty(pbp); +} + +void +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(); + } +} + +int +findname(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (dirp->d_ino != idesc->id_parent) + return (KEEPON); + memmove(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1); + return (STOP|FOUND); +} + +int +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); +} + +void +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); + printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]); +} + +void +blkerror(ino, type, blk) + ino_t ino; + char *type; + ufs_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: + errx(EEXIT, "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); + 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 + */ +void +freeino(ino) + ino_t ino; +{ + struct inodesc idesc; + struct dinode *dp; + + memset(&idesc, 0, 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 000000000000..f719ac9fc88c --- /dev/null +++ b/sbin/fsck/main.c @@ -0,0 +1,340 @@ +/* + * 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.6 (Berkeley) 5/14/95"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/mount.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/ufsmount.h> +#include <ufs/ffs/fs.h> + +#include <ctype.h> +#include <err.h> +#include <fstab.h> +#include <string.h> + +#include "fsck.h" + +int returntosingle; + +static int argtoi __P((int flag, char *req, char *str, int base)); +static int docheck __P((struct fstab *fsp)); +static int checkfilesys __P((char *filesys, char *mntpt, long auxdata, + int child)); +void main __P((int argc, char *argv[])); + +void +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + int ret, maxrun = 0; + extern char *optarg; + 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) + errx(EEXIT, "bad mode to -m: %o", 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: + errx(EEXIT, "%c option?", 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); +} + +static int +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) + errx(EEXIT, "-%c flag requires a %s", flag, req); + return (ret); +} + +/* + * Determine whether a filesystem should be checked. + */ +static int +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 */ +static int +checkfilesys(filesys, mntpt, auxdata, child) + char *filesys, *mntpt; + long auxdata; + int child; +{ + ufs_daddr_t n_ffree, n_bfree; + struct dups *dp; + struct zlncnt *zlnp; + int cylno, flags; + + if (preen && child) + (void)signal(SIGQUIT, voidquit); + cdevname = filesys; + if (debug && preen) + pwarn("starting\n"); + switch (setup(filesys)) { + case 0: + if (preen) + pfatal("CAN'T CHECK FILE SYSTEM."); + /* fall through */ + case -1: + 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); + } + if (!hotroot) { + ckfini(1); + } else { + struct statfs stfs_buf; + /* + * Check to see if root is mounted read-write. + */ + if (statfs("/", &stfs_buf) == 0) + flags = stfs_buf.f_flags; + else + flags = 0; + ckfini(flags & MNT_RDONLY); + } + free(blockmap); + free(statemap); + free((char *)lncntp); + if (!fsmodified) + return (0); + if (!preen) + printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); + if (hotroot) { + struct ufs_args args; + int ret; + /* + * We modified the root. Do a mount update on + * it, unless it is read-write, so we can continue. + */ + if (flags & MNT_RDONLY) { + args.fspec = 0; + args.export.ex_flags = 0; + args.export.ex_root = 0; + flags |= MNT_UPDATE | MNT_RELOAD; + ret = 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 000000000000..1169adbbd59d --- /dev/null +++ b/sbin/fsck/pass1.c @@ -0,0 +1,320 @@ +/* + * 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.6 (Berkeley) 4/28/95"; +#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 <err.h> +#include <string.h> + +#include "fsck.h" + +static ufs_daddr_t badblk; +static ufs_daddr_t dupblk; +static void checkinode __P((ino_t inumber, struct inodesc *)); + +void +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. + */ + memset(&idesc, 0, 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(); +} + +static void +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; + + dp = getnextinode(inumber); + mode = dp->di_mode & IFMT; + if (mode == 0) { + if (memcmp(dp->di_db, zino.di_db, + NDADDR * sizeof(ufs_daddr_t)) || + memcmp(dp->di_ib, zino.di_ib, + NIADDR * sizeof(ufs_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 || + (mode == IFDIR && dp->di_size > MAXDIRSIZE)) { + 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) { + symbuf = alloca(secsize); + if (bread(fsreadfd, symbuf, + fsbtodb(&sblock, dp->di_db[0]), + (long)secsize) != 0) + errx(EEXIT, "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); + memmove(dp->di_shortlink, symbuf, (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(ufs_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) + exit(EEXIT); + } 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(); + } +} + +int +pass1check(idesc) + register struct inodesc *idesc; +{ + int res = KEEPON; + int anyout, nfrags; + ufs_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) + exit(EEXIT); + 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) + exit(EEXIT); + return (STOP); + } + new = (struct dups *)malloc(sizeof(struct dups)); + if (new == NULL) { + pfatal("DUP TABLE OVERFLOW."); + if (reply("CONTINUE") == 0) + exit(EEXIT); + 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 000000000000..203fb53c37d6 --- /dev/null +++ b/sbin/fsck/pass1b.c @@ -0,0 +1,104 @@ +/* + * 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.4 (Berkeley) 4/28/95"; +#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" + +static struct dups *duphead; +static int pass1bcheck __P((struct inodesc *)); + +void +pass1b() +{ + register int c, i; + register struct dinode *dp; + struct inodesc idesc; + ino_t inumber; + + memset(&idesc, 0, 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; + } + } +} + +static int +pass1bcheck(idesc) + register struct inodesc *idesc; +{ + register struct dups *dlp; + int nfrags, res = KEEPON; + ufs_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 000000000000..27490feafa28 --- /dev/null +++ b/sbin/fsck/pass2.c @@ -0,0 +1,467 @@ +/* + * 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.9 (Berkeley) 4/28/95"; +#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 <err.h> +#include <string.h> + +#include "fsck.h" + +#define MINDIRSIZE (sizeof (struct dirtemplate)) + +static int blksort __P((const void *, const void *)); +static int pass2check __P((struct inodesc *)); + +void +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) + exit(EEXIT); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); + break; + + case DCLEAR: + pfatal("DUPS/BAD IN ROOT INODE"); + if (reply("REALLOCATE")) { + freeino(ROOTINO); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); + break; + } + if (reply("CONTINUE") == 0) + exit(EEXIT); + break; + + case FSTATE: + case FCLEAR: + pfatal("ROOT INODE NOT DIRECTORY"); + if (reply("REALLOCATE")) { + freeino(ROOTINO); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); + break; + } + if (reply("FIX") == 0) + exit(EEXIT); + dp = ginode(ROOTINO); + dp->di_mode &= ~IFMT; + dp->di_mode |= IFDIR; + inodirty(); + break; + + case DSTATE: + break; + + default: + errx(EEXIT, "BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); + } + statemap[ROOTINO] = DFOUND; + if (newinofmt) { + statemap[WINO] = FSTATE; + typemap[WINO] = DT_WHT; + } + /* + * Sort the directory list into disk block order. + */ + qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); + /* + * Check the integrity of each directory. + */ + memset(&curino, 0, 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; + } + } + memset(&dino, 0, sizeof(struct dinode)); + dino.di_mode = IFDIR; + dp->di_size = inp->i_isize; + memmove(&dp->di_db[0], &inp->i_blks[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(); +} + +static int +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, "."); +# if BYTE_ORDER == LITTLE_ENDIAN + if (!newinofmt) { + u_char tmp; + + tmp = proto.d_type; + proto.d_type = proto.d_namlen; + proto.d_namlen = tmp; + } +# endif + 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; + memmove(dirp, &proto, (size_t)entrysize); + if (reply("FIX") == 1) + ret |= ALTERED; + } else { + n = dirp->d_reclen - entrysize; + proto.d_reclen = entrysize; + memmove(dirp, &proto, (size_t)entrysize); + idesc->id_entryno++; + lncntp[dirp->d_ino]--; + dirp = (struct direct *)((char *)(dirp) + entrysize); + memset(dirp, 0, (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, ".."); +# if BYTE_ORDER == LITTLE_ENDIAN + if (!newinofmt) { + u_char tmp; + + tmp = proto.d_type; + proto.d_type = proto.d_namlen; + proto.d_namlen = tmp; + } +# endif + 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); + memset(dirp, 0, (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; + memmove(dirp, &proto, (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 if (newinofmt && + ((dirp->d_ino == WINO && dirp->d_type != DT_WHT) || + (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) { + fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY"); + dirp->d_ino = WINO; + dirp->d_type = DT_WHT; + if (reply("FIX") == 1) + ret |= ALTERED; + } 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: + errx(EEXIT, "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. + */ +static int +blksort(arg1, arg2) + const void *arg1, *arg2; +{ + + return ((*(struct inoinfo **)arg1)->i_blks[0] - + (*(struct inoinfo **)arg2)->i_blks[0]); +} diff --git a/sbin/fsck/pass3.c b/sbin/fsck/pass3.c new file mode 100644 index 000000000000..e8c94467bdc5 --- /dev/null +++ b/sbin/fsck/pass3.c @@ -0,0 +1,74 @@ +/* + * 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.2 (Berkeley) 4/27/95"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> + +#include "fsck.h" + +void +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 000000000000..97bc7d01ac44 --- /dev/null +++ b/sbin/fsck/pass4.c @@ -0,0 +1,136 @@ +/* + * 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.4 (Berkeley) 4/28/95"; +#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 <string.h> + +#include "fsck.h" + +void +pass4() +{ + register ino_t inumber; + register struct zlncnt *zlnp; + struct dinode *dp; + struct inodesc idesc; + int n; + + memset(&idesc, 0, 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: + errx(EEXIT, "BAD STATE %d FOR INODE I=%d", + statemap[inumber], inumber); + } + } +} + +int +pass4check(idesc) + register struct inodesc *idesc; +{ + register struct dups *dlp; + int nfrags, res = KEEPON; + ufs_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 000000000000..27509d3936e9 --- /dev/null +++ b/sbin/fsck/pass5.c @@ -0,0 +1,328 @@ +/* + * 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.9 (Berkeley) 4/28/95"; +#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 <string.h> + +#include "fsck.h" + +void +pass5() +{ + int c, blk, frags, basesize, sumsize, mapsize, savednrpos; + struct fs *fs = &sblock; + struct cg *cg = &cgrp; + ufs_daddr_t dbase, dmax; + ufs_daddr_t d; + 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; + + statemap[WINO] = USTATE; + memset(newcg, 0, (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_firstfield); + sumsize = &ocg->cg_iused[0] - (u_int8_t *)(&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_firstfield); + 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_firstfield); + sumsize = newcg->cg_iusedoff - newcg->cg_btotoff; + mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff; + break; + + default: + sumsize = 0; /* keep lint happy */ + errx(EEXIT, "UNKNOWN ROTATIONAL TABLE FORMAT %d", + fs->fs_postblformat); + } + memset(&idesc[0], 0, sizeof idesc); + for (i = 0; i < 3; i++) { + idesc[i].id_type = ADDR; + if (doinglevel2) + idesc[i].id_fix = FIX; + } + memset(&cstotal, 0, 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; + memset(&newcg->cg_frsum[0], 0, sizeof newcg->cg_frsum); + memset(&cg_blktot(newcg)[0], 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; + errx(EEXIT, "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) { + int32_t *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 (memcmp(&newcg->cg_cs, cs, sizeof *cs) != 0 && + dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) { + memmove(cs, &newcg->cg_cs, sizeof *cs); + sbdirty(); + } + if (doinglevel1) { + memmove(cg, newcg, (size_t)fs->fs_cgsize); + cgdirty(); + continue; + } + if (memcmp(cg_inosused(newcg), + cg_inosused(cg), mapsize) != 0 && + dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) { + memmove(cg_inosused(cg), cg_inosused(newcg), + (size_t)mapsize); + cgdirty(); + } + if ((memcmp(newcg, cg, basesize) != 0 || + memcmp(&cg_blktot(newcg)[0], + &cg_blktot(cg)[0], sumsize) != 0) && + dofix(&idesc[2], "SUMMARY INFORMATION BAD")) { + memmove(cg, newcg, (size_t)basesize); + memmove(&cg_blktot(cg)[0], + &cg_blktot(newcg)[0], (size_t)sumsize); + cgdirty(); + } + } + if (fs->fs_postblformat == FS_42POSTBLFMT) + fs->fs_nrpos = savednrpos; + if (memcmp(&cstotal, &fs->fs_cstotal, sizeof *cs) != 0 + && dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) { + memmove(&fs->fs_cstotal, &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 000000000000..b5fae6b24f7d --- /dev/null +++ b/sbin/fsck/preen.c @@ -0,0 +1,370 @@ +/* + * 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.5 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <ufs/ufs/dinode.h> + +#include <ctype.h> +#include <fstab.h> +#include <string.h> + +#include "fsck.h" + +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; + +static void addpart __P((char *name, char *fsname, long auxdata)); +static struct disk *finddisk __P((char *name)); +static char *rawname __P((char *name)); +static int startdisk __P((struct disk *dk, + int (*checkit)(char *, char *, long, int))); +static char *unrawname __P((char *name)); + +int +checkfstab(preen, maxrun, docheck, chkit) + int preen; + int maxrun; + int (*docheck)(struct fstab *); + int (*chkit)(char *, char *, long, int); +{ + 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)) != 0) { + if ((sumstatus = (*chkit)(name, + fsp->fs_file, auxdata, 0)) != 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); +} + +static struct disk * +finddisk(name) + char *name; +{ + register struct disk *dk, **dkp; + register char *p; + size_t len; + + for (len = strlen(name), p = name + len - 1; p >= name; --p) + if (isdigit(*p)) { + len = p - name + 1; + break; + } + + 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); +} + +static void +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; +} + +static int +startdisk(dk, checkit) + register struct disk *dk; + int (*checkit)(char *, char *, long, int); +{ + 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(origname) + char *origname; +{ + struct stat stslash, stblock, stchar; + char *newname, *raw; + int retried = 0; + + hotroot = 0; + if (stat("/", &stslash) < 0) { + perror("/"); + printf("Can't stat root\n"); + return (origname); + } + newname = origname; +retry: + if (stat(newname, &stblock) < 0) { + perror(newname); + printf("Can't stat %s\n", newname); + return (origname); + } + if ((stblock.st_mode & S_IFMT) == S_IFBLK) { + if (stslash.st_dev == stblock.st_rdev) + hotroot++; + raw = rawname(newname); + if (stat(raw, &stchar) < 0) { + perror(raw); + printf("Can't stat %s\n", raw); + return (origname); + } + if ((stchar.st_mode & S_IFMT) == S_IFCHR) { + return (raw); + } else { + printf("%s is not a character device\n", raw); + return (origname); + } + } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) { + newname = unrawname(newname); + retried++; + goto retry; + } + /* + * Not a block or character device, just return name and + * let the user decide whether to use it. + */ + return (origname); +} + +static char * +unrawname(name) + char *name; +{ + char *dp; + struct stat stb; + + if ((dp = strrchr(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); +} + +static char * +rawname(name) + char *name; +{ + static char rawbuf[32]; + char *dp; + + if ((dp = strrchr(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 000000000000..a9ab99416613 --- /dev/null +++ b/sbin/fsck/setup.c @@ -0,0 +1,500 @@ +/* + * 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.10 (Berkeley) 5/9/95"; +#endif /* not lint */ + +#define DKTYPENAMES +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/disklabel.h> +#include <sys/file.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <string.h> + +#include "fsck.h" + +struct bufarea asblk; +#define altsblock (*asblk.b_un.b_fs) +#define POWEROF2(num) (((num) & ((num) - 1)) == 0) + +static void badsb __P((int listerr, char *s)); +static int calcsb __P((char *dev, int devfd, struct fs *fs)); +static struct disklabel *getdisklabel __P((char *s, int fd)); +static int readsb __P((int listerr)); + +/* + * Read in a superblock finding an alternate if necessary. + * Return 1 if successful, 0 if unsuccessful, -1 if filesystem + * is already clean (preen mode only). + */ +int +setup(dev) + char *dev; +{ + long cg, size, asked, i, j; + long skipclean, bmapsize; + struct disklabel *lp; + off_t sizepb; + struct stat statb; + struct fs proto; + + havesb = 0; + fswritefd = -1; + skipclean = preen; + 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) + errx(EEXIT, "cannot allocate space for superblock"); + if (lp = getdisklabel(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) { + skipclean = 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); + } + if (skipclean && sblock.fs_clean) { + pwarn("FILESYSTEM CLEAN; SKIPPING CHECKS\n"); + return (-1); + } + 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_firstfield); + sblock.fs_rotbloff = &sblock.fs_space[0] - + (u_char *)(&sblock.fs_firstfield); + sblock.fs_cgsize = + fragroundup(&sblock, CGSIZE(&sblock)); + sbdirty(); + dirty(&asblk); + } + if (asblk.b_dirty && !bflag) { + memmove(&altsblock, &sblock, (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) + exit(EEXIT); + 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(0); + return (0); +} + +/* + * Read in the super block and its summary info. + */ +static int +readsb(listerr) + int listerr; +{ + ufs_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_firstfield = sblock.fs_firstfield; + altsblock.fs_unused_1 = sblock.fs_unused_1; + 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; + memmove(altsblock.fs_csp, sblock.fs_csp, sizeof sblock.fs_csp); + altsblock.fs_maxcluster = sblock.fs_maxcluster; + memmove(altsblock.fs_fsmnt, sblock.fs_fsmnt, sizeof sblock.fs_fsmnt); + memmove(altsblock.fs_sparecon, + sblock.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_state = sblock.fs_state; + 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 (memcmp(&sblock, &altsblock, (int)sblock.fs_sbsize)) { + if (debug) { + long *nlp, *olp, *endlp; + + printf("superblock mismatches\n"); + nlp = (long *)&altsblock; + olp = (long *)&sblock; + endlp = olp + (sblock.fs_sbsize / sizeof *olp); + for ( ; olp < endlp; olp++, nlp++) { + if (*olp == *nlp) + continue; + printf("offset %d, original %d, alternate %d\n", + olp - (long *)&sblock, *olp, *nlp); + } + } + badsb(listerr, + "VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE"); + return (0); + } + havesb = 1; + return (1); +} + +static void +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! + */ +static int +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 = strchr(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); + } + memset(fs, 0, 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); +} + +static 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)); + errx(EEXIT, "%s: can't read disk label", s); + } + return (&lab); +} diff --git a/sbin/fsck/utilities.c b/sbin/fsck/utilities.c new file mode 100644 index 000000000000..a5c56da04d04 --- /dev/null +++ b/sbin/fsck/utilities.c @@ -0,0 +1,628 @@ +/* + * 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.6 (Berkeley) 5/19/95"; +#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 <ctype.h> +#include <err.h> +#include <string.h> + +#include "fsck.h" + +long diskreads, totalreads; /* Disk cache statistics */ + +static void rwerror __P((char *mesg, ufs_daddr_t blk)); + +int +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); + } +} + +int +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. + */ +void +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) + errx(EEXIT, "cannot allocate buffer pool"); + 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; + errx(EEXIT, "cannot allocate buffer pool"); + } + 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) + ufs_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) + errx(EEXIT, "deadlocked buffer pool"); + 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; + ufs_daddr_t blk; + long size; +{ + ufs_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; + } +} + +void +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); + } +} + +static void +rwerror(mesg, blk) + char *mesg; + ufs_daddr_t blk; +{ + + if (preen == 0) + printf("\n"); + pfatal("CANNOT %s: BLK %ld", mesg, blk); + if (reply("CONTINUE") == 0) + exit(EEXIT); +} + +void +ckfini(markclean) + int markclean; +{ + register struct bufarea *bp, *nbp; + int ofsmodified, 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) + errx(EEXIT, "Panic: lost %d buffers", bufhead.b_size - cnt); + pbp = pdirbp = (struct bufarea *)0; + if (markclean && sblock.fs_clean == 0) { + sblock.fs_clean = 1; + sbdirty(); + ofsmodified = fsmodified; + flush(fswritefd, &sblk); + fsmodified = ofsmodified; + if (!preen) + printf("\n***** FILE SYSTEM MARKED CLEAN *****\n"); + } + if (debug) + printf("cache missed %ld of %ld (%d%%)\n", diskreads, + totalreads, (int)(diskreads * 100 / totalreads)); + (void)close(fsreadfd); + (void)close(fswritefd); +} + +int +bread(fd, buf, blk, size) + int fd; + char *buf; + ufs_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; + memset(buf, 0, (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); +} + +void +bwrite(fd, buf, blk, size) + int fd; + char *buf; + ufs_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 + */ +ufs_daddr_t +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 + */ +void +freeblk(blkno, frags) + ufs_daddr_t blkno; + long frags; +{ + struct inodesc idesc; + + idesc.id_blkno = blkno; + idesc.id_numfrags = frags; + (void)pass4check(&idesc); +} + +/* + * Find a pathname + */ +void +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; + memset(&idesc, 0, 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; + memmove(cp, namebuf, (size_t)len); + *--cp = '/'; + if (cp < &namebuf[MAXNAMLEN]) + break; + ino = idesc.id_number; + } + busy = 0; + if (ino != ROOTINO) + *--cp = '?'; + memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp)); +} + +void +catch(sig) + int sig; +{ + if (!doinglevel2) + ckfini(0); + 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(sig) + int sig; +{ + 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(sig) + int sig; +{ + + sleep(1); + (void)signal(SIGQUIT, SIG_IGN); + (void)signal(SIGQUIT, SIG_DFL); +} + +/* + * determine whether an inode should be fixed. + */ +int +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: + errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix); + } + /* NOTREACHED */ + return (0); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +/* + * An unexpected inconsistency occured. + * Die if preening, otherwise just print message and continue. + */ +void +#if __STDC__ +pfatal(const char *fmt, ...) +#else +pfatal(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + if (!preen) { + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + return; + } + (void)fprintf(stderr, "%s: ", cdevname); + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, + "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", + cdevname); + exit(EEXIT); +} + +/* + * Pwarn just prints a message when not preening, + * or a warning (preceded by filename) when preening. + */ +void +#if __STDC__ +pwarn(const char *fmt, ...) +#else +pwarn(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + if (preen) + (void)fprintf(stderr, "%s: ", cdevname); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); +} + +/* + * Stub for routines from kernel. + */ +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 + pfatal("INTERNAL INCONSISTENCY:"); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + exit(EEXIT); +} diff --git a/sbin/mount_cd9660/mount_cd9660.c b/sbin/mount_cd9660/mount_cd9660.c new file mode 100644 index 000000000000..91cf5afca3f0 --- /dev/null +++ b/sbin/mount_cd9660/mount_cd9660.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 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.7 (Berkeley) 5/1/95 + */ + +#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.7 (Berkeley) 5/1/95"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/../isofs/cd9660/cd9660_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; + + 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(optarg, mopts, &mntflags, 0); + 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 + /* + * ISO 9660 filesystems are not writeable. + */ + mntflags |= MNT_RDONLY; + args.export.ex_flags = MNT_EXRDONLY; + args.fspec = dev; + args.export.ex_root = DEFAULT_ROOTUID; + args.flags = opts; + + if (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/mount_fdesc.c b/sbin/mount_fdesc/mount_fdesc.c new file mode 100644 index 000000000000..e2bef61bf596 --- /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.3 (Berkeley) 4/26/95"; +#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, 0); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (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/mount_kernfs.c b/sbin/mount_kernfs/mount_kernfs.c new file mode 100644 index 000000000000..69fcb77c78b3 --- /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.3 (Berkeley) 5/4/95"; +#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, 0); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (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/mount_lfs.c b/sbin/mount_lfs/mount_lfs.c new file mode 100644 index 000000000000..67b7f80e3cf5 --- /dev/null +++ b/sbin/mount_lfs/mount_lfs.c @@ -0,0 +1,148 @@ +/*- + * 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.4 (Berkeley) 4/26/95"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> +#include <ufs/ufs/ufsmount.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, 0); + 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("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_nfs/mount_nfs.8 b/sbin/mount_nfs/mount_nfs.8 new file mode 100644 index 000000000000..07d2c5ff5baa --- /dev/null +++ b/sbin/mount_nfs/mount_nfs.8 @@ -0,0 +1,241 @@ +.\" Copyright (c) 1992, 1993, 1994, 1995 +.\" 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.3 (Berkeley) 3/29/95 +.\" +.Dd March 29, 1995 +.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 3KPTUbcdilqs +.Op Fl D Ar deadthresh +.Op Fl I Ar readdirsize +.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 and +.%T "NFS: Network File System Version 3 Protocol Specification" , +Appendix I. +.Pp +The options are: +.Bl -tag -width indent +.It Fl 3 +Use the NFS Version 3 protocol (Version 2 is the default). +.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 I +Set the readdir read size to the specified value. The value should normally +be a multiple of DIRBLKSIZ that is <= the read size for the mount. +.It Fl K +Pass Kerberos authenticators to the server for client-to-server +user-credential mapping. +This requires that the kernel be built with the NFSKERB option. +(Refer to the INTERNET-DRAFT titled +.%T "Authentication Mechanisms for ONC RPC" , +for more information.) +.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 P +Use a reserved socket port number. +This is useful for mounting servers that require clients to use a +reserved port number on the mistaken belief that this makes NFS +more secure. (For the rare case where the client has a trusted root account +but untrusworthy users and the network cables are in secure areas this does +help, but for normal desktop clients this does not apply.) +.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 U +Force the mount protocol to use UDP transport, even for TCP NFS mounts. +(Necessary for some old 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. +Trying a value greater than 1 for this is suggested 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 NFS port number 2049. +.It Fl d +Turn off the dynamic retransmit timeout estimator. +This may be useful for UDP mounts that exhibit high retry rates, +since it is possible that the dynamically estimated timeout interval is too +short. +.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 l +Used with NQNFS and NFSV3 to specify that the \fBReaddirPlus\fR RPC should +be used. +This option reduces RPC traffic for cases such as +.Dq "ls -l" , +but tends to flood the attribute and name caches with prefetched entries. +Try this option and see whether performance improves or degrades. Probably +most useful for client to server network interconnects with a large bandwidth +times delay product. +.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 NFS Version 3 protocol +to maintain cache consistency. +This protocol version 2 revision to Not Quite Nfs (NQNFS) +is only supported by this updated release of NFS code. +It is not backwards compatible with the version 1 NQNFS protocol +that was part of the first release of 4.4BSD-Lite. +To interoperate with a first release 4.4BSD-Lite NFS system you will have to +avoid this option until you have had an oppurtunity to upgrade the NFS code +to release 2 of 4.4BSD-Lite on all your systems. +.It Fl r +Set the read data size to the specified value. +It should normally 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. +(Normally, the -d option should be specified when using this option to manually +tune the timeout +interval.) +.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 000000000000..91508d3f7021 --- /dev/null +++ b/sbin/mount_nfs/mount_nfs.c @@ -0,0 +1,745 @@ +/* + * 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.11 (Berkeley) 5/4/95"; +#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 NFSKERB +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> +#endif + +#include <nfs/rpcv2.h> +#include <nfs/nfsproto.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" + +#define ALTF_BG 0x1 +#define ALTF_NOCONN 0x2 +#define ALTF_DUMBTIMR 0x4 +#define ALTF_INTR 0x8 +#define ALTF_KERB 0x10 +#define ALTF_NFSV3 0x20 +#define ALTF_RDIRPLUS 0x40 +#define ALTF_MNTUDP 0x80 +#define ALTF_RESVPORT 0x100 +#define ALTF_SEQPACKET 0x200 +#define ALTF_NQNFS 0x400 +#define ALTF_SOFT 0x800 +#define ALTF_TCP 0x1000 + +struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_FORCE, + MOPT_UPDATE, + { "bg", 0, ALTF_BG, 1 }, + { "conn", 1, ALTF_NOCONN, 1 }, + { "dumbtimer", 0, ALTF_DUMBTIMR, 1 }, + { "intr", 0, ALTF_INTR, 1 }, +#ifdef NFSKERB + { "kerb", 0, ALTF_KERB, 1 }, +#endif + { "nfsv3", 0, ALTF_NFSV3, 1 }, + { "rdirplus", 0, ALTF_RDIRPLUS, 1 }, + { "mntudp", 0, ALTF_MNTUDP, 1 }, + { "resvport", 0, ALTF_RESVPORT, 1 }, +#ifdef ISO + { "seqpacket", 0, ALTF_SEQPACKET, 1 }, +#endif + { "nqnfs", 0, ALTF_NQNFS, 1 }, + { "soft", 0, ALTF_SOFT, 1 }, + { "tcp", 0, ALTF_TCP, 1 }, + { NULL } +}; + +struct nfs_args nfsdefargs = { + NFS_ARGSVERSION, + (struct sockaddr *)0, + sizeof (struct sockaddr_in), + SOCK_DGRAM, + 0, + (u_char *)0, + 0, + 0, + NFS_WSIZE, + NFS_RSIZE, + NFS_READDIRSIZE, + 10, + NFS_RETRANS, + NFS_MAXGRPS, + NFS_DEFRAHEAD, + NQ_DEFLEASE, + NQ_DEADTHRESH, + (char *)0, +}; + +struct nfhret { + u_long stat; + long vers; + long auth; + long fhsize; + u_char nfh[NFSX_V3FHMAX]; +}; +#define DEF_RETRY 10000 +#define BGRND 1 +#define ISBGRND 2 +int retrycnt = DEF_RETRY; +int opflags = 0; +int nfsproto = IPPROTO_UDP; +int mnttcp_ok = 1; + +#ifdef NFSKERB +char inst[INST_SZ]; +char realm[REALM_SZ]; +struct { + u_long kind; + KTEXT_ST kt; +} ktick; +struct nfsrpc_nickverf kverf; +struct nfsrpc_fullblock kin, kout; +NFSKERBKEY_T kivec; +CREDENTIALS kcr; +struct timeval ktv; +NFSKERBKEYSCHED_T kerb_keysched; +#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, altflags, i, nfssvc_flag, num; + char *name, *p, *spec; + int error = 0; +#ifdef NFSKERB + uid_t last_ruid; + + last_ruid = -1; + (void)strcpy(realm, KRB_REALM); + if (sizeof (struct nfsrpc_nickverf) != RPCX_NICKVERF || + sizeof (struct nfsrpc_fullblock) != RPCX_FULLBLOCK || + ((char *)&ktick.kt) - ((char *)&ktick) != NFSX_UNSIGNED || + ((char *)ktick.kt.dat) - ((char *)&ktick) != 2 * NFSX_UNSIGNED) + fprintf(stderr, "Yikes! NFSKERB structs not packed!!\n"); +#endif /* NFSKERB */ + retrycnt = DEF_RETRY; + + mntflags = 0; + altflags = 0; + nfsargs = nfsdefargs; + nfsargsp = &nfsargs; + while ((c = getopt(argc, argv, + "3a:bcdD:g:I:iKL:lm:o:PpqR:r:sTt:w:x:U")) != EOF) + switch (c) { + case '3': + nfsargsp->flags |= NFSMNT_NFSV3; + break; + 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': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -I value -- %s", optarg); + nfsargsp->readdirsize = num; + nfsargsp->flags |= NFSMNT_READDIRSIZE; + break; + case 'i': + nfsargsp->flags |= NFSMNT_INT; + break; +#ifdef NFSKERB + case 'K': + nfsargsp->flags |= NFSMNT_KERB; + break; +#endif + 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_RDIRPLUS; + break; +#ifdef NFSKERB + case 'm': + (void)strncpy(realm, optarg, REALM_SZ - 1); + realm[REALM_SZ - 1] = '\0'; + break; +#endif + case 'o': + getmntopts(optarg, mopts, &mntflags, &altflags); + if(altflags & ALTF_BG) + opflags |= BGRND; + if(altflags & ALTF_NOCONN) + nfsargsp->flags |= NFSMNT_NOCONN; + if(altflags & ALTF_DUMBTIMR) + nfsargsp->flags |= NFSMNT_DUMBTIMR; + if(altflags & ALTF_INTR) + nfsargsp->flags |= NFSMNT_INT; +#ifdef NFSKERB + if(altflags & ALTF_KERB) + nfsargsp->flags |= NFSMNT_KERB; +#endif + if(altflags & ALTF_NFSV3) + nfsargsp->flags |= NFSMNT_NFSV3; + if(altflags & ALTF_RDIRPLUS) + nfsargsp->flags |= NFSMNT_RDIRPLUS; + if(altflags & ALTF_MNTUDP) + mnttcp_ok = 0; + if(altflags & ALTF_RESVPORT) + nfsargsp->flags |= NFSMNT_RESVPORT; +#ifdef ISO + if(altflags & ALTF_SEQPACKET) + nfsargsp->sotype = SOCK_SEQPACKET; +#endif + if(altflags & ALTF_NQNFS) + nfsargsp->flags |= (NFSMNT_NQNFS|NFSMNT_NFSV3); + if(altflags & ALTF_SOFT) + nfsargsp->flags |= NFSMNT_SOFT; + if(altflags & ALTF_TCP) { + nfsargsp->sotype = SOCK_STREAM; + nfsproto = IPPROTO_TCP; + } + altflags = 0; + break; + case 'P': + nfsargsp->flags |= NFSMNT_RESVPORT; + break; +#ifdef ISO + case 'p': + nfsargsp->sotype = SOCK_SEQPACKET; + break; +#endif + case 'q': + nfsargsp->flags |= (NFSMNT_NQNFS | NFSMNT_NFSV3); + 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; + nfsproto = IPPROTO_TCP; + 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; + case 'U': + mnttcp_ok = 0; + break; + default: + usage(); + break; + } + argc -= optind; + argv += optind; + + if (argc != 2) { + usage(); + /* NOTREACHED */ + } + + spec = *argv++; + name = *argv; + + if (!getnfsargs(spec, nfsargsp)) + exit(1); + + if (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 NFSKERB + /* + * 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. + * Get the Kerberos credential structure so that + * we have the seesion key and get a ticket for + * this uid. + * For more info see the IETF Draft "Authentication + * in ONC RPC". + */ + if (ncd.ncd_authuid != last_ruid) { + krb_set_tkt_string(""); + last_ruid = ncd.ncd_authuid; + } + setreuid(ncd.ncd_authuid, 0); + kret = krb_get_cred(NFS_KERBSRV, inst, realm, &kcr); + if (kret == RET_NOTKT) { + kret = get_ad_tkt(NFS_KERBSRV, inst, realm, + DEFAULT_TKT_LIFE); + if (kret == KSUCCESS) + kret = krb_get_cred(NFS_KERBSRV, inst, realm, + &kcr); + } + if (kret == KSUCCESS) + kret = krb_mk_req(&ktick.kt, NFS_KERBSRV, inst, + realm, 0); + + /* + * Fill in the AKN_FULLNAME authenticator and verfier. + * Along with the Kerberos ticket, we need to build + * the timestamp verifier and encrypt it in CBC mode. + */ + if (kret == KSUCCESS && + ktick.kt.length <= (RPCAUTH_MAXSIZ-3*NFSX_UNSIGNED) + && gettimeofday(&ktv, (struct timezone *)0) == 0) { + ncd.ncd_authtype = RPCAUTH_KERB4; + ncd.ncd_authstr = (u_char *)&ktick; + ncd.ncd_authlen = nfsm_rndup(ktick.kt.length) + + 3 * NFSX_UNSIGNED; + ncd.ncd_verfstr = (u_char *)&kverf; + ncd.ncd_verflen = sizeof (kverf); + memmove(ncd.ncd_key, kcr.session, + sizeof (kcr.session)); + kin.t1 = htonl(ktv.tv_sec); + kin.t2 = htonl(ktv.tv_usec); + kin.w1 = htonl(NFS_KERBTTL); + kin.w2 = htonl(NFS_KERBTTL - 1); + bzero((caddr_t)kivec, sizeof (kivec)); + + /* + * Encrypt kin in CBC mode using the session + * key in kcr. + */ + XXX + + /* + * Finally, fill the timestamp verifier into the + * authenticator and verifier. + */ + ktick.kind = htonl(RPCAKN_FULLNAME); + kverf.kind = htonl(RPCAKN_FULLNAME); + NFS_KERBW1(ktick.kt) = kout.w1; + ktick.kt.length = htonl(ktick.kt.length); + kverf.verf.t1 = kout.t1; + kverf.verf.t2 = kout.t2; + kverf.verf.w2 = kout.w2; + nfssvc_flag = NFSSVC_MNTD | NFSSVC_GOTAUTH; + } + setreuid(0, 0); +#endif /* NFSKERB */ + } + } + 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, nfsvers, mntvers; + char *hostp, *delimp; +#ifdef NFSKERB + 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); + } + memset(&isoaddr, 0, sizeof (isoaddr)); + memmove(&isoaddr.siso_addr, isop, sizeof (struct iso_addr)); + isoaddr.siso_len = sizeof (isoaddr); + isoaddr.siso_family = AF_ISO; + isoaddr.siso_tlen = 2; + isoport = htons(NFS_PORT); + memmove(TSEL(&isoaddr), &isoport, 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); + } + } else if ((hp = gethostbyname(hostp)) != NULL) + memmove(&saddr.sin_addr, hp->h_addr, hp->h_length); + else { + warnx("can't get net id for host"); + return (0); + } +#ifdef NFSKERB + if ((nfsargsp->flags & NFSMNT_KERB)) { + if ((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); + } + memmove(&saddr.sin_addr, hp->h_addr, hp->h_length); + strncpy(inst, hp->h_name, INST_SZ); + inst[INST_SZ - 1] = '\0'; + if (cp = strchr(inst, '.')) + *cp = '\0'; + } +#endif /* NFSKERB */ + + if (nfsargsp->flags & NFSMNT_NFSV3) { + nfsvers = 3; + mntvers = 3; + } else { + nfsvers = 2; + mntvers = 1; + } + 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, + nfsvers, nfsproto)) == 0) { + if ((opflags & ISBGRND) == 0) + clnt_pcreateerror("NFS Portmap"); + } else { + saddr.sin_port = 0; + pertry.tv_sec = 10; + pertry.tv_usec = 0; + if (mnttcp_ok && nfsargsp->sotype == SOCK_STREAM) + clp = clnttcp_create(&saddr, RPCPROG_MNT, mntvers, + &so, 0, 0); + else + clp = clntudp_create(&saddr, RPCPROG_MNT, mntvers, + pertry, &so); + if (clp == 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; + if (nfsargsp->flags & NFSMNT_KERB) + nfhret.auth = RPCAUTH_KERB4; + else + nfhret.auth = RPCAUTH_UNIX; + nfhret.vers = mntvers; + 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); + warnx("can't access %s: %s", spec, strerror(nfhret.stat)); + 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->fhsize = nfhret.fhsize; + 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; + register struct nfhret *np; +{ + register int i; + long auth, authcnt, authfnd = 0; + + if (!xdr_u_long(xdrsp, &np->stat)) + return (0); + if (np->stat) + return (1); + switch (np->vers) { + case 1: + np->fhsize = NFSX_V2FH; + return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH)); + case 3: + if (!xdr_long(xdrsp, &np->fhsize)) + return (0); + if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX) + return (0); + if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) + return (0); + if (!xdr_long(xdrsp, &authcnt)) + return (0); + for (i = 0; i < authcnt; i++) { + if (!xdr_long(xdrsp, &auth)) + return (0); + if (auth == np->auth) + authfnd++; + } + /* + * Some servers, such as DEC's OSF/1 return a nil authenticator + * list to indicate RPCAUTH_UNIX. + */ + if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX)) + np->stat = EAUTH; + return (1); + }; + return (0); +} + +__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/mount_null.8 b/sbin/mount_null/mount_null.8 new file mode 100644 index 000000000000..1bdc04f44a75 --- /dev/null +++ b/sbin/mount_null/mount_null.8 @@ -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 +.\" 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.6 (Berkeley) 5/1/95 +.\" +.\" +.Dd May 1, 1995 +.Dt MOUNT_NULL 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_null +.Nd mount a loopback filesystem sub-tree; +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. +This allows existing files and directories to be accessed +using a different pathname. +.Pp +The primary differences between a virtual copy of the filesystem +and a symbolic link are that +.Xr getcwd 3 +functions correctly in the virtual copy, and that other filesystems +may be mounted on the virtual copy without affecting the original. +A different device number for the virtual copy is returned by +.Xr stat 2 , +but in other respects it is indistinguishable from the original. +.Pp +The +.Nm mount_null +filesystem differs from a traditional +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 000000000000..f7568b6ac827 --- /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.6 (Berkeley) 4/26/95"; +#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, 0); + 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("loopback", 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/activate.c b/sbin/mount_portal/activate.c new file mode 100644 index 000000000000..b6d397124b58 --- /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.3 (Berkeley) 4/28/95 + * + * $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; + + memset(&msg, 0, 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 + */ + memset(&msg, 0, 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/mount_portal.c b/sbin/mount_portal/mount_portal.c new file mode 100644 index 000000000000..ad6d041dce0c --- /dev/null +++ b/sbin/mount_portal/mount_portal.c @@ -0,0 +1,259 @@ +/* + * 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.6 (Berkeley) 4/26/95"; +#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 && errno != ECHILD) + 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, 0); + 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("portal", mountpt, mntflags, &args); + if (rc < 0) + err(1, NULL); + + /* + * Everything is ready to go - now is a good time to fork + */ + daemon(0, 0); + + /* + * 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); + exit(0); + 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/pt_file.c b/sbin/mount_portal/pt_file.c new file mode 100644 index 000000000000..1d8e59489d06 --- /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.3 (Berkeley) 7/3/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; + gid_t 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 000000000000..47ca7bf20e2b --- /dev/null +++ b/sbin/mount_portal/pt_tcp.c @@ -0,0 +1,159 @@ +/* + * 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. + * + * @(#)pt_tcp.c 8.5 (Berkeley) 4/28/95 + * + * $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 = strtoul(port, &p, 0); + if (s_port == 0 || *p != '\0') + return (EINVAL); + s_port = htons(s_port); + } + + memset(&sain, 0, 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/mount_procfs.8 b/sbin/mount_procfs/mount_procfs.8 new file mode 100644 index 000000000000..db0eeab08488 --- /dev/null +++ b/sbin/mount_procfs/mount_procfs.8 @@ -0,0 +1,254 @@ +.\" +.\" 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.3 (Berkeley) 6/1/94 +.\" +.\" +.Dd June 1, 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. +.ne 1i +.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 000000000000..7e0f1876f089 --- /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.4 (Berkeley) 4/26/95"; +#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, 0); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (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/mount_umap.8 b/sbin/mount_umap/mount_umap.8 new file mode 100644 index 000000000000..54df4018e064 --- /dev/null +++ b/sbin/mount_umap/mount_umap.8 @@ -0,0 +1,130 @@ +.\" 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.4 (Berkeley) 5/1/95 +.\" +.Dd "May 1, 1995" +.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 +.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 000000000000..fb8349ccb66b --- /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.5 (Berkeley) 4/26/95"; +#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, 0); + 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(gfp, "%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(gfp, "%lu %lu\n", + &(gmapdata[count][0]), &(gmapdata[count][1]))) != 2) { + if (ferror(gfp)) + err(1, "%s%s", gmapfile, not); + if (feof(gfp)) + 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("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_union/Makefile b/sbin/mount_union/Makefile new file mode 100644 index 000000000000..d2f4e23203ce --- /dev/null +++ b/sbin/mount_union/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.4 (Berkeley) 7/13/94 + +PROG= mount_union +SRCS= mount_union.c getmntopts.c +MAN8= mount_union.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I/sys -I${MOUNT} +.PATH: ${MOUNT} + +.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 000000000000..93b191e09842 --- /dev/null +++ b/sbin/mount_union/mount_union.8 @@ -0,0 +1,201 @@ +.\" 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.7 (Berkeley) 5/1/95 +.\" +.Dd May 1, 1995 +.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_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_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 000000000000..971da643a3cd --- /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.6 (Berkeley) 4/26/95"; +#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, 0); + 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("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/exports.5 b/sbin/mountd/exports.5 new file mode 100644 index 000000000000..f4ae1665ae86 --- /dev/null +++ b/sbin/mountd/exports.5 @@ -0,0 +1,254 @@ +.\" 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.3 (Berkeley) 3/29/95 +.\" +.Dd March 29, 1995 +.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" +and +.%T "NFS: Network File System Version 3 Specification, Appendix I" . +.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 at any point within the filesystem, +including regular files if the +.Fl r +option is used on mountd. +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. +This option requires that the kernel be built with the NFSKERB option. +.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 000000000000..c6adaca9f47b --- /dev/null +++ b/sbin/mountd/mountd.8 @@ -0,0 +1,115 @@ +.\" 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.4 (Berkeley) 4/28/95 +.\" +.Dd April 28, 1995 +.Dt MOUNTD 8 +.Os +.Sh NAME +.Nm mountd +.Nd service remote +.Tn NFS +mount requests +.Sh SYNOPSIS +.Nm /sbin/mountd +.Op Fl nr +.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, Appendix A and +.%T "NFS: Network File System Version 3 Protocol Specification" , +Appendix I. +.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 Fl r +The +.Fl r +option allows mount RPCs requests for regular files to be served. +Although this seems to violate the mount protocol specification, +some diskless workstations do mount requests for +their swapfiles and expect them to be regular files. +Since a regular file cannot be specified in +.Pa /etc/exports , +the entire file system in which the swapfiles resides +will have to be exported with the +.Fl alldirs +flag. +.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 \-s 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 000000000000..706790983602 --- /dev/null +++ b/sbin/mountd/mountd.c @@ -0,0 +1,2064 @@ +/* + * 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.15 (Berkeley) 5/1/95"; +#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/nfsproto.h> +#include <ufs/ufs/ufsmount.h> +#include <sys/../isofs/cd9660/cd9660_mount.h> /* XXX need isofs in include */ + +#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 +#define DP_HOSTSET 0x2 +#define DP_KERB 0x4 + +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 { + int ht_flag; /* Uses DP_xx bits */ + struct grouplist *ht_grp; + struct hostlist *ht_next; +}; + +struct fhreturn { + int fhr_flag; + int fhr_vers; + nfsfh_t fhr_fh; +}; + +/* Global defs */ +char *add_expdir __P((struct dirlist **, char *, int)); +void add_dlist __P((struct dirlist **, struct dirlist *, + struct grouplist *, int)); +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 *, 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 *)); +int get_num __P((char *)); +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 *, caddr_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 resvport_only = 1; +int dir_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 *udptransp, *tcptransp; + int c; + + while ((c = getopt(argc, argv, "nr")) != EOF) + switch (c) { + case 'n': + resvport_only = 0; + break; + case 'r': + dir_only = 0; + break; + default: + fprintf(stderr, "Usage: mountd [-r] [-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 ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL || + (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) { + syslog(LOG_ERR, "Can't create socket"); + exit(1); + } + pmap_unset(RPCPROG_MNT, 1); + pmap_unset(RPCPROG_MNT, 3); + if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_UDP) || + !svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_UDP) || + !svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_TCP) || + !svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_TCP)) { + 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; + struct fhreturn fhr; + struct stat stb; + struct statfs fsb; + struct hostent *hp; + u_long saddr; + u_short sport; + char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN]; + int bad = ENOENT, defset, hostset; + sigset_t sighup_mask; + + sigemptyset(&sighup_mask); + sigaddset(&sighup_mask, SIGHUP); + saddr = transp->xp_raddr.sin_addr.s_addr; + sport = ntohs(transp->xp_raddr.sin_port); + 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 (sport >= IPPORT_RESERVED && resvport_only) { + 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 + * or a regular file if the -r option was specified + * and it exists. + */ + if (realpath(rpcpath, dirpath) == 0 || + stat(dirpath, &stb) < 0 || + (!S_ISDIR(stb.st_mode) && + (dir_only || !S_ISREG(stb.st_mode))) || + 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 */ + sigprocmask(SIG_BLOCK, &sighup_mask, NULL); + ep = ex_search(&fsb.f_fsid); + hostset = defset = 0; + if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) || + ((dp = dirp_search(ep->ex_dirl, dirpath)) && + chk_host(dp, saddr, &defset, &hostset)) || + (defset && scan_tree(ep->ex_defdir, saddr) == 0 && + scan_tree(ep->ex_dirl, saddr) == 0))) { + if (hostset & DP_HOSTSET) + fhr.fhr_flag = hostset; + else + fhr.fhr_flag = defset; + fhr.fhr_vers = rqstp->rq_vers; + /* Get the file handle */ + memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t)); + if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 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"); + sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); + return; + } + if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr)) + 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"); + } + sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); + 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 (sport >= IPPORT_RESERVED && resvport_only) { + 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 (sport >= IPPORT_RESERVED && resvport_only) { + 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 file handle reply + */ +int +xdr_fhs(xdrsp, cp) + XDR *xdrsp; + caddr_t cp; +{ + register struct fhreturn *fhrp = (struct fhreturn *)cp; + long ok = 0, len, auth; + + if (!xdr_long(xdrsp, &ok)) + return (0); + switch (fhrp->fhr_vers) { + case 1: + return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH)); + case 3: + len = NFSX_V3FH; + if (!xdr_long(xdrsp, &len)) + return (0); + if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len)) + return (0); + if (fhrp->fhr_flag & DP_KERB) + auth = RPCAUTH_KERB4; + else + auth = RPCAUTH_UNIX; + len = 1; + if (!xdr_long(xdrsp, &len)) + return (0); + return (xdr_long(xdrsp, &auth)); + }; + return (0); +} + +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 putdef; + sigset_t sighup_mask; + + sigemptyset(&sighup_mask); + sigaddset(&sighup_mask, SIGHUP); + sigprocmask(SIG_BLOCK, &sighup_mask, NULL); + 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; + } + sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); + if (!xdr_bool(xdrsp, &false)) + return (0); + return (1); +errout: + sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); + 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 "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; + + if (!strcmp(fsp->f_fstypename, "mfs") || + !strcmp(fsp->f_fstypename, "ufs") || + !strcmp(fsp->f_fstypename, "cd9660")) { + targs.ua.fspec = NULL; + targs.ua.export.ex_flags = MNT_DELEXPORT; + if (mount(fsp->f_fstypename, 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); + endnetgrent(); + 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); + grp->gr_next = grphead; + grphead = tgrp; + } else { + hang_dirp(dirhead, (struct grouplist *)NULL, ep, + opt_flags); + 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(); + memset(ep, 0, 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(); + memset(gp, 0, 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, flags) + struct dirlist *dp; + struct grouplist *grp; + struct exportlist *ep; + int flags; +{ + struct hostlist *hp; + struct dirlist *dp2; + + if (flags & OP_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; + if (flags & OP_KERB) + ep->ex_defdir->dp_flag |= DP_KERB; + } else while (grp) { + hp = get_ht(); + if (flags & OP_KERB) + hp->ht_flag |= DP_KERB; + 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, flags); + 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, flags) + struct dirlist **dpp; + struct dirlist *newdp; + struct grouplist *grp; + int flags; +{ + 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, flags); + return; + } else if (cmp < 0) { + add_dlist(&dp->dp_right, newdp, grp, flags); + 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(); + if (flags & OP_KERB) + hp->ht_flag |= DP_KERB; + 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; + if (flags & OP_KERB) + dp->dp_flag |= DP_KERB; + } +} + +/* + * 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, hostsetp) + struct dirlist *dp; + u_long saddr; + int *defsetp; + int *hostsetp; +{ + struct hostlist *hp; + struct grouplist *grp; + u_long **addrp; + + if (dp) { + if (dp->dp_flag & DP_DEFSET) + *defsetp = dp->dp_flag; + 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) { + *hostsetp = (hp->ht_flag | DP_HOSTSET); + return (1); + } + addrp++; + } + break; + case GT_NET: + if ((saddr & grp->gr_ptr.gt_net.nt_mask) == + grp->gr_ptr.gt_net.nt_net) { + *hostsetp = (hp->ht_flag | DP_HOSTSET); + 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, hostset; + + if (dp) { + if (scan_tree(dp->dp_left, saddr)) + return (1); + if (chk_host(dp, saddr, &defset, &hostset)) + 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 = strchr(cpopt, ',')) { + *cpoptend++ = '\0'; + if (cpoptarg = strchr(cpopt, '=')) + *cpoptarg++ = '\0'; + } else { + if (cpoptarg = strchr(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 for %s", cp); + 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 for %s", cp); + 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(); + memmove(nhp, hp, sizeof(struct hostent)); + i = strlen(hp->h_name)+1; + nhp->h_name = (char *)malloc(i); + if (nhp->h_name == (char *)NULL) + out_of_mem(); + memmove(nhp->h_name, hp->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(); + memmove(*naddrp, *addrp, 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; + hp->ht_flag = 0; + 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(); + memset(isoaddr, 0, sizeof(struct sockaddr_iso)); + memmove(&isoaddr->siso_addr, isop, 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; + memset(&sin, 0, sizeof(sin)); + memset(&imask, 0, 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 "ufs". + */ + while (mount(fsb->f_fstypename, 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, "Could not remount %s: %m", + dirp); + 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 *host, *dirp, *cp; + 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) { + cp = str; + host = strsep(&cp, " \t\n"); + dirp = strsep(&cp, " \t\n"); + if (host == NULL || dirp == NULL) + continue; + mlp = (struct mountlist *)malloc(sizeof (*mlp)); + strncpy(mlp->ml_host, host, 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; + 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 || !S_ISDIR(sb.st_mode)) + ret = 0; + *cp = '/'; + } + cp++; + } + if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) + ret = 0; + return (ret); +} + +/* + * Just translate an ascii string to an integer. + */ +int +get_num(cp) + register char *cp; +{ + register int res = 0; + + while (*cp) { + if (*cp < '0' || *cp > '9') + return (-1); + res = res * 10 + (*cp++ - '0'); + } + return (res); +} diff --git a/sbin/newlfs/config.h b/sbin/newlfs/config.h new file mode 100644 index 000000000000..2747f61d1bf8 --- /dev/null +++ b/sbin/newlfs/config.h @@ -0,0 +1,140 @@ +/*- + * 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.3 (Berkeley) 5/24/95 + */ + +/* + * 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 + +#define DFL_LFSFRAG 4096 +#define DFL_LFS_FFMASK DFL_LFSBLOCK_MASK +#define DFL_LFS_FFSHIFT DFL_LFSBLOCK_SHIFT +#define DFL_LFS_FBMASK 0 +#define DFL_LFS_FBSHIFT 0 diff --git a/sbin/newlfs/extern.h b/sbin/newlfs/extern.h new file mode 100644 index 000000000000..27da83561bc4 --- /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.2 (Berkeley) 5/24/95 + */ + +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)); +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 000000000000..dc271c4b2782 --- /dev/null +++ b/sbin/newlfs/lfs.c @@ -0,0 +1,683 @@ +/*- + * 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.5 (Berkeley) 5/24/95"; +#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_LFSFRAG, + /* 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 */ DFL_LFS_FFMASK, + /* lfs_ffshift */ DFL_LFS_FFSHIFT, + /* lfs_fbmask */ DFL_LFS_FBMASK, + /* lfs_fbshift */ DFL_LFS_FBSHIFT, + /* 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, + /* lfs_maxsymlinklen */ MAXSYMLINKLEN +}; + + +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, frag_size, seg_size) + int fd; + struct disklabel *lp; + struct partition *partp; + int minfree; + int block_size; + int frag_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 fsize; /* Fragment 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 (!(fsize = frag_size)) + fsize = DFL_LFSFRAG; + if (!(ssize = seg_size)) + ssize = DFL_LFSSEG; + + /* Modify parts of superblock overridden by command line arguments */ + if (bsize != DFL_LFSBLOCK || fsize != DFL_LFSFRAG) { + 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 = fsize; + lfsp->lfs_bmask = bsize - 1; + lfsp->lfs_inopb = bsize / sizeof(struct dinode); + lfsp->lfs_ffmask = fsize - 1; + lfsp->lfs_ffshift = log2(fsize); + if (1 << lfsp->lfs_ffshift != fsize) + fatal("%d: frag size not a power of 2", fsize); + lfsp->lfs_frag = numfrags(lfsp, bsize); + lfsp->lfs_fbmask = lfsp->lfs_frag - 1; + lfsp->lfs_fbshift = log2(lfsp->lfs_frag); +/* 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; + memset(dip, 0, 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 */ + memset(ipagep, 0, 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; + + memset(ipagep, 0, 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_magic = SS_MAGIC; + 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; + memmove(sump, &summary, sizeof(SEGSUM)); + sump += sizeof(SEGSUM); + + /* Now, add the ifile */ + file_info.fi_nblocks = block_array_size; + file_info.fi_version = 1; + file_info.fi_lastlength = lfsp->lfs_bsize; + file_info.fi_ino = LFS_IFILE_INUM; + + memmove(sump, &file_info, sizeof(FINFO) - sizeof(u_long)); + sump += sizeof(FINFO) - sizeof(u_long); + memmove(sump, block_array, 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_lastlength = lfsp->lfs_bsize; + file_info.fi_ino = ROOTINO; + file_info.fi_blocks[0] = 0; + memmove(sump, &file_info, sizeof(FINFO)); + sump += sizeof(FINFO); + + /* Now, add the lost and found */ + file_info.fi_ino = LOSTFOUNDINO; + memmove(sump, &file_info, 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; + sp->ss_magic = SS_MAGIC; + + /* 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. + */ + +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 = dip->di_mtime = dip->di_ctime = lfsp->lfs_tstamp; + dip->di_atimensec = dip->di_mtimensec = dip->di_ctimensec = 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]); + memmove(cp, &protodir[i], 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; + memmove(cp, &protodir[i], DIRSIZ(NEWDIRFMT, &protodir[i])); +} diff --git a/sbin/newlfs/newfs.c b/sbin/newlfs/newfs.c new file mode 100644 index 000000000000..88ca63845a12 --- /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.5 (Berkeley) 5/24/95"; +#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 = strrchr(*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 (strchr(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 = strchr(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, fsize, 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)); + memset(blk, 0, 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/nfsd/nfsd.8 b/sbin/nfsd/nfsd.8 new file mode 100644 index 000000000000..e98aa13ac08e --- /dev/null +++ b/sbin/nfsd/nfsd.8 @@ -0,0 +1,115 @@ +.\" 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.4 (Berkeley) 3/29/95 +.\" +.Dd March 29, 1995 +.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 and +.%T "NFS: Network File System Version 3 Protocol Specification" . +.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 000000000000..164e08cde5c6 --- /dev/null +++ b/sbin/nfsd/nfsd.c @@ -0,0 +1,643 @@ +/* + * 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.9 (Berkeley) 3/29/95"; +#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/nfsproto.h> +#include <nfs/nfs.h> + +#ifdef NFSKERB +#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 NFSKERB +char lnam[ANAME_SZ]; +KTEXT_ST kt; +AUTH_DAT kauth; +char inst[INST_SZ]; +struct nfsrpc_fullblock kin, kout; +struct nfsrpc_fullverf kverf; +NFSKERBKEY_T kivec; +struct timeval ktv; +NFSKERBKEYSCHED_T kerb_keysched; +#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 + struct timeval ktv; + 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, 2, IPPROTO_UDP, NFS_PORT) || + !pmap_set(RPCPROG_NFS, 3, IPPROTO_UDP, NFS_PORT))) + err(1, "can't register with portmap for UDP."); + if (tcpflag && + (!pmap_set(RPCPROG_NFS, 2, IPPROTO_TCP, NFS_PORT) || + !pmap_set(RPCPROG_NFS, 3, 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("server"); + nfssvc_flag = NFSSVC_NFSD; + nsd.nsd_nfsd = NULL; +#ifdef NFSKERB + if (sizeof (struct nfsrpc_fullverf) != RPCX_FULLVERF || + sizeof (struct nfsrpc_fullblock) != RPCX_FULLBLOCK) + syslog(LOG_ERR, "Yikes NFSKERB structs not packed!"); + nsd.nsd_authstr = (u_char *)&kt; + nsd.nsd_authlen = sizeof (kt); + nsd.nsd_verfstr = (u_char *)&kverf; + nsd.nsd_verflen = sizeof (kverf); +#endif + while (nfssvc(nfssvc_flag, &nsd) < 0) { + if (errno != ENEEDAUTH) { + syslog(LOG_ERR, "nfssvc: %m"); + exit(1); + } + nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHINFAIL; +#ifdef NFSKERB + /* + * Get the Kerberos ticket out of the authenticator + * verify it and convert the principal name to a user + * name. The user name is then converted to a set of + * user credentials via the password and group file. + * Finally, decrypt the timestamp and validate it. + * For more info see the IETF Draft "Authentication + * in ONC RPC". + */ + kt.length = ntohl(kt.length); + if (gettimeofday(&ktv, (struct timezone *)0) == 0 && + kt.length > 0 && kt.length <= + (RPCAUTH_MAXSIZ - 3 * NFSX_UNSIGNED)) { + kin.w1 = NFS_KERBW1(kt); + kt.mbz = 0; + (void)strcpy(inst, "*"); + if (krb_rd_req(&kt, NFS_KERBSRV, + inst, nsd.nsd_haddr, &kauth, "") == RD_AP_OK && + krb_kntoln(&kauth, 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(); + + /* + * Get the timestamp verifier out of the + * authenticator and verifier strings. + */ + kin.t1 = kverf.t1; + kin.t2 = kverf.t2; + kin.w2 = kverf.w2; + bzero((caddr_t)kivec, sizeof (kivec)); + bcopy((caddr_t)kauth.session, + (caddr_t)nsd.nsd_key,sizeof(kauth.session)); + + /* + * Decrypt the timestamp verifier in CBC mode. + */ + XXX + + /* + * Validate the timestamp verifier, to + * check that the session key is ok. + */ + nsd.nsd_timestamp.tv_sec = ntohl(kout.t1); + nsd.nsd_timestamp.tv_usec = ntohl(kout.t2); + nsd.nsd_ttl = ntohl(kout.w1); + if ((nsd.nsd_ttl - 1) == ntohl(kout.w2)) + nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHIN; + } +#endif /* NFSKERB */ + } + 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, 2, IPPROTO_UDP, NFS_PORT) || + !pmap_set(RPCPROG_NFS, 3, 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, 2, IPPROTO_TCP, NFS_PORT) || + !pmap_set(RPCPROG_NFS, 3, 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("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, "usage: 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) > 0); +} + +void +setproctitle(a) + char *a; +{ + register char *cp; + char buf[80]; + + cp = Argv[0]; + (void)snprintf(buf, sizeof(buf), "nfsd-%s", a); + (void)strncpy(cp, buf, LastArg - cp); + cp += strlen(cp); + while (cp < LastArg) + *cp++ = '\0'; +} diff --git a/sbin/nfsiod/nfsiod.c b/sbin/nfsiod/nfsiod.c new file mode 100644 index 000000000000..1e3d74857e7a --- /dev/null +++ b/sbin/nfsiod/nfsiod.c @@ -0,0 +1,170 @@ +/* + * 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.4 (Berkeley) 5/3/95"; +#endif not lint + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/syslog.h> +#include <sys/ucred.h> +#include <sys/wait.h> + +#include <sys/mount.h> +#include <sys/time.h> +#include <nfs/rpcv2.h> +#include <nfs/nfsproto.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/quotacheck/quotacheck.c b/sbin/quotacheck/quotacheck.c new file mode 100644 index 000000000000..31c94d1fbf83 --- /dev/null +++ b/sbin/quotacheck/quotacheck.c @@ -0,0 +1,609 @@ +/* + * 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.6 (Berkeley) 4/28/95"; +#endif /* not lint */ + +/* + * Fix up / report on disk quotas & usage + */ +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/queue.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> +#include <err.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; +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 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(1, "%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(1, "%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(); + dev_bsize = 1; + 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 = strchr(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(1, "%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) + memmove(fup->fu_name, 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(1, "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(1, "%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(1, "block %ld", bno); +} diff --git a/sbin/restore/dirs.c b/sbin/restore/dirs.c new file mode 100644 index 000000000000..d6c12d31e340 --- /dev/null +++ b/sbin/restore/dirs.c @@ -0,0 +1,753 @@ +/* + * 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.7 (Berkeley) 5/1/95"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <protocols/dumprestore.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <machine/endian.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]; + mode_t mode; + uid_t uid; + gid_t gid; + int flags; +}; + +/* + * 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) { + 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); + } +} + +/* + * 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) + 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 (Bcvt) + swabst((u_char *)"ls", (u_char *) dp); + if (oldinofmt && dp->d_ino != 0) { +# if BYTE_ORDER == BIG_ENDIAN + if (Bcvt) + dp->d_namlen = dp->d_type; +# else + if (!Bcvt) + dp->d_namlen = dp->d_type; +# endif + dp->d_type = DT_UNKNOWN; + } + 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; + } + memmove(dirbuf + dirloc, dp, (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; +{ + + memset(ndp, 0, (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) + return (NULL); + 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); + (void) chflags(cp, node.flags); + 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 = open(name, O_WRONLY | O_CREAT | O_TRUNC, 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; + node.timep[0].tv_usec = dip->di_atimensec / 1000; + node.timep[1].tv_sec = dip->di_mtime; + node.timep[1].tv_usec = dip->di_mtimensec / 1000; + node.mode = dip->di_mode; + node.flags = dip->di_flags; + 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/interactive.c b/sbin/restore/interactive.c new file mode 100644 index 000000000000..76aa74f9626b --- /dev/null +++ b/sbin/restore/interactive.c @@ -0,0 +1,774 @@ +/* + * 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.5 (Berkeley) 5/1/95"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.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((char *, 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, namelen; + char locname[MAXPATHLEN + 1]; + + dp = pathsearch(name); + if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) || + (!vflag && dp->d_ino == WINO)) + return; + if ((dirp = rst_opendir(name)) == NULL) { + entries = 1; + list = &single; + mkentry(name, 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; + (void) strncpy(locname, name, MAXPATHLEN); + (void) strncat(locname, "/", MAXPATHLEN); + namelen = strlen(locname); + while (dp = rst_readdir(dirp)) { + if (dp == NULL) + break; + if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) + continue; + if (!vflag && (dp->d_ino == WINO || + strcmp(dp->d_name, ".") == 0 || + strcmp(dp->d_name, "..") == 0)) + continue; + 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); + mkentry(locname, 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(name, dp, fp) + char *name; + 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 = lookupname(name)) != 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_WHT: + 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 (!vflag && dp->d_ino == WINO) + 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; + memmove(adirent.d_name, dp->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) || + (!vflag && dp->d_ino == WINO)) + 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 000000000000..57620fe0c582 --- /dev/null +++ b/sbin/restore/main.c @@ -0,0 +1,352 @@ +/* + * 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.6 (Berkeley) 5/4/95"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#include <protocols/dumprestore.h> + +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.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 *usedinomap; +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\n\t%s\n\t%s\n\t%s\n\t%s\n", + "restore -i [-chmvy] [-b blocksize] [-f file] [-s fileno]", + "restore -r [-cvy] [-b blocksize] [-f file] [-s fileno]", + "restore -R [-cvy] [-b blocksize] [-f file] [-s fileno]", + "restore -x [-chmvy] [-b blocksize] [-f file] [-s fileno] [file ...]", + "restore -t [-chvy] [-b blocksize] [-f file] [-s fileno] [file ...]"); + 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 (*argv == NULL) { + warnx("option requires an argument -- %c", *ap); + usage(); + } + 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); + ++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++); + + /* Update argument count. */ + *argcp = nargv - *argvp - 1; +} diff --git a/sbin/restore/restore.8 b/sbin/restore/restore.8 new file mode 100644 index 000000000000..6ad1b46f3d37 --- /dev/null +++ b/sbin/restore/restore.8 @@ -0,0 +1,426 @@ +.\" 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.4 (Berkeley) 5/1/95 +.\" +.Dd May 1, 1995 +.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 +.Fl i +.Op Fl chmvy +.Op Fl b Ar blocksize +.Op Fl f Ar file +.Op Fl s Ar fileno +.Nm restore +.Fl R +.Op Fl cvy +.Op Fl b Ar blocksize +.Op Fl f Ar file +.Op Fl s Ar fileno +.Nm restore +.Fl r +.Op Fl cvy +.Op Fl b Ar blocksize +.Op Fl f Ar file +.Op Fl s Ar fileno +.Nm restore +.Fl t +.Op Fl chvy +.Op Fl b Ar blocksize +.Op Fl f Ar file +.Op Fl s Ar fileno +.Op file ... +.Nm restore +.Fl x +.Op Fl chmvy +.Op Fl b Ar blocksize +.Op Fl f Ar file +.Op Fl s Ar fileno +.Op file ... +.Pp +.in -\\n(iSu +(The +.Bx 4.3 +option syntax is implemented for backward compatibility, but +is not documented here.) +.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. +Other arguments to the command are file or directory +names specifying the files that are to be restored. +Unless the +.Fl h +flag is specified (see below), +the appearance of a directory name refers to +the files and (recursively) subdirectories of that directory. +.Pp +Exactly one of the following flags is required: +.Bl -tag -width Ds +.It Fl 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 +.Fl h +flag 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 +.Fl h +flag 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 +flag 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 +.Fl v +flag is toggled. +When set, the verbose flag 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 +.It Fl R +.Nm Restore +requests a particular tape of a multi volume set on which to restart +a full restore +(see the +.Fl r +flag below). +This is useful if the restore has been interrupted. +.It Fl 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 +.Fl r +flag may be used to restore +any necessary incremental backups on top of the level 0. +The +.Fl r +flag 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 Fl 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 +.Fl h +flag has been specified. +Note that the +.Fl t +flag replaces the function of the old +.Xr dumpdir 8 +program. +.ne 1i +.It Fl 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 +.Fl h +flag 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 +.Fl h +flag has been specified. +.El +.Pp +The following additional options may be specified: +.Bl -tag -width Ds +.It Fl b Ar blocksize +The number of kilobytes per dump record. +If the +.Fl b +option is not specified, +.Nm restore +tries to determine the block size dynamically. +.It Fl c +Normally, +.Nm restore +will try to determine dynamically whether the dump was made from an +old (pre-4.4) or new format file sytem. The +.Fl c +flag disables this check, and only allows reading a dump in the old +format. +.It Fl f Ar file +Read the backup from +.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 input). +If the name of the file is of the form +.Dq host:file , +or +.Dq user@host:file , +.Nm restore +reads from the named file on the remote host using +.Xr rmt 8 . +.Pp +.It Fl h +Extract the actual directory, +rather than the files that it references. +This prevents hierarchical restoration of complete subtrees +from the dump. +.It Fl m +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 Fl s Ar fileno +Read from the specified +.Ar fileno +on a multi-file tape. +File numbering starts at 1. +.It Fl v +Normally +.Nm restore +does its work silently. +The +.Fl v +(verbose) +flag causes it to type the name of each file it treats +preceded by its file type. +.It Fl y +Do not ask the user whether to abort the restore in the event of an error. +Always try to skip over the bad block(s) and continue. +.El +.Sh DIAGNOSTICS +Complaints if it gets a read error. +If +.Fl 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 +.Fl x +or +.Fl i +flag 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 +dumps 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 dump 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 000000000000..b62899747e84 --- /dev/null +++ b/sbin/restore/restore.c @@ -0,0 +1,851 @@ +/* + * 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.3 (Berkeley) 9/13/94"; +#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 (ino == WINO && command == 'i' && !vflag) + 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 = lookupname(name); + if (ep != NULL) { + ep->e_flags &= ~NEW; + ep->e_flags |= REMOVED; + if (ep->e_type != NODE) + freeentry(ep); + } + 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 invalid whiteouts from the old tree. + * Remove unneeded leaves from the old tree. + * Remove directories from the lookup chains. + */ +void +removeoldleaves() +{ + register struct entry *ep, *nextep; + register ino_t i, mydirino; + + vprintf(stdout, "Mark entries to be removed.\n"); + if (ep = lookupino(WINO)) { + vprintf(stdout, "Delete whiteouts\n"); + for ( ; ep != NULL; ep = nextep) { + nextep = ep->e_links; + mydirino = ep->e_parent->e_ino; + /* + * We remove all whiteouts that are in directories + * that have been removed or that have been dumped. + */ + if (TSTINO(mydirino, usedinomap) && + !TSTINO(mydirino, dumpmap)) + continue; + delwhiteout(ep); + freeentry(ep); + } + } + for (i = ROOTINO + 1; i < maxino; i++) { + ep = lookupino(i); + if (ep == NULL) + continue; + if (TSTINO(i, usedinomap)) + 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]; + + if (ep = lookupino(WINO)) { + vprintf(stdout, "Add whiteouts\n"); + for ( ; ep != NULL; ep = ep->e_links) { + if ((ep->e_flags & NEW) == 0) + continue; + (void) addwhiteout(myname(ep)); + ep->e_flags &= ~NEW; + } + } + 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 = WINO; 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 000000000000..8dbcee81c01e --- /dev/null +++ b/sbin/restore/restore.h @@ -0,0 +1,139 @@ +/* + * 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.3 (Berkeley) 9/13/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 *usedinomap; /* map of inodes that are in use on this fs */ +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 SETINO(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 000000000000..16b1d8485e66 --- /dev/null +++ b/sbin/restore/symtab.c @@ -0,0 +1,628 @@ +/* + * 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.3 (Berkeley) 4/28/95"; +#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 < WINO || 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 < WINO || 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 < WINO || 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 = strrchr(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; + memmove(cp, ep->e_name, (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; + memset(np, 0, (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(strrchr(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 = strrchr(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 = WINO; 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 = WINO; i <= maxino; i++) { + for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { + memmove(tep, ep, (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 000000000000..a8d833f28f1b --- /dev/null +++ b/sbin/restore/tape.c @@ -0,0 +1,1378 @@ +/* + * 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.9 (Berkeley) 5/1/95"; +#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 (strchr(source, ':')) { + host = source; + source = strchr(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 active inode map\n"); + usedinomap = 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); + /* + * If there may be whiteout entries on the tape, pretend that the + * whiteout inode exists, so that the whiteout entries can be + * extracted. + */ + if (oldinofmt == 0) + SETINO(WINO, dumpmap); +} + +/* + * 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 flags; + mode_t mode; + struct timeval timep[2]; + struct entry *ep; + + curfile.name = name; + curfile.action = USING; + timep[0].tv_sec = curfile.dip->di_atime; + timep[0].tv_usec = curfile.dip->di_atimensec / 1000; + timep[1].tv_sec = curfile.dip->di_mtime; + timep[1].tv_usec = curfile.dip->di_mtimensec / 1000; + mode = curfile.dip->di_mode; + flags = curfile.dip->di_flags; + 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); + (void) chflags(name, flags); + skipfile(); + utimes(name, timep); + return (GOOD); + + case IFIFO: + vprintf(stdout, "extract fifo %s\n", name); + if (Nflag) { + skipfile(); + return (GOOD); + } + if (mkfifo(name, mode) < 0) { + fprintf(stderr, "%s: cannot create fifo: %s\n", + name, strerror(errno)); + skipfile(); + return (FAIL); + } + (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid); + (void) chmod(name, mode); + (void) chflags(name, flags); + skipfile(); + utimes(name, timep); + return (GOOD); + + case IFREG: + vprintf(stdout, "extract file %s\n", name); + if (Nflag) { + skipfile(); + return (GOOD); + } + if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, + 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); + (void) fchflags(ofile, flags); + 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; + quad_t 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, (long)(size > TP_BSIZE ? + fssize : (curblk - 1) * TP_BSIZE + size)); + curblk = 0; + } + } else { + if (curblk > 0) { + (*fill)((char *)buf, (long)(size > TP_BSIZE ? + curblk * TP_BSIZE : + (curblk - 1) * TP_BSIZE + size)); + curblk = 0; + } + (*skip)(clearedbuf, (long)(size > TP_BSIZE ? + 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, (long)((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; +{ + + memmove(map, buf, 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) { + memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (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; + memset(tapebuf, 0, 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(); + memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE); + } + blkcnt = 0; + memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (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)); + memset(buf, 0, (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 = u_ospcl.s_ospcl.c_dinode.odi_atime; + buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime; + buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime; + buf->c_count = u_ospcl.s_ospcl.c_count; + memmove(buf->c_addr, u_ospcl.s_ospcl.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, "Dumped inodes map header"); + break; + case TS_CLRI: + fprintf(stderr, "Used inodes map 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 000000000000..29ac8bd2d3ba --- /dev/null +++ b/sbin/restore/utilities.c @@ -0,0 +1,434 @@ +/* + * 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.5 (Berkeley) 4/28/95"; +#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 = strchr(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); +} + +/* + * Create a whiteout. + */ +int +addwhiteout(name) + char *name; +{ + + if (!Nflag && mknod(name, S_IFWHT, 0) < 0) { + fprintf(stderr, "warning: cannot create whiteout %s: %s\n", + name, strerror(errno)); + return (FAIL); + } + vprintf(stdout, "Create whiteout %s\n", name); + return (GOOD); +} + +/* + * Delete a whiteout. + */ +void +delwhiteout(ep) + register struct entry *ep; +{ + char *name; + + if (ep->e_type != LEAF) + badentry(ep, "delwhiteout: not a leaf"); + ep->e_flags |= REMOVED; + ep->e_flags &= ~TMPNAME; + name = myname(ep); + if (!Nflag && undelete(name) < 0) { + fprintf(stderr, "warning: cannot delete whiteout %s: %s\n", + name, strerror(errno)); + return; + } + vprintf(stdout, "Delete whiteout %s\n", name); +} + +/* + * 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/umount/Makefile b/sbin/umount/Makefile new file mode 100644 index 000000000000..2df87189db77 --- /dev/null +++ b/sbin/umount/Makefile @@ -0,0 +1,13 @@ +# @(#)Makefile 8.4 (Berkeley) 6/22/95 + +PROG= umount +SRCS= umount.c vfslist.c +MAN8= umount.0 +DPADD= ${LIBRPC} +LDADD= -lrpc + +MOUNT= ${.CURDIR}/../../sbin/mount +CFLAGS+= -I${MOUNT} +.PATH: ${MOUNT} + +.include <bsd.prog.mk> diff --git a/sbin/umount/umount.8 b/sbin/umount/umount.8 new file mode 100644 index 000000000000..0ef285295ff8 --- /dev/null +++ b/sbin/umount/umount.8 @@ -0,0 +1,126 @@ +.\" 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.2 (Berkeley) 5/8/95 +.\" +.Dd May 8, 1995 +.Dt UMOUNT 8 +.Os BSD 4 +.Sh NAME +.Nm umount +.Nd unmount filesystems +.Sh SYNOPSIS +.Nm umount +.Op Fl fv +.Ar special | node +.Nm umount +.Fl a | A +.Op Fl fv +.Op Fl h Ar host +.Op Fl t Ar 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 filesystem 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 the filesystems described in +.Xr fstab 5 +are unmounted. +.It Fl A +All the currently mounted filesystems except +the root are unmounted. +.It Fl f +The filesystem is forcibly unmounted. +Active special devices continue to work, +but all other files return errors if further accesses are attempted. +The root filesystem 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 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 filesystem +is unmounted. +.El +.Sh FILES +.Bl -tag -width /etc/fstab -compact +.It Pa /etc/fstab +filesystem 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 000000000000..09cb0b87d9d2 --- /dev/null +++ b/sbin/umount/umount.c @@ -0,0 +1,385 @@ +/*- + * 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.8 (Berkeley) 5/8/95"; +#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; +char *nfshost; + +int checkvfsname __P((const char *, char **)); +char *getmntname __P((char *, mntwhat, char **)); +char **makevfslist __P((char *)); +int selected __P((int)); +int namematch __P((struct hostent *)); +int umountall __P((char **)); +int umountfs __P((char *, char **)); +void usage __P((void)); +int xdr_dir __P((XDR *, char *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int all, ch, errs, mnts; + char **typelist = NULL; + struct statfs *mntbuf; + + /* Start disks transferring immediately. */ + sync(); + + all = 0; + while ((ch = getopt(argc, argv, "AaFfh:t:v")) != EOF) + switch (ch) { + case 'A': + all = 2; + break; + case 'a': + all = 1; + break; + case 'F': + fake = 1; + break; + case 'f': + fflag = MNT_FORCE; + break; + case 'h': /* -h implies -A. */ + all = 2; + nfshost = optarg; + break; + case 't': + if (typelist != NULL) + errx(1, "only one -t option may be specified."); + typelist = makevfslist(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)) + typelist = makevfslist("nfs"); + + switch (all) { + case 2: + if ((mnts = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { + warn("getmntinfo"); + errs = 1; + break; + } + for (errs = 0, mnts--; mnts > 0; mnts--) { + if (checkvfsname(mntbuf[mnts].f_fstypename, typelist)) + continue; + if (umountfs(mntbuf[mnts].f_mntonname, typelist) != 0) + errs = 1; + } + break; + case 1: + if (setfsent() == 0) + err(1, "%s", _PATH_FSTAB); + errs = umountall(typelist); + break; + case 0: + for (errs = 0; *argv != NULL; ++argv) + if (umountfs(*argv, typelist) != 0) + errs = 1; + break; + } + exit(errs); +} + +int +umountall(typelist) + char **typelist; +{ + struct fstab *fs; + int rval, type; + char *cp; + struct vfsconf vfc; + + 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 (getvfsbyname(fs->fs_vfstype, &vfc) < 0) { + warnx("%s: unknown mount type", fs->fs_vfstype); + continue; + } + if (checkvfsname(fs->fs_vfstype, typelist)) + 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(typelist); + return (umountfs(cp, typelist) || rval); + } + return (0); +} + +int +umountfs(name, typelist) + char *name; + char **typelist; +{ + enum clnt_stat clnt_stat; + struct hostent *hp; + struct sockaddr_in saddr; + struct stat sb; + struct timeval pertry, try; + CLIENT *clp; + int so; + char *type, *delimp, *hostp, *mntpt, rname[MAXPATHLEN]; + + if (realpath(name, rname) == NULL) { + warn("%s", rname); + return (1); + } + + 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 (checkvfsname(type, typelist)) + return (1); + + hp = NULL; + if (!strcmp(type, "nfs")) { + 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 = ':'; + } + } + + if (!namematch(hp)) + return (1); + + 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; + char **type; +{ + static struct statfs *mntbuf; + static int mntsize; + int i; + + if (mntbuf == NULL && + (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_fstypename; + return (mntbuf[i].f_mntonname); + } + if ((what == MNTFROM) && !strcmp(mntbuf[i].f_mntonname, name)) { + if (type) + *type = mntbuf[i].f_fstypename; + return (mntbuf[i].f_mntfromname); + } + } + return (NULL); +} + +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); +} |
