volinfo: Use new form of cmd_AddParamWithOffset
[openafs.git] / src / vol / vol-info.c
index 1b387f2..d9865bc 100644 (file)
 #include <afs/afssyscalls.h>
 #include <afs/afsutil.h>
 
+#ifndef AFS_NT40_ENV
+#include "AFS_component_version_number.c"
+#endif
+
 static const char *progname = "volinfo";
 
-int DumpVnodes = 0;            /* Dump everything, i.e. summary of all vnodes */
-int DumpInodeNumber = 0;       /* Dump inode numbers with vnodes */
-int DumpDate = 0;              /* Dump vnode date (server modify date) with vnode */
-int InodeTimes = 0;            /* Dump some of the dates associated with inodes */
+/* Command line options */
+typedef enum {
+    P_ONLINE,
+    P_VNODE,
+    P_DATE,
+    P_INODE,
+    P_ITIME,
+    P_PART,
+    P_VOLUMEID,
+    P_HEADER,
+    P_SIZEONLY,
+    P_FIXHEADER,
+    P_SAVEINODES,
+    P_ORPHANED,
+    P_FILENAMES
+} volinfo_parm_t;
+
+/* Modes */
+static int DumpInfo = 1;            /**< Dump volume information, defualt mode*/
+static int DumpHeader = 0;          /**< Dump volume header files info */
+static int DumpVnodes = 0;          /**< Dump vnode info */
+static int DumpInodeNumber = 0;     /**< Dump inode numbers with vnodes */
+static int DumpDate = 0;            /**< Dump vnode date (server modify date) with vnode */
+static int InodeTimes = 0;          /**< Dump some of the dates associated with inodes */
 #if defined(AFS_NAMEI_ENV)
-int PrintFileNames = 0;
+static int PrintFileNames = 0;      /**< Dump vnode and special file name filenames */
 #endif
-int dheader = 0;
-int dsizeOnly = 0;
-int fixheader = 0, saveinodes = 0, orphaned = 0;
-int VolumeChanged;
+static int ShowOrphaned = 0;        /**< Show "orphaned" vnodes (vnodes with parent of 0) */
+static int ShowSizes = 0;           /**< Show volume size summary */
+static int SaveInodes = 0;          /**< Save vnode data to files */
+static int FixHeader = 0;           /**< Repair header files magic and version fields. */
 
 /**
  * Volume size running totals
@@ -72,6 +96,7 @@ struct sizeTotals {
 static struct sizeTotals volumeTotals = { 0, 0, 0, 0, 0, 0 };
 static struct sizeTotals partitionTotals = { 0, 0, 0, 0, 0, 0 };
 static struct sizeTotals serverTotals = { 0, 0, 0, 0, 0, 0 };
+static int PrintingVolumeSizes = 0;    /*print volume size lines */
 
 /* Forward Declarations */
 void PrintHeader(Volume * vp);
@@ -83,8 +108,20 @@ Volume *AttachVolume(struct DiskPartition64 *dp, char *volname,
                     struct VolumeHeader *header);
 void PrintVnode(afs_foff_t offset, VnodeDiskObject * vnode, VnodeId vnodeNumber,
                Inode ino, Volume * vp);
-void PrintVnodes(Volume * vp, VnodeClass class);
+void HandleVnodes(Volume * vp, VnodeClass class);
 
+/**
+ * Format time as a timestamp string
+ *
+ * @param[in] date  time value to format
+ *
+ * @return timestamp string in the form YYYY/MM/DD.hh:mm:ss
+ *
+ * @note A static array of 8 strings are stored by this
+ *       function. The array slots are overwritten, so the
+ *       caller must not reference the returned string after
+ *       seven additional calls to this function.
+ */
 char *
 date(time_t date)
 {
@@ -100,15 +137,37 @@ date(time_t date)
     return results[next];
 }
 
