/*
 * Copyright 2000, International Business Machines Corporation and others.
 * All Rights Reserved.
 * 
 * This software has been released under the terms of the IBM Public
 * License.  For details, see the LICENSE file in the top-level source
 * directory or online at http://www.openafs.org/dl/license10.html
 */

#include <afsconfig.h>
#include <afs/param.h>

RCSID("$Header: /afs/sipb/project/openafs/debian/cvs/openafs/src/vol/fs_conv_411.c,v 1.1.1.5 2001/09/11 14:35:41 hartmans Exp $");

#if	defined(AFS_DUX40_ENV) || (defined(AFS_SUN_ENV) && !defined(AFS_SUN5_ENV)) || defined(AFS_SUN54_ENV)
#if defined AFS_DUX40_ENV
#define OSVERS "DUX 4.0D"
#else
#if	defined(AFS_SUN54_ENV)
#define OSVERS "SunOS 5.6"
#else
#define OSVERS "SunOS 4.1.1"
#endif
#endif
#include <sys/types.h>
#include <rx/xdr.h>
#include <afs/afsint.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef	AFS_SUN5_ENV
#include <sys/fs/ufs_fs.h>
#else
#include <ufs/fs.h>
#endif
#include <sys/stat.h>
#include <dirent.h>
#include <sys/mount.h>
#include <sys/file.h>
#ifndef ITIMER_REAL
#include <sys/time.h>
#endif /* ITIMER_REAL */
#include <time.h>
#ifdef AFS_VFSINCL_ENV
#include <sys/vnode.h>
#ifdef	AFS_SUN5_ENV
#include <sys/fs/ufs_inode.h>
#else
#include <ufs/inode.h>
#endif
#else /* AFS_VFSINCL_ENV */
#ifdef	AFS_DUX40_ENV
#include <ufs/inode.h>
#else
#include <sys/inode.h>
#endif
#endif /* AFS_VFSINCL_ENV */
#ifdef	AFS_AIX_ENV
#include <sys/vfs.h>
#include <sys/lockf.h>
#else
#ifdef	AFS_HPUX_ENV
#include <unistd.h>
#include <checklist.h>
#else
#ifdef	  AFS_SUN5_ENV
#include <sys/mnttab.h>
#include <sys/mntent.h>
#include <sys/vfstab.h>
#else
#include <fstab.h>
#endif
#endif
#endif
#include <fcntl.h>
#include <afs/osi_inode.h>
#include <errno.h>

#include <nfs.h>
#include <partition.h>
#include <afs/cmd.h>
#include <stdlib.h>
#include <unistd.h>

int icount=0, iarraysize=0, *iarray;

char *rawname(),  *unrawname(), *vol_DevName(), *blockcheck();
extern int errno;
#define ROOTINODE	2	
int force = 0,  verbose = 0,  unconv = 0;

#ifdef	AFS_DUX40_ENV
int sortinodes(a,b) 
  int *a, *b;
{
  if (*a < *b) return -1;
  if (*a > *b) return +1;
  return 0;
}

int BuildInodes(path)
  char *path;
{
  DIR *d;
  struct dirent *de;
  char p[256];
  struct stat stats;
  int code=0;

  d = opendir(path);
  if (!d) {
     printf("Cannot open %s: error %d\n", path, errno);
     return -1;
  }

  while (de = readdir(d)) {
     if (iarraysize == 0) {
        iarraysize = 1000;
        iarray = (int *) malloc(iarraysize * sizeof(int));
     }
     if (icount >= iarraysize) {
        iarraysize += 1000;
        iarray = (int *) realloc(iarray, iarraysize * sizeof(int));
     }
     iarray[icount] = de->d_ino;
     icount++;

     if ( (strcmp(de->d_name,".")  == 0) || (strcmp(de->d_name,"..") == 0) )
        continue;

     strcpy(p, path);
     strcat(p, "/");
     strcat(p, de->d_name);

     code = stat(p, &stats);
     if (code) {
        printf("Cannot stat %s: error %d\n", path, errno);
	return -1;
     }
     if (S_ISDIR(stats.st_mode)) {
        code = BuildInodes(p);
	if (code) break;
     }
  }

  closedir(d);
  return code;
}
#endif

static ConvCmd(as)
    struct cmd_syndesc *as;
{
    unconv = 0;
    handleit(as);
}

static UnConvCmd(as)
    struct cmd_syndesc *as;
{
    unconv = 1;
    handleit(as);
}

#if	defined(AFS_SUN54_ENV)

