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