e821223c7bfb390a1ec382cfe272d27d341e594c
[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 #if defined(AFS_NAMEI_ENV)
261     if (as->parms[12].items) {
262         PrintFileNames = 1;
263         DumpVnodes = 1;
264     } else
265 #endif
266         orphaned = 0;
267
268     DInit(10);
269
270     err = VAttachPartitions();
271     if (err) {
272         printf("%d partitions had errors during attach.\n", err);
273     }
274
275     if (partName) {
276         partP = VGetPartition(partName, 0);
277         if (!partP) {
278             printf("%s is not an AFS partition name on this server.\n",
279                    partName);
280             exit(1);
281         }
282     }
283
284     if (!volumeId) {
285         if (!partP) {
286             HandleAllPart();
287         } else {
288             HandlePart(partP);
289         }
290     } else {
291         char name1[128];
292
293         if (!partP) {
294             partP = FindCurrentPartition();
295             if (!partP) {
296                 printf("Current partition is not a vice partition.\n");
297                 exit(1);
298             }
299         }
300         snprintf(name1, sizeof name1, VFORMAT,
301                  afs_printable_uint32_lu(volumeId));
302         if (dsizeOnly && !saveinodes)
303             printf
304                 ("Volume-Id\t  Volsize  Auxsize Inodesize  AVolsize SizeDiff                (VolName)\n");
305         HandleVolume(partP, name1);
306     }
307     return 0;
308 }
309
310 #ifdef AFS_NT40_ENV
311 #include <direct.h>
312 struct DiskPartition64 *
313 FindCurrentPartition(void)
314 {
315     int dr = _getdrive();
316     struct DiskPartition64 *dp;
317
318     dr--;
319     for (dp = DiskPartitionList; dp; dp = dp->next) {
320         if (*dp->devName - 'A' == dr)
321             break;
322     }
323     if (!dp) {
324         printf("Current drive is not a valid vice partition.\n");
325     }
326     return dp;
327 }
328 #else
329 struct DiskPartition64 *
330 FindCurrentPartition(void)
331 {
332     char partName[1024];
333     char tmp = '\0';
334     char *p;
335     struct DiskPartition64 *dp;
336
337     if (!getcwd(partName, 1023)) {
338         perror("pwd");
339         exit(1);
340     }
341     p = strchr(&partName[1], OS_DIRSEPC);
342     if (p) {
343         tmp = *p;
344         *p = '\0';
345     }
346     if (!(dp = VGetPartition(partName, 0))) {
347         if (tmp)
348             *p = tmp;
349         printf("%s is not a valid vice partition.\n", partName);
350         exit(1);
351     }
352     return dp;
353 }
354 #endif
355
356 void
357 HandleAllPart(void)
358 {
359     struct DiskPartition64 *partP;
360
361
362     for (partP = DiskPartitionList; partP; partP = partP->next) {
363         printf("Processing Partition %s:\n", partP->name);
364         HandlePart(partP);
365         Stotvolsize += Totvolsize;
366         SVauxsize += TVauxsize;
367         SVvnodesize += TVvnodesize;
368         SVdiskused += TVdiskused;
369     }
370
371     if (dsizeOnly) {
372         printf("\nServer Totals%12d%9d%10d%10d%9d\n", SVdiskused, SVauxsize,
373                SVvnodesize, Stotvolsize, Stotvolsize - SVdiskused);
374     }
375 }
376
377
378 void
379 HandlePart(struct DiskPartition64 *partP)
380 {
381     int nvols = 0;
382     DIR *dirp;
383     struct dirent *dp;
384 #ifdef AFS_NT40_ENV
385     char pname[64];
386     char *p = pname;
387     (void)sprintf(pname, "%s\\", VPartitionPath(partP));
388 #else
389     char *p = VPartitionPath(partP);
390 #endif
391
392     if ((dirp = opendir(p)) == NULL) {
393         printf("Can't read directory %s; giving up\n", p);
394         exit(1);
395     }
396     if (dsizeOnly && !saveinodes)
397         printf
398             ("Volume-Id\t  Volsize  Auxsize Inodesize  AVolsize SizeDiff                (VolName)\n");
399     while ((dp = readdir(dirp))) {
400         p = (char *)strrchr(dp->d_name, '.');
401         if (p != NULL && strcmp(p, VHDREXT) == 0) {
402             HandleVolume(partP, dp->d_name);
403             Totvolsize += totvolsize;
404             TVauxsize += Vauxsize;
405             TVvnodesize += Vvnodesize;
406             TVdiskused += Vdiskused;
407             nvols++;
408         }
409     }
410     closedir(dirp);
411     if (dsizeOnly) {
412         printf("\nPart Totals  %12d%9d%10d%10d%9d (%d volumes)\n\n",
413                TVdiskused, TVauxsize, TVvnodesize, Totvolsize,
414                Totvolsize - TVdiskused, nvols);
415     }
416 }
417
418
419 void
420 HandleVolume(struct DiskPartition64 *dp, char *name)
421 {
422     struct VolumeHeader header;
423     struct VolumeDiskHeader diskHeader;
424     FD_t fd = INVALID_FD;
425     Volume *vp;
426     IHandle_t *ih;
427     char headerName[1024];
428
429     if (online) {
430         printf("volinfo: -online not supported\n");
431         exit(1);
432     } else {
433         afs_sfsize_t n;
434
435         snprintf(headerName, sizeof headerName, "%s" OS_DIRSEP "%s",
436                  VPartitionPath(dp), name);
437         if ((fd = OS_OPEN(headerName, O_RDONLY, 0666)) == INVALID_FD
438             || OS_SIZE(fd) < 0) {
439             printf("Volinfo: Cannot read volume header %s\n", name);
440             OS_CLOSE(fd);
441             exit(1);
442         }
443         n = OS_READ(fd, &diskHeader, sizeof(diskHeader));
444
445         if (n != sizeof(diskHeader)
446             || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
447             printf("Volinfo: Error reading volume header %s\n", name);
448             exit(1);
449         }
450         if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
451             printf
452                 ("Volinfo: Volume %s, version number is incorrect; volume needs salvage\n",
453                  name);
454             exit(1);
455         }
456         DiskToVolumeHeader(&header, &diskHeader);
457
458         if (dheader) {
459             FdHandle_t *fdP;
460             afs_sfsize_t size = 0;
461             afs_sfsize_t code;
462
463             if (!dsizeOnly && !saveinodes) {
464                 size = OS_SIZE(fd);
465                 printf("Volume header (size = %d):\n", (int)size);
466                 printf("\tstamp\t= 0x%x\n", header.stamp.version);
467                 printf("\tVolId\t= %u\n", header.id);
468             }
469
470             IH_INIT(ih, dp->device, header.parent, header.volumeInfo);
471             fdP = IH_OPEN(ih);
472             if (fdP == NULL) {
473                 perror("opening volume info");
474                 exit(1);
475             }
476             code = FDH_SIZE(fdP);
477             if (code == -1) {
478                 perror("fstat");
479                 exit(1);
480             }
481             FDH_REALLYCLOSE(fdP);
482             IH_RELEASE(ih);
483             size += code;
484             if (!dsizeOnly && !saveinodes) {
485                 printf("\tparent\t= %u\n", header.parent);
486                 printf("\tInfo inode\t= %s (size = %d)\n",
487                        PrintInode(NULL, header.volumeInfo), (int)code);
488             }
489
490             IH_INIT(ih, dp->device, header.parent, header.smallVnodeIndex);
491             fdP = IH_OPEN(ih);
492             if (fdP == NULL) {
493                 perror("opening small vnode index");
494                 exit(1);
495             }
496             code = FDH_SIZE(fdP);
497             if (code == -1) {
498                 perror("fstat");
499                 exit(1);
500             }
501             FDH_REALLYCLOSE(fdP);
502             IH_RELEASE(ih);
503             size += code;
504             if (!dsizeOnly && !saveinodes) {
505                 printf("\tSmall inode\t= %s (size = %d)\n",
506                        PrintInode(NULL, header.smallVnodeIndex), (int)code);
507             }
508
509             IH_INIT(ih, dp->device, header.parent, header.largeVnodeIndex);
510             fdP = IH_OPEN(ih);
511             if (fdP == NULL) {
512                 perror("opening large vnode index");
513                 exit(1);
514             }
515             code = FDH_SIZE(fdP);
516             if (code == -1) {
517                 perror("fstat");
518                 exit(1);
519             }
520             FDH_REALLYCLOSE(fdP);
521             IH_RELEASE(ih);
522             size += code;
523             if (!dsizeOnly && !saveinodes) {
524                 printf("\tLarge inode\t= %s (size = %d)\n",
525                        PrintInode(NULL, header.largeVnodeIndex), (int)code);
526 #ifndef AFS_NT40_ENV
527                 printf("Total aux volume size = %d\n\n", (int)size);
528 #endif
529             }
530 #ifdef AFS_NAMEI_ENV
531             IH_INIT(ih, dp->device, header.parent, header.linkTable);
532             fdP = IH_OPEN(ih);
533             if (fdP == NULL) {
534                 perror("opening link table index");
535                 exit(1);
536             }
537             code = FDH_SIZE(fdP);
538             if (code == -1) {
539                 perror("fstat");
540                 exit(1);
541             }
542             FDH_REALLYCLOSE(fdP);
543             IH_RELEASE(ih);
544             size += code;
545             if (!dsizeOnly && !saveinodes) {
546                 printf("\tLink inode\t= %s (size = %d)\n",
547                        PrintInode(NULL, header.linkTable), (int)code);
548                 printf("Total aux volume size = %d\n\n", (int)size);
549             }
550 #endif
551             Vauxsize = size;
552             Vauxsize_k = size / 1024;
553         }
554         OS_CLOSE(fd);
555         vp = AttachVolume(dp, name, &header);
556         if (!vp) {
557             printf("Volinfo: Error attaching volume header %s\n", name);
558             return;
559         }
560     }
561     PrintHeader(vp);
562     if (DumpVnodes) {
563         if (!dsizeOnly && !saveinodes)
564             printf("\nLarge vnodes (directories)\n");
565         PrintVnodes(vp, vLarge);
566         if (!dsizeOnly && !saveinodes) {
567             printf("\nSmall vnodes(files, symbolic links)\n");
568             fflush(stdout);
569         }
570         if (saveinodes)
571             printf("Saving all volume files to current directory ...\n");
572         PrintVnodes(vp, vSmall);
573     }
574     if (dsizeOnly) {
575         totvolsize = Vauxsize_k + Vvnodesize_k;
576         if (saveinodes)
577             printf
578                 ("Volume-Id\t  Volsize  Auxsize Inodesize  AVolsize SizeDiff                (VolName)\n");
579         printf("%u\t%9d%9d%10d%10d%9d\t%24s\n", V_id(vp), Vdiskused,
580                Vauxsize_k, Vvnodesize_k, totvolsize, totvolsize - Vdiskused,
581                V_name(vp));
582     }
583     free(vp->header);
584     free(vp);
585 }
586
587 int
588 main(int argc, char **argv)
589 {
590     struct cmd_syndesc *ts;
591     afs_int32 code;
592
593     ts = cmd_CreateSyntax(NULL, handleit, NULL, "Dump volume's internal state");
594     cmd_AddParm(ts, "-online", CMD_FLAG, CMD_OPTIONAL,
595                 "Get info from running fileserver");
596     cmd_AddParm(ts, "-vnode", CMD_FLAG, CMD_OPTIONAL, "Dump vnode info");
597     cmd_AddParm(ts, "-date", CMD_FLAG, CMD_OPTIONAL,
598                 "Also dump vnode's mod date");
599     cmd_AddParm(ts, "-inode", CMD_FLAG, CMD_OPTIONAL,
600                 "Dump vnode's inode number");
601     cmd_AddParm(ts, "-itime", CMD_FLAG, CMD_OPTIONAL,
602                 "Dump special inode's mod times");
603     cmd_AddParm(ts, "-part", CMD_LIST, CMD_OPTIONAL,
604                 "AFS partition name (default current partition)");
605     cmd_AddParm(ts, "-volumeid", CMD_LIST, CMD_OPTIONAL, "Volume id");
606     cmd_AddParm(ts, "-header", CMD_FLAG, CMD_OPTIONAL,
607                 "Dump volume's header info");
608     cmd_AddParm(ts, "-sizeOnly", CMD_FLAG, CMD_OPTIONAL,
609                 "Dump volume's size");
610     cmd_AddParm(ts, "-fixheader", CMD_FLAG, CMD_OPTIONAL,
611                 "Try to fix header");
612     cmd_AddParm(ts, "-saveinodes", CMD_FLAG, CMD_OPTIONAL,
613                 "Try to save all inodes");
614     cmd_AddParm(ts, "-orphaned", CMD_FLAG, CMD_OPTIONAL,
615                 "List all dir/files without a parent");
616 #if defined(AFS_NAMEI_ENV)
617     cmd_AddParm(ts, "-filenames", CMD_FLAG, CMD_OPTIONAL, "Print filenames");
618 #endif
619     code = cmd_Dispatch(argc, argv);
620     return code;
621 }
622
623 #define typestring(type) (type == RWVOL? "read/write": type == ROVOL? "readonly": type == BACKVOL? "backup": "unknown")
624
625 void
626 PrintHeader(Volume * vp)
627 {
628     Vdiskused = V_diskused(vp);
629     if (dsizeOnly || saveinodes)
630         return;
631     printf("Volume header for volume %u (%s)\n", V_id(vp), V_name(vp));
632     printf("stamp.magic = %x, stamp.version = %u\n", V_stamp(vp).magic,
633            V_stamp(vp).version);
634     printf
635         ("inUse = %d, inService = %d, blessed = %d, needsSalvaged = %d, dontSalvage = %d\n",
636          V_inUse(vp), V_inService(vp), V_blessed(vp), V_needsSalvaged(vp),
637          V_dontSalvage(vp));
638     printf
639         ("type = %d (%s), uniquifier = %u, needsCallback = %d, destroyMe = %x\n",
640          V_type(vp), typestring(V_type(vp)), V_uniquifier(vp),
641          V_needsCallback(vp), V_destroyMe(vp));
642     printf
643         ("id = %u, parentId = %u, cloneId = %u, backupId = %u, restoredFromId = %u\n",
644          V_id(vp), V_parentId(vp), V_cloneId(vp), V_backupId(vp),
645          V_restoredFromId(vp));
646     printf
647         ("maxquota = %d, minquota = %d, maxfiles = %d, filecount = %d, diskused = %d\n",
648          V_maxquota(vp), V_minquota(vp), V_maxfiles(vp), V_filecount(vp),
649          V_diskused(vp));
650     printf("creationDate = %s, copyDate = %s\n", date(V_creationDate(vp)),
651            date(V_copyDate(vp)));
652     printf("backupDate = %s, expirationDate = %s\n", date(V_backupDate(vp)),
653            date(V_expirationDate(vp)));
654     printf("accessDate = %s, updateDate = %s\n", date(V_accessDate(vp)),
655            date(V_updateDate(vp)));
656     printf("owner = %u, accountNumber = %u\n", V_owner(vp),
657            V_accountNumber(vp));
658     printf
659         ("dayUse = %u; week = (%u, %u, %u, %u, %u, %u, %u), dayUseDate = %s\n",
660          V_dayUse(vp), V_weekUse(vp)[0], V_weekUse(vp)[1], V_weekUse(vp)[2],
661          V_weekUse(vp)[3], V_weekUse(vp)[4], V_weekUse(vp)[5],
662          V_weekUse(vp)[6], date(V_dayUseDate(vp)));
663     printf("volUpdateCounter = %u\n", V_volUpCounter(vp));
664 }
665
666 /* GetFileInfo
667  * OS independent file info. Die on failure.
668  */
669 #ifdef AFS_NT40_ENV
670 char *
671 NT_date(FILETIME * ft)
672 {
673     static char result[8][64];
674     static int next = 0;
675     SYSTEMTIME st;
676     FILETIME lft;
677
678     if (!FileTimeToLocalFileTime(ft, &lft)
679         || !FileTimeToSystemTime(&lft, &st)) {
680         printf("Time conversion failed.\n");
681         exit(1);
682     }
683     sprintf(result[next = ((next + 1) & 7)], "%4d/%02d/%02d.%2d:%2d:%2d",
684             st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
685     return result[next];
686 }
687 #endif
688
689 static void
690 GetFileInfo(FD_t fd, int *size, char **ctime, char **mtime, char **atime)
691 {
692 #ifdef AFS_NT40_ENV
693     BY_HANDLE_FILE_INFORMATION fi;
694     if (!GetFileInformationByHandle(fd, &fi)) {
695         printf("GetFileInformationByHandle failed, exiting\n");
696         exit(1);
697     }
698     *size = (int)fi.nFileSizeLow;
699     *ctime = "N/A";
700     *mtime = NT_date(&fi.ftLastWriteTime);
701     *atime = NT_date(&fi.ftLastAccessTime);
702 #else
703     struct afs_stat_st status;
704     if (afs_fstat(fd, &status) == -1) {
705         printf("fstat failed %d\n", errno);
706         exit(1);
707     }
708     *size = (int)status.st_size;
709     *ctime = date(status.st_ctime);
710     *mtime = date(status.st_mtime);
711     *atime = date(status.st_atime);
712 #endif
713 }
714
715 void
716 PrintVnodes(Volume * vp, VnodeClass class)
717 {
718     afs_int32 diskSize =
719         (class == vSmall ? SIZEOF_SMALLDISKVNODE : SIZEOF_LARGEDISKVNODE);
720     char buf[SIZEOF_LARGEDISKVNODE];
721     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
722     StreamHandle_t *file;
723     int vnodeIndex, nVnodes;
724     afs_foff_t offset = 0;
725     Inode ino;
726     IHandle_t *ih = vp->vnodeIndex[class].handle;
727     FdHandle_t *fdP;
728     int size;
729     char *ctime, *atime, *mtime;
730     char nfile[50], buffer[256];
731     int ofd, bad = 0;
732     afs_foff_t total;
733     ssize_t len;
734
735     fdP = IH_OPEN(ih);
736     if (fdP == NULL) {
737         printf("open failed\n");
738         exit(1);
739     }
740
741     file = FDH_FDOPEN(fdP, "r");
742     if (!file) {
743         printf("fdopen failed\n");
744         exit(1);
745     }
746
747     GetFileInfo(fdP->fd_fd, &size, &ctime, &atime, &mtime);
748     if (InodeTimes && !dsizeOnly) {
749         printf("ichanged : %s\nimodified: %s\niaccessed: %s\n\n", ctime,
750                mtime, atime);
751     }
752
753     nVnodes = (size / diskSize) - 1;
754     if (nVnodes > 0) {
755         STREAM_ASEEK(file, diskSize);
756     } else
757         nVnodes = 0;
758
759     for (vnodeIndex = 0;
760          nVnodes && STREAM_READ(vnode, diskSize, 1, file) == 1;
761          nVnodes--, vnodeIndex++, offset += diskSize) {
762
763         ino = VNDISK_GET_INO(vnode);
764         if (saveinodes) {
765             if (VALID_INO(ino) && (class == vSmall)) {
766                 IHandle_t *ih1;
767                 FdHandle_t *fdP1;
768                 IH_INIT(ih1, V_device(vp), V_parentId(vp), ino);
769                 fdP1 = IH_OPEN(ih1);
770                 if (fdP1 == NULL) {
771                     printf("Can't open inode %s error %d (ignored)\n",
772                            PrintInode(NULL, ino), errno);
773                     continue;
774                 }
775                 snprintf(nfile, sizeof nfile, "TmpInode.%s",
776                          PrintInode(NULL, ino));
777                 ofd = afs_open(nfile, O_CREAT | O_RDWR | O_TRUNC, 0600);
778                 if (ofd < 0) {
779                     printf("Can't create file %s; error %d (ignored)\n",
780                            nfile, errno);
781                     continue;
782                 }
783                 total = bad = 0;
784                 while (1) {
785                     ssize_t nBytes;
786                     len = FDH_PREAD(fdP1, buffer, sizeof(buffer), total);
787                     if (len < 0) {
788                         FDH_REALLYCLOSE(fdP1);
789                         IH_RELEASE(ih1);
790                         close(ofd);
791                         unlink(nfile);
792                         printf
793                             ("Error while reading from inode %s (%d - ignored)\n",
794                              PrintInode(NULL, ino), errno);
795                         bad = 1;
796                         break;
797                     }
798                     if (len == 0)
799                         break;  /* No more input */
800                     nBytes = write(ofd, buffer, len);
801                     if (nBytes != len) {
802                         FDH_REALLYCLOSE(fdP1);
803                         IH_RELEASE(ih1);
804                         close(ofd);
805                         unlink(nfile);
806                         printf
807                             ("Error while writing to \"%s\" (%d - ignored)\n",
808                              nfile, errno);
809                         bad = 1;
810                         break;
811                     }
812                     total += len;
813                 }
814                 if (bad)
815                     continue;
816                 FDH_REALLYCLOSE(fdP1);
817                 IH_RELEASE(ih1);
818                 close(ofd);
819                 printf("... Copied inode %s to file %s (%lu bytes)\n",
820                        PrintInode(NULL, ino), nfile, (unsigned long)total);
821             }
822         } else {
823 #if defined(AFS_NAMEI_ENV)
824             PrintVnode(offset, vnode,
825                        bitNumberToVnodeNumber(vnodeIndex, class), ino, vp);
826 #else
827             PrintVnode(offset, vnode,
828                        bitNumberToVnodeNumber(vnodeIndex, class), ino);
829 #endif
830         }
831     }
832     STREAM_CLOSE(file);
833     FDH_CLOSE(fdP);
834 }
835
836 #if defined(AFS_NAMEI_ENV)
837 void
838 PrintVnode(afs_foff_t offset, VnodeDiskObject * vnode, VnodeId vnodeNumber,
839            Inode ino, Volume * vp)
840 #else
841 void
842 PrintVnode(afs_foff_t offset, VnodeDiskObject * vnode, VnodeId vnodeNumber,
843            Inode ino)
844 #endif
845 {
846 #if defined(AFS_NAMEI_ENV)
847     IHandle_t *ihtmpp;
848     namei_t filename;
849 #endif
850     afs_fsize_t fileLength;
851
852     VNDISK_GET_LEN(fileLength, vnode);
853     Vvnodesize += fileLength;
854     Vvnodesize_k += fileLength / 1024;
855     if (dsizeOnly)
856         return;
857     if (orphaned && (fileLength == 0 || vnode->parent || !offset))
858         return;
859     printf
860         ("%10lld Vnode %u.%u.%u cloned: %u, length: %llu linkCount: %d parent: %u",
861          (long long)offset, vnodeNumber, vnode->uniquifier, vnode->dataVersion,
862          vnode->cloned, (afs_uintmax_t) fileLength, vnode->linkCount,
863          vnode->parent);
864     if (DumpInodeNumber)
865         printf(" inode: %s", PrintInode(NULL, ino));
866     if (DumpDate)
867         printf(" ServerModTime: %s", date(vnode->serverModifyTime));
868 #if defined(AFS_NAMEI_ENV)
869     if (PrintFileNames) {
870         IH_INIT(ihtmpp, V_device(vp), V_parentId(vp), ino);
871         namei_HandleToName(&filename, ihtmpp);
872 #if !defined(AFS_NT40_ENV)
873         printf(" UFS-Filename: %s", filename.n_path);
874 #else
875         printf(" NTFS-Filename: %s", filename.n_path);
876 #endif
877     }
878 #endif
879     printf("\n");
880 }