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