-#ifndef AFS_NT40_ENV
-#include "AFS_component_version_number.c"
-#endif
-
-char name[VMAXPATHLEN];
-
-char BU[1000];
+#ifdef AFS_NT40_ENV
+/**
+ * Format file time as a timestamp string
+ *
+ * @param[in] ft  file time
+ *
+ * @return timestamp string in the form YYYY/MM/DD.hh:mm:ss
+ *
+ * @note A static array of 8 strings are stored by this
+ *       function. The array slots are overwritten, so the
+ *       caller must not reference the returned string after
+ *       seven additional calls to this function.
+ */
+char *
+NT_date(FILETIME * ft)
+{
+    static char result[8][64];
+    static int next = 0;
+    SYSTEMTIME st;
+    FILETIME lft;
 
-static int PrintingVolumeSizes = 0;    /* printing volume size lines */
+    if (!FileTimeToLocalFileTime(ft, &lft)
+       || !FileTimeToSystemTime(&lft, &st)) {
+       fprintf(stderr, "%s: Time conversion failed.\n", progname);
+       exit(1);
+    }
+    sprintf(result[next = ((next + 1) & 7)], "%4d/%02d/%02d.%2d:%2d:%2d",
+           st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
+    return result[next];
+}
+#endif
 
 /**
  * Print the volume size table heading line, if needed.
@@ -191,6 +250,19 @@ PrintServerTotals(void)
           serverTotals.vnodesize, serverTotals.size_k, diff_k);
 }
 
+/**
+ * Read a volume header file
+ *
+ * @param[in] ih        ihandle of the header file
+ * @param[in] to        destination
+ * @param[in] size      expected header size
+ * @param[in] magic     expected header magic number
+ * @param[in] version   expected header version number
+ *
+ * @return error code
+ *   @retval 0 success
+ *   @retval -1 failed to read file
+ */
 int
 ReadHdr1(IHandle_t * ih, char *to, int size, u_int magic, u_int version)
 {
@@ -217,7 +289,7 @@ ReadHdr1(IHandle_t * ih, char *to, int size, u_int magic, u_int version)
                progname,
                PrintInode(NULL, ih->ih_ino), vsn->version, version);
     }
