3ba1216a3dd251d8735e9a2748c37efd15a8a889
[openafs.git] / src / vol / vol-info.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11    System:              VICE-TWO
12    Module:              vol-info.c
13    Institution: The Information Technology Center, Carnegie-Mellon University
14
15    */
16
17 #include <afsconfig.h>
18 #include <afs/param.h>
19
20 #include <roken.h>
21
22 #include <ctype.h>
23
24 #ifdef HAVE_SYS_FILE_H
25 #include <sys/file.h>
26 #endif
27
28 #include <afs/cmd.h>
29 #include <afs/dir.h>
30 #include <afs/afsint.h>
31 #include <afs/errors.h>
32 #include <opr/queue.h>
33
34 #include "nfs.h"
35 #include "lock.h"
36 #include "ihandle.h"
37 #include "vnode.h"
38 #include "volume.h"
39 #include "partition.h"
40 #include "daemon_com_inline.h"
41 #include "fssync_inline.h"
42
43 #ifndef AFS_NT40_ENV
44 #include "AFS_component_version_number.c"
45 #endif
46
47 static const char *progname = "volinfo";
48
49 /* Command line options */
50 typedef enum {
51     P_CHECKOUT,
52     P_VNODE,
53     P_DATE,
54     P_INODE,
55     P_ITIME,
56     P_PART,
57     P_VOLUMEID,
58     P_HEADER,
59     P_SIZEONLY,
60     P_FIXHEADER,
61     P_SAVEINODES,
62     P_ORPHANED,
63     P_FILENAMES
64 } volinfo_parm_t;
65
66 /* Vnode details for vnode handling procedures */
67 struct VnodeDetails {
68     Volume *vp;
69     VnodeClass class;
70     VnodeDiskObject *vnode;
71     VnodeId vnodeNumber;
72     afs_foff_t offset;
73     int index;
74 };
75
76 /* Modes */
77 static int Checkout = 0;            /**< Use FSSYNC to checkout volumes from the fileserver. */
78 static int DumpInfo = 0;            /**< Dump volume information */
79 static int DumpHeader = 0;          /**< Dump volume header files info */
80 static int DumpVnodes = 0;          /**< Dump vnode info */
81 static int DumpInodeNumber = 0;     /**< Dump inode numbers with vnodes */
82 static int DumpDate = 0;            /**< Dump vnode date (server modify date) with vnode */
83 static int InodeTimes = 0;          /**< Dump some of the dates associated with inodes */
84 #if defined(AFS_NAMEI_ENV)
85 static int PrintFileNames = 0;      /**< Dump vnode and special file name filenames */
86 #endif
87 static int ShowOrphaned = 0;        /**< Show "orphaned" vnodes (vnodes with parent of 0) */
88 static int ShowSizes = 0;           /**< Show volume size summary */
89 static int SaveInodes = 0;          /**< Save vnode data to files */
90 static int FixHeader = 0;           /**< Repair header files magic and version fields. */
91
92 /**
93  * Volume size running totals
94  */
95 struct sizeTotals {
96     afs_uint64 diskused_k;      /**< volume size from disk data file, in kilobytes */
97     afs_uint64 auxsize;         /**< size of header files, in bytes  */
98     afs_uint64 auxsize_k;       /**< size of header files, in kilobytes */
99     afs_uint64 vnodesize;       /**< size of the large and small vnodes, in bytes */
100     afs_uint64 vnodesize_k;     /**< size of the large and small vnodes, in kilobytes */
101     afs_uint64 size_k;          /**< size of headers and vnodes, in kilobytes */
102 };
103
104 static struct sizeTotals volumeTotals = { 0, 0, 0, 0, 0, 0 };
105 static struct sizeTotals partitionTotals = { 0, 0, 0, 0, 0, 0 };
106 static struct sizeTotals serverTotals = { 0, 0, 0, 0, 0, 0 };
107 static int PrintingVolumeSizes = 0;     /*print volume size lines */
108
109 /**
110  * List of procedures to call when scanning vnodes.
111  */
112 struct VnodeScanProc {
113     struct opr_queue link;
114     const char *heading;
115     void (*proc) (struct VnodeDetails * vdp);
116 };
117 static struct opr_queue VnodeScanLists[nVNODECLASSES];
118
119 /* Forward Declarations */
120 void PrintHeader(Volume * vp);
121 void HandleAllPart(void);
122 void HandlePart(struct DiskPartition64 *partP);
123 void HandleVolume(struct DiskPartition64 *partP, char *name);
124 struct DiskPartition64 *FindCurrentPartition(void);
125 Volume *AttachVolume(struct DiskPartition64 *dp, char *volname,
126                      struct VolumeHeader *header);
127 void HandleVnodes(Volume * vp, VnodeClass class);
128 static void AddVnodeToSizeTotals(struct VnodeDetails *vdp);
129 static void SaveInode(struct VnodeDetails *vdp);
130 static void PrintVnode(struct VnodeDetails *vdp);
131
132 /**
133  * Format time as a timestamp string
134  *
135  * @param[in] date  time value to format
136  *
137  * @return timestamp string in the form YYYY/MM/DD.hh:mm:ss
138  *
139  * @note A static array of 8 strings are stored by this
140  *       function. The array slots are overwritten, so the
141  *       caller must not reference the returned string after
142  *       seven additional calls to this function.
143  */
144 char *
145 date(time_t date)
146 {
147 #define MAX_DATE_RESULT 100
148     static char results[8][MAX_DATE_RESULT];
149     static int next;
150     struct tm *tm = localtime(&date);
151     char buf[32];
152
153     (void)strftime(buf, 32, "%Y/%m/%d.%H:%M:%S", tm);   /* NT does not have %T */
154     snprintf(results[next = (next + 1) & 7], MAX_DATE_RESULT,
155              "%lu (%s)", (unsigned long)date, buf);
156     return results[next];
157 }
158
159 #ifdef AFS_NT40_ENV
160 /**
161  * Format file time as a timestamp string
162  *
163  * @param[in] ft  file time
164  *
165  * @return timestamp string in the form YYYY/MM/DD.hh:mm:ss
166  *
167  * @note A static array of 8 strings are stored by this
168  *       function. The array slots are overwritten, so the
169  *       caller must not reference the returned string after
170  *       seven additional calls to this function.
171  */
172 char *
173 NT_date(FILETIME * ft)
174 {
175     static char result[8][64];
176     static int next = 0;
177     SYSTEMTIME st;
178     FILETIME lft;
179
180     if (!FileTimeToLocalFileTime(ft, &lft)
181         || !FileTimeToSystemTime(&lft, &st)) {
182         fprintf(stderr, "%s: Time conversion failed.\n", progname);
183         exit(1);
184     }
185     sprintf(result[next = ((next + 1) & 7)], "%4d/%02d/%02d.%2d:%2d:%2d",
186             st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
187     return result[next];
188 }
189 #endif
190
191 /**
192  * Add vnode size to the running volume totals.
193  *
194  * @param[in]  vdp   vnode details object
195  *
196  * @return none
197  */
198 static void
199 AddVnodeToSizeTotals(struct VnodeDetails *vdp)
200 {
201     afs_fsize_t fileLength;
202
203     VNDISK_GET_LEN(fileLength, vdp->vnode);
204     if (fileLength > 0) {
205         volumeTotals.vnodesize += fileLength;
206         volumeTotals.vnodesize_k += fileLength / 1024;
207     }
208 }
209
210 /**
211  * Print the volume size table heading line, if needed.
212  *
213  * @return none
214  */
215 static void
216 PrintVolumeSizeHeading(void)
217 {
218     if (!PrintingVolumeSizes) {
219         printf
220             ("Volume-Id\t  Volsize  Auxsize Inodesize  AVolsize SizeDiff                (VolName)\n");
221         PrintingVolumeSizes = 1;
222     }
223 }
224
225 /**
226  * Accumulate totals.
227  *
228  * @return 0
229  */
230 static void
231 AddSizeTotals(struct sizeTotals *a, struct sizeTotals *b)
232 {
233     a->diskused_k += b->diskused_k;
234     a->auxsize += b->auxsize;
235     a->auxsize_k += b->auxsize_k;
236     a->vnodesize += b->vnodesize;
237     a->vnodesize_k += b->vnodesize_k;
238     a->size_k += b->size_k;
239 }
240
241 /**
242  * Print the sizes for a volume.
243  *
244  * @return none
245  */
246 static void
247 PrintVolumeSizes(Volume * vp)
248 {
249     afs_int64 diff_k = volumeTotals.size_k - volumeTotals.diskused_k;
250
251     PrintVolumeSizeHeading();
252     printf("%u\t%9llu%9llu%10llu%10llu%9lld\t%24s\n",
253            V_id(vp),
254            volumeTotals.diskused_k,
255            volumeTotals.auxsize_k, volumeTotals.vnodesize_k,
256            volumeTotals.size_k, diff_k, V_name(vp));
257 }
258
259 /**
260  * Print the size totals for the partition.
261  *
262  * @return none
263  */
264 static void
265 PrintPartitionTotals(afs_uint64 nvols)
266 {
267     afs_int64 diff_k = partitionTotals.size_k - partitionTotals.diskused_k;
268
269     PrintVolumeSizeHeading();
270     printf("\nPart Totals  %12llu%9llu%10llu%10llu%9lld (%llu volumes)\n\n",
271            partitionTotals.diskused_k, partitionTotals.auxsize,
272            partitionTotals.vnodesize, partitionTotals.size_k, diff_k, nvols);
273     PrintingVolumeSizes = 0;
274 }
275
276 /**
277  * Print the size totals for all the partitions.
278  *
279  * @return none
280  */
281 static void
282 PrintServerTotals(void)
283 {
284     afs_int64 diff_k = serverTotals.size_k - serverTotals.diskused_k;
285
286     printf("\nServer Totals%12lld%9lld%10lld%10lld%9lld\n",
287            serverTotals.diskused_k, serverTotals.auxsize,
288            serverTotals.vnodesize, serverTotals.size_k, diff_k);
289 }
290
291 /**
292  * Read a volume header file
293  *
294  * @param[in] ih        ihandle of the header file
295  * @param[in] to        destination
296  * @param[in] size      expected header size
297  * @param[in] magic     expected header magic number
298  * @param[in] version   expected header version number
299  *
300  * @return error code
301  *   @retval 0 success
302  *   @retval -1 failed to read file
303  */
304 int
305 ReadHdr1(IHandle_t * ih, char *to, afs_sfsize_t size, u_int magic, u_int version)
306 {
307     struct versionStamp *vsn;
308     int bad = 0;
309     int code;
310
311     vsn = (struct versionStamp *)to;
312
313     code = IH_IREAD(ih, 0, to, size);
314     if (code != size)
315         return -1;
316
317     if (vsn->magic != magic) {
318         bad++;
319         fprintf(stderr, "%s: Inode %s: Bad magic %x (%x): IGNORED\n",
320                 progname, PrintInode(NULL, ih->ih_ino), vsn->magic, magic);
321     }
322
323     /* Check is conditional, in case caller wants to inspect version himself */
324     if (version && vsn->version != version) {
325         bad++;
326         fprintf(stderr, "%s: Inode %s: Bad version %x (%x): IGNORED\n",
327                 progname,
328                 PrintInode(NULL, ih->ih_ino), vsn->version, version);
329     }
330     if (bad && FixHeader) {
331         vsn->magic = magic;
332         vsn->version = version;
333         printf("Special index inode %s has a bad header. Reconstructing...\n",
334                PrintInode(NULL, ih->ih_ino));
335         code = IH_IWRITE(ih, 0, to, size);
336         if (code != size) {
337             fprintf(stderr,
338                     "%s: Write failed for inode %s; header left in damaged state\n",
339                     progname, PrintInode(NULL, ih->ih_ino));
340         }
341     }
342     if (!bad && DumpInfo) {
343         printf("Inode %s: Good magic %x and version %x\n",
344                PrintInode(NULL, ih->ih_ino), magic, version);
345     }
346     return 0;
347 }
348
349 /**
350  * Simplified attach volume
351  *
352  * param[in] dp       vice disk partition object
353  * param[in] volname  volume header file name
354  * param[in] header   volume header object
355  *
356  * @return volume object or null on error
357  */
358 Volume *
359 AttachVolume(struct DiskPartition64 * dp, char *volname,
360              struct VolumeHeader * header)
361 {
362     Volume *vp;
363     afs_int32 ec = 0;
364
365     if (Checkout) {
366         afs_int32 code;
367         SYNC_response response;
368         VolumeId volid = header->id;
369
370         memset(&response, 0, sizeof(response));
371         code =
372             FSYNC_VolOp(volid, dp->name, FSYNC_VOL_NEEDVOLUME, V_DUMP,
373                         &response);
374         if (code != SYNC_OK) {
375             if (response.hdr.reason == FSYNC_SALVAGE) {
376                 fprintf(stderr,
377                         "%s: file server says volume %lu is salvaging.\n",
378                         progname, afs_printable_uint32_lu(volid));
379                 return NULL;
380             } else {
381                 fprintf(stderr,
382                         "%s: attach of volume %lu denied by file server.\n",
383                         progname, afs_printable_uint32_lu(volid));
384                 return NULL;
385             }
386         }
387     }
388
389     vp = (Volume *) calloc(1, sizeof(Volume));
390     if (!vp) {
391         fprintf(stderr, "%s: Failed to allocate volume object.\n", progname);
392         return NULL;
393     }
394     vp->specialStatus = 0;
395     vp->device = dp->device;
396     vp->partition = dp;
397     IH_INIT(vp->vnodeIndex[vLarge].handle, dp->device, header->parent,
398             header->largeVnodeIndex);
399     IH_INIT(vp->vnodeIndex[vSmall].handle, dp->device, header->parent,
400             header->smallVnodeIndex);
401     IH_INIT(vp->diskDataHandle, dp->device, header->parent,
402             header->volumeInfo);
403     IH_INIT(V_linkHandle(vp), dp->device, header->parent, header->linkTable);
404     vp->cacheCheck = 0;         /* XXXX */
405     vp->shuttingDown = 0;
406     vp->goingOffline = 0;
407     vp->nUsers = 1;
408     vp->header = (struct volHeader *)calloc(1, sizeof(*vp->header));
409     if (!vp->header) {
410         fprintf(stderr, "%s: Failed to allocate volume header.\n", progname);
411         free(vp);
412         return NULL;
413     }
414     ec = ReadHdr1(V_diskDataHandle(vp), (char *)&V_disk(vp),
415                   sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
416     if (!ec) {
417         struct IndexFileHeader iHead;
418         ec = ReadHdr1(vp->vnodeIndex[vSmall].handle, (char *)&iHead,
419                       sizeof(iHead), SMALLINDEXMAGIC, SMALLINDEXVERSION);
420     }
421     if (!ec) {
422         struct IndexFileHeader iHead;
423         ec = ReadHdr1(vp->vnodeIndex[vLarge].handle, (char *)&iHead,
424                       sizeof(iHead), LARGEINDEXMAGIC, LARGEINDEXVERSION);
425     }
426 #ifdef AFS_NAMEI_ENV
427     if (!ec) {
428         struct versionStamp stamp;
429         ec = ReadHdr1(V_linkHandle(vp), (char *)&stamp, sizeof(stamp),
430                       LINKTABLEMAGIC, LINKTABLEVERSION);
431     }
432 #endif
433     if (ec)
434         return (Volume *) 0;
435     return vp;
436 }
437
438 /**
439  * Simplified detach volume
440  *
441  * param[in] vp       volume object from AttachVolume
442  *
443  * @return none
444  */
445 static void
446 DetachVolume(Volume * vp)
447 {
448     if (Checkout) {
449         afs_int32 code;
450         SYNC_response response;
451         memset(&response, 0, sizeof(response));
452
453         code = FSYNC_VolOp(V_id(vp), V_partition(vp)->name,
454                            FSYNC_VOL_ON, FSYNC_WHATEVER, &response);
455         if (code != SYNC_OK) {
456             fprintf(stderr, "%s: FSSYNC error %d (%s)\n", progname, code,
457                     SYNC_res2string(code));
458             fprintf(stderr, "%s:  protocol response code was %d (%s)\n",
459                     progname, response.hdr.response,
460                     SYNC_res2string(response.hdr.response));
461             fprintf(stderr, "%s:  protocol reason code was %d (%s)\n",
462                     progname, response.hdr.reason,
463                     FSYNC_reason2string(response.hdr.reason));
464         }
465     }
466
467     IH_RELEASE(vp->vnodeIndex[vLarge].handle);
468     IH_RELEASE(vp->vnodeIndex[vSmall].handle);
469     IH_RELEASE(vp->diskDataHandle);
470     IH_RELEASE(V_linkHandle(vp));
471     free(vp->header);
472     free(vp);
473 }
474
475 /**
476  * Convert the partition device number into a partition name.
477  *
478  * @param[in]   partId      partition number, 0 to 254
479  * @param[out]  partName    buffer to hold partition name (e.g. /vicepa)
480  * @param[in]   partNameSize    size of partName buffer
481  *
482  * @return status
483  *   @retval 0 success
484  *   @retval -1 error, partId is out of range
485  *   @retval -2 error, partition name exceeds partNameSize
486  */
487 static int
488 GetPartitionName(afs_uint32 partId, char *partName, afs_size_t partNameSize)
489 {
490     const afs_uint32 OLD_NUM_DEVICES = 26;      /* a..z */
491
492     if (partId < OLD_NUM_DEVICES) {
493         if (partNameSize < 8) {
494             return -2;
495         }
496         strlcpy(partName, "/vicep", partNameSize);
497         partName[6] = partId + 'a';
498         partName[7] = '\0';
499         return 0;
500     }
501     if (partId < VOLMAXPARTS) {
502         if (partNameSize < 9) {
503             return -2;
504         }
505         strlcpy(partName, "/vicep", partNameSize);
506         partId -= OLD_NUM_DEVICES;
507         partName[6] = 'a' + (partId / OLD_NUM_DEVICES);
508         partName[7] = 'a' + (partId % OLD_NUM_DEVICES);
509         partName[8] = '\0';
510         return 0;
511     }
512     return -1;
513 }
514
515 /**
516  * Scan the volumes in the partitions
517  *
518  * Scan the specified volume in the specified partition if both
519  * are given. Scan all the volumes in the specified partition if
520  * only the partition is given.  If neither a partition nor volume
521  * is given, scan all the volumes in all the partitions.  If only
522  * the volume is given, attempt to find it in the current working
523  * directory.
524  *
525  * @param[in] partNameOrId   partition name or id to be scannned
526  * @param[in] volumeId       volume id to be scanned
527  *
528  * @return 0 if successful
529  */
530 static int
531 ScanPartitions(char *partNameOrId, afs_uint32 volumeId)
532 {
533     int err = 0;
534     char partName[64] = "";
535     struct DiskPartition64 *partP = NULL;
536
537 #ifndef AFS_NT40_ENV
538     if (geteuid() != 0) {
539         fprintf(stderr, "%s: Must be run as root; sorry\n", progname);
540         return 1;
541     }
542 #endif
543
544     if (Checkout) {
545         if (!FSYNC_clientInit()) {
546             fprintf(stderr, "%s: Failed to connect to fileserver.\n",
547                     progname);
548             return 1;
549         }
550     }
551
552     DInit(10);
553
554     /* Allow user to specify partition by name or id. */
555     if (partNameOrId) {
556         afs_uint32 partId = volutil_GetPartitionID(partNameOrId);
557         if (partId == -1) {
558             fprintf(stderr, "%s: Could not parse '%s' as a partition name.\n",
559                     progname, partNameOrId);
560             err = 1;
561             goto cleanup;
562         }
563         if (GetPartitionName(partId, partName, sizeof(partName))) {
564             fprintf(stderr,
565                     "%s: Could not format '%s' as a partition name.\n",
566                     progname, partNameOrId);
567             err = 1;
568             goto cleanup;
569         }
570     }
571
572     err = VAttachPartitions();
573     if (err) {
574         fprintf(stderr, "%s: %d partitions had errors during attach.\n",
575                 progname, err);
576         err = 1;
577         goto cleanup;
578     }
579
580     if (partName[0]) {
581         partP = VGetPartition(partName, 0);
582         if (!partP) {
583             fprintf(stderr,
584                     "%s: %s is not an AFS partition name on this server.\n",
585                     progname, partName);
586             err = 1;
587             goto cleanup;
588         }
589     }
590
591     if (!volumeId) {
592         if (!partP) {
593             HandleAllPart();
594         } else {
595             HandlePart(partP);
596         }
597     } else {
598         char name1[128];
599
600         if (!partP) {
601             partP = FindCurrentPartition();
602             if (!partP) {
603                 fprintf(stderr,
604                         "%s: Current partition is not a vice partition.\n",
605                         progname);
606                 err = 1;
607                 goto cleanup;
608             }
609         }
610         snprintf(name1, sizeof name1, VFORMAT,
611                  afs_printable_uint32_lu(volumeId));
612         HandleVolume(partP, name1);
613     }
614
615   cleanup:
616     if (Checkout) {
617         FSYNC_clientFinis();
618     }
619     return err;
620 }
621
622 /**
623  * Add a vnode scanning procedure
624  *
625  * @param[in]   class   vnode class for this handler
626  * @param[in]   proc    handler proc to be called by HandleVnodes
627  * @param[in]   heading optional string to pring before scanning vnodes
628  *
629  * @return none
630  */
631 static void
632 AddVnodeHandler(VnodeClass class, void (*proc) (struct VnodeDetails * vdp),
633                 const char *heading)
634 {
635     struct VnodeScanProc *entry = malloc(sizeof(struct VnodeScanProc));
636     entry->proc = proc;
637     entry->heading = heading;
638     opr_queue_Append(&VnodeScanLists[class], (struct opr_queue *)entry);
639 }
640
641 /**
642  * Process command line options and start scanning
643  *
644  * @param[in] as     command syntax object
645  * @param[in] arock  opaque object; not used
646  *
647  * @return error code
648  */
649 static int
650 handleit(struct cmd_syndesc *as, void *arock)
651 {
652     struct cmd_item *ti;
653     afs_uint32 volumeId = 0;
654     char *partNameOrId = NULL;
655
656     DumpInfo = 1;               /* volinfo default mode */
657
658     if (as->parms[P_CHECKOUT].items) {
659         Checkout = 1;
660     }
661     if (as->parms[P_VNODE].items) {
662         DumpVnodes = 1;
663     }
664     if (as->parms[P_DATE].items) {
665         DumpDate = 1;
666     }
667     if (as->parms[P_INODE].items) {
668         DumpInodeNumber = 1;
669     }
670     if (as->parms[P_ITIME].items) {
671         InodeTimes = 1;
672     }
673     if ((ti = as->parms[P_PART].items)) {
674         partNameOrId = ti->data;
675     }
676     if ((ti = as->parms[P_VOLUMEID].items)) {
677         volumeId = strtoul(ti->data, NULL, 10);
678     }
679     if (as->parms[P_HEADER].items) {
680         DumpHeader = 1;
681     }
682     if (as->parms[P_SIZEONLY].items) {
683         ShowSizes = 1;
684     }
685     if (as->parms[P_FIXHEADER].items) {
686         FixHeader = 1;
687     }
688     if (as->parms[P_SAVEINODES].items) {
689         SaveInodes = 1;
690     }
691     if (as->parms[P_ORPHANED].items) {
692         ShowOrphaned = 1;
693     }
694 #if defined(AFS_NAMEI_ENV)
695     if (as->parms[P_FILENAMES].items) {
696         PrintFileNames = 1;
697     }
698 #endif
699
700     /* -saveinodes and -sizeOnly override the default mode.
701      * For compatibility with old versions of volinfo, -orphaned
702      * and -filename options imply -vnodes */
703     if (SaveInodes || ShowSizes) {
704         DumpInfo = 0;
705         DumpHeader = 0;
706         DumpVnodes = 0;
707         InodeTimes = 0;
708         ShowOrphaned = 0;
709     } else if (ShowOrphaned) {
710         DumpVnodes = 1;         /* implied */
711 #ifdef AFS_NAMEI_ENV
712     } else if (PrintFileNames) {
713         DumpVnodes = 1;         /* implied */
714 #endif
715     }
716
717     if (SaveInodes) {
718         AddVnodeHandler(vSmall, SaveInode,
719                         "Saving all volume files to current directory ...\n");
720     }
721     if (ShowSizes) {
722         AddVnodeHandler(vLarge, AddVnodeToSizeTotals, NULL);
723         AddVnodeHandler(vSmall, AddVnodeToSizeTotals, NULL);
724     }
725     if (DumpVnodes) {
726         AddVnodeHandler(vLarge, PrintVnode, "\nLarge vnodes (directories)\n");
727         AddVnodeHandler(vSmall, PrintVnode,
728                         "\nSmall vnodes(files, symbolic links)\n");
729     }
730
731     return ScanPartitions(partNameOrId, volumeId);
732 }
733
734 /**
735  * Determine if the current directory is a vice partition
736  *
737  * @return disk partition object
738  */
739 #ifdef AFS_NT40_ENV
740 #include <direct.h>
741 struct DiskPartition64 *
742 FindCurrentPartition(void)
743 {
744     int dr = _getdrive();
745     struct DiskPartition64 *dp;
746
747     dr--;
748     for (dp = DiskPartitionList; dp; dp = dp->next) {
749         if (*dp->devName - 'A' == dr)
750             break;
751     }
752     if (!dp) {
753         fprintf(stderr, "%s: Current drive is not a valid vice partition.\n",
754                 progname);
755     }
756     return dp;
757 }
758 #else
759 struct DiskPartition64 *
760 FindCurrentPartition(void)
761 {
762     char partName[1024];
763     char tmp = '\0';
764     char *p;
765     struct DiskPartition64 *dp;
766
767     if (!getcwd(partName, 1023)) {
768         fprintf(stderr, "%s: Failed to get current working directory: ",
769                 progname);
770         perror("pwd");
771         return NULL;
772     }
773     p = strchr(&partName[1], OS_DIRSEPC);
774     if (p) {
775         tmp = *p;
776         *p = '\0';
777     }
778     if (!(dp = VGetPartition(partName, 0))) {
779         if (tmp)
780             *p = tmp;
781         fprintf(stderr, "%s: %s is not a valid vice partition.\n", progname,
782                 partName);
783         return NULL;
784     }
785     return dp;
786 }
787 #endif
788
789 /**
790  * Scan and handle all the partitions detected on this server
791  *
792  * @return none
793  */
794 void
795 HandleAllPart(void)
796 {
797     struct DiskPartition64 *partP;
798
799
800     for (partP = DiskPartitionList; partP; partP = partP->next) {
801         if (DumpInfo || SaveInodes || ShowSizes) {
802             printf("Processing Partition %s:\n", partP->name);
803         }
804         HandlePart(partP);
805         if (ShowSizes) {
806             AddSizeTotals(&serverTotals, &partitionTotals);
807         }
808     }
809
810     if (ShowSizes) {
811         PrintServerTotals();
812     }
813 }
814
815 /**
816  * Scan the partition and handle volumes
817  *
818  * @param[in] partP  disk partition to scan
819  *
820  * @return none
821  */
822 void
823 HandlePart(struct DiskPartition64 *partP)
824 {
825     afs_int64 nvols = 0;
826     DIR *dirp;
827     struct dirent *dp;
828 #ifdef AFS_NT40_ENV
829     char pname[64];
830     char *p = pname;
831     (void)sprintf(pname, "%s\\", VPartitionPath(partP));
832 #else
833     char *p = VPartitionPath(partP);
834 #endif
835
836     if ((dirp = opendir(p)) == NULL) {
837         fprintf(stderr, "%s: Can't read directory %s; giving up\n", progname,
838                 p);
839         return;
840     }
841     while ((dp = readdir(dirp))) {
842         p = (char *)strrchr(dp->d_name, '.');
843         if (p != NULL && strcmp(p, VHDREXT) == 0) {
844             HandleVolume(partP, dp->d_name);
845             if (ShowSizes) {
846                 nvols++;
847                 AddSizeTotals(&partitionTotals, &volumeTotals);
848             }
849         }
850     }
851     closedir(dirp);
852     if (ShowSizes) {
853         PrintPartitionTotals(nvols);
854     }
855 }
856
857 /**
858  * Inspect a volume header special file.
859  *
860  * @param[in]  name       descriptive name of the type of header special file
861  * @param[in]  dp         partition object for this volume
862  * @param[in]  header     header object for this volume
863  * @param[in]  inode      fileserver inode number for this header special file
864  * @param[out] psize      total of the header special file
865  *
866  * @return none
867  */
868 void
869 HandleSpecialFile(const char *name, struct DiskPartition64 *dp,
870                   struct VolumeHeader *header, Inode inode,
871                   afs_sfsize_t * psize)
872 {
873     afs_sfsize_t size = 0;
874     IHandle_t *ih = NULL;
875     FdHandle_t *fdP = NULL;
876 #ifdef AFS_NAMEI_ENV
877     namei_t filename;
878 #endif /* AFS_NAMEI_ENV */
879
880     IH_INIT(ih, dp->device, header->parent, inode);
881     fdP = IH_OPEN(ih);
882     if (fdP == NULL) {
883         fprintf(stderr,
884                 "%s: Error opening header file '%s' for volume %u", progname,
885                 name, header->id);
886         perror("open");
887         goto error;
888     }
889     size = FDH_SIZE(fdP);
890     if (size == -1) {
891         fprintf(stderr,
892                 "%s: Error getting size of header file '%s' for volume %u",
893                 progname, name, header->id);
894         perror("fstat");
895         goto error;
896     }
897     if (DumpInfo) {
898         printf("\t%s inode\t= %s (size = %lld)\n",
899                name, PrintInode(NULL, inode), size);
900 #ifdef AFS_NAMEI_ENV
901         namei_HandleToName(&filename, ih);
902         printf("\t%s namei\t= %s\n", name, filename.n_path);
903 #endif /* AFS_NAMEI_ENV */
904     }
905     *psize += size;
906
907   error:
908     if (fdP != NULL) {
909         FDH_REALLYCLOSE(fdP);
910     }
911     if (ih != NULL) {
912         IH_RELEASE(ih);
913     }
914 }
915
916 /**
917  * Inspect this volume header files.
918  *
919  * @param[in]  dp         partition object for this volume
920  * @param[in]  header_fd  volume header file descriptor
921  * @param[in]  header     volume header object
922  * @param[out] psize      total of the header special file
923  *
924  * @return none
925  */
926 void
927 HandleHeaderFiles(struct DiskPartition64 *dp, FD_t header_fd,
928                   struct VolumeHeader *header)
929 {
930     afs_sfsize_t size = 0;
931
932     if (DumpInfo) {
933         size = OS_SIZE(header_fd);
934         printf("Volume header (size = %lld):\n", size);
935         printf("\tstamp\t= 0x%x\n", header->stamp.version);
936         printf("\tVolId\t= %u\n", header->id);
937         printf("\tparent\t= %u\n", header->parent);
938     }
939
940     HandleSpecialFile("Info", dp, header, header->volumeInfo, &size);
941     HandleSpecialFile("Small", dp, header, header->smallVnodeIndex,
942                       &size);
943     HandleSpecialFile("Large", dp, header, header->largeVnodeIndex,
944                       &size);
945 #ifdef AFS_NAMEI_ENV
946     HandleSpecialFile("Link", dp, header, header->linkTable, &size);
947 #endif /* AFS_NAMEI_ENV */
948
949     if (DumpInfo) {
950         printf("Total aux volume size = %lld\n\n", size);
951     }
952
953     if (ShowSizes) {
954         volumeTotals.auxsize = size;
955         volumeTotals.auxsize_k = size / 1024;
956     }
957 }
958
959 /**
960  * Attach and scan the volume and handle the header and vnodes
961  *
962  * Print the volume header and vnode information, depending on the
963  * current modes.
964  *
965  * @param[in] dp    vice partition object for this volume
966  * @param[in] name  volume header file name
967  *
968  * @return none
969  */
970 void
971 HandleVolume(struct DiskPartition64 *dp, char *name)
972 {
973     struct VolumeHeader header;
974     struct VolumeDiskHeader diskHeader;
975     FD_t fd = INVALID_FD;
976     Volume *vp = NULL;
977     char headerName[1024];
978     afs_sfsize_t n;
979
980     snprintf(headerName, sizeof headerName, "%s" OS_DIRSEP "%s",
981              VPartitionPath(dp), name);
982     if ((fd = OS_OPEN(headerName, O_RDONLY, 0666)) == INVALID_FD) {
983         fprintf(stderr, "%s: Cannot open volume header %s\n", progname, name);
984         goto cleanup;
985     }
986     if (OS_SIZE(fd) < 0) {
987         fprintf(stderr, "%s: Cannot read volume header %s\n", progname, name);
988         goto cleanup;
989     }
990     n = OS_READ(fd, &diskHeader, sizeof(diskHeader));
991     if (n != sizeof(diskHeader)
992         || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
993         fprintf(stderr, "%s: Error reading volume header %s\n", progname,
994                 name);
995         goto cleanup;
996     }
997     if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
998         fprintf(stderr,
999                 "%s: Volume %s, version number is incorrect; volume needs to be salvaged\n",
1000                 progname, name);
1001         goto cleanup;
1002     }
1003
1004     DiskToVolumeHeader(&header, &diskHeader);
1005     if (DumpHeader || ShowSizes) {
1006         HandleHeaderFiles(dp, fd, &header);
1007     }
1008
1009     vp = AttachVolume(dp, name, &header);
1010     if (!vp) {
1011         fprintf(stderr, "%s: Error attaching volume header %s\n",
1012                 progname, name);
1013         goto cleanup;
1014     }
1015
1016     if (DumpInfo) {
1017         PrintHeader(vp);
1018     }
1019
1020     HandleVnodes(vp, vLarge);
1021     HandleVnodes(vp, vSmall);
1022
1023     if (ShowSizes) {
1024         volumeTotals.diskused_k = V_diskused(vp);
1025         volumeTotals.size_k =
1026             volumeTotals.auxsize_k + volumeTotals.vnodesize_k;
1027         if (SaveInodes) {
1028             PrintingVolumeSizes = 0;    /* print heading again */
1029         }
1030         PrintVolumeSizes(vp);
1031     }
1032
1033   cleanup:
1034     if (fd != INVALID_FD) {
1035         OS_CLOSE(fd);
1036     }
1037     if (vp) {
1038         DetachVolume(vp);
1039     }
1040 }
1041
1042 /**
1043  * volinfo program entry
1044  */
1045 int
1046 main(int argc, char **argv)
1047 {
1048     struct cmd_syndesc *ts;
1049     afs_int32 code;
1050
1051     opr_queue_Init(&VnodeScanLists[vLarge]);
1052     opr_queue_Init(&VnodeScanLists[vSmall]);
1053
1054     ts = cmd_CreateSyntax(NULL, handleit, NULL,
1055                           "Dump volume's internal state");
1056     cmd_AddParmAtOffset(ts, P_CHECKOUT, "-checkout", CMD_FLAG, CMD_OPTIONAL,
1057                         "Checkout volumes from running fileserver");
1058     cmd_AddParmAtOffset(ts, P_VNODE, "-vnode", CMD_FLAG, CMD_OPTIONAL,
1059                         "Dump vnode info");
1060     cmd_AddParmAtOffset(ts, P_DATE, "-date", CMD_FLAG, CMD_OPTIONAL,
1061                         "Also dump vnode's mod date");
1062     cmd_AddParmAtOffset(ts, P_INODE, "-inode", CMD_FLAG, CMD_OPTIONAL,
1063                         "Also dump vnode's inode number");
1064     cmd_AddParmAtOffset(ts, P_ITIME, "-itime", CMD_FLAG, CMD_OPTIONAL,
1065                         "Dump special inode's mod times");
1066     cmd_AddParmAtOffset(ts, P_PART, "-part", CMD_LIST, CMD_OPTIONAL,
1067                         "AFS partition name or id (default current partition)");
1068     cmd_AddParmAtOffset(ts, P_VOLUMEID, "-volumeid", CMD_LIST, CMD_OPTIONAL,
1069                         "Volume id");
1070     cmd_AddParmAtOffset(ts, P_HEADER, "-header", CMD_FLAG, CMD_OPTIONAL,
1071                         "Dump volume's header info");
1072     cmd_AddParmAtOffset(ts, P_SIZEONLY, "-sizeonly", CMD_FLAG, CMD_OPTIONAL,
1073                         "Dump volume's size");
1074     cmd_AddParmAtOffset(ts, P_FIXHEADER, "-fixheader", CMD_FLAG,
1075                         CMD_OPTIONAL, "Try to fix header");
1076     cmd_AddParmAtOffset(ts, P_SAVEINODES, "-saveinodes", CMD_FLAG,
1077                         CMD_OPTIONAL, "Try to save all inodes");
1078     cmd_AddParmAtOffset(ts, P_ORPHANED, "-orphaned", CMD_FLAG, CMD_OPTIONAL,
1079                         "List all dir/files without a parent");
1080 #if defined(AFS_NAMEI_ENV)
1081     cmd_AddParmAtOffset(ts, P_FILENAMES, "-filenames", CMD_FLAG,
1082                         CMD_OPTIONAL, "Also dump vnode's namei filename");
1083 #endif
1084
1085     /* For compatibility with older versions. */
1086     cmd_AddParmAlias(ts, P_SIZEONLY, "-sizeOnly");
1087
1088     code = cmd_Dispatch(argc, argv);
1089     return code;
1090 }
1091
1092 /**
1093  * Return a display string for the volume type.
1094  *
1095  * @param[in]  type  volume type
1096  *
1097  * @return volume type description string
1098  */
1099 static_inline char *
1100 volumeTypeString(int type)
1101 {
1102     return
1103         (type == RWVOL ? "read/write" :
1104          (type == ROVOL ? "readonly" :
1105           (type == BACKVOL ? "backup" : "unknown")));
1106 }
1107
1108 /**
1109  * Print the volume header information
1110  *
1111  * @param[in]  volume object
1112  *
1113  * @return none
1114  */
1115 void
1116 PrintHeader(Volume * vp)
1117 {
1118     printf("Volume header for volume %u (%s)\n", V_id(vp), V_name(vp));
1119     printf("stamp.magic = %x, stamp.version = %u\n", V_stamp(vp).magic,
1120            V_stamp(vp).version);
1121     printf
1122         ("inUse = %d, inService = %d, blessed = %d, needsSalvaged = %d, dontSalvage = %d\n",
1123          V_inUse(vp), V_inService(vp), V_blessed(vp), V_needsSalvaged(vp),
1124          V_dontSalvage(vp));
1125     printf
1126         ("type = %d (%s), uniquifier = %u, needsCallback = %d, destroyMe = %x\n",
1127          V_type(vp), volumeTypeString(V_type(vp)), V_uniquifier(vp),
1128          V_needsCallback(vp), V_destroyMe(vp));
1129     printf
1130         ("id = %u, parentId = %u, cloneId = %u, backupId = %u, restoredFromId = %u\n",
1131          V_id(vp), V_parentId(vp), V_cloneId(vp), V_backupId(vp),
1132          V_restoredFromId(vp));
1133     printf
1134         ("maxquota = %d, minquota = %d, maxfiles = %d, filecount = %d, diskused = %d\n",
1135          V_maxquota(vp), V_minquota(vp), V_maxfiles(vp), V_filecount(vp),
1136          V_diskused(vp));
1137     printf("creationDate = %s, copyDate = %s\n", date(V_creationDate(vp)),
1138            date(V_copyDate(vp)));
1139     printf("backupDate = %s, expirationDate = %s\n", date(V_backupDate(vp)),
1140            date(V_expirationDate(vp)));
1141     printf("accessDate = %s, updateDate = %s\n", date(V_accessDate(vp)),
1142            date(V_updateDate(vp)));
1143     printf("owner = %u, accountNumber = %u\n", V_owner(vp),
1144            V_accountNumber(vp));
1145     printf
1146         ("dayUse = %u; week = (%u, %u, %u, %u, %u, %u, %u), dayUseDate = %s\n",
1147          V_dayUse(vp), V_weekUse(vp)[0], V_weekUse(vp)[1], V_weekUse(vp)[2],
1148          V_weekUse(vp)[3], V_weekUse(vp)[4], V_weekUse(vp)[5],
1149          V_weekUse(vp)[6], date(V_dayUseDate(vp)));
1150     printf("volUpdateCounter = %u\n", V_volUpCounter(vp));
1151 }
1152
1153 /**
1154  * Get the size and times of a file
1155  *
1156  * @param[in]  fd     file descriptor of file to stat
1157  * @param[out] size   size of the file
1158  * @param[out] ctime  ctime of file as a formatted string
1159  * @param[out] mtime  mtime of file as a formatted string
1160  * @param[out] atime  atime of file as a formatted string
1161  *
1162  * @return error code
1163  *   @retval 0 success
1164  *   @retval -1 failed to retrieve file information
1165  */
1166 static int
1167 GetFileInfo(FD_t fd, afs_sfsize_t * size, char **ctime, char **mtime,
1168             char **atime)
1169 {
1170 #ifdef AFS_NT40_ENV
1171     BY_HANDLE_FILE_INFORMATION fi;
1172     LARGE_INTEGER fsize;
1173     if (!GetFileInformationByHandle(fd, &fi)) {
1174         fprintf(stderr, "%s: GetFileInformationByHandle failed\n", progname);
1175         return -1;
1176     }
1177     if (!GetFileSizeEx(fd, &fsize)) {
1178         fprintf(stderr, "%s: GetFileSizeEx failed\n", progname);
1179         return -1;
1180     }
1181     *size = fsize.QuadPart;
1182     *ctime = "N/A";
1183     *mtime = NT_date(&fi.ftLastWriteTime);
1184     *atime = NT_date(&fi.ftLastAccessTime);
1185 #else
1186     struct afs_stat_st status;
1187     if (afs_fstat(fd, &status) == -1) {
1188         fprintf(stderr, "%s: fstat failed %d\n", progname, errno);
1189         return -1;
1190     }
1191     *size = status.st_size;
1192     *ctime = date(status.st_ctime);
1193     *mtime = date(status.st_mtime);
1194     *atime = date(status.st_atime);
1195 #endif
1196     return 0;
1197 }
1198
1199 /**
1200  * Copy the inode data to a file in the current directory.
1201  *
1202  * @param[in] vdp     vnode details object
1203  *
1204  * @return none
1205  */
1206 static void
1207 SaveInode(struct VnodeDetails *vdp)
1208 {
1209     IHandle_t *ih;
1210     FdHandle_t *fdP;
1211     char nfile[50], buffer[256];
1212     int ofd = 0;
1213     afs_foff_t total;
1214     ssize_t len;
1215     Inode ino = VNDISK_GET_INO(vdp->vnode);
1216
1217     if (!VALID_INO(ino)) {
1218         return;
1219     }
1220
1221     IH_INIT(ih, V_device(vdp->vp), V_parentId(vdp->vp), ino);
1222     fdP = IH_OPEN(ih);
1223     if (fdP == NULL) {
1224         fprintf(stderr,
1225                 "%s: Can't open inode %s error %d (ignored)\n",
1226                 progname, PrintInode(NULL, ino), errno);
1227         return;
1228     }
1229     snprintf(nfile, sizeof nfile, "TmpInode.%s", PrintInode(NULL, ino));
1230     ofd = afs_open(nfile, O_CREAT | O_RDWR | O_TRUNC, 0600);
1231     if (ofd < 0) {
1232         fprintf(stderr,
1233                 "%s: Can't create file %s; error %d (ignored)\n",
1234                 progname, nfile, errno);
1235
1236         FDH_REALLYCLOSE(fdP);
1237         IH_RELEASE(ih);
1238         return;
1239     }
1240     total = 0;
1241     while (1) {
1242         ssize_t nBytes;
1243         len = FDH_PREAD(fdP, buffer, sizeof(buffer), total);
1244         if (len < 0) {
1245             FDH_REALLYCLOSE(fdP);
1246             IH_RELEASE(ih);
1247             close(ofd);
1248             unlink(nfile);
1249             fprintf(stderr,
1250                     "%s: Error while reading from inode %s (%d)\n",
1251                     progname, PrintInode(NULL, ino), errno);
1252             return;
1253         }
1254         if (len == 0)
1255             break;              /* No more input */
1256         nBytes = write(ofd, buffer, (size_t)len);
1257         if (nBytes != len) {
1258             FDH_REALLYCLOSE(fdP);
1259             IH_RELEASE(ih);
1260             close(ofd);
1261             unlink(nfile);
1262             fprintf(stderr,
1263                     "%s: Error while writing to \"%s\" (%d - ignored)\n",
1264                     progname, nfile, errno);
1265             return;
1266         }
1267         total += len;
1268     }
1269
1270     FDH_REALLYCLOSE(fdP);
1271     IH_RELEASE(ih);
1272     close(ofd);
1273     printf("... Copied inode %s to file %s (%lu bytes)\n",
1274            PrintInode(NULL, ino), nfile, (unsigned long)total);
1275 }
1276
1277 /**
1278  * Scan a volume index and handle each vnode
1279  *
1280  * @param[in] vp      volume object
1281  * @param[in] class   which index to scan
1282  *
1283  * @return none
1284  */
1285 void
1286 HandleVnodes(Volume * vp, VnodeClass class)
1287 {
1288     afs_int32 diskSize =
1289         (class == vSmall ? SIZEOF_SMALLDISKVNODE : SIZEOF_LARGEDISKVNODE);
1290     char buf[SIZEOF_LARGEDISKVNODE];
1291     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
1292     StreamHandle_t *file = NULL;
1293     int vnodeIndex;
1294     afs_sfsize_t nVnodes;
1295     afs_foff_t offset = 0;
1296     IHandle_t *ih = vp->vnodeIndex[class].handle;
1297     FdHandle_t *fdP = NULL;
1298     afs_sfsize_t size;
1299     char *ctime, *atime, *mtime;
1300     struct opr_queue *scanList = &VnodeScanLists[class];
1301     struct opr_queue *cursor;
1302
1303     if (opr_queue_IsEmpty(scanList)) {
1304         return;
1305     }
1306
1307     for (opr_queue_Scan(scanList, cursor)) {
1308         struct VnodeScanProc *entry = (struct VnodeScanProc *)cursor;
1309         if (entry->heading) {
1310             printf("%s", entry->heading);
1311         }
1312     }
1313
1314     fdP = IH_OPEN(ih);
1315     if (fdP == NULL) {
1316         fprintf(stderr, "%s: open failed: ", progname);
1317         goto error;
1318     }
1319
1320     file = FDH_FDOPEN(fdP, "r");
1321     if (!file) {
1322         fprintf(stderr, "%s: fdopen failed\n", progname);
1323         goto error;
1324     }
1325
1326     if (GetFileInfo(fdP->fd_fd, &size, &ctime, &atime, &mtime) != 0) {
1327         goto error;
1328     }
1329     if (InodeTimes) {
1330         printf("ichanged : %s\nimodified: %s\niaccessed: %s\n\n", ctime,
1331                mtime, atime);
1332     }
1333
1334     nVnodes = (size / diskSize) - 1;
1335     if (nVnodes > 0) {
1336         STREAM_ASEEK(file, diskSize);
1337     } else
1338         nVnodes = 0;
1339
1340     for (vnodeIndex = 0;
1341          nVnodes && STREAM_READ(vnode, diskSize, 1, file) == 1;
1342          nVnodes--, vnodeIndex++, offset += diskSize) {
1343
1344         struct VnodeDetails vnodeDetails;
1345
1346         vnodeDetails.vp = vp;
1347         vnodeDetails.class = class;
1348         vnodeDetails.vnode = vnode;
1349         vnodeDetails.vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
1350         vnodeDetails.offset = offset;
1351         vnodeDetails.index = vnodeIndex;
1352
1353         for (opr_queue_Scan(scanList, cursor)) {
1354             struct VnodeScanProc *entry = (struct VnodeScanProc *)cursor;
1355             if (entry->proc) {
1356                 (*entry->proc) (&vnodeDetails);
1357             }
1358         }
1359     }
1360
1361   error:
1362     if (file) {
1363         STREAM_CLOSE(file);
1364     }
1365     if (fdP) {
1366         FDH_CLOSE(fdP);
1367     }
1368 }
1369
1370 /**
1371  * Print vnode information
1372  *
1373  * @param[in] vdp          vnode details object
1374  *
1375  * @return none
1376  */
1377 void
1378 PrintVnode(struct VnodeDetails *vdp)
1379 {
1380 #if defined(AFS_NAMEI_ENV)
1381     IHandle_t *ihtmpp;
1382     namei_t filename;
1383 #endif
1384     afs_foff_t offset = vdp->offset;
1385     VnodeDiskObject *vnode = vdp->vnode;
1386     afs_fsize_t fileLength;
1387     Inode ino;
1388
1389     ino = VNDISK_GET_INO(vnode);
1390     VNDISK_GET_LEN(fileLength, vnode);
1391
1392     /* The check for orphaned vnodes is currently limited to non-empty
1393      * vnodes with a parent of zero (and which are not the first entry
1394      * in the index). */
1395     if (ShowOrphaned && (fileLength == 0 || vnode->parent || !offset))
1396         return;
1397
1398     printf
1399         ("%10lld Vnode %u.%u.%u cloned: %u, length: %llu linkCount: %d parent: %u",
1400          (long long)offset, vdp->vnodeNumber, vnode->uniquifier,
1401          vnode->dataVersion, vnode->cloned, (afs_uintmax_t) fileLength,
1402          vnode->linkCount, vnode->parent);
1403     if (DumpInodeNumber)
1404         printf(" inode: %s", PrintInode(NULL, ino));
1405     if (DumpDate)
1406         printf(" ServerModTime: %s", date(vnode->serverModifyTime));
1407 #if defined(AFS_NAMEI_ENV)
1408     if (PrintFileNames) {
1409         IH_INIT(ihtmpp, V_device(vdp->vp), V_parentId(vdp->vp), ino);
1410         namei_HandleToName(&filename, ihtmpp);
1411 #if !defined(AFS_NT40_ENV)
1412         printf(" UFS-Filename: %s", filename.n_path);
1413 #else
1414         printf(" NTFS-Filename: %s", filename.n_path);
1415 #endif
1416     }
1417 #endif
1418     printf("\n");
1419 }