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