6b92e212960722f84c5073daf414dab5a5b49a26
[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 RCSID
21     ("$Header$");
22
23 #include <ctype.h>
24 #include <errno.h>
25 #include <sys/stat.h>
26 #include <stdio.h>
27 #include <string.h>
28 #ifdef AFS_NT40_ENV
29 #include <fcntl.h>
30 #include <time.h>
31 #include <io.h>
32 #else
33 #include <sys/param.h>
34 #include <sys/file.h>
35 #include <sys/time.h>
36 #endif
37 #include <afs/cmd.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 "volinodes.h"
52 #include <afs/afssyscalls.h>
53
54 #ifdef _AIX
55 #include <time.h>
56 #endif
57
58 #include <dirent.h>
59
60 #ifdef O_LARGEFILE
61 #define afs_stat        stat64
62 #define afs_fstat       fstat64
63 #define afs_open        open64
64 #else /* !O_LARGEFILE */
65 #define afs_stat        stat
66 #define afs_fstat       fstat
67 #define afs_open        open
68 #endif /* !O_LARGEFILE */
69
70 int DumpVnodes = 0;             /* Dump everything, i.e. summary of all vnodes */
71 int DumpInodeNumber = 0;        /* Dump inode numbers with vnodes */
72 int DumpDate = 0;               /* Dump vnode date (server modify date) with vnode */
73 int InodeTimes = 0;             /* Dump some of the dates associated with inodes */
74 #if defined(AFS_NAMEI_ENV)
75 int PrintFileNames = 0;
76 #endif
77 int online = 0;
78 int dheader = 0;
79 int dsizeOnly = 0, totvolsize = 0, Vauxsize = 0, Vdiskused = 0, Vvnodesize =
80     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 DiskPartition *partP);
90 void HandleVolume(struct DiskPartition *partP, char *name);
91 struct DiskPartition *FindCurrentPartition(void);
92 Volume *AttachVolume(struct DiskPartition *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 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 DiskPartition * 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)
220 {
221     register struct cmd_item *ti;
222     int err = 0;
223     int volumeId = 0;
224     char *partName = 0;
225     struct DiskPartition *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 = atoi(ti->data);
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                            (unsigned long)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 DiskPartition *
336 FindCurrentPartition()
337 {
338     int dr = _getdrive();
339     struct DiskPartition *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 DiskPartition *
353 FindCurrentPartition()
354 {
355     char partName[1024];
356     char tmp = '\0';
357     char *p;
358     struct DiskPartition *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 DiskPartition *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 DiskPartition *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 (chdir(p) == -1) {
416         printf("Can't chdir to partition %s; giving up\n", p);
417         exit(1);
418     }
419     if ((dirp = opendir(".")) == NULL) {
420         printf("Can't read directory %s; giving up\n", p);
421         exit(1);
422     }
423     if (dsizeOnly && !saveinodes)
424         printf
425             ("Volume-Id\t  Volsize  Auxsize Inodesize  AVolsize SizeDiff                (VolName)\n");
426     while ((dp = readdir(dirp))) {
427         p = (char *)strrchr(dp->d_name, '.');
428         if (p != NULL && strcmp(p, VHDREXT) == 0) {
429             HandleVolume(partP, dp->d_name);
430             Totvolsize += totvolsize;
431             TVauxsize += Vauxsize;
432             TVvnodesize += Vvnodesize;
433             TVdiskused += Vdiskused;
434             nvols++;
435         }
436     }
437     closedir(dirp);
438     if (dsizeOnly) {
439         printf("\nPart Totals  %12d%9d%10d%10d%9d (%d volumes)\n\n",
440                TVdiskused, TVauxsize, TVvnodesize, Totvolsize,
441                Totvolsize - TVdiskused, nvols);
442     }
443 }
444
445
446 void
447 HandleVolume(struct DiskPartition *dp, char *name)
448 {
449     struct VolumeHeader header;
450     struct VolumeDiskHeader diskHeader;
451     struct afs_stat status, stat;
452     register int fd;
453     Volume *vp;
454     IHandle_t *ih;
455     char headerName[1024];
456
457     if (online) {
458         printf("volinfo: -online not supported\n");
459         exit(1);
460     } else {
461         afs_int32 n;
462
463         (void)afs_snprintf(headerName, sizeof headerName, "%s/%s",
464                            VPartitionPath(dp), name);
465         if ((fd = afs_open(headerName, O_RDONLY)) == -1
466             || afs_fstat(fd, &status) == -1) {
467             printf("Volinfo: Cannot read volume header %s\n", name);
468             close(fd);
469             exit(1);
470         }
471         n = read(fd, &diskHeader, sizeof(diskHeader));
472
473         if (n != sizeof(diskHeader)
474             || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
475             printf("Volinfo: Error reading volume header %s\n", name);
476             exit(1);
477         }
478         if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
479             printf
480                 ("Volinfo: Volume %s, version number is incorrect; volume needs salvage\n",
481                  name);
482             exit(1);
483         }
484         DiskToVolumeHeader(&header, &diskHeader);
485
486         if (dheader) {
487             FdHandle_t *fdP;
488             int size = 0;
489             int code;
490
491             if (afs_fstat(fd, &stat) == -1) {
492                 perror("stat");
493                 exit(1);
494             }
495             if (!dsizeOnly && !saveinodes) {
496                 printf("Volume header (size = %d):\n", size = stat.st_size);
497                 printf("\tstamp\t= 0x%x\n", header.stamp.version);
498                 printf("\tVolId\t= %u\n", header.id);
499             }
500
501             IH_INIT(ih, dp->device, header.id, header.volumeInfo);
502             fdP = IH_OPEN(ih);
503             if (fdP == NULL) {
504                 perror("opening volume info");
505                 exit(1);
506             }
507             code = FDH_SIZE(fdP);
508             if (code == -1) {
509                 perror("fstat");
510                 exit(1);
511             }
512             FDH_REALLYCLOSE(fdP);
513             IH_RELEASE(ih);
514             size += code;
515             if (!dsizeOnly && !saveinodes) {
516                 printf("\tparent\t= %u\n", header.parent);
517                 printf("\tInfo inode\t= %s (size = %d)\n",
518                        PrintInode(NULL, header.volumeInfo), code);
519             }
520
521             IH_INIT(ih, dp->device, header.id, header.smallVnodeIndex);
522             fdP = IH_OPEN(ih);
523             if (fdP == NULL) {
524                 perror("opening small vnode index");
525                 exit(1);
526             }
527             code = FDH_SIZE(fdP);
528             if (code == -1) {
529                 perror("fstat");
530                 exit(1);
531             }
532             FDH_REALLYCLOSE(fdP);
533             IH_RELEASE(ih);
534             size += code;
535             if (!dsizeOnly && !saveinodes) {
536                 printf("\tSmall inode\t= %s (size = %d)\n",
537                        PrintInode(NULL, header.smallVnodeIndex), code);
538             }
539
540             IH_INIT(ih, dp->device, header.id, header.largeVnodeIndex);
541             fdP = IH_OPEN(ih);
542             if (fdP == NULL) {
543                 perror("opening large vnode index");
544                 exit(1);
545             }
546             code = FDH_SIZE(fdP);
547             if (code == -1) {
548                 perror("fstat");
549                 exit(1);
550             }
551             FDH_REALLYCLOSE(fdP);
552             IH_RELEASE(ih);
553             size += code;
554             if (!dsizeOnly && !saveinodes) {
555                 printf("\tLarge inode\t= %s (size = %d)\n",
556                        PrintInode(NULL, header.largeVnodeIndex), code);
557 #ifndef AFS_NT40_ENV
558                 printf("Total aux volume size = %d\n\n", size);
559 #endif
560             }
561 #ifdef AFS_NAMEI_ENV
562             IH_INIT(ih, dp->device, header.id, header.linkTable);
563             fdP = IH_OPEN(ih);
564             if (fdP == NULL) {
565                 perror("opening link table index");
566                 exit(1);
567             }
568             code = FDH_SIZE(fdP);
569             if (code == -1) {
570                 perror("fstat");
571                 exit(1);
572             }
573             FDH_REALLYCLOSE(fdP);
574             IH_RELEASE(ih);
575             size += code;
576             if (!dsizeOnly && !saveinodes) {
577                 printf("\tLink inode\t= %s (size = %d)\n",
578                        PrintInode(NULL, header.linkTable), code);
579                 printf("Total aux volume size = %d\n\n", size);
580             }
581 #endif
582             Vauxsize = size;
583         }
584         close(fd);
585         vp = AttachVolume(dp, name, &header);
586         if (!vp) {
587             printf("Volinfo: Error attaching volume header %s\n", name);
588             exit(1);
589         }
590     }
591     PrintHeader(vp);
592     if (DumpVnodes) {
593         if (!dsizeOnly && !saveinodes)
594             printf("\nLarge vnodes (directories)\n");
595         PrintVnodes(vp, vLarge);
596         if (!dsizeOnly && !saveinodes) {
597             printf("\nSmall vnodes(files, symbolic links)\n");
598             fflush(stdout);
599         }
600         if (saveinodes)
601             printf("Saving all volume files to current directory ...\n");
602         PrintVnodes(vp, vSmall);
603     }
604     if (dsizeOnly) {
605         Vauxsize = Vauxsize / 1024;
606         Vvnodesize = Vvnodesize / 1024;
607         totvolsize = Vauxsize + Vvnodesize;
608         if (saveinodes)
609             printf
610                 ("Volume-Id\t  Volsize  Auxsize Inodesize  AVolsize SizeDiff                (VolName)\n");
611
612         printf("%u\t%9d%9d%10d%10d%9d\t%24s\n", V_id(vp), Vdiskused, Vauxsize,
613                Vvnodesize, totvolsize, totvolsize - Vdiskused, V_name(vp));
614     }
615 }
616
617
618 main(int argc, char **argv)
619 {
620     register struct cmd_syndesc *ts;
621     afs_int32 code;
622
623     ts = cmd_CreateSyntax(NULL, handleit, 0, "Dump volume's internal state");
624     cmd_AddParm(ts, "-online", CMD_FLAG, CMD_OPTIONAL,
625                 "Get info from running fileserver");
626     cmd_AddParm(ts, "-vnode", CMD_FLAG, CMD_OPTIONAL, "Dump vnode info");
627     cmd_AddParm(ts, "-date", CMD_FLAG, CMD_OPTIONAL,
628                 "Also dump vnode's mod date");
629     cmd_AddParm(ts, "-inode", CMD_FLAG, CMD_OPTIONAL,
630                 "Dump vnode's inode number");
631     cmd_AddParm(ts, "-itime", CMD_FLAG, CMD_OPTIONAL,
632                 "Dump special inode's mod times");
633     cmd_AddParm(ts, "-part", CMD_LIST, CMD_OPTIONAL,
634                 "AFS partition name (default current partition)");
635     cmd_AddParm(ts, "-volumeid", CMD_LIST, CMD_OPTIONAL, "Volume id");
636     cmd_AddParm(ts, "-header", CMD_FLAG, CMD_OPTIONAL,
637                 "Dump volume's header info");
638     cmd_AddParm(ts, "-sizeOnly", CMD_FLAG, CMD_OPTIONAL,
639                 "Dump volume's size");
640     cmd_AddParm(ts, "-fixheader", CMD_FLAG, CMD_OPTIONAL,
641                 "Try to fix header");
642     cmd_AddParm(ts, "-saveinodes", CMD_FLAG, CMD_OPTIONAL,
643                 "Try to save all inodes");
644     cmd_AddParm(ts, "-orphaned", CMD_FLAG, CMD_OPTIONAL,
645                 "List all dir/files without a parent");
646 #if defined(AFS_NAMEI_ENV)
647     cmd_AddParm(ts, "-filenames", CMD_FLAG, CMD_OPTIONAL, "Print filenames");
648 #endif
649     code = cmd_Dispatch(argc, argv);
650     return code;
651 }
652
653 #define typestring(type) (type == RWVOL? "read/write": type == ROVOL? "readonly": type == BACKVOL? "backup": "unknown")
654
655 void
656 PrintHeader(register Volume * vp)
657 {
658     Vdiskused = V_diskused(vp);
659     if (dsizeOnly || saveinodes)
660         return;
661     printf("Volume header for volume %u (%s)\n", V_id(vp), V_name(vp));
662     printf("stamp.magic = %x, stamp.version = %u\n", V_stamp(vp).magic,
663            V_stamp(vp).version);
664     printf
665         ("inUse = %d, inService = %d, blessed = %d, needsSalvaged = %d, dontSalvage = %d\n",
666          V_inUse(vp), V_inService(vp), V_blessed(vp), V_needsSalvaged(vp),
667          V_dontSalvage(vp));
668     printf
669         ("type = %d (%s), uniquifier = %u, needsCallback = %d, destroyMe = %x\n",
670          V_type(vp), typestring(V_type(vp)), V_uniquifier(vp),
671          V_needsCallback(vp), V_destroyMe(vp));
672     printf
673         ("id = %u, parentId = %u, cloneId = %u, backupId = %u, restoredFromId = %u\n",
674          V_id(vp), V_parentId(vp), V_cloneId(vp), V_backupId(vp),
675          V_restoredFromId(vp));
676     printf
677         ("maxquota = %d, minquota = %d, maxfiles = %d, filecount = %d, diskused = %d\n",
678          V_maxquota(vp), V_minquota(vp), V_maxfiles(vp), V_filecount(vp),
679          V_diskused(vp));
680     printf("creationDate = %s, copyDate = %s\n", date(V_creationDate(vp)),
681            date(V_copyDate(vp)));
682     printf("backupDate = %s, expirationDate = %s\n", date(V_backupDate(vp)),
683            date(V_expirationDate(vp)));
684     printf("accessDate = %s, updateDate = %s\n", date(V_accessDate(vp)),
685            date(V_updateDate(vp)));
686     printf("owner = %u, accountNumber = %u\n", V_owner(vp),
687            V_accountNumber(vp));
688     printf
689         ("dayUse = %u; week = (%u, %u, %u, %u, %u, %u, %u), dayUseDate = %s\n",
690          V_dayUse(vp), V_weekUse(vp)[0], V_weekUse(vp)[1], V_weekUse(vp)[2],
691          V_weekUse(vp)[3], V_weekUse(vp)[4], V_weekUse(vp)[5],
692          V_weekUse(vp)[6], date(V_dayUseDate(vp)));
693 }
694
695 /* GetFileInfo
696  * OS independent file info. Die on failure.
697  */
698 #ifdef AFS_NT40_ENV
699 char *
700 NT_date(FILETIME * ft)
701 {
702     static char result[8][64];
703     static int next = 0;
704     SYSTEMTIME st;
705     FILETIME lft;
706
707     if (!FileTimeToLocalFileTime(ft, &lft)
708         || !FileTimeToSystemTime(&lft, &st)) {
709         printf("Time conversion failed.\n");
710         exit(1);
711     }
712     sprintf(result[next = ((next + 1) & 7)], "%4d/%02d/%02d.%2d:%2d:%2d",
713             st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
714     return result[next];
715 }
716 #endif
717
718 static void
719 GetFileInfo(FD_t fd, int *size, char **ctime, char **mtime, char **atime)
720 {
721 #ifdef AFS_NT40_ENV
722     BY_HANDLE_FILE_INFORMATION fi;
723     if (!GetFileInformationByHandle(fd, &fi)) {
724         printf("GetFileInformationByHandle failed, exiting\n");
725         exit(1);
726     }
727     *size = (int)fi.nFileSizeLow;
728     *ctime = "N/A";
729     *mtime = NT_date(&fi.ftLastWriteTime);
730     *atime = NT_date(&fi.ftLastAccessTime);
731 #else
732     struct afs_stat status;
733     if (afs_fstat(fd, &status) == -1) {
734         printf("fstat failed %d\n", errno);
735         exit(1);
736     }
737     *size = (int)status.st_size;
738     *ctime = date(status.st_ctime);
739     *mtime = date(status.st_mtime);
740     *atime = date(status.st_atime);
741 #endif
742 }
743
744 void
745 PrintVnodes(Volume * vp, VnodeClass class)
746 {
747     afs_int32 diskSize =
748         (class == vSmall ? SIZEOF_SMALLDISKVNODE : SIZEOF_LARGEDISKVNODE);
749     char buf[SIZEOF_LARGEDISKVNODE];
750     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
751     StreamHandle_t *file;
752     register int vnodeIndex, nVnodes, offset = 0;
753     Inode ino;
754     IHandle_t *ih = vp->vnodeIndex[class].handle;
755     FdHandle_t *fdP;
756     int size;
757     char *ctime, *atime, *mtime;
758     char nfile[50], buffer[256];
759     int total, ofd, len, code, bad = 0;
760
761     fdP = IH_OPEN(ih);
762     if (fdP == NULL) {
763         printf("open failed\n");
764         exit(1);
765     }
766
767     file = FDH_FDOPEN(fdP, "r");
768     if (!file) {
769         printf("fdopen failed\n");
770         exit(1);
771     }
772
773     GetFileInfo(fdP->fd_fd, &size, &ctime, &atime, &mtime);
774     if (InodeTimes && !dsizeOnly) {
775         printf("ichanged : %s\nimodified: %s\niaccessed: %s\n\n", ctime,
776                mtime, atime);
777     }
778
779     nVnodes = (size / diskSize) - 1;
780     if (nVnodes > 0) {
781         STREAM_SEEK(file, diskSize, 0);
782     } else
783         nVnodes = 0;
784
785     for (vnodeIndex = 0;
786          nVnodes && STREAM_READ(vnode, diskSize, 1, file) == 1;
787          nVnodes--, vnodeIndex++, offset += diskSize) {
788
789         ino = VNDISK_GET_INO(vnode);
790         if (saveinodes) {
791             if (VALID_INO(ino) && (class == vSmall)) {
792                 IHandle_t *ih1;
793                 FdHandle_t *fdP1;
794                 IH_INIT(ih1, V_device(vp), V_parentId(vp), ino);
795                 fdP1 = IH_OPEN(ih1);
796                 if (fdP1 == NULL) {
797                     printf("Can't open inode %s error %d (ignored)\n",
798                            PrintInode(NULL, ino), errno);
799                     continue;
800                 }
801                 (void)afs_snprintf(nfile, sizeof nfile, "TmpInode.%s",
802                                    PrintInode(NULL, ino));
803                 ofd = afs_open(nfile, O_CREAT | O_RDWR | O_TRUNC, 0600);
804                 if (ofd < 0) {
805                     printf("Can't create file %s; error %d (ignored)\n",
806                            nfile, errno);
807                     continue;
808                 }
809                 total = bad = 0;
810                 while (1) {
811                     len = FDH_READ(fdP1, buffer, sizeof(buffer));
812                     if (len < 0) {
813                         FDH_REALLYCLOSE(fdP1);
814                         IH_RELEASE(ih1);
815                         close(ofd);
816                         unlink(nfile);
817                         printf
818                             ("Error while reading from inode %s (%d - ignored)\n",
819                              PrintInode(NULL, ino), errno);
820                         bad = 1;
821                         break;
822                     }
823                     if (len == 0)
824                         break;  /* No more input */
825                     code = write(ofd, buffer, len);
826                     if (code != len) {
827                         FDH_REALLYCLOSE(fdP1);
828                         IH_RELEASE(ih1);
829                         close(ofd);
830                         unlink(nfile);
831                         printf
832                             ("Error while writing to \"%s\" (%d - ignored)\n",
833                              nfile, errno);
834                         bad = 1;
835                         break;
836                     }
837                     total += len;
838                 }
839                 if (bad)
840                     continue;
841                 FDH_REALLYCLOSE(fdP1);
842                 IH_RELEASE(ih1);
843                 close(ofd);
844                 printf("... Copied inode %s to file %s (%d bytes)\n",
845                        PrintInode(NULL, ino), nfile, total);
846             }
847         } else {
848 #if defined(AFS_NAMEI_ENV)
849             PrintVnode(offset, vnode,
850                        bitNumberToVnodeNumber(vnodeIndex, class), ino, vp);
851 #else
852             PrintVnode(offset, vnode,
853                        bitNumberToVnodeNumber(vnodeIndex, class), ino);
854 #endif
855         }
856     }
857     STREAM_CLOSE(file);
858     FDH_CLOSE(fdP);
859 }
860
861 #if defined(AFS_NAMEI_ENV)
862 void
863 PrintVnode(int offset, VnodeDiskObject * vnode, VnodeId vnodeNumber,
864            Inode ino, Volume * vp)
865 #else
866 void
867 PrintVnode(int offset, VnodeDiskObject * vnode, VnodeId vnodeNumber,
868            Inode ino)
869 #endif
870 {
871 #if defined(AFS_NAMEI_ENV)
872     IHandle_t *ihtmpp;
873 #if !defined(AFS_NT40_ENV)
874     namei_t filename;
875 #else
876     char filename[MAX_PATH];
877 #endif
878 #endif
879     afs_fsize_t fileLength;
880
881     VNDISK_GET_LEN(fileLength, vnode);
882     Vvnodesize += fileLength;
883     if (dsizeOnly)
884         return;
885     if (orphaned && (fileLength == 0 || vnode->parent || !offset))
886         return;
887     printf
888         ("%10d Vnode %u.%u.%u cloned: %u, length: %llu linkCount: %d parent: %u",
889          offset, vnodeNumber, vnode->uniquifier, vnode->dataVersion,
890          vnode->cloned, (afs_uintmax_t) fileLength, vnode->linkCount,
891          vnode->parent);
892     if (DumpInodeNumber)
893         printf(" inode: %s", PrintInode(NULL, ino));
894     if (DumpDate)
895         printf(" ServerModTime: %s", date(vnode->serverModifyTime));
896 #if defined(AFS_NAMEI_ENV)
897     if (PrintFileNames) {
898         IH_INIT(ihtmpp, V_device(vp), V_parentId(vp), ino);
899 #if !defined(AFS_NT40_ENV)
900         namei_HandleToName(&filename, ihtmpp);
901         printf(" UFS-Filename: %s", filename.n_path);
902 #else
903         nt_HandleToName(filename, ihtmpp);
904         printf(" NTFS-Filename: %s", filename);
905 #endif
906     }
907 #endif
908     printf("\n");
909 }