static handleit(as)
    struct cmd_syndesc *as;
{
    register struct cmd_item *ti;
    char *dname;
    afs_int32 haspart = 0, hasDevice = 0;
    struct vfstab mnt;

    if (as->parms[1].items)
	verbose = 1;
    else
	verbose = 0;
    if (as->parms[2].items)
	force = 1;
    else
	force = 0;

    if (unconv) {
       printf("Unconverts from a %s AFS partition to a pre-%s compatible format.\n\n",
	      OSVERS, OSVERS);
    }
    else {
	printf("Converts a pre-%s AFS partition to a %s compatible format.\n\n",
	       OSVERS, OSVERS);
    }
    if (!force) {
	printf("*** Must use the '-force' option for command to have effect! ***\n");
	if (!verbose) {
	   printf("*** Use the '-verbose' option to report what will be converted ***\n");
	   exit(1);
	}
    }

    for (ti = as->parms[0].items; ti; ti=ti->next) {
	FILE *fsent;
	char *namep = ti->data;
	int found = 0;

	haspart = 1;
	if ( ( fsent = fopen(VFSTAB, "r")) == NULL )
	{
		printf("Unable to open %s ( errno = %d)\n", VFSTAB, errno);
		exit(2);
	}
	while (!getvfsent(fsent, &mnt)) {
	    char *part = mnt.vfs_mountp;

	    if ( !part)
		continue;

	    if (!strncmp(namep, VICE_PARTITION_PREFIX, VICE_PREFIX_SIZE)) {
		if (!strncmp(part, VICE_PARTITION_PREFIX, VICE_PREFIX_SIZE)) {
		    if (!strncmp(part, namep, strlen(part)+1)) {
			if (dname = mnt.vfs_fsckdev) {
			    printf("ProcessFileSys %s %s\n", dname, namep);
			    ProcessFileSys(dname, namep);
			    found = 1;
			    break;
			}
		    }
		}
	    }
	}
	if (!found)
	    printf("Unknown input AFS partition name or device: %s\n", namep);
	fclose(fsent);
    }

	/* if raw devices are specified */
    for (ti = as->parms[3].items; ti; ti=ti->next) {
	char *namep = ti->data;

	hasDevice = 1;
	if ( namep )
	{
		if ( !CheckMountedDevice(namep) )
		{
		    printf("Device %s may be mounted, aborting...\n", namep);
		    continue;
		}
		ProcessFileSys(namep, namep);
	}
    }

    if (!haspart && !hasDevice) {
	int didSome = 0;
	FILE *fsent;

        if ( ( fsent = fopen(VFSTAB, "r")) ==NULL )
        {
                printf("Unable to open %s ( errno = %d)\n", VFSTAB, errno);
                exit(2);
        }
        while (!getvfsent(fsent, &mnt)) {
            char *part = mnt.vfs_mountp;

	    if ( !part)
		continue;

	    if (strncmp(part, VICE_PARTITION_PREFIX, VICE_PREFIX_SIZE) == 0) {
		if (dname = mnt.vfs_fsckdev )
		    ProcessFileSys(dname, part);
		    didSome++;
		}
	}
	fclose(fsent);
	if (!didSome)
	    printf("No file system partitions named %s* found; not processed\n",VICE_PARTITION_PREFIX);
    }
}

#else /* AFS_SUN54_ENV */

handleit(as)
    struct cmd_syndesc *as;
{
    register struct cmd_item *ti;
    char *dname;
    afs_int32 haspart = 0;

    if (as->parms[1].items)
	verbose = 1;
    else
	verbose = 0;
    if (as->parms[2].items)
	force = 1;
    else
	force = 0;

    if (unconv) {
       printf("Unconverts from a %s AFS partition to a pre-%s compatible format.\n\n",
	      OSVERS, OSVERS);
    }
    else {
	printf("Converts a pre-%s AFS partition to a %s compatible format.\n\n",
	       OSVERS, OSVERS);
    }
    if (!force) {
	printf("*** Must use the '-force' option for command to have effect! ***\n");
	if (!verbose) {
	   printf("*** Use the '-verbose' option to report what will be converted ***\n");
	   exit(1);
	}
    }

