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