/* $Header: /afs/sipb/project/afs/src/sipb-3.3a/src/vol/RCS/listinodes.c,v 1.1 1996/10/21 17:34:55 warlord Exp warlord $ */
/* $Source: /afs/sipb/project/afs/src/sipb-3.3a/src/vol/RCS/listinodes.c,v $ */

#ifndef lint
static char *rcsid = "$Header: /afs/sipb/project/afs/src/sipb-3.3a/src/vol/RCS/listinodes.c,v 1.1 1996/10/21 17:34:55 warlord Exp warlord $";
#endif

/*
 * P_R_P_Q_# (C) COPYRIGHT IBM CORPORATION 1987
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/*

	System:		VICE-TWO
	Module:		listinodes.c
	Institution:	The Information Technology Center, Carnegie-Mellon University

 */

#define ITC	/* Required by inode.h */

#include <afs/param.h>
#ifdef AFS_NETBSD_ENV
#include <sys/param.h>
#include <sys/user.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#endif
#include <ctype.h>
#include <sys/param.h>
#if defined(AFS_SGI_ENV)
#else
#ifdef	AFS_OSF_ENV
#include <ufs/fs.h>
#else	/* AFS_OSF_ENV */
#ifdef AFS_VFSINCL_ENV
#define VFS
#ifdef	  AFS_SUN5_ENV
#include <sys/fs/ufs_fs.h>
#else
#ifdef AFS_NETBSD_ENV
#include <ufs/ffs/fs.h>
#else
#include <ufs/fs.h>
#endif
#endif
#else /* AFS_VFSINCL_ENV */
#ifdef	AFS_AIX_ENV
#include <sys/filsys.h>
#else
#ifdef AFS_LINUX_ENV
#include <linux/fs.h>
#else
#include <sys/fs.h>
#endif
#endif
#endif /* AFS_VFSINCL_ENV */
#endif	/* AFS_OSF_ENV */
#include <sys/time.h>
#ifdef AFS_VFSINCL_ENV
#include <sys/vnode.h>
#ifdef	  AFS_SUN5_ENV
#include <sys/fs/ufs_inode.h>
#else
#ifndef AFS_NETBSD_ENV
#include <ufs/inode.h>
#endif
#endif
#else /* AFS_VFSINCL_ENV */
#ifndef AFS_LINUX_ENV
#ifdef AFS_DEC_ENV
#include <sys/time.h>
#endif AFS_DEC_ENV
#ifdef	AFS_OSF_ENV
#include <ufs/inode.h>
#else	/* AFS_OSF_ENV */
#include <sys/inode.h>
#endif
#endif
#endif AFS_VFSINCL_ENV
#endif /* AFS_SGI_ENV */
#include <afs/auxinode.h>
#include <sys/file.h>
#include <stdio.h>
#include <rx/xdr.h>
#include <afs/afsint.h>
#include "nfs.h"
#include "viceinode.h"
#include <sys/stat.h>
#if defined (AFS_AIX_ENV) || defined (AFS_HPUX_ENV)
#include <sys/ino.h>
#endif
#ifdef AFS_LINUX_ENV
#undef private
#include <linux/ext2_fs.h>
#include <ext2fs/ext2fs.h>
#endif
#include <afs/assert.h>
#include "partition.h"

/* Notice:  parts of this module have been cribbed from vfsck.c */

#define	ROOTINODE	2
static char *partition;
extern Testing;
int pfd;

#ifdef	AFS_NEXT30_ENV
int Bsize = AFS_DEV_BSIZE;		/* XXX SBSIZE XXX */
#endif


#ifdef	AFS_AIX32_ENV
#include <jfs/filsys.h>

#ifndef FSBSIZE
#define FSBSIZE         (4096)		/* filesystem block size	*/
#define FSBSHIFT        (12)		/* log2(FSBSIZE)		*/
#define FSBMASK         (FSBSIZE - 1)	/* FSBSIZE mask			*/

#define MIN_FSIZE	DISKMAP_B	/* minimum fs size (FSblocks)   */
#define LAST_RSVD_I	15		/* last reserved inode          */
#endif

#ifndef INOPB
/*
 * This will hopefully eventually make it into the system include files
 */
#define INOPB		(FSBSIZE / sizeof (struct dinode))
#endif

/*
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 XX This was lifted from some `com/cmd/fs/fshlpr_aix3/Fs.h', which indicated X
 XX a longing to see it make it into a readily accessible include file. XXXXXX
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 *
 * itoo - inode number to offset within disk block
 */
#undef itoo
#define itoo(x)         (int) ((unsigned)(x) % INOPB)

int Bsize = FSBSIZE;	/* block size for this system			*/
daddr_t	fmax;		/* total number of blocks n file system		*/
ino_t	imax, inum;	/* total number of I-nodes in file system	*/

static struct superblock fs;
struct dinode *ginode();



