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