    for (ti = as->parms[0].items; ti; ti=ti->next) {
	struct fstab *fsent;
	char *namep = ti->data;
	int found = 0;

	haspart = 1;
	setfsent();
	while (fsent = getfsent()) {
	    char *part = fsent->fs_file;

	    if (!strncmp(namep, VICE_PARTITION_PREFIX, VICE_PREFIX_SIZE)) {
		if (!strncmp(part, VICE_PARTITION_PREFIX, VICE_PREFIX_SIZE)) {
		    if (!strncmp(part, namep, strlen(part)+1)) {
			if (dname = unrawname(fsent->fs_spec)) {
			    ProcessFileSys(dname, namep);
			    found = 1;
			    break;
			}
		    }
		}
	    } else {
		if (!rawname(namep) || !unrawname(namep))
		    break;
		if (!strcmp(fsent->fs_spec, rawname(namep)) ||
		    !strcmp(fsent->fs_spec, unrawname(namep))) {
		    if (dname = unrawname(fsent->fs_spec)) {
			ProcessFileSys(dname, fsent->fs_file);
			found = 1;
			break;
		    }			
		}
	    }
	}
	if (!found)
	    printf("Unknown input AFS partition name or device: %s\n", namep);
	endfsent();
    }

    if (!haspart) {
	int didSome = 0;
	struct fstab *fsent;

	setfsent();
	while (fsent = getfsent()) {
	    char *part = fsent->fs_file;

	    if (strncmp(part, VICE_PARTITION_PREFIX, VICE_PREFIX_SIZE) == 0) {
		if (dname = unrawname(fsent->fs_spec)) {
		    ProcessFileSys(dname, part);
		    didSome++;
		}
	    }
	}
	endfsent();
	if (!didSome)
	    printf("No file system partitions named %s* found; not processed\n",VICE_PARTITION_PREFIX);
    }
}
#endif /* AFS_SUN54_ENV */


#include "AFS_component_version_number.c"

main(argc,argv)
char **argv;
{
    register struct cmd_syndesc *ts;
    afs_int32 code;
    
    if (geteuid() != 0) {
	printf("must be run as root; sorry\n");
	exit(1);
    }
#ifdef	AFS_DUX40_ENV
    ts = cmd_CreateSyntax("convert", ConvCmd, 0, "Convert to DUX 4.0D format");
#else
#if	defined(AFS_SUN54_ENV)
    ts = cmd_CreateSyntax("convert", ConvCmd, 0, "Convert to AFS SunOS 5.6 format");
#else
    ts = cmd_CreateSyntax("convert", ConvCmd, 0, "Convert to AFS 4.1.1 format");
#endif
#endif
    cmd_AddParm(ts, "-part", CMD_LIST, CMD_OPTIONAL, "AFS partition name");
    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose mode");
    cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "Safeguard enforce switch");
    cmd_AddParm(ts, "-device", CMD_LIST, CMD_OPTIONAL, "AFS raw device name");
#ifdef	AFS_DUX40_ENV
    ts = cmd_CreateSyntax("unconvert", UnConvCmd, 0, "Convert back from OSF 4.0D to earlier formats");
#else
#if	defined(AFS_SUN54_ENV)
    ts = cmd_CreateSyntax("unconvert", UnConvCmd, 0, "Convert back from AFS SunOS 5.6 to earlier formats");
#else
    ts = cmd_CreateSyntax("unconvert", UnConvCmd, 0, "Convert back from AFS 4.1.1 to earlier formats");
#endif
#endif
    cmd_AddParm(ts, "-part", CMD_LIST, CMD_OPTIONAL, "AFS partition name");
    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose mode");
    cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "Safeguard enforce switch");
    cmd_AddParm(ts, "-device", CMD_LIST, CMD_OPTIONAL, "AFS raw device name");

    code = cmd_Dispatch(argc, argv);
    exit(code);
}


