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