ListViceInodes(devname, mountedOn, resultFile, judgeInode, judgeParam, forcep, forceR, wpath)
char *devname, *mountedOn, *resultFile, *wpath;
int (*judgeInode)(); 
int *forcep, forceR; {
	FILE *inodeFile = NULL;
	char dev[50], rdev[51];
	struct stat status;
	struct dinode *p;
	struct ViceInodeInfo info;
	struct stat root_inode;
	int ninodes = 0, err = 0;

	*forcep = 0;
	sync(); sleep(1);	/* simulate operator	*/
	sync(); sleep(1);
	sync(); sleep(1);

	partition = mountedOn;
	sprintf(dev, "/dev/%s", devname);
	sprintf(rdev, "/dev/r%s", devname);

	if (stat(mountedOn, &root_inode) < 0) {
		Log("cannot stat: %s\n", mountedOn);
		return -1;
	}

	if (root_inode.st_ino != ROOTDIR_I) {
		Log("%s is not root of a filesystem\n", mountedOn);
		return -1;
	}

	/*
	 * in the JFS, the superblock is described via an inode.
	 */
	pfd = iopen(root_inode.st_dev, SUPER_I, O_RDONLY);
	if (pfd < 0) {
		Log("Unable to open superblock inode for reading\n");
		return -1;
	}
   
	if (read(pfd, &fs, sizeof (fs))  != sizeof (fs)) {
		Log("Unable to read superblock, paritition %s\n", partition);
		goto out;
	}
 
	switch (fs.s_fmod) {
	    default:
	    case FM_CLEAN:	/* clean and unmounted			*/
		Log("Most peculiar!\n");
		goto out;

	    case FM_MOUNT:	/* mounted cleanly			*/
		break;

	    case FM_MDIRTY:	/* dirty when mounted or commit fail	*/
	    case FM_LOGREDO:	/* log redo attempted but failed	*/
		Log("File system %s is in a bad state.\n", rdev);
		Log("Call your IBM representative.\n");
		goto out;
	}

#ifdef	notdef	
	/* This is the wrong calculation */
	fmax = (fs.s_fsize*512)/FSBSIZE;	/* first invalid blk num */
#else
	fmax = fs.s_fsize / (FSBSIZE/512);	/* first invalid blk num */
#endif
	close(pfd);

	/*
	 * done with the superblock, now try to read the raw device.
	 */
	pfd = open(rdev, O_RDONLY);
	if (pfd < 0) {
		Log("Unable to open `%s' inode for reading\n", rdev);
		return -1;
	}
   
	inodeFile = fopen(resultFile, "w");
	if (inodeFile == NULL) {
		Log("Unable to create inode description file %s\n"
		    , resultFile);
		goto out;
	}
  
	/*
	 * calculate the maximum number of inodes possible
	 */
	imax = ((fmax / fs.s_agsize +
		 ((fmax % fs.s_agsize) >= fs.s_agsize/INOPB ? 1 : 0))
		* fs.s_agsize) - 1;

	/*
	 * check for "FORCESALVAGE" equivalent:
	 *	LAST_RSVD_I is a vice inode, with dead beef, and
	 *	di_nlink == 2 to indicate the FORCE.
	 */
	assert(p = ginode(LAST_RSVD_I));

	if (p->di_vicemagic == VICEMAGIC
	    && p->di_vicep1 == 0xdeadbeef
	    && p->di_nlink  == 2) {
	    *forcep = 1;
	    idec(root_inode.st_dev, LAST_RSVD_I, 0xdeadbeef);
	}

	for (inum = LAST_RSVD_I + 1; inum <= imax; ++inum) {
		if ((p = ginode(inum)) == NULL
		    ||  p->di_vicemagic != VICEMAGIC
		    || (p->di_mode & IFMT) != IFREG)
			continue;

		info.inodeNumber = inum;
		info.byteCount   = p->di_size;
		info.linkCount   = p->di_nlink;
		info.u.param[0]  = p->di_vicep1;
		info.u.param[1]  = p->di_vicep2;
		info.u.param[2]  = p->di_vicep3;
		info.u.param[3]  = p->di_vicep4;

		if (judgeInode && (*judgeInode)(&info, judgeParam) == 0)
			continue;

		if (fwrite(&info, sizeof info, 1, inodeFile) != 1) {
			Log("Error writing inode file for partition %s\n"
			    , partition);
			goto out;
		}
		++ninodes;
	}

	if (fflush(inodeFile) == EOF) {
	    Log("Unable to successfully flush inode file for %s\n", partition);
	    err = -2;
	    goto out1;
	}
	if (fsync(fileno(inodeFile)) == -1) {
	    Log("Unable to successfully fsync inode file for %s\n", partition);
	    err = -2;
	    goto out1;
	}
	if (fclose(inodeFile) == EOF) {
	    Log("Unable to successfully close inode file for %s\n", partition);
	    err = -2;
	    goto out1;
	}

	/*
	 * Paranoia:  check that the file is really the right size
	 */
	if (stat(resultFile, &status) == -1) {
	    Log("Unable to successfully stat inode file for %s\n", partition);
	    err = -2;
	    goto out1;
	}
	if (status.st_size != ninodes * sizeof (struct ViceInodeInfo)) {
	    Log("Wrong size (%d instead of %d) in inode file for %s\n", 
		status.st_size, ninodes * sizeof (struct ViceInodeInfo), partition);
	    err = -2;
	    goto out1;
	}
	close(pfd);
	return 0;

    out:
	err = -1;
    out1:
	close(pfd);
	if (inodeFile)
		fclose(inodeFile);

	return err;
}

struct dinode *
ginode(inum) {
	int ag;
	daddr_t pblk;
	struct dinode *dp;
	static char buf[FSBSIZE];
	static daddr_t last_blk = -1;

	ag   = inum/fs.s_agsize;
	pblk = (ag == 0) ? INODES_B + inum/INOPB
		: ag*fs.s_agsize + (inum - ag*fs.s_agsize)/INOPB;
	
	if (last_blk != pblk) {
		if (bread(pfd, buf, pblk, sizeof(buf)) < 0) {
			last_blk = -1;
			return 0;
		}
		last_blk = pblk;
	}

	dp = (struct dinode *)buf;
	dp += itoo(inum);
	return (dp);
}

#else	/* !AFS_AIX31_ENV	*/

#if defined(AFS_SGI_ENV)

#include "libefs.h"
extern int Log();