-    if (bad && fixheader) {
+    if (bad && FixHeader) {
        vsn->magic = magic;
        vsn->version = version;
        printf("Special index inode %s has a bad header. Reconstructing...\n",
@@ -229,7 +301,7 @@ ReadHdr1(IHandle_t * ih, char *to, int size, u_int magic, u_int version)
                    progname, PrintInode(NULL, ih->ih_ino));
        }
     } else {
-       if (!dsizeOnly && !saveinodes) {
+       if (DumpInfo) {
            printf("Inode %s: Good magic %x and version %x\n",
                   PrintInode(NULL, ih->ih_ino), magic, version);
        }
@@ -237,7 +309,15 @@ ReadHdr1(IHandle_t * ih, char *to, int size, u_int magic, u_int version)
     return 0;
 }
 
-
+/**
+ * Simplified attach volume
+ *
+ * param[in] dp       vice disk partition object
+ * param[in] volname  volume header file name
+ * param[in] header   volume header object
+ *
+ * @return volume object or null on error
+ */
 Volume *
 AttachVolume(struct DiskPartition64 * dp, char *volname,
             struct VolumeHeader * header)
@@ -285,81 +365,144 @@ AttachVolume(struct DiskPartition64 * dp, char *volname,
     return vp;
 }
 
+/**
+ * Convert the partition device number into a partition name.
+ *
+ * @param[in]   partId      partition number, 0 to 254
+ * @param[out]  partName    buffer to hold partition name (e.g. /vicepa)
+ * @param[in]   partNameSize    size of partName buffer
+ *
+ * @return status
+ *   @retval 0 success
+ *   @retval -1 error, partId is out of range
+ *   @retval -2 error, partition name exceeds partNameSize
+ */
+static int
+GetPartitionName(afs_uint32 partId, char *partName, afs_size_t partNameSize)
+{
+    const int OLD_NUM_DEVICES = 26;    /* a..z */
 
+    if (partId < OLD_NUM_DEVICES) {
+       if (partNameSize < 8) {
+           return -2;
+       }
+       strlcpy(partName, "/vicep", partNameSize);
+       partName[6] = partId + 'a';
+       partName[7] = '\0';
+       return 0;
+    }
+    if (partId < VOLMAXPARTS) {
+       if (partNameSize < 9) {
+           return -2;
+       }
+       strlcpy(partName, "/vicep", partNameSize);
+       partId -= OLD_NUM_DEVICES;
+       partName[6] = 'a' + (partId / OLD_NUM_DEVICES);
+       partName[7] = 'a' + (partId % OLD_NUM_DEVICES);
+       partName[8] = '\0';
+       return 0;
+    }
+    return -1;
+}
+
+/**
+ * Process command line options and start scanning
+ *
+ * @param[in] as     command syntax object
+ * @param[in] arock  opaque object; not used
+ *
+ * @return error code
+ */
 static int
 handleit(struct cmd_syndesc *as, void *arock)
 {
     struct cmd_item *ti;
     int err = 0;
     afs_uint32 volumeId = 0;
-    char *partName = 0;
+    char *partNameOrId = 0;
+    char partName[64] = "";
     struct DiskPartition64 *partP = NULL;
 
 
 #ifndef AFS_NT40_ENV
     if (geteuid() != 0) {
        fprintf(stderr, "%s: Must be run as root; sorry\n", progname);
-       exit(1);
+       return 1;
     }
 #endif
 
-    if (as->parms[0].items) {
+    if (as->parms[P_ONLINE].items) {
        fprintf(stderr, "%s: -online not supported\n", progname);
        return 1;
     }
-    if (as->parms[1].items)
+    if (as->parms[P_VNODE].items) {
        DumpVnodes = 1;
-    else
-       DumpVnodes = 0;
-    if (as->parms[2].items)
+    }
+    if (as->parms[P_DATE].items) {
        DumpDate = 1;
-    else
-       DumpDate = 0;
-    if (as->parms[3].items)
+    }
+    if (as->parms[P_INODE].items) {
        DumpInodeNumber = 1;
-    else
-       DumpInodeNumber = 0;
-    if (as->parms[4].items)
+    }
+    if (as->parms[P_ITIME].items) {
        InodeTimes = 1;
-    else
-       InodeTimes = 0;
-    if ((ti = as->parms[5].items))
-       partName = ti->data;
-    if ((ti = as->parms[6].items))
+    }
+    if ((ti = as->parms[P_PART].items)) {
+       partNameOrId = ti->data;
+    }
+    if ((ti = as->parms[P_VOLUMEID].items)) {
        volumeId = strtoul(ti->data, NULL, 10);
-    if (as->parms[7].items)
-       dheader = 1;
-    else
-       dheader = 0;
-    if (as->parms[8].items) {
-       dsizeOnly = 1;
-       dheader = 1;
-       DumpVnodes = 1;
-    } else
-       dsizeOnly = 0;
-    if (as->parms[9].items) {
-       fixheader = 1;
-    } else
-       fixheader = 0;
-    if (as->parms[10].items) {
-       saveinodes = 1;
-       dheader = 1;
-       DumpVnodes = 1;
-    } else
-       saveinodes = 0;
-    if (as->parms[11].items) {
-       orphaned = 1;
-       DumpVnodes = 1;
-    } else
-       orphaned = 0;
+    }
+    if (as->parms[P_HEADER].items) {
+       DumpHeader = 1;
+    }
+    if (as->parms[P_SIZEONLY].items) {
+       ShowSizes = 1;
+    }
+    if (as->parms[P_FIXHEADER].items) {
+       FixHeader = 1;
+    }
+    if (as->parms[P_SAVEINODES].items) {
+       SaveInodes = 1;
+    }
+    if (as->parms[P_ORPHANED].items) {
+       ShowOrphaned = 1;
+    }
 #if defined(AFS_NAMEI_ENV)
-    if (as->parms[12].items) {
+    if (as->parms[P_FILENAMES].items) {
        PrintFileNames = 1;
-       DumpVnodes = 1;
-    } else
-       PrintFileNames = 0;
+    }
 #endif
 
+    /* -saveinodes and -sizeOnly override the default mode.
+     * For compatibility with old versions of volinfo, -orphaned
+     * and -filename options imply -vnodes */
+    if (SaveInodes || ShowSizes) {
+       DumpInfo = 0;
+       DumpHeader = 0;
+       DumpVnodes = 0;
+       InodeTimes = 0;
+       ShowOrphaned = 0;
+    } else if (ShowOrphaned || PrintFileNames) {
+       DumpVnodes = 1;         /* implied */
+    }
+
+    /* Allow user to specify partition by name or id. */
+    if (partNameOrId) {
+       afs_uint32 partId = volutil_GetPartitionID(partNameOrId);
+       if (partId == -1) {
+           fprintf(stderr, "%s: Could not parse '%s' as a partition name.\n",
+                   progname, partNameOrId);
+           return 1;
+       }
+       if (GetPartitionName(partId, partName, sizeof(partName))) {
+           fprintf(stderr,
+                   "%s: Could not format '%s' as a partition name.\n",
+                   progname, partNameOrId);
+           return 1;
+       }
+    }
+
     DInit(10);
 
     err = VAttachPartitions();
@@ -368,12 +511,13 @@ handleit(struct cmd_syndesc *as, void *arock)
                progname, err);
     }
 
-    if (partName) {
+    if (partName[0]) {
        partP = VGetPartition(partName, 0);
        if (!partP) {
-           fprintf(stderr, "%s: %s is not an AFS partition name on this server.\n",
-                  progname, partName);
-           exit(1);
+           fprintf(stderr,
+                   "%s: %s is not an AFS partition name on this server.\n",
+                   progname, partName);
+           return 1;
        }
     }
 
@@ -392,7 +536,7 @@ handleit(struct cmd_syndesc *as, void *arock)
                fprintf(stderr,
                        "%s: Current partition is not a vice partition.\n",
                        progname);
-               exit(1);
+               return 1;
            }
        }
        snprintf(name1, sizeof name1, VFORMAT,
@@ -402,6 +546,11 @@ handleit(struct cmd_syndesc *as, void *arock)
     return 0;
 }
 
