Import some CSRG 4.4BSD-Lite2 components for sbin onto vendor branch.

(note that some of these have already been imported, this is a no-op)
This commit is contained in:
Peter Wemm 1997-03-11 11:59:39 +00:00
parent 662909a780
commit 1811bdf372
65 changed files with 23758 additions and 0 deletions

354
sbin/dump/dump.8 Normal file
View file

@ -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.

213
sbin/dump/dump.h Normal file
View file

@ -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

374
sbin/dump/dumprmt.c Normal file
View file

@ -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();
}

586
sbin/dump/main.c Normal file
View file

@ -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;
}

859
sbin/dump/tape.c Normal file
View file

@ -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);
}

607
sbin/dump/traverse.c Normal file
View file

@ -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);
}
}

106
sbin/dump/unctime.c Normal file
View file

@ -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);
}

322
sbin/dumpfs/dumpfs.c Normal file
View file

@ -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);
}

10
sbin/fsck/Makefile Normal file
View file

@ -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>

725
sbin/fsck/dir.c Normal file
View file

@ -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);
}

301
sbin/fsck/fsck.8 Normal file
View file

@ -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

278
sbin/fsck/fsck.h Normal file
View file

@ -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));

562
sbin/fsck/inode.c Normal file
View file

@ -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--;
}

340
sbin/fsck/main.c Normal file
View file

@ -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);
}

320
sbin/fsck/pass1.c Normal file
View file

@ -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);
}

104
sbin/fsck/pass1b.c Normal file
View file

@ -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);
}

467
sbin/fsck/pass2.c Normal file
View file

@ -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]);
}

74
sbin/fsck/pass3.c Normal file
View file

@ -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();
}
}

136
sbin/fsck/pass4.c Normal file
View file

@ -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);
}

328
sbin/fsck/pass5.c Normal file
View file

@ -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();
}
}

370
sbin/fsck/preen.c Normal file
View file

@ -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);
}

500
sbin/fsck/setup.c Normal file
View file

@ -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);
}

628
sbin/fsck/utilities.c Normal file
View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

148
sbin/mount_lfs/mount_lfs.c Normal file
View file

@ -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);
}

241
sbin/mount_nfs/mount_nfs.8 Normal file
View file

@ -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.

745
sbin/mount_nfs/mount_nfs.c Normal file
View file

@ -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);
}

View file

@ -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.

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

106
sbin/mount_portal/pt_file.c Normal file
View file

@ -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);
}

159
sbin/mount_portal/pt_tcp.c Normal file
View file

@ -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);
}

View file

@ -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.

View file

@ -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);
}

View file

@ -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.

View file

@ -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);
}

11
sbin/mount_union/Makefile Normal file
View file

@ -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>

View file

@ -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 .

View file

@ -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);
}

254
sbin/mountd/exports.5 Normal file
View file

@ -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.

115
sbin/mountd/mountd.8 Normal file
View file

@ -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.

2064
sbin/mountd/mountd.c Normal file

File diff suppressed because it is too large Load diff

140
sbin/newlfs/config.h Normal file
View file

@ -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

45
sbin/newlfs/extern.h Normal file
View file

@ -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;

683
sbin/newlfs/lfs.c Normal file
View file

@ -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]));
}

466
sbin/newlfs/newfs.c Normal file
View file

@ -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);
}

115
sbin/nfsd/nfsd.8 Normal file
View file

@ -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.

643
sbin/nfsd/nfsd.c Normal file
View file

@ -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';
}

170
sbin/nfsiod/nfsiod.c Normal file
View file

@ -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);
}

View file

@ -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);
}

753
sbin/restore/dirs.c Normal file
View file

@ -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);
}

774
sbin/restore/interactive.c Normal file
View file

@ -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);
}

352
sbin/restore/main.c Normal file
View file

@ -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;
}

426
sbin/restore/restore.8 Normal file
View file

@ -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 .

851
sbin/restore/restore.c Normal file
View file

@ -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);
}

139
sbin/restore/restore.h Normal file
View file

@ -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

628
sbin/restore/symtab.c Normal file
View file

@ -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];
}
}

1378
sbin/restore/tape.c Normal file

File diff suppressed because it is too large Load diff

434
sbin/restore/utilities.c Normal file
View file

@ -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);
}
}

13
sbin/umount/Makefile Normal file
View file

@ -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>

126
sbin/umount/umount.8 Normal file
View file

@ -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 .

385
sbin/umount/umount.c Normal file
View file

@ -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);
}