ListViceInodes(devname, mountedOn, resultFile, judgeInode, judgeParam, forcep, forceR, wpath)
char *devname, *mountedOn, *resultFile, *wpath;
int (*judgeInode)(); 
int *forcep, forceR; {
	FILE *inodeFile = NULL;
	char dev[50], rdev[51];
	struct stat status;
	struct efs_dinode *p;
	struct ViceInodeInfo info;
	struct stat root_inode;
	int ninodes = 0, err = 0;
	EFS_MOUNT *mp;
	ino_t	imax, inum;	/* total number of I-nodes in file system */

	*forcep = 0;
	sync(); sleep(1);	/* simulate operator	*/
	sync(); sleep(1);
	sync(); sleep(1);

	partition = mountedOn;
	sprintf(dev, "/dev/dsk/%s", devname);
	sprintf(rdev, "/dev/rdsk/%s", devname);

	if (stat(mountedOn, &root_inode) < 0) {
		Log("cannot stat: %s\n", mountedOn);
		return -1;
	}

	if (root_inode.st_ino != EFS_ROOTINO) {
		Log("%s is not root of a filesystem\n", mountedOn);
		return -1;
	}

	/*
	 * open raw device
	 */
	efs_init(Log);
	if ((mp = efs_mount(rdev, O_RDONLY)) == NULL) {
		Log("Unable to open `%s' inode for reading\n", rdev);
		return -1;
	}
   
	inodeFile = fopen(resultFile, "w");
	if (inodeFile == NULL) {
		Log("Unable to create inode description file %s\n"
		    , resultFile);
		goto out;
	}
  
	/*
	 * calculate the maximum number of inodes possible
	 */
	imax = mp->m_fs->fs_ncg * mp->m_fs->fs_ipcg;

	for (inum = 2; inum < imax; ++inum) {
		if (((p = efs_figet(mp, inum)) == NULL) ||
			!IS_DVICEMAGIC(p) ||
			!((p->di_mode&IFMT) == IFREG)) {

			continue;
		}

#if defined(AFS_SGI_EXMAG)
		/* volume ID */
		info.u.param[0] = dmag(p, 0) << 24 | dmag(p, 1) << 16 |
				  dmag(p, 2) << 8 | dmag(p, 3) << 0;
		if ((p)->di_version == EFS_IVER_AFSSPEC) {
			info.u.param[1] = INODESPECIAL;
			/* type */
			info.u.param[2] = dmag(p, 8);
			/* parentId */
			info.u.param[3] = dmag(p, 4) << 24 | dmag(p, 5) << 16 |
					  dmag(p, 6) << 8 | dmag(p, 7) << 0;
		} else {
			/* vnode number */
			info.u.param[1] = dmag(p, 4) << 16 |
					  dmag(p, 5) << 8 | dmag(p, 6) << 0;
			/* disk uniqifier */
			info.u.param[2] = dmag(p, 7) << 16 |
					  dmag(p, 8) << 8 | dmag(p, 9) << 0;
			/* data version */
			info.u.param[3] = dmag(p, 10) << 16 |
					  dmag(p, 11) << 8 | (p)->di_spare;
		}
#else
BOMB!!
#endif

		info.inodeNumber = inum;
		info.byteCount   = p->di_size;
		info.linkCount   = p->di_nlink;
#ifdef notdef 
Log("Ino=%d, bytes=%d, linkCnt=%d, [%x,%x,%x,%x]\n", inum, p->di_size, p->di_nlink,
       info.u.param[0],info.u.param[1],info.u.param[2],info.u.param[3]);
#endif
		if (judgeInode && (*judgeInode)(&info, judgeParam) == 0)
			continue;

		if (fwrite(&info, sizeof info, 1, inodeFile) != 1) {
			Log("Error writing inode file for partition %s\n"
			    , partition);
			goto out;
		}
		++ninodes;
	}

	if (fflush(inodeFile) == EOF) {
	    Log("Unable to successfully flush inode file for %s\n", partition);
	    err = -2;
	    goto out1;
	}
	if (fsync(fileno(inodeFile)) == -1) {
	    Log("Unable to successfully fsync inode file for %s\n", partition);
	    err = -2;
	    goto out1;
	}
	if (fclose(inodeFile) == EOF) {
	    Log("Unable to successfully close inode file for %s\n", partition);
	    err = -2;
	    goto out1;
	}

	/*
	 * Paranoia:  check that the file is really the right size
	 */
	if (stat(resultFile, &status) == -1) {
	    Log("Unable to successfully stat inode file for %s\n", partition);
	    err = -2;
	    goto out1;
	}
	if (status.st_size != ninodes * sizeof (struct ViceInodeInfo)) {
	    Log("Wrong size (%d instead of %d) in inode file for %s\n", 
		status.st_size, ninodes * sizeof (struct ViceInodeInfo), partition);
	    err = -2;
	    goto out1;
	}
	efs_umount(mp);
	return 0;

    out:
	err = -1;
    out1:
	efs_umount(mp);
	if (inodeFile)
		fclose(inodeFile);

	return err;
}

#else /* AFS_SGI_ENV */