ProcessFileSys(dname, path)
    char *dname, *path;
{
    struct stat status, stat1;
#if	!defined(AFS_SUN54_ENV)
    struct ufs_args ufsargs;
#endif
    char devbuffer[120], *devname;
    int i, j, mounted = 0, code;

    if (stat(path,&status) == -1) {
	printf("Couldn't find file system \"%s\"\n", path);
	return;
    }
    if (status.st_ino == ROOTINODE) {
	if (force) {
	    printf("Partition %s is mounted; only unmounted partitions are processed\n", path);
	    return;
	}
	mounted = 1;
    }
    strcpy(devbuffer, dname);
    devname = devbuffer;
#if	!defined(AFS_SUN54_ENV)
    EnsureDevice(devname);
#endif
    if (!mounted) {
#ifdef	AFS_DUX40_ENV
       ufsargs.fspec = devname;
       code = mount(MOUNT_UFS, path, 0, &ufsargs);
#else
#if	defined(AFS_SUN54_ENV)
	code = 0;
#else
       ufsargs.fspec = devname;
       code = mount("4.2", path, M_NEWTYPE, &ufsargs);
#endif
#endif
       if (code < 0) {
	  printf("Couldn't mount %s on %s (err=%d)\n", devname, path, errno);
	  return;
       }
    }
    
    if (stat(path,&status) == -1) {
	printf("Couldn't find file system \"%s\"\n", path);
	return;
    }

#ifdef	AFS_DUX40_ENV
    icount = 0;
    code = BuildInodes(path);
    if (code) {
       printf("Couldn't generate list of all inodes in directory %s\n", path);
       return;
    }
    if (icount)
       qsort(iarray, icount, sizeof(int), sortinodes);
#endif

#if	!defined(AFS_SUN54_ENV)
    if (!(devname = vol_DevName(status.st_dev))) {
	printf("Couldn't get devname for %d\n", status.st_dev);
	return;
    }
#endif

    if (ProcessAfsInodes(devname, path) == -1)
	printf("Unable to get inodes for \"%s\"; not processed\n", path);
    if (!mounted) {
#ifdef	AFS_DUX40_ENV
       umount(path);
#endif
    }
}