+/**
+ * Determine if the current directory is a vice partition
+ *
+ * @return disk partition object
+ */
 #ifdef AFS_NT40_ENV
 #include <direct.h>
 struct DiskPartition64 *
@@ -434,7 +583,7 @@ FindCurrentPartition(void)
        fprintf(stderr, "%s: Failed to get current working directory: ",
                progname);
        perror("pwd");
-       exit(1);
+       return NULL;
     }
     p = strchr(&partName[1], OS_DIRSEPC);
     if (p) {
@@ -446,12 +595,17 @@ FindCurrentPartition(void)
            *p = tmp;
        fprintf(stderr, "%s: %s is not a valid vice partition.\n", progname,
                partName);
-       exit(1);
+       return NULL;
     }
     return dp;
 }
 #endif
 
+/**
+ * Scan and handle all the partitions detected on this server
+ *
+ * @return none
+ */
 void
 HandleAllPart(void)
 {
@@ -461,17 +615,23 @@ HandleAllPart(void)
     for (partP = DiskPartitionList; partP; partP = partP->next) {
        printf("Processing Partition %s:\n", partP->name);
        HandlePart(partP);
-       if (dsizeOnly) {
+       if (ShowSizes) {
            AddSizeTotals(&serverTotals, &partitionTotals);
        }
     }
 
-    if (dsizeOnly) {
+    if (ShowSizes) {
        PrintServerTotals();
     }
 }
 
-
+/**
+ * Scan the partition and handle volumes
+ *
+ * @param[in] partP  disk partition to scan
+ *
+ * @return none
+ */
 void
 HandlePart(struct DiskPartition64 *partP)
 {
@@ -489,20 +649,20 @@ HandlePart(struct DiskPartition64 *partP)
     if ((dirp = opendir(p)) == NULL) {
        fprintf(stderr, "%s: Can't read directory %s; giving up\n", progname,
                p);
-       exit(1);
+       return;
     }
     while ((dp = readdir(dirp))) {
        p = (char *)strrchr(dp->d_name, '.');
        if (p != NULL && strcmp(p, VHDREXT) == 0) {
            HandleVolume(partP, dp->d_name);
-           if (dsizeOnly) {
+           if (ShowSizes) {
                nvols++;
                AddSizeTotals(&partitionTotals, &volumeTotals);
            }
        }
     }
     closedir(dirp);
-    if (dsizeOnly) {
+    if (ShowSizes) {
        PrintPartitionTotals(nvols);
     }
 }
@@ -547,7 +707,7 @@ HandleSpecialFile(const char *name, struct DiskPartition64 *dp,
        perror("fstat");
        goto error;
     }
-    if (!dsizeOnly && !saveinodes) {
+    if (DumpInfo) {
        printf("\t%s inode\t= %s (size = %lld)\n",
               name, PrintInode(NULL, inode), size);
 #ifdef AFS_NAMEI_ENV
@@ -582,7 +742,7 @@ HandleHeaderFiles(struct DiskPartition64 *dp, FD_t header_fd,
 {
     afs_sfsize_t size = 0;
 
-    if (!dsizeOnly && !saveinodes) {
+    if (DumpInfo) {
        size = OS_SIZE(header_fd);
        printf("Volume header (size = %lld):\n", size);
        printf("\tstamp\t= 0x%x\n", header->stamp.version);
@@ -599,16 +759,27 @@ HandleHeaderFiles(struct DiskPartition64 *dp, FD_t header_fd,
     HandleSpecialFile("Link", dp, header, header->linkTable, &size);
 #endif /* AFS_NAMEI_ENV */
 
-    if (!dsizeOnly && !saveinodes) {
+    if (DumpInfo) {
        printf("Total aux volume size = %lld\n\n", size);
     }
 
-    if (dsizeOnly) {
+    if (ShowSizes) {
         volumeTotals.auxsize = size;
         volumeTotals.auxsize_k = size / 1024;
     }
 }
 
+/**
+ * Attach and scan the volume and handle the header and vnodes
+ *
+ * Print the volume header and vnode information, depending on the
+ * current modes.
+ *
+ * @param[in] dp    vice partition object for this volume
+ * @param[in] name  volume header file name
+ *
+ * @return none
+ */
 void
 HandleVolume(struct DiskPartition64 *dp, char *name)
 {
@@ -646,7 +817,7 @@ HandleVolume(struct DiskPartition64 *dp, char *name)
        return;
     }
     DiskToVolumeHeader(&header, &diskHeader);
-    if (dheader) {
+    if (DumpHeader || ShowSizes) {
        HandleHeaderFiles(dp, fd, &header);
     }
     OS_CLOSE(fd);
@@ -656,25 +827,16 @@ HandleVolume(struct DiskPartition64 *dp, char *name)
                progname, name);
        return;
     }
-
-    if (!dsizeOnly && !saveinodes) {
+    if (DumpInfo) {
        PrintHeader(vp);
     }
-    if (DumpVnodes) {
-       if (!dsizeOnly && !saveinodes)
-           printf("\nLarge vnodes (directories)\n");
-       PrintVnodes(vp, vLarge);
-       if (!dsizeOnly && !saveinodes) {
-           printf("\nSmall vnodes(files, symbolic links)\n");
-           fflush(stdout);
-       }
-       if (saveinodes) {
-           printf("Saving all volume files to current directory ...\n");
-           PrintingVolumeSizes = 0;    /* -saveinodes interfers with -sizeOnly */
-       }
-       PrintVnodes(vp, vSmall);
+    if (DumpVnodes || ShowSizes || ShowOrphaned) {
+       HandleVnodes(vp, vLarge);
+    }
+    if (DumpVnodes || ShowSizes || SaveInodes || ShowOrphaned) {
+       HandleVnodes(vp, vSmall);
     }
-    if (dsizeOnly) {
+    if (ShowSizes) {
        volumeTotals.diskused_k = V_diskused(vp);
        volumeTotals.size_k =
            volumeTotals.auxsize_k + volumeTotals.vnodesize_k;
@@ -684,44 +846,63 @@ HandleVolume(struct DiskPartition64 *dp, char *name)
     free(vp);
 }
 
+
+/**
+ * volinfo program entry
+ */
 int
 main(int argc, char **argv)
 {
     struct cmd_syndesc *ts;
     afs_int32 code;
 
-    ts = cmd_CreateSyntax(NULL, handleit, NULL, "Dump volume's internal state");
-    cmd_AddParm(ts, "-online", CMD_FLAG, CMD_OPTIONAL,
-               "Get info from running fileserver");
-    cmd_AddParm(ts, "-vnode", CMD_FLAG, CMD_OPTIONAL, "Dump vnode info");
-    cmd_AddParm(ts, "-date", CMD_FLAG, CMD_OPTIONAL,
-               "Also dump vnode's mod date");
-    cmd_AddParm(ts, "-inode", CMD_FLAG, CMD_OPTIONAL,
-               "Also dump vnode's inode number");
-    cmd_AddParm(ts, "-itime", CMD_FLAG, CMD_OPTIONAL,
-               "Dump special inode's mod times");
-    cmd_AddParm(ts, "-part", CMD_LIST, CMD_OPTIONAL,
-               "AFS partition name (default current partition)");
-    cmd_AddParm(ts, "-volumeid", CMD_LIST, CMD_OPTIONAL, "Volume id");
-    cmd_AddParm(ts, "-header", CMD_FLAG, CMD_OPTIONAL,
-               "Dump volume's header info");
-    cmd_AddParm(ts, "-sizeOnly", CMD_FLAG, CMD_OPTIONAL,
-               "Dump volume's size");
-    cmd_AddParm(ts, "-fixheader", CMD_FLAG, CMD_OPTIONAL,
-               "Try to fix header");
-    cmd_AddParm(ts, "-saveinodes", CMD_FLAG, CMD_OPTIONAL,
-               "Try to save all inodes");
-    cmd_AddParm(ts, "-orphaned", CMD_FLAG, CMD_OPTIONAL,
-               "List all dir/files without a parent");
+    ts = cmd_CreateSyntax(NULL, handleit, NULL,
+                         "Dump volume's internal state");
+    cmd_AddParmAtOffset(ts, P_ONLINE, "-online", CMD_FLAG, CMD_OPTIONAL,
+                       "Get info from running fileserver");
+    cmd_AddParmAtOffset(ts, P_VNODE, "-vnode", CMD_FLAG, CMD_OPTIONAL,
+                       "Dump vnode info");
+    cmd_AddParmAtOffset(ts, P_DATE, "-date", CMD_FLAG, CMD_OPTIONAL,
+                       "Also dump vnode's mod date");
+    cmd_AddParmAtOffset(ts, P_INODE, "-inode", CMD_FLAG, CMD_OPTIONAL,
+                       "Also dump vnode's inode number");
+    cmd_AddParmAtOffset(ts, P_ITIME, "-itime", CMD_FLAG, CMD_OPTIONAL,
+                       "Dump special inode's mod times");
+    cmd_AddParmAtOffset(ts, P_PART, "-part", CMD_LIST, CMD_OPTIONAL,
+                       "AFS partition name or id (default current partition)");
+    cmd_AddParmAtOffset(ts, P_VOLUMEID, "-volumeid", CMD_LIST, CMD_OPTIONAL,
+                       "Volume id");
+    cmd_AddParmAtOffset(ts, P_HEADER, "-header", CMD_FLAG, CMD_OPTIONAL,
+                       "Dump volume's header info");
+    cmd_AddParmAtOffset(ts, P_SIZEONLY, "-sizeonly", CMD_FLAG, CMD_OPTIONAL,
+                       "Dump volume's size");
+    cmd_AddParmAtOffset(ts, P_FIXHEADER, "-fixheader", CMD_FLAG,
+                       CMD_OPTIONAL, "Try to fix header");
+    cmd_AddParmAtOffset(ts, P_SAVEINODES, "-saveinodes", CMD_FLAG,
+                       CMD_OPTIONAL, "Try to save all inodes");
+    cmd_AddParmAtOffset(ts, P_ORPHANED, "-orphaned", CMD_FLAG, CMD_OPTIONAL,
+                       "List all dir/files without a parent");
 #if defined(AFS_NAMEI_ENV)
-    cmd_AddParm(ts, "-filenames", CMD_FLAG, CMD_OPTIONAL, "Also dump vnode's namei filename");
+    cmd_AddParmAtOffset(ts, P_FILENAMES, "-filenames", CMD_FLAG,
+                       CMD_OPTIONAL, "Also dump vnode's namei filename");
 #endif
+
+    /* For compatibility with older versions. */
+    cmd_AddParmAlias(ts, P_SIZEONLY, "-sizeOnly");
+
     code = cmd_Dispatch(argc, argv);
     return code;
 }
 
 #define typestring(type) (type == RWVOL? "read/write": type == ROVOL? "readonly": type == BACKVOL? "backup": "unknown")
 
+/**
+ * Print the volume header information
+ *
+ * @param[in]  volume object
+ *
+ * @return none
+ */
 void
 PrintHeader(Volume * vp)
 {
@@ -760,40 +941,35 @@ PrintHeader(Volume * vp)
     printf("volUpdateCounter = %u\n", V_volUpCounter(vp));
 }
 
-/* GetFileInfo
- * OS independent file info. Die on failure.
+/**
+ * Get the size and times of a file
+ *
+ * @param[in]  fd     file descriptor of file to stat
+ * @param[out] size   size of the file
+ * @param[out] ctime  ctime of file as a formatted string
+ * @param[out] mtime  mtime of file as a formatted string
+ * @param[out] atime  atime of file as a formatted string
+ *
+ * @return error code
+ *   @retval 0 success
+ *   @retval -1 failed to retrieve file information
  */
-#ifdef AFS_NT40_ENV
-char *
-NT_date(FILETIME * ft)
-{
-    static char result[8][64];
-    static int next = 0;
-    SYSTEMTIME st;
-    FILETIME lft;
-
-    if (!FileTimeToLocalFileTime(ft, &lft)
-       || !FileTimeToSystemTime(&lft, &st)) {
-       fprintf(stderr, "%s: Time conversion failed.\n", progname);
-       exit(1);
-    }
-    sprintf(result[next = ((next + 1) & 7)], "%4d/%02d/%02d.%2d:%2d:%2d",
-           st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
-    return result[next];
-}
-#endif
-
-static void
-GetFileInfo(FD_t fd, int *size, char **ctime, char **mtime, char **atime)
+static int
+GetFileInfo(FD_t fd, afs_sfsize_t * size, char **ctime, char **mtime,
+           char **atime)
 {
 #ifdef AFS_NT40_ENV
     BY_HANDLE_FILE_INFORMATION fi;
+    LARGE_INTEGER fsize;
     if (!GetFileInformationByHandle(fd, &fi)) {
-       fprintf(stderr, "%s: GetFileInformationByHandle failed, exiting\n",
-               progname);
-       exit(1);
+       fprintf(stderr, "%s: GetFileInformationByHandle failed\n", progname);
+       return -1;
+    }
+    if (!GetFileSizeEx(fd, &fsize)) {
+       fprintf(stderr, "%s: GetFileSizeEx failed\n", progname);
+       return -1;
     }
-    *size = (int)fi.nFileSizeLow;
+    *size = fsize.QuadPart;
     *ctime = "N/A";
     *mtime = NT_date(&fi.ftLastWriteTime);
     *atime = NT_date(&fi.ftLastAccessTime);
@@ -801,13 +977,14 @@ GetFileInfo(FD_t fd, int *size, char **ctime, char **mtime, char **atime)
     struct afs_stat_st status;
     if (afs_fstat(fd, &status) == -1) {
        fprintf(stderr, "%s: fstat failed %d\n", progname, errno);
-       exit(1);
+       return -1;
     }
-    *size = (int)status.st_size;
+    *size = status.st_size;
     *ctime = date(status.st_ctime);
     *mtime = date(status.st_mtime);
     *atime = date(status.st_atime);
 #endif
+    return 0;
 }
 
 /**
@@ -817,7 +994,7 @@ GetFileInfo(FD_t fd, int *size, char **ctime, char **mtime, char **atime)
  * @param[in] vnode  vnode object
  * @param[in] inode  inode of the source file
  *
- * @returns none
+ * @return none
  */
 static void
 SaveInode(Volume * vp, struct VnodeDiskObject *vnode, Inode ino)
@@ -829,6 +1006,10 @@ SaveInode(Volume * vp, struct VnodeDiskObject *vnode, Inode ino)
     afs_foff_t total;
     ssize_t len;
 
+    if (!VALID_INO(ino)) {
+        return;
+    }
+
     IH_INIT(ih, V_device(vp), V_parentId(vp), ino);
     fdP = IH_OPEN(ih);
     if (fdP == NULL) {
@@ -885,36 +1066,65 @@ SaveInode(Volume * vp, struct VnodeDiskObject *vnode, Inode ino)
           PrintInode(NULL, ino), nfile, (unsigned long)total);
 }
 
+/**
+ * Scan a volume index and handle each vnode
+ *
+ * @param[in] vp      volume object
+ * @param[in] class   which index to scan
+ *
+ * @return none
+ */
 void
-PrintVnodes(Volume * vp, VnodeClass class)
+HandleVnodes(Volume * vp, VnodeClass class)
 {
     afs_int32 diskSize =
        (class == vSmall ? SIZEOF_SMALLDISKVNODE : SIZEOF_LARGEDISKVNODE);
     char buf[SIZEOF_LARGEDISKVNODE];
     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
-    StreamHandle_t *file;
-    int vnodeIndex, nVnodes;
+    StreamHandle_t *file = NULL;
+    int vnodeIndex;
+    afs_sfsize_t nVnodes;
     afs_foff_t offset = 0;
     Inode ino;
     IHandle_t *ih = vp->vnodeIndex[class].handle;
-    FdHandle_t *fdP;
-    int size;
+    FdHandle_t *fdP = NULL;
+    afs_sfsize_t size;
     char *ctime, *atime, *mtime;
 
+    /* print vnode table heading */
+    if (class == vLarge) {
+       if (DumpInfo) {
+           printf("\nLarge vnodes (directories)\n");
+       }
+    } else {
+       if (DumpInfo) {
+           printf("\nSmall vnodes(files, symbolic links)\n");
+           fflush(stdout);
+       }
+       if (SaveInodes) {
+           printf("Saving all volume files to current directory ...\n");
+           if (ShowSizes) {
+               PrintingVolumeSizes = 0;        /* print heading again */
+           }
+       }
+    }
+
     fdP = IH_OPEN(ih);
     if (fdP == NULL) {
        fprintf(stderr, "%s: open failed: ", progname);
-       exit(1);
+       goto error;
     }
 
     file = FDH_FDOPEN(fdP, "r");
     if (!file) {
        fprintf(stderr, "%s: fdopen failed\n", progname);
-       exit(1);
+       goto error;
     }
 
-    GetFileInfo(fdP->fd_fd, &size, &ctime, &atime, &mtime);
-    if (InodeTimes && !dsizeOnly) {
+    if (GetFileInfo(fdP->fd_fd, &size, &ctime, &atime, &mtime) != 0) {
+       goto error;
+    }
+    if (InodeTimes) {
        printf("ichanged : %s\nimodified: %s\niaccessed: %s\n\n", ctime,
               mtime, atime);
     }
@@ -930,31 +1140,44 @@ PrintVnodes(Volume * vp, VnodeClass class)
         nVnodes--, vnodeIndex++, offset += diskSize) {
 
        ino = VNDISK_GET_INO(vnode);
+       if (ShowSizes) {
+           afs_fsize_t fileLength;
 
-       if (saveinodes) {
-           if (!VALID_INO(ino)) {
-               continue;
+           VNDISK_GET_LEN(fileLength, vnode);
+           if (fileLength > 0) {
+               volumeTotals.vnodesize += fileLength;
+               volumeTotals.vnodesize_k += fileLength / 1024;
            }
-           if (dsizeOnly && (class == vLarge)) {
-               afs_fsize_t fileLength;
-
-               VNDISK_GET_LEN(fileLength, vnode);
-               if (fileLength > 0) {
-                   volumeTotals.vnodesize += fileLength;
-                   volumeTotals.vnodesize_k += fileLength / 1024;
-               }
-           } else if (class == vSmall) {
-                SaveInode(vp, vnode, ino);
-            }
-       } else {
+       }
+       if (SaveInodes && (class == vSmall)) {
+           SaveInode(vp, vnode, ino);
+       }
+       if (DumpVnodes || ShowOrphaned) {
            PrintVnode(offset, vnode,
                       bitNumberToVnodeNumber(vnodeIndex, class), ino, vp);
        }
     }
-    STREAM_CLOSE(file);
-    FDH_CLOSE(fdP);
+
+  error:
+    if (file) {
+       STREAM_CLOSE(file);
+    }
+    if (fdP) {
+       FDH_CLOSE(fdP);
+    }
 }
 
+/**
+ * Print vnode information
+ *
+ * @param[in] offset       index offset of this vnode
+ * @param[in] vnode        vnode object to be printed
+ * @param[in] vnodeNumber  vnode number
+ * @param[in] ino          fileserver inode number
+ * @param[in] vp           parent volume of the vnode
+ *
+ * @return none
+ */
 void
 PrintVnode(afs_foff_t offset, VnodeDiskObject * vnode, VnodeId vnodeNumber,
           Inode ino, Volume * vp)
@@ -966,14 +1189,13 @@ PrintVnode(afs_foff_t offset, VnodeDiskObject * vnode, VnodeId vnodeNumber,
     afs_fsize_t fileLength;
 
     VNDISK_GET_LEN(fileLength, vnode);
-    if (fileLength > 0) {
-       volumeTotals.vnodesize += fileLength;
-       volumeTotals.vnodesize_k += fileLength / 1024;
-    }
-    if (dsizeOnly)
-       return;
-    if (orphaned && (fileLength == 0 || vnode->parent || !offset))
+
+    /* The check for orphaned vnodes is currently limited to non-empty
+     * vnodes with a parent of zero (and which are not the first entry
+     * in the index). */
+    if (ShowOrphaned && (fileLength == 0 || vnode->parent || !offset))
        return;
+
     printf
        ("%10lld Vnode %u.%u.%u cloned: %u, length: %llu linkCount: %d parent: %u",
         (long long)offset, vnodeNumber, vnode->uniquifier, vnode->dataVersion,