#ifdef AFS_LINUX_ENV
int ListViceInodes(devname, mountedOn, resultFile, judgeInode, judgeParam, forcep, forceR)
    char *devname, *mountedOn, *resultFile;
    int (*judgeInode)(), *forcep, forceR;
{
  FILE *inodeFile = NULL;
  char dev[50], testFile[50];
  struct ViceInodeInfo info;
  struct stat root_inode, status;
  ext2_filsys fs;
  errcode_t err;
  ino_t cur_ino;
  struct ext2_inode cur_inode;
  ext2_inode_scan scan;
  int tfd, ninodes;
  
   *forcep = 0;
   ninodes=0;
   
   partition = mountedOn;
   /* Check that the file system is writeable (not mounted read-only) */
   sprintf(testFile, "%s/.....zzzzz.....", mountedOn);
   if ((tfd = open(testFile,O_WRONLY|O_CREAT)) == -1) {
       Log("File system \"%s\" is not writeable (is it mounted read only?)\n",
       	    mountedOn);
       return -1;
   }
   close(tfd);
   unlink(testFile);
   sync(); sleep(1);	/* simulate operator	*/
   sync(); sleep(1);
   sync(); sleep(1);
   inodeFile = fopen(resultFile, "w");
   if (inodeFile == NULL) {
       Log("Unable to create inode description file %s\n", resultFile);
       goto out;
   }      
   sprintf(dev, "/dev/%s", devname);
    
  if (stat(mountedOn, &root_inode) < 0) {
    Log("cannot stat: %s\n", mountedOn);
    return -1;
  }
  
  if (root_inode.st_ino != EXT2_ROOT_INO) {
    Log("%s is not root of a filesystem\n", mountedOn);
    return -1;
  }
  initialize_ext2_error_table();
  err=ext2fs_open(dev, 0, 0, 0, unix_io_manager, &fs);
  if (err) {
    Log("Error initializing ext2 on %s: %s\n", dev, error_message(err));
    return -1;
  }
  err=ext2fs_open_inode_scan(fs,0,&scan);
  if (err) {
    Log("Error starting inode scan on %s: %s\n", dev, error_message(err));
    ext2fs_close(fs);
    return -1;
  }
  cur_ino=EXT2_FIRST_INO(fs->super)-1;
  while (scan->groups_left || scan->inodes_left) {
    err=ext2fs_get_next_inode(scan, &cur_ino, &cur_inode); 
    if (err) {
      Log("Error in inode scan on %s (next expected inode %d): %s\n", dev,
          cur_ino + 1,error_message(err));
      ext2fs_close(fs);
      return -1;
    }
    if (!cur_inode.i_mode)
      continue;
    if (!(cur_inode.i_flags & 0x40000000))
        continue;
    info.u.param[0]=cur_inode.osd1.linux1.l_i_reserved1;
    info.u.param[1]=cur_inode.osd2.linux2.l_i_reserved2[0];
    info.u.param[2]=cur_inode.osd2.linux2.l_i_reserved2[1];
    info.u.param[3]=cur_inode.i_version;
    info.inodeNumber=cur_ino;
    info.byteCount=cur_inode.i_size;
    info.linkCount=cur_inode.i_links_count;
    if (judgeInode && (*judgeInode)(&info, judgeParam) == 0)
      continue;
    if (fwrite(&info, sizeof(struct ViceInodeInfo), 1, inodeFile) != 1) {
      Log("Error writing inode file for partition %s\n", partition);
      goto out;
    }
    ninodes++;
  }
  if (fflush(inodeFile) == EOF) {
    Log("Unable to successfully flush inode file for %s\n", partition);
    err = -2;
    goto out1;
  }
  if (fsync(fileno(inodeFile)) == -1) {
    Log("Unable to successfully fsync inode file for %s\n", partition);
    err = -2;
    goto out1;
  }
  if (fclose(inodeFile) == EOF) {
    Log("Unable to successfully close inode file for %s\n", partition);
    err = -2;
    goto out1;
  }
  
  /*
   * Paranoia:  check that the file is really the right size
   */
  if (stat(resultFile, &status) == -1) {
    Log("Unable to successfully stat inode file for %s\n", partition);
    err = -2;
    goto out1;
  }
  if (status.st_size != ninodes * sizeof (struct ViceInodeInfo)) {
    Log("Wrong size (%d instead of %d) in inode file for %s\n", 
        status.st_size, ninodes * sizeof (struct ViceInodeInfo), partition);
	    err = -2;
	    goto out1;
  }  
   ext2fs_close_inode_scan(scan);
   ext2fs_close(fs);
   return 0;
   
out:
   err = -1;
out1:
   if (inodeFile)
   	fclose(inodeFile);
   ext2fs_close_inode_scan(scan);
   ext2fs_close(fs);
   return err;
}

#else

#ifdef AFS_NETBSD_ENV
int ListViceInodes(devname, mountedOn, resultFile, judgeInode, judgeParam, forcep, forceR)
    char *devname, *mountedOn, *resultFile;
    int (*judgeInode)(), *forcep, forceR;
{
  printf("ListViceInodes -- Not implemented under NetBSD--insufficient spares in the inode\n");
}
#else

#ifdef AFS_AIX22_ENV
int Bsize = BSIZE;	/* block size for this system */
int Bshift = BSHIFT;   	/* log2 of Bsize */
int rawiblks = 10;	/* max blocks in raw I-node buffer */
char   *rawibuf=NULL;	/* buffer for large raw I-node reads */
daddr_t startib;	/* block number of first block in rawibuf */
daddr_t	fmax;		/* total number of blocks n file system */
ino_t	imax, inum;	/* total number of I-nodes in file system */
struct dinode *ginode();
#endif
#ifdef AFS_AUXVOLFILE
int Auxfd = -1;
static struct dauxinode auxbuf;
struct dauxinode *IsAfsInode();
#endif

#ifdef AFS_HPUX_ENV
#define SPERB	(MAXBSIZE / sizeof(short))
#define MAXNINDIR (MAXBSIZE / sizeof(daddr_t))

struct bufarea {
	struct bufarea	*b_next;		/* must be first */
	daddr_t	b_bno;
	int	b_size;
	union {
		char	b_buf[MAXBSIZE];	/* buffer space */
		short	b_lnks[SPERB];		/* link counts */
		daddr_t	b_indir[MAXNINDIR];	/* indirect block */
		struct	fs b_fs;		/* super block */
		struct	cg b_cg;		/* cylinder group */
	} b_un;
	char	b_dirty;
};
typedef struct bufarea BUFAREA;

BUFAREA sblk;
#define sblock sblk.b_un.b_fs
#endif /* AFS_HPUX_ENV */

