diff options
author | Thorsten Lockert <tholo@cvs.openbsd.org> | 1996-06-25 04:43:46 +0000 |
---|---|---|
committer | Thorsten Lockert <tholo@cvs.openbsd.org> | 1996-06-25 04:43:46 +0000 |
commit | 1000f00db6180c405687936e8dd38e59656bdae0 (patch) | |
tree | d06ba52a19bb8dc9ff5a5cff45dc1157ac78ca9b /sbin | |
parent | 8291926efe8bd198d777afac06227f3daa9723c7 (diff) |
ncheck(8) implementation
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/Makefile | 4 | ||||
-rw-r--r-- | sbin/ncheck_ffs/Makefile | 9 | ||||
-rw-r--r-- | sbin/ncheck_ffs/ncheck_ffs.8 | 74 | ||||
-rw-r--r-- | sbin/ncheck_ffs/ncheck_ffs.c | 460 |
4 files changed, 545 insertions, 2 deletions
diff --git a/sbin/Makefile b/sbin/Makefile index e4d47573728..da91cf8d4e9 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -1,11 +1,11 @@ -# $OpenBSD: Makefile,v 1.14 1996/06/12 11:21:23 deraadt Exp $ +# $OpenBSD: Makefile,v 1.15 1996/06/25 04:43:43 tholo Exp $ # $NetBSD: Makefile,v 1.29 1996/05/14 17:39:21 ws Exp $ # Not ported: XNSrouted enpload scsiformat startslip # Missing: icheck ncheck SUBDIR= badsect disklabel ccdconfig dmesg fastboot ifconfig init ipf ipfstat \ - ipnat kbd mknod modload modunload mount mountd nfsd nfsiod \ + ipnat kbd mknod modload modunload mount mountd ncheck_ffs nfsd nfsiod \ nologin ping quotacheck reboot route routed savecore scsi shutdown \ slattach swapon ttyflags umount diff --git a/sbin/ncheck_ffs/Makefile b/sbin/ncheck_ffs/Makefile new file mode 100644 index 00000000000..4017948d06c --- /dev/null +++ b/sbin/ncheck_ffs/Makefile @@ -0,0 +1,9 @@ +# $OpenBSD: Makefile,v 1.1 1996/06/25 04:43:44 tholo Exp $ + +PROG= ncheck_ffs +MAN= ncheck_ffs.8 + +LINKS= ${BINDIR}/newfs ${BINDIR}/mount_mfs +MLINK= ncheck_ffs.8 ncheck.8 + +.include <bsd.prog.mk> diff --git a/sbin/ncheck_ffs/ncheck_ffs.8 b/sbin/ncheck_ffs/ncheck_ffs.8 new file mode 100644 index 00000000000..d3aaf8f2096 --- /dev/null +++ b/sbin/ncheck_ffs/ncheck_ffs.8 @@ -0,0 +1,74 @@ +.\" $OpenBSD: ncheck_ffs.8,v 1.1 1996/06/25 04:43:44 tholo Exp $ +.\" +.\" Copyright (c) 1995, 1996 SigmaSoft, Th. Lockert <tholo@sigmasoft.com> +.\" 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 SigmaSoft, Th. Lockert +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd June 24, 1996 +.Dt NCHECK_FFS 8 +.Os OpenBSD 1.2 +.Sh NAME +.Nm ncheck_ffs +.Nd generate names from inode-numbers +.Sh SYNOPSIS +.Nm ncheck_ffs +.Op Fl i Ar numbers +.Op Fl asv +.Ar filesystem +.Sh DESCRIPTION +.Nm ncheck_ffs +generates a list of filenames and inode numbers for the given +file system. Names of directories are followed by a `.' +.Pp +The report is not sorted. +.Pp +The options to +.Nm ncheck_ffs +are as follows: +.Bl -tag -width indent +.It Fl i Ar numbers +Report only those files whose inode numbers follows. +.It Fl a +Print the file names `.' and `..', which are ordinarily skipped. +.It Fl s +Report only special files and files with set-user-ID or set-group-ID +set. This is meant to find hidden violations of security policies. +.It Fl v +Give more verbose information on inodes +.Sh SEE ALSO +.Xr sort 1 , +.Xr fsck 8 +.Sh HISTORY +The +.Nm ncheck_ffs +command was designed to be similar in functionality to the corresponding +command in +.Tn "SunOS 4.1.3" . +.Sh AUTHOR +.Bl -tag +Thorsten Lockert <tholo@sigmasoft.com> +.El diff --git a/sbin/ncheck_ffs/ncheck_ffs.c b/sbin/ncheck_ffs/ncheck_ffs.c new file mode 100644 index 00000000000..a3c0e3a54f9 --- /dev/null +++ b/sbin/ncheck_ffs/ncheck_ffs.c @@ -0,0 +1,460 @@ +/* $OpenBSD: ncheck_ffs.c,v 1.1 1996/06/25 04:43:45 tholo Exp $ */ + +/*- + * Copyright (c) 1995, 1996 SigmaSoft, Th. Lockert <tholo@sigmasoft.com> + * 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 SigmaSoft, Th. Lockert + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 rcsid[] = "$OpenBSD: ncheck_ffs.c,v 1.1 1996/06/25 04:43:45 tholo Exp $"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <ufs/ffs/fs.h> +#include <ufs/ufs/dir.h> +#include <ufs/ufs/dinode.h> + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <errno.h> +#include <err.h> + +#define MAXINOPB (MAXBSIZE / sizeof(struct dinode)) + +char *disk; /* name of the disk file */ +int diskfd; /* disk file descriptor */ +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) */ +ino_t *ilist; /* list of inodes to check */ +int ninodes; /* number of inodes in list */ +int sflag; /* only suid and special files */ +int aflag; /* print the . and .. entries too */ +int mflag; /* verbose output */ + +struct icache_s { + ino_t ino; + struct dinode di; +} *icache; +int nicache; + +void addinode __P((ino_t inum)); +struct dinode *getino __P((ino_t inum)); +void findinodes __P((ino_t)); +void bread __P((daddr_t, char *, int)); +void usage __P((void)); +void scanonedir __P((ino_t, const char *)); +void dirindir __P((ino_t, daddr_t, int, long *, const char *)); +void searchdir __P((ino_t, daddr_t, long, long, const char *)); +int matchino __P((const void *, const void *)); +int matchcache __P((const void *, const void *)); +void cacheino __P((ino_t, struct dinode *)); +struct dinode *cached __P((ino_t)); +int main __P((int, char *[])); + +/* + * Check to see if the indicated inodes are the same + */ +int +matchino(key, val) + const void *key, *val; +{ + ino_t k = *(ino_t *)key; + ino_t v = *(ino_t *)val; + + if (k < v) + return -1; + else if (k > v) + return 0; + return 0; +} + +/* + * Check if the indicated inode match the entry in the cache + */ +int matchcache(key, val) + const void *key, *val; +{ + ino_t ino = *(ino_t *)key; + struct icache_s *ic = (struct icache_s *)val; + + if (ino < ic->ino) + return -1; + else if (ino > ic->ino) + return 1; + return 0; +} + +/* + * Add an inode to the cached entries + */ +void +cacheino(ino, ip) + ino_t ino; + struct dinode *ip; +{ + if (nicache) + icache = realloc(icache, (nicache + 1) * sizeof(struct icache_s)); + else + icache = malloc(sizeof(struct icache_s)); + icache[nicache].ino = ino; + icache[nicache++].di = *ip; +} + +/* + * Get a cached inode + */ +struct dinode * +cached(ino) + ino_t ino; +{ + struct icache_s *ic; + + ic = (struct icache_s *)bsearch(&ino, icache, nicache, sizeof(struct icache_s), matchcache); + return ic ? &ic->di : NULL; +} + +/* + * Walk the inode list for a filesystem to find all allocated inodes + * Remember inodes we want to give information about and cache all + * inodes pointing to directories + */ +void +findinodes(maxino) + ino_t maxino; +{ + register ino_t ino; + register struct dinode *dp; + mode_t mode; + + for (ino = ROOTINO; ino < maxino; ino++) { + dp = getino(ino); + mode = dp->di_mode & IFMT; + if (!mode) + continue; + if (mode == IFDIR) + cacheino(ino, dp); + if (sflag && + (((dp->di_mode & (ISGID | ISUID)) == 0) && + ((mode == IFREG) || (mode == IFDIR) || (mode == IFLNK)))) + continue; + addinode(ino); + } +} + +/* + * Get a specified inode from disk. Attempt to minimize reads to once + * per cylinder group + */ +struct dinode * +getino(inum) + ino_t inum; +{ + static struct dinode *itab = NULL; + static daddr_t iblk = -1; + struct dinode *ip; + + if (inum < ROOTINO || inum >= sblock->fs_ncg * sblock->fs_ipg) + return NULL; + if ((ip = cached(inum)) != NULL) + return ip; + if ((inum / sblock->fs_ipg) != iblk || itab == NULL) { + iblk = inum / sblock->fs_ipg; + if (itab == NULL && + (itab = calloc(sizeof(struct dinode), sblock->fs_ipg)) == NULL) + errx(1, "no memory for inodes"); + bread(fsbtodb(sblock, cgimin(sblock, iblk)), (char *)itab, + sblock->fs_ipg * sizeof(struct dinode)); + } + return &itab[inum % sblock->fs_ipg]; +} + +/* + * 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; + +loop: + if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0) + warnx("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) + warnx("read error from %s: %s: [block %d]: count=%d\n", + disk, strerror(errno), blkno, size); + else + warnx("short read error from %s: [block %d]: count=%d, got=%d\n", + disk, blkno, size, cnt); + if (++breaderrors > BREADEMAX) + errx(1, "More than %d block read errors from %s\n", BREADEMAX, disk); + /* + * 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 (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0) + warnx("bread: lseek2 fails!\n"); + if ((cnt = read(diskfd, buf, (int)dev_bsize)) == dev_bsize) + continue; + if (cnt == -1) { + warnx("read error from %s: %s: [sector %d]: count=%d\n", + disk, strerror(errno), blkno, dev_bsize); + continue; + } + warnx("short read error from %s: [sector %d]: count=%d, got=%d\n", + disk, blkno, dev_bsize, cnt); + } +} + +/* + * Add an inode to the in-memory list of inodes to dump + */ +void +addinode(ino) + ino_t ino; +{ + if (ninodes) + ilist = realloc(ilist, sizeof(ino_t) * (ninodes + 1)); + else + ilist = malloc(sizeof(ino_t)); + if (ilist == NULL) + errx(4, "not enough memory to allocate tables"); + ilist[ninodes] = ino; + ninodes++; +} + +/* + * Scan the directory pointer at by ino + */ +void +scanonedir(ino, path) + ino_t ino; + const char *path; +{ + struct dinode *dp; + long filesize; + int i; + + if ((dp = cached(ino)) == NULL) + return; + filesize = dp->di_size; + for (i = 0; filesize > 0 && i < NDADDR; i++) { + if (dp->di_db[i]) + searchdir(ino, dp->di_db[i], dblksize(sblock, dp, i), filesize, path); + filesize -= sblock->fs_bsize; + } + for (i = 0; filesize > 0 && i < NIADDR; i++) { + if (dp->di_ib[i]) + dirindir(ino, dp->di_ib[i], i, &filesize, path); + } +} + +/* + * 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. + */ +void +dirindir(ino, blkno, ind_level, filesize, path) + ino_t ino; + daddr_t blkno; + int ind_level; + long *filesize; + const char *path; +{ + daddr_t idblk[MAXBSIZE / sizeof(daddr_t)]; + int i; + + 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) + searchdir(ino, blkno, sblock->fs_bsize, *filesize, path); + } + return; + } + ind_level--; + for (i = 0; *filesize > 0 && NINDIR(sblock); i++) { + blkno = idblk[i]; + if (blkno) + dirindir(ino, blkno, ind_level, filesize, path); + } +} + +/* + * Scan a disk block containing directory information looking to see if + * any of the entries are on the inode list and to see if the directory + * contains any subdirectories. Display entries for marked inodes. + * Pass inodes pointing to directories back to scanonedir(). + */ +void +searchdir(ino, blkno, size, filesize, path) + ino_t ino; + daddr_t blkno; + long size; + long filesize; + const char *path; +{ + char dblk[MAXBSIZE]; + struct direct *dp; + struct dinode *di; + mode_t mode; + char *npath; + long loc; + + 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) { + warnx("corrupted directory, inode %lu", ino); + break; + } + loc += dp->d_reclen; + if (!dp->d_ino) + continue; + if (dp->d_name[0] == '.') { + if (!aflag && (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) + continue; + } + di = getino(dp->d_ino); + mode = di->di_mode & IFMT; + if (bsearch(&dp->d_ino, ilist, ninodes, sizeof(*ilist), matchino)) { + if (mflag) + printf("mode %-6o uid %-5lu gid %-5lu ino ", di->di_mode, di->di_uid, di->di_gid); + printf("%-7lu %s/%s%s\n", dp->d_ino, path, dp->d_name, mode == IFDIR ? "/." : ""); + if (dp->d_name[0] == '.') { + if (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0')) + continue; + } + } + if (mode == IFDIR) { + npath = malloc(strlen(path) + strlen(dp->d_name) + 2); + strcpy(npath, path); + strcat(npath, "/"); + strcat(npath, dp->d_name); + scanonedir(dp->d_ino, npath); + free(npath); + } + } +} + +void +usage() +{ + fprintf(stderr, "Usage: ncheck [-i numbers] [-as] filesystem\n"); + exit(3); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int c, iflag = 0; + ino_t ino; + + while ((c = getopt(argc, argv, "ai:ms")) != EOF) + switch (c) { + case 'a': + aflag++; + break; + case 'i': + iflag++; + addinode(strtoul(optarg, NULL, 10)); + while (optind < argc && (ino = strtoul(argv[optind], NULL, 10)) != 0) { + addinode(ino); + optind++; + } + break; + case 'm': + mflag++; + break; + case 's': + sflag++; + break; + case '?': + exit(2); + } + if (optind != argc - 1) + usage(); + disk = argv[optind]; + + if ((diskfd = open(disk, O_RDONLY)) < 0) + err(1, "cannot open %s", disk); + sblock = (struct fs *)sblock_buf; + bread(SBOFF, (char *)sblock, SBSIZE); + if (sblock->fs_magic != FS_MAGIC) + errx(1, "not a file system"); + dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1); + dev_bshift = ffs(dev_bsize) - 1; + if (dev_bsize != (1 << dev_bshift)) + errx(2, "blocksize (%d) not a power of 2", dev_bsize); + if (!iflag) + findinodes(sblock->fs_ipg * sblock->fs_ncg); + printf("%s:\n", disk); + scanonedir(ROOTINO, ""); + close(diskfd); + return 0; +} |