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