int ProcessAfsInodes(devname, partition)
    char *devname, *partition;
{
   union {
       struct fs fs;
       char block[SBSIZE];
   } super;
   int pfd, i, c, e, bufsize, cnt =0, mod = 0, wcnt = 0, ccnt = 0, ncnt;
   FILE *inodeFile = NULL;
   char  rdev[25];
   struct dinode *inodes = NULL, *einodes, *p;
   int code, istep=0;

   sync(); 
   sleep(5);		/* XXXX */
#if	defined(AFS_SUN54_ENV)
   sprintf(rdev, "%s", devname);
#else
   sprintf(rdev, "/dev/r%s", devname);
#endif
   pfd = open(rdev, O_RDWR);
   if (pfd < 0) {
       printf("Could not read device %s to get inode list\n", rdev);
       printf("errno %d: %s\n", errno, strerror(errno));
       return -1;
   }
   if (bread(pfd, super.block, SBLOCK, SBSIZE) == -1) {
       printf("Unable to read superblock, partition %s\n", partition);
       goto out;
   }
   
   /*
    * run a few consistency checks of the superblock
    * (Cribbed from vfsck)
    */
   if ((super.fs.fs_magic != FS_MAGIC) || (super.fs.fs_ncg < 1) || (super.fs.fs_cpg < 1) || 
       (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)) {
       printf("There's something wrong with the superblock for partition %s; run vfsck\n", partition);
       goto out;
   }
   bufsize = super.fs.fs_ipg * sizeof(struct dinode);   
   if (!(inodes = (struct dinode *) malloc(bufsize))) {
       printf("Unable to allocate enough memory to scan inodes; help!\n");
       goto out;
   }
   einodes = (struct dinode *) (((char *)inodes) + bufsize);
   printf("Processing all AFS inodes on partition %s (device %s)...\n", partition, rdev);
   for (c = 0; c < super.fs.fs_ncg; c++) {
      daddr_t dblk1;
#if	defined(AFS_SUN54_ENV)
	daddr_t f1;
	offset_t off;
#endif

      i = c*super.fs.fs_ipg; 
      e = i+super.fs.fs_ipg;
#ifdef	AFS_DUX40_ENV
      dblk1 = fsbtodb(&super.fs, itod(&super.fs, i));
      code = lseek(pfd, (off_t) ((off_t)dblk1 * DEV_BSIZE), L_SET);
#else
#if	defined(AFS_SUN54_ENV)
	f1 = fsbtodb(&super.fs,itod(&super.fs,i));
	off = (offset_t)f1 << DEV_BSHIFT;
	code = llseek(pfd, off, L_SET) ;
#else
      code = lseek(pfd, dbtob(fsbtodb(&super.fs,itod(&super.fs,i))), L_SET);
#endif
#endif
      if (code == -1) {
	 printf("Error reading inodes for partition %s; run vfsck\n", partition);
	 printf("%d: %s\n", errno, strerror(errno));
#if	defined(AFS_SUN54_ENV)
	 printf("file number = %d; offset = %ld\n", pfd, off);
#else
	 printf("file number = %d\n", pfd);
#endif
	 goto out;
      }
      while (i < e) {
	 wcnt = ncnt = mod = 0;
         code = read(pfd, inodes, bufsize);
	 if (code != bufsize) {
	    printf("Error reading inodes for partition %s; run vfsck\n", partition);
            if (code<0) {
               printf("errno %d: %s\n", errno, strerror(errno));
            }
	    goto out;
	 }

	 /* Step through each inode */
	 for (p = inodes; p < einodes && i < e; i++, p++)
#ifdef	AFS_DUX40_ENV
	 {
	    afs_uint32 v1, v2, v3, v4;
	    afs_uint32 volid, vnode, uniq, vers;
	    afs_uint32 type, rwvol;
	    afs_uint32 t;
	    int     special;

	    if ((p->di_mode&IFMT) != IFREG) {
	       /* This inode is not an AFS inode so 
		* zero out all the spare fields.
		*/
	      zeroSpares:
	        if (!(p->di_flags & IC_PROPLIST)) {
		   p->di_proplb=0;
	        }
		if (!(p->di_flags & IC_XUID)) {
		   p->di_uid=0;
	        }
		if (!(p->di_flags & IC_XGID)) {
		   p->di_gid=0;
	        }
	        mod = 1;
	        ncnt++;
	        continue;
	    }

/*	    printf("Inode %d: proplb=%u, uid=%u, bcuid=0x%x, bcgid=0x%x, gid=0x%x\n",
 *                 i , p->di_proplb, p->di_uid, p->di_bcuid, p->di_bcgid,
 *		       p->di_gid);
 */	    

	    if (unconv) { /*unconvert*/
	       /* Check if the inode is not a DUX40D AFS inode */
	       if (p->di_proplb != VICEMAGIC || (p->di_flags & IC_PROPLIST)) { 
		  ccnt++;
		  if (verbose)
		     printf("   AFS Inode %d: already unconverted\n", i);
		  continue;
	       }

	       v1 = p->di_uid;
	       v2 = p->di_gid;
	       v3 = ((u_int)(p->di_bcuid)) << 16 | ((u_int)(p->di_bcgid));

	       /* We have a sorted list of ufs inodes. Is it one of these */
	       while ((istep < icount) && (i > iarray[istep])) {
		  istep++;
	       }
	       if ((istep < icount) && (i == iarray[istep])) {
		  /* Yes, its a ufs inode */
		  if (verbose)
		     printf("   Inode %d: Not an AFS Inode\n", i);
		  goto zeroSpares;
	       }

	       /* Is it not an AFS inode. !IS_VICEMAGIC check */
	       if (!(v2 || v3)) {
		  if (verbose)
		     printf("Warning: Inode %d: Unreferenced inode\n", i);
		  continue;       /* skip, making no change */
	       }

	       volid = v1;
	       if (((v2 >> 3) == 0x1fffffff) && (v2 & 0x3)) {
		  special = 1;                      /* INODESPECIAL */
		  type  = v2 & 0x3;
		  rwvol = v3;	       
		  if (verbose) {
		     printf("   %s AFS Inode %d: Vol=%u, RWVol=%u, Type=%d\n",
			    (force?"Unconverting":"Would have unconverted"), i,
			    volid, rwvol, type);
		  }
	       } else {
		  special = 0;
		  vnode = ((v2 >> 27) << 16) + (v3 & 0xffff);
		  uniq  = (v2 & 0x3fffff);
		  vers  = (((v2 >> 22) & 0x1f) << 16) + ((v3 >> 16) & 0xffff);
		  if (verbose) {
		     printf("   %s AFS Inode %d: RWVol=%u, VNode=%u, Uniq=%u, Vers=%u\n",
			    (force?"Unconverting":"Would have unconverted"), i,
			    volid, vnode, uniq, vers);
		  }
	       }

	       /* Now unconvert the DUX40D AFS inode to the OSF30 version */
	       p->di_proplb   = v1;
	       p->di_uid      = v2;
	       p->di_gid      = v3;
	       p->di_flags    &= ~(IC_PROPLIST|IC_XUID|IC_XGID);
	       p->di_bcuid    = 0;
	       p->di_bcgid    = -2;
	       mod = 1; cnt++; wcnt++;

	    } else { /*convert*/
	       if ((p->di_proplb == VICEMAGIC && !(p->di_flags & IC_PROPLIST))
		|| (p->di_uid == 0 && p->di_gid == 0)
		|| (p->di_flags & (IC_XUID|IC_XGID))) {
		  /* This inode is either already converted or not an AFS inode */
		  /* We have a sorted list of ufs inodes. Is it one of these    */
		  while ((istep < icount) && (i > iarray[istep])) {
		     istep++;
		  }
		  if ((istep < icount) && (i == iarray[istep])) {
		     /* Yes, its a ufs inode */
		     if (verbose) {
		        printf("   Inode %d: Not an AFS Inode %s\n", 
			       i, ((p->di_spare[1] || p->di_spare[2])?"(fixed)":""));
		     }
		     goto zeroSpares;
		  }

		  /* Is it a AFS inode. IS_VICEMAGIC check */
		  if ((p->di_uid || p->di_gid)
		      && !(p->di_flags & (IC_XUID|IC_XGID))) {
		     ccnt++;
		     if (verbose)
		        printf("   AFS Inode %d: already converted\n", i);
		     continue;
		  }

		  /* Not an AFS nor UFS inode */
		  printf("Warning: Inode %d: Unreferenced inode\n", i);
		  continue;            /* skip, making no changes */
	       }

	       v1 = p->di_proplb;
	       v2 = p->di_uid;
	       v3 = p->di_gid;

	       volid = v1;
	       if (((v2 >> 3) == 0x1fffffff) && (v2 & 0x3)) {
		  special = 1;                      /* INODESPECIAL */
		  type  = v2 & 0x3;
		  rwvol = v3;	       
		  if (verbose) {
		     printf("   %s AFS Inode %d: Vol=%u, RWVol=%u, Type=%d\n",
			    (force?"Converting":"Would have converted"),i,
			    volid, rwvol, type);
		  }
	       } else {
		  special = 0;
		  vnode = ((v2 >> 27) << 16) + (v3 & 0xffff);
		  uniq  = (v2 & 0x3fffff);
		  vers  = (((v2 >> 22) & 0x1f) << 16) + ((v3 >> 16) & 0xffff);
		  if (verbose) {
		     printf("   %s AFS Inode %d: RWVol=%u, VNode=%u, Uniq=%u, Vers=%u\n",
			    (force?"Converting":"Would have converted"),i,
			    volid, (vnode & 0x1fffff), (uniq & 0x3fffff), 
			    (vers & 0x1fffff));
		  }
	       }

	       /* Now convert the OSF30 AFS inode to the DUX40D version */
	       p->di_proplb   = VICEMAGIC;
	       p->di_uid      = v1;
	       p->di_gid      = v2;
	       p->di_bcuid    = (u_short)(v3 >> 16);
	       p->di_bcgid    = (u_short)v3;
	       p->di_flags &= ~(IC_PROPLIST);
	       p->di_flags |= (IC_XUID|IC_XGID);
	       mod = 1; cnt++; wcnt++;
	    } /*convert*/
	 }
#else
	 {
	    afs_uint32 p1, p2, p3, p4;
	    int  p5;
	    quad*	q;

#if	defined(AFS_SUN56_ENV)
	    q = (quad *)&(p->di_ic.ic_lsize);
#else
	    q = &(p->di_ic.ic_size);
#endif
		
	    p1 = p->di_gen;
	    p2 = p->di_ic.ic_flags;
	    p3 = q->val[0];
	    p4 = p->di_ic.ic_uid; 
	    p5 = p->di_ic.ic_gid;

	    /* the game is afoot, Dr Watson! */
	    if ( !verbose && ! ((ccnt+cnt+ncnt) % 5000 ) )
	    {
		printf(".");
		fflush(stdout);
	    }

	    if (unconv) {
	       /*
		* Convert from a 5.6 format to a pre 5.6 format
		*/
	    	if ( (p2 || p3) && !p4 && (p5 == -2) ) 
	        {
			if ( verbose)
		  	    printf("AFS Inode %d: Already in pre-%s AFS format (%x,%x,%x,%x,%x) ignoring..\n",
			 	i, OSVERS, p1, p2, p3,p4, p5);
		  	ccnt++;
		  	continue;
		}

		 if (  p5 == VICEMAGIC)
		 {
			/* This is a sol 2.6 AFS inode */
		  	mod = 1;
		  	cnt++; wcnt++;
		  	if (verbose) 
		     		printf("AFS Inode %d: %s from Sol5.6 (%x,%x,%x,%x,%x) \n", 
			    	i, (force ? "Unconverting" : 
				"Would have unconverted"), p1,p2,p3,p4,p5);
			if ( force )
			{
		  		q->val[0] = p->di_uid;
		  		p->di_uid = 0;
		  		p->di_gid = -2;
				p->di_suid = UID_LONG;
				p->di_sgid = GID_LONG;
			}
			continue;
		  }
	    } else {
		if (p5 == VICEMAGIC)
		{	/* Assume an already converted 5.6 afs inode */
		  if ( verbose )
		      printf("AFS Inode %d: Already in %s AFS format (p1=%x,p2=%x,p3=%x,p4=%x,p5=%x); ignoring..\n",
			 i, OSVERS, p1,p2,p3,p4, p5);
		  ccnt++;
		  continue;
	       }

		/* for inodes created in solaris 2.4, there is a possibility
		** that the gid gets chopped off to an unsigned short(2 bytes)
		*/
	    	if ( (p2 || p3) && !p4 && ((p5 == -2) || 
			((unsigned short)p5 == (unsigned short)-2) ))
		{
		  /* This is a pre Sol2.6 inode */
		  mod = 1;
		  cnt++; wcnt++;
		  if (verbose) 
		     printf("AFS Inode %d: %s to 5.6 format (p1=%x,p2=%x,p3=%x,p4=%x, p5=%x)\n", 
			    i, (force ? "Converting" : "Would have converted"),
			    p1, p2, p3, p4, p5);
		  if ( force )
		  {
		  	p->di_gid = VICEMAGIC;
		  	p->di_uid = q->val[0];
			p->di_suid = UID_LONG;
			p->di_sgid = GID_LONG;
		  	q->val[0]=0;
		  }
		  continue;
		}
	    }
	    /* If not an AFS inode, ignore */
	    ncnt++;
	    if ( verbose )
	  	printf("Non AFS Inode %d: (p1=%x,p2=%x,p3=%x, p4=%x,p5=%x) ignoring..\n", 
			  i, p1, p2, p3, p4,p5);
	 }
#endif		    

	 if (mod && force) {
	    if (lseek(pfd, -bufsize, SEEK_CUR) == -1) {	/* Point to loc bef read */
	       printf("Error Seeking backwards %d bytes\n", bufsize);
               printf("errno %d: %s\n", errno, strerror(errno));
	       goto out;
	    }
            code = write(pfd, inodes, bufsize);
	    if (code != bufsize) {	/* Update inodes */
	       printf("Error writing modified inodes for partition %s\n", partition);
               if (code<0)
                  printf("errno %d: %s\n", errno, strerror(errno)); 
	       goto out;
	    }
	    if (verbose) {
	       printf("  Write %d AFS inodes and %d non-AFS inodes to disk\n",
		      wcnt, ncnt);
	    }
	 }
      }
   }

   printf("\n%s: %d AFS inodes %s ", partition, cnt, (force?"were":"would have been"));
   if (unconv) 
      printf("unconverted to a pre-%s format; %d already unconverted.\n", OSVERS, ccnt);
   else
      printf("converted to a %s format; %d already converted.\n", OSVERS, ccnt);

   close(pfd);
   return 0;

out:
   close(pfd);
   return -1;
}




