File: [local] / src / sbin / ncheck_ffs / ncheck_ffs.c (download)
Revision 1.5, Wed Jan 15 23:41:30 1997 UTC (27 years, 4 months ago) by millert
Branch: MAIN
CVS Tags: OPENBSD_2_9_BASE, OPENBSD_2_9, OPENBSD_2_8_BASE, OPENBSD_2_8, OPENBSD_2_7_BASE, OPENBSD_2_7, OPENBSD_2_6_BASE, OPENBSD_2_6, OPENBSD_2_5_BASE, OPENBSD_2_5, OPENBSD_2_4_BASE, OPENBSD_2_4, OPENBSD_2_3_BASE, OPENBSD_2_3, OPENBSD_2_2_BASE, OPENBSD_2_2, OPENBSD_2_1_BASE, OPENBSD_2_1 Changes since 1.4: +3 -3 lines
getopt(3) returns -1 when out of args, not EOF, whee!
|
/* $OpenBSD: ncheck_ffs.c,v 1.5 1997/01/15 23:41:30 millert 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.5 1997/01/15 23:41:30 millert 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 <fstab.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 */
int iflag; /* specific inode */
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 *[]));
char *rawname __P((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 1;
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 (iflag ||
(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 (mode == IFDIR) {
if (dp->d_name[0] == '.') {
if (dp->d_name[1] == '\0' ||
(dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
continue;
}
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);
}
}
}
char *
rawname(name)
char *name;
{
static char newname[MAXPATHLEN];
char *p;
if ((p = strrchr(name, '/')) == NULL)
return name;
*p = '\0';
strcpy(newname, name);
*p++ = '/';
strcat(newname, "/r");
strcat(newname, p);
return(newname);
}
void
usage()
{
fprintf(stderr, "Usage: ncheck_ffs [-i numbers] [-ams] filesystem\n");
exit(3);
}
int
main(argc, argv)
int argc;
char *argv[];
{
struct stat stblock;
struct fstab *fsp;
int c;
ino_t ino;
while ((c = getopt(argc, argv, "ai:ms")) != -1)
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 (stat(disk, &stblock) < 0)
err(1, "cannot stat %s", disk);
if (S_ISBLK(stblock.st_mode)) {
disk = rawname(disk);
}
else if (!S_ISCHR(stblock.st_mode)) {
if ((fsp = getfsfile(disk)) == NULL)
err(1, "cound not find file system %s", disk);
disk = rawname(fsp->fs_spec);
}
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);
findinodes(sblock->fs_ipg * sblock->fs_ncg);
printf("%s:\n", disk);
scanonedir(ROOTINO, "");
close(diskfd);
return 0;
}