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