int bread(fd, buf, blk, size)
    int fd;
    char *buf;
    daddr_t blk;
    afs_int32 size;
{
    if (lseek(fd, (off_t)dbtob(blk), L_SET) < 0 || read(fd, buf, size) != size) {
	printf("bread: lseek failed (errno = %d)\n", errno);
	return -1;
    }
    return 0;
}


/* 
 * Ensure that we don't have a "/" instead of a "/dev/rxd0a" type of device.
 * returns pointer to static storage; copy it out quickly!
 */
char *vol_DevName(adev)
    dev_t adev; 
{
    struct dirent *dp;
    static char pbuffer[128];
    struct stat tstat;
    DIR *dirp;
    char *dirName;
    int code;

    /* now, look in /dev for the appropriate file */
    dirp = opendir(dirName = "/dev");
    while (dp = readdir(dirp)) {
	strcpy(pbuffer, dirName);
	strcat(pbuffer, "/");
	strcat(pbuffer, dp->d_name);
	if (stat(pbuffer, &tstat) != -1 &&
	    (tstat.st_mode & S_IFMT) == S_IFBLK &&
	    (tstat.st_rdev == adev)) {
	        strcpy(pbuffer, dp->d_name);
		closedir(dirp);
		return pbuffer;
	    }
    }
    closedir(dirp);
    return (char *) 0;	/* failed */
}
  
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);
}

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