int ListViceInodes(devname, mountedOn, resultFile, judgeInode, judgeParam, forcep, forceR, wpath)
    char *devname, *mountedOn, *resultFile, *wpath;
    int (*judgeInode)(), *forcep, forceR;
{
   union {
#ifdef	AFS_AIX_ENV
       struct filsys fs;
       char block[BSIZE];
#else	/* !AFS_AIX_ENV */
       struct fs fs;
       char block[SBSIZE];
#endif
   } super;
   int tfd, i, c, e, bufsize, code, err =0;
   FILE *inodeFile = NULL;
   char testFile[50], dev[50], rdev[51], err1[512];
   struct dinode *inodes = NULL, *einodes, *dptr;
   struct stat status;
   int ninodes = 0;
   struct dinode *p;
   struct ViceInodeInfo info;
#ifdef	AFS_AUXVOLFILE
#define	NHAUXINO    	512	    	/* Must be a power of 2 */
#define	AHash(ainode)   ((int)(ainode) & (NHAUXINO-1))
   char AuxFile[200];
   struct afs_auxheader auxheader;
   struct sauxinode *ino_table[NHAUXINO], *bufp, *sbufp;
   int num, remdups = 0, removed = 0;
#endif

   *forcep = 0;
   partition = mountedOn;
#ifdef	notdef		/* XXX dev not used XXX */
   sprintf(dev, "%s/%s", AFS_DSKDEV, devname);
#endif
   sprintf(rdev, "%s%s", AFS_RDSKDEV, devname);
   /* Check that the file system is writeable (not mounted read-only) */
#ifdef	AFS_AIX_ENV
   sprintf(testFile, "%s/....zzzz....", mountedOn);
#else
   sprintf(testFile, "%s/.....zzzzz.....", mountedOn);
#endif
   if ((tfd = open(testFile,O_WRONLY|O_CREAT)) == -1) {
       Log("File system \"%s\" is not writeable (is it mounted read only?)\n",
       	    mountedOn);
       return -1;
   }
   close(tfd);
   unlink(testFile);
   
   sync(); 
   /* Bletch:  this is terrible;  is there a better way to do this? Does this work? vfsck doesn't even sleep!! */
#ifdef	AFS_AIX_ENV
   sleep(5);	/* Trying a smaller one for aix */
#else
   sleep(10);
#endif

   pfd = open(rdev, O_RDONLY);
#ifdef	AFS_HPS800_ENV
   if (pfd <= 0) {
       sprintf(rdev, "%s/r%s", wpath, devname);
       pfd = open(rdev, O_RDONLY);
   }
#endif
   if (pfd <= 0) {
       sprintf(err1, "Could not open device %s to get inode list\n", rdev);
       perror(err1);
       return -1;
   }
   
#ifdef	AFS_NEXT30_ENVXXX
   if (fstat(pfd, &status) < 0) {
       sprintf(err1, "Could not stat device %s to get inode list\n", rdev);
       perror(err1);
       return -1;
   }       
   Bsize = status.st_blksize;
#endif

#ifdef	AFS_AIX_ENV
   if (bread(pfd, (char *)&super.fs, SUPERB, sizeof super.fs) == -1) {
#else
#ifdef AFS_HPUX_ENV
   if (bread(pfd, (char *)&sblock, SBLOCK, SBSIZE) == -1) {
#else
#ifdef	AFS_NEXT30_ENV
   if (bread(pfd, super.block, SBLOCK/AFS_DEV_BSIZE, SBSIZE) == -1) {
#else
   if (bread(pfd, super.block, SBLOCK, SBSIZE) == -1) {
#endif
#endif /* AFS_HPUX_ENV */
#endif
       Log("Unable to read superblock, partition %s\n", partition);
       goto out;
   }
 
   inodeFile = fopen(resultFile, "w");
   if (inodeFile == NULL) {
       Log("Unable to create inode description file %s\n", resultFile);
       goto out;
   }
  
#ifdef	AFS_AIX_ENV
   /*
     char *FSlabel(), *fslabel=0;
     fslabel = FSlabel(&super.fs);
     */
   if (super.fs.s_bsize == 0)
       super.fs.s_bsize = 512;
   if (super.fs.s_bsize != BSIZE ) {
       Log("SuperBlk: Cluster size not %d; run vfsck\n", BSIZE);
       goto out;
   }
   fmax = super.fs.s_fsize;		/* first invalid blk num */
   imax = ((ino_t)super.fs.s_isize - (SUPERB+1)) * INOPB;
   if (imax == 0) {
       Log("Size check: imax==0!\n");
       goto out;
   }
   if (GetAuxInodeFile(partition, &status) == 0) {
       Log("Can't access Aux inode file for partition %s, aborting\n", partition);
       goto out;
   }
   for (inum=1; inum <= imax; inum++) {
       struct dauxinode *auxp;
       if ((auxp = IsAfsInode(inum)) == NULL){
	   /* Not an afs inode, keep going */
	   continue;
       }
       if ((p = ginode(inum)) == NULL)
	   continue;
       /* deleted/non-existent inode when di_mode == 0 */
       if (!p->di_mode)
	   continue;
       info.inodeNumber = (int)inum;
       info.byteCount = p->di_size;
       info.linkCount = p->di_nlink;
       info.u.param[0] = auxp->aux_param1;
       info.u.param[1] = auxp->aux_param2;
       info.u.param[2] = auxp->aux_param3;
       info.u.param[3] = auxp->aux_param4;
       if (judgeInode && (*judgeInode)(&info, judgeParam) == 0)
	   continue;
       if (fwrite(&info, sizeof info, 1, inodeFile) != 1) {
	   Log("Error writing inode file for partition %s\n", partition);
	   goto out;
       }
       ninodes++;
   }
#else
   /*
    * run a few consistency checks of the superblock
    * (Cribbed from vfsck)
    */
#ifdef AFS_HPUX_ENV
#if defined(FD_FSMAGIC)
   if ((sblock.fs_magic != FS_MAGIC) && (sblock.fs_magic != FS_MAGIC_LFN) && (sblock.fs_magic != FD_FSMAGIC)) {
#else
   if ((sblock.fs_magic != FS_MAGIC) && (sblock.fs_magic != FS_MAGIC_LFN)) {
#endif
       Log("There's something wrong with the superblock for partition %s; bad magic (%d) run vfsck\n", 
	   partition, sblock.fs_magic);
       goto out;
   }	
   if (sblock.fs_ncg < 1 ) {
       Log("There's something wrong with the superblock for partition %s; NCG OUT OF RANGE (%d) run vfsck\n", 
	   partition, sblock.fs_ncg);
       goto out;
   }
   if (sblock.fs_cpg < 1
#ifdef MAXCPG
 || sblock.fs_cpg > MAXCPG
#endif
       ) {
       Log("There's something wrong with the superblock for partition %s; CPG OUT OF RANGE (%d) run vfsck\n", 
	   partition, sblock.fs_cpg);
       goto out;
   }
   if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl ||
	(sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl) {
       Log("There's something wrong with the superblock for partition %s; NCYL LESS THAN NCG*CPG run vfsck\n", partition);
       goto out;
   }	
   if (sblock.fs_sbsize > SBSIZE ) {
       Log("There's something wrong with the superblock for partition %s; bsize too large (%d vs. %d) run vfsck\n", 
	   partition, sblock.fs_sbsize, sblock.fs_bsize);
       goto out;
   }
	
#else
   if (
      (super.fs.fs_magic != FS_MAGIC)
   || (super.fs.fs_ncg < 1)
#if	defined(AFS_SUN_ENV) || defined(AFS_OSF_ENV)
   || (super.fs.fs_cpg < 1)
#else
   || (super.fs.fs_cpg < 1 || super.fs.fs_cpg > MAXCPG)
#endif
   || (super.fs.fs_ncg * super.fs.fs_cpg < super.fs.fs_ncyl ||
	(super.fs.fs_ncg - 1) * super.fs.fs_cpg >= super.fs.fs_ncyl)
   || (super.fs.fs_sbsize > SBSIZE)) {
       Log("There's something wrong with the superblock for partition %s; run vfsck\n", partition);
       goto out;
   }
#endif /* AFS_HPUX_ENV */

#ifdef AFS_HPUX_ENV
   bufsize = sblock.fs_ipg * sizeof(struct dinode);   
#else
   bufsize = super.fs.fs_ipg * sizeof(struct dinode);   
#endif /* AFS_HPUX_ENV */
   inodes = (struct dinode *) malloc(bufsize);
   einodes = (struct dinode *) (((char *)inodes) + bufsize);
   if (inodes == NULL) {
       Log("Unable to allocate enough memory to scan inodes; help!\n");
       goto out;
   }
   Log("Scanning inodes on device %s...\n", rdev);
#ifdef	AFS_AUXVOLFILE
   if (GetAuxInodeFile(partition, &status) == 0) {
       Log("Can't access Aux inode file for partition %s, aborting\n", partition);
       goto out;
   }
   for (i = 0; i < NHAUXINO; i++) 
       ino_table[i] = NULL;
   num = (status.st_size - sizeof(struct afs_auxheader)) / sizeof(struct dauxinode);
   sbufp = bufp = (struct sauxinode *) malloc(num * sizeof(struct sauxinode));
   if (!bufp) {
       Log("Not enough memory: Can't malloc %d sauxinode entries (%d bytes)\n", 
	   num, num * sizeof(struct sauxinode));
       goto out;
   }
   if (read(Auxfd, &auxheader, sizeof(struct afs_auxheader)) != sizeof(struct afs_auxheader)) {
       Log("Reading auxvolfile's header failed\n");
       goto out;
   }
   if (auxheader.magic != VICEMAGIC) {
       Log("Auxvolfile ISN'T a proper one (magic=%x)\n", auxheader.magic);
       goto out;
   }
   for (i = 0; i < num; i++) {
       register struct sauxinode **uvc, *wvc, *sauxp;
       struct dauxinode daux;
       register int hash;

       if (read(Auxfd, &daux, sizeof(struct dauxinode)) != sizeof(struct dauxinode)) {
	   Log("Reading auxvolfile failed (i=%d, num=%d)\n", i, num);
	   goto out;
       }
       sbufp->aux_inode = daux.aux_inode;
       sbufp->aux_alive = 0;	
       sbufp->aux_param1 = daux.aux_param1;
       sbufp->aux_param2 = daux.aux_param2;
       sbufp->aux_param3 = daux.aux_param3;
       sbufp->aux_param4 = daux.aux_param4;
       /*
	* Note that we may have more than one entry with the same inode; that happens when
	* afs inodes are deleted and recreated. We'll only keep the last inode (still valid)
	*/
       hash = AHash(daux.aux_inode);
#ifdef	notdef
       /*
	* Not needed since we store them in LIFO order and thus we'll always validate
	* only the last inode
        */
       for (sauxp = ino_table[hash]; sauxp; sauxp = sauxp->aux_next) {
	   if (sauxp->aux_inode == daux.aux_inode) {
	       /* 
		* Remove entry from the hash chain 
		*/
	       remdups++;
	       uvc = &ino_table[hash];
	       for (wvc = *uvc; wvc; uvc = &wvc->aux_next, wvc = *uvc) {
		   if (sauxp == wvc) {
		       *uvc = sauxp->aux_next;
		       break;
		   }
	       }
	   }
       }
#endif
       sbufp->aux_next = ino_table[hash];
       ino_table[hash] = sbufp++;
   }
   close(Auxfd);
#endif
#ifdef AFS_HPUX_ENV
   for (c = 0; c < sblock.fs_ncg; c++) {
	i = c*sblock.fs_ipg; e = i+sblock.fs_ipg;
	if (lseek(pfd, dbtob(fsbtodb(&sblock,itod(&sblock,i))), L_SET) == -1) {
#else
   for (c = 0; c < super.fs.fs_ncg; c++) {
       daddr_t dblk1;
	i = c*super.fs.fs_ipg; e = i+super.fs.fs_ipg;
#ifdef AFS_NEXT30_ENV
	if (lseek(pfd, dbtob(fsbtodb(&super.fs,itod(&super.fs,i)) << 1, Bsize), L_SET) == -1) {
#else
#ifdef	AFS_OSF_ENV
	dblk1 = fsbtodb(&super.fs, itod(&super.fs, i));
	if (lseek(pfd, (off_t) ((off_t)dblk1 * DEV_BSIZE), L_SET) == -1) {
#else
	if (lseek(pfd, dbtob(fsbtodb(&super.fs,itod(&super.fs,i))), L_SET) == -1) {
#endif
#endif
#endif /* AFS_HPUX_ENV */
	    Log("Error reading inodes for partition %s; run vfsck\n", partition);
	    goto out;
	}
	while (i<e) {
	    if (!forceR) {
		if (read(pfd, inodes, bufsize) != bufsize) {
		    Log("Error reading inodes for partition %s; run vfsck\n", partition);
		    goto out;
		}
	    } else {
		register int bj, bk;
		dptr = inodes;
		for (bj=bk=0; bj < bufsize; bj=bj+512, bk++) {
		    if ((code = read(pfd, dptr, 512)) != 512) {
			Log("Error reading inode %d? for partition %s (errno = %d); run vfsck\n",  bk+i, partition, errno);
			if (lseek(pfd, 512, L_SET) == -1) {			    
			    Log("Lseek failed\n"); 
			    goto out;
			}
			dptr->di_mode = 0; dptr++;
			dptr->di_mode = 0; dptr++;
			dptr->di_mode = 0; dptr++;
			dptr->di_mode = 0; dptr++;
		    } else
			dptr += 4;
		}
	    }
	    for (p=inodes; p<einodes && i<e; i++,p++) {
#ifdef notdef 
printf("Ino=%d, mag=%x, vol=%d, mode=%x\n", i, p->di_vicemagic, p->di_vicep1, p->di_mode);
#endif
#if	defined(AFS_AUXVOLFILE) && defined(AFS_SUN_ENV)
	        if (p->di_vicemagic == VICEMAGIC && (p->di_mode&IFMT) == IFREG) {
		    for (sbufp = ino_table[AHash(i)]; sbufp; sbufp = sbufp->aux_next) {
			if (sbufp->aux_inode == i)
			    break;
		    }
		    if (sbufp == NULL) {
			Log("Inode %d should have existed in the VOLFILE...\n", i);
			continue;		    /* Not an afs inode, keep going */
		    }
		    sbufp->aux_alive = 1;		/* Indicate it's alive */
	            info.u.param[0] = sbufp->aux_param1;
	            info.u.param[1] = sbufp->aux_param2;
	            info.u.param[2] = sbufp->aux_param3;
	            info.u.param[3] = sbufp->aux_param4;
#else
	        if (IS_DVICEMAGIC(p) && (p->di_mode&IFMT) == IFREG) {
		    u_int32  p2 = p->di_vicep2, p3 = p->di_vicep3;

	            info.u.param[0] = p->di_vicep1;
#ifdef	AFS_3DISPARES
		    if (((p2 >> 3) == INODESPECIAL) && (p2 & 0x3)) {
			info.u.param[1] = INODESPECIAL;
			info.u.param[2] = p3;
			info.u.param[3] = p2 & 0x3;
		    } else {
			info.u.param[1] = ((p2 >> 27) << 16) + (p3 & 0xffff);
			info.u.param[2] = (p2 & 0x3fffff);
			info.u.param[3] = (((p2 >> 22) & 0x1f) << 16) + (p3 >> 16);
		    }
#else
	            info.u.param[1] = p->di_vicep2;
	            info.u.param[2] = p->di_vicep3;
	            info.u.param[3] = p->di_vicep4;
#endif
#endif
	            info.inodeNumber = i;
	            info.byteCount = p->di_size;
	            info.linkCount = p->di_nlink;
		    if (judgeInode && (*judgeInode)(&info, judgeParam) == 0)
		        continue;
	            if (fwrite(&info, sizeof info, 1, inodeFile) != 1) {
	                Log("Error writing inode file for partition %s\n", partition);
		        goto out;
		    }
		    ninodes++;
#if	defined(AFS_AUXVOLFILE) && defined(AFS_SUN_ENV)
		} else {
		    /*
		     * We would typically have many entries in the volfile that are obsolete
		     * (since we don't discard removed inodes automatically) we clear them here
		     */
		    if (i <= num) {
			register struct sauxinode **uvc, *wvc;

			for (sbufp = ino_table[AHash(i)]; sbufp; sbufp = sbufp->aux_next) {
			    if (sbufp->aux_inode == i)
				break;
			}
			if (sbufp) {
			    removed++;
			    /* 
			     * Remove entry from the hash chain 
			     */
			    uvc = &ino_table[AHash(i)];
			    for (wvc = *uvc; wvc; uvc = &wvc->aux_next, wvc = *uvc) {
				if (sbufp == wvc) {
				    *uvc = sbufp->aux_next;
				    break;
				}
			    }
			}
		    }
#endif
	        }
	    }
	}
   }
   if (inodes) free(inodes);
#endif
#if	defined(AFS_AUXVOLFILE) && defined(AFS_SUN_ENV)
    sprintf(AuxFile, "%s/%s", partition, AUXVOLFILE);
    if ((Auxfd = open(AuxFile, O_RDWR | O_TRUNC | O_CREAT, 0600)) == -1) {
	 Log("Can't open %s for writing; quiting\n", AuxFile);
	 return 0;
    }
   if (write(Auxfd, &auxheader, sizeof(struct afs_auxheader)) != sizeof(struct afs_auxheader)) {
       Log("Writing auxvolfile's header failed\n");
       goto out;
   }
   for (i = 0; i < NHAUXINO; i++) {
       for (sbufp = ino_table[i]; sbufp; sbufp = sbufp->aux_next) {
	   struct dauxinode daux;

	   if (!sbufp->aux_alive) {
	       removed++;
	       continue;			/* It's not an AFS one; remove it */
	   }
	   daux.aux_inode = sbufp->aux_inode;
	   daux.aux_param1 = sbufp->aux_param1;
	   daux.aux_param2 = sbufp->aux_param2;
	   daux.aux_param3 = sbufp->aux_param3;
	   daux.aux_param4 = sbufp->aux_param4;
	   if (write(Auxfd, &daux, sizeof(struct dauxinode)) != sizeof(struct dauxinode)) {
	       Log("Reading auxvolfile failed (i=%d, num=%d)\n", i, num);
	       goto out;
	   }
       }
   }
   close(Auxfd);
   if (bufp) free(bufp);
   Log("%d (obsolete) entries were removed from %s/VOLFILE...\n", removed, partition);
#endif
	if (fflush(inodeFile) == EOF) {
	    Log("Unable to successfully flush inode file for %s\n", partition);
	    err = -2;
	    goto out1;
	}
	if (fsync(fileno(inodeFile)) == -1) {
	    Log("Unable to successfully fsync inode file for %s\n", partition);
	    err = -2;
	    goto out1;
	}
	if (fclose(inodeFile) == EOF) {
	    Log("Unable to successfully close inode file for %s\n", partition);
	    err = -2;
	    goto out1;
	}

	/*
	 * Paranoia:  check that the file is really the right size
	 */
	if (stat(resultFile, &status) == -1) {
	    Log("Unable to successfully stat inode file for %s\n", partition);
	    err = -2;
	    goto out1;
	}
	if (status.st_size != ninodes * sizeof (struct ViceInodeInfo)) {
	    Log("Wrong size (%d instead of %d) in inode file for %s\n", 
		status.st_size, ninodes * sizeof (struct ViceInodeInfo), partition);
	    err = -2;
	    goto out1;
	}
   close(pfd);
   return 0;

out:
   err = -1;
out1:
   close(pfd);
   if (inodeFile)
   	fclose(inodeFile);
   if (inodes)
        free(inodes);
   return err;
}
#endif	/* !AFS_NETBSD_ENV */
#endif	/* !AFS_LINUX_ENV */
#endif 	/* !AFS_SGI_ENV */
#endif	/* !AFS_AIX31_ENV	*/


#ifndef AFS_LINUX_ENV
int bread(fd, buf, blk, size)
	int fd;
	char *buf;
	daddr_t blk;
	int32 size;
{
#ifdef	AFS_AIX_ENV
	if (lseek(fd, blk * Bsize, 0) < 0
#else
#ifdef	AFS_NEXT30_ENV
	if (lseek(fd, (off_t)dbtob(blk, Bsize), L_SET) < 0
#else
	if (lseek(fd, (off_t)dbtob(blk), L_SET) < 0
#endif
#endif
	  || read(fd, buf, size) != size) {
	     Log("Unable to read block %d, partition %s\n", blk, partition);
		return -1;
	  }
	return 0;
}
#endif
#ifdef AFS_AUXVOLFILE
int GetAuxInodeFile(partition, statp)
char *partition;
struct stat *statp;
{
    char AuxFile[200];

    if (stat(partition, statp) == -1) {
	Log("Couldn't find file system \"%s\"\n", partition);
	return 0;
    }
    if (statp->st_ino != ROOTINODE) {
	Log("\"%s\" is not a mounted file system\n", partition);
	return 0;
    }
    sprintf(AuxFile, "%s/%s", partition, AUXVOLFILE);
    if (stat(AuxFile, statp)) {
	/* Oh, Oh losing this file is bad news; for the time being just use a backup in /usr/afs/backup/<partition>/AUXVOLFILE; this copy is currently done every 5 mins */
	Log("AuxFile %s missing, trying /usr/afs/backup/%s\n", AuxFile, AuxFile);
	sprintf(AuxFile, "/usr/afs/backup/%s/%s", partition, AUXVOLFILE);
	if (stat(AuxFile, statp)) {
	    Log("%s AuxFile also missing; quitting\n", AuxFile);
	    return 0;
	}
    }
    if ((Auxfd = open(AuxFile, O_RDONLY)) == -1) {
	 Log("Can't open %s; quiting\n", AuxFile);
	 return 0;
    }
    /* presume success at this point */
    return 1;
}
	    
	    
struct dauxinode *IsAfsInode(inum)
ino_t inum;
{
    struct dauxinode buf;
    int code;

    if (Auxfd == -1) {
	Log("Aux File not opened yet\n");
	return (struct dauxinode *)0;
    }
    if (lseek(Auxfd, inum * sizeof(struct dauxinode) + sizeof(struct afs_auxheader), 0) < 0) {
	return (struct dauxinode *)0;
    }
    if (read(Auxfd, &auxbuf, sizeof(struct dauxinode)) != sizeof(struct dauxinode)) {
	return (struct dauxinode *)0;
    }
#ifdef	AFS_SUN_ENV
    return &auxbuf;
#else
    if (auxbuf.aux_magic == VICEMAGIC) {    /* Seems to be an afs inode */
	return &auxbuf;
    }
    return (struct dauxinode *)0;
#endif
}


#ifdef AFS_AIX22_ENV

struct dinode *ginode(inum)
ino_t inum;
{
	register struct dinode *dp;
	register daddr_t iblk;

	if(inum > imax)
		return(NULL);
	iblk = itod(inum);
	if (!rawibuf) {
	    rawibuf = (char *) malloc(rawiblks * Bsize);
	    if (!rawibuf) {
		Log("Malloc failed to allocate space; aborting\n");
		return 0;
	    }
	    startib = fmax;	/* mark buffer contents invalid */
	}
	if(iblk < startib || iblk >= startib+rawiblks) {
	    if(bread(pfd,rawibuf,iblk,rawiblks*Bsize) == -1) {
		startib = fmax;
		return(NULL);
	    }
	    startib = iblk;
	}
	dp = (struct dinode *)&rawibuf[(unsigned)((iblk-startib)<<Bshift)];

/* AIX Version 2.2.1 security enhancement */
	dp += itoo(inum);
	dp->di_nlink &= 0x7FFF;
/* TCSEC Division C Class C2 */
	return(dp);
}


#endif
#endif
