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