char *blockcheck(name)
	char *name;
{
	struct stat stslash, stblock, stchar;
	char *raw;
	int retried = 0;

retry:
	if (stat(name, &stblock) < 0) {
		perror(name);
		printf("Can't stat %s\n", name);
		return (0);
	}
	if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
		raw = rawname(name);
		if (stat(raw, &stchar) < 0) {
			perror(raw);
			printf("Can't stat %s\n", raw);
			return (name);
		}
		if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
			return (raw);
		} else {
			printf("%s is not a character device\n", raw);
			return (name);
		}
	} else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
		name = unrawname(name);
		retried++;
		goto retry;
	}
	printf("Can't make sense out of name %s\n", name);
	return (0);
}


/* ensure that we don't have a "/" instead of a "/dev/rxd0a" type of device.
 * Overwrites abuffer with the corrected name.
 */
EnsureDevice(abuffer)
char *abuffer; {
    struct dirent *dp;
    char pbuffer[128];
    struct stat tstat;
    DIR *dirp;
    char *dirName;
    int code;
    short dev;

    code = stat(abuffer, &tstat);
    if (code) return code;
    if (((tstat.st_mode & S_IFMT) == S_IFBLK) || ((tstat.st_mode & S_IFMT) == S_IFCHR)) {
	return 0;	/* already a block or char device */
    }
    /* otherwise, assume we've got a normal file, and we look up its device */
    dev = tstat.st_dev;		/* remember device for this file */

    /* now, look in /dev for the appropriate file */
    dirp = opendir(dirName = "/dev");

    while (dp=readdir(dirp)) {
	strcpy(pbuffer, dirName);
	strcat(pbuffer, "/");
	strcat(pbuffer, dp->d_name);
	if (stat(pbuffer, &tstat) != -1 &&
	    (tstat.st_mode & S_IFMT) == S_IFBLK &&
	    (tstat.st_rdev == dev)) {
		strcpy(abuffer, pbuffer);
		closedir(dirp);
		return 0;
	    }
    }
    closedir(dirp);
    return 1;	/* failed */
}
#else

#include "AFS_component_version_number.c"

main(argc, argv)
    int argc;
    char *argv[];
{
    printf("%s: **ONLY** supported for SunOS 3.5-4.x and dux4.0D systems ...\n",
	   argv[0]);
}
#endif /*	defined(AFS_SUN_ENV) && !defined(AFS_SUN5_ENV) */

#if	defined(AFS_SUN54_ENV)

/*
** Open the /etc/vfstab file to map the raw device name to the mountable
** device name. Then open the /etc/mnttab file to see if this mountable 
** device is mounted.
**
** Returns 1 if the conversion process should continue, otherwise returns 0
*/
CheckMountedDevice(devName)
char*	devName;		/* raw device name */
{
	FILE *vfsent, *mntent;
	struct mnttab mnt;
	struct vfstab vnt;
	char*  unRawDev=0;
	char   YesNo='y';
	int found = 0; 

        if ( ( vfsent = fopen(VFSTAB, "r")) == NULL )
        {
                printf("Unable to open %s ( errno = %d)\n", VFSTAB, errno);
                exit(2);
        }
        while (!getvfsent(vfsent, &vnt)) 
	{
            char *rawDev = vnt.vfs_fsckdev;
	    if ( rawDev && !strcmp(rawDev, devName) )
	    {
		/* this is the device we are looking for */
		unRawDev = vnt.vfs_special;
		break;
	    }
	}
	fclose(vfsent);

	if ( !unRawDev ) goto done; /* could not find it in /etc/vfstab */
		
	/* we found the entry in /etc/vfstab. Now we open /etc/mnnttab and
	** verify that it is not mounted 
	*/
        if ( ( mntent = fopen(MNTTAB, "r")) == NULL )
        {
                printf("Unable to open %s ( errno = %d)\n", VFSTAB, errno);
                exit(2);
        }

        while (!getmntent(mntent, &mnt)) 
	{
	    char*	resource = mnt.mnt_special;
	    if ( resource && !strcmp(resource, unRawDev) )
	    {
		found = 1; 
		break;
	    }
	}
	fclose(mntent);


	/* if we found an entry in the /etc/mnttab file, then this 
	** device must be mounted
	*/
	if ( found ) 
	{
done:
	    do
	    {
		printf("Device %s may be mounted. Can corrupt data. Continue anyway(y/n)?", devName);
		fflush(stdout);
		fflush(stdin);
		YesNo = getc(stdin);
	    }
	    while ( YesNo != 'y' && YesNo != 'Y' &&
		    YesNo != 'n' && YesNo != 'N' );
	}
	if ( (YesNo == 'y') || ( YesNo == 'Y' ) )
		return 1;
	return 0;
}

#endif /* AFS_SUN54_ENV */
	
	
