Remove the RCSID macro
[openafs.git] / src / volser / vol-dump.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-dump.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
37 #include <rx/xdr.h>
38 #include <afs/afsint.h>
39 #include "nfs.h"
40 #include <afs/errors.h>
41 #include "lock.h"
42 #include "lwp.h"
43 #include <afs/afssyscalls.h>
44 #include "ihandle.h"
45 #include "vnode.h"
46 #include "volume.h"
47 #include "partition.h"
48 #include "viceinode.h"
49 #include <afs/afssyscalls.h>
50 #include "acl.h"
51
52 #ifdef HAVE_UNISTD_H
53 #include <unistd.h>
54 #endif
55
56 #ifdef HAVE_STDLIB_H
57 #include <stdlib.h>
58 #endif
59
60 #ifdef HAVE_FCNTL_H
61 #include <fcntl.h>
62 #endif
63
64 #ifdef _AIX
65 #include <time.h>
66 #endif
67
68 #include <dirent.h>
69
70 #include "volser/volser.h"
71 #include "volser/volint.h"
72 #include "volser/dump.h"
73
74 #define putint32(p, v)  *p++ = v>>24, *p++ = v>>16, *p++ = v>>8, *p++ = v
75 #define putshort(p, v) *p++ = v>>8, *p++ = v
76
77 #ifdef O_LARGEFILE
78 #define afs_stat        stat64
79 #define afs_fstat       fstat64
80 #define afs_open        open64
81 #else /* !O_LARGEFILE */
82 #define afs_stat        stat
83 #define afs_fstat       fstat
84 #define afs_open        open
85 #endif /* !O_LARGEFILE */
86
87 int VolumeChanged;              /* needed by physio - leave alone */
88 int verbose = 0;
89
90 /* Forward Declarations */
91 void HandleVolume(struct DiskPartition64 *partP, char *name, char *filename, int fromtime);
92 Volume *AttachVolume(struct DiskPartition64 *dp, char *volname,
93                      register struct VolumeHeader *header);
94 static void DoMyVolDump(Volume * vp, struct DiskPartition64 *dp,
95                         char *dumpfile, int fromtime);
96
97 #ifndef AFS_NT40_ENV
98 #include "AFS_component_version_number.c"
99 #endif
100
101 char name[VMAXPATHLEN];
102
103
104 int
105 ReadHdr1(IHandle_t * ih, char *to, int size, u_int magic, u_int version)
106 {
107     int bad = 0;
108     int code;
109
110     code = IH_IREAD(ih, 0, to, size);
111     if (code != size)
112         return -1;
113
114     return 0;
115 }
116
117
118 Volume *
119 AttachVolume(struct DiskPartition64 * dp, char *volname,
120              register struct VolumeHeader * header)
121 {
122     register Volume *vp;
123     afs_int32 ec = 0;
124
125     vp = (Volume *) calloc(1, sizeof(Volume));
126     vp->specialStatus = 0;
127     vp->device = dp->device;
128     vp->partition = dp;
129     IH_INIT(vp->vnodeIndex[vLarge].handle, dp->device, header->parent,
130             header->largeVnodeIndex);
131     IH_INIT(vp->vnodeIndex[vSmall].handle, dp->device, header->parent,
132             header->smallVnodeIndex);
133     IH_INIT(vp->diskDataHandle, dp->device, header->parent,
134             header->volumeInfo);
135     IH_INIT(V_linkHandle(vp), dp->device, header->parent, header->linkTable);
136     vp->cacheCheck = 0;         /* XXXX */
137     vp->shuttingDown = 0;
138     vp->goingOffline = 0;
139     vp->nUsers = 1;
140     vp->header = (struct volHeader *)calloc(1, sizeof(*vp->header));
141     ec = ReadHdr1(V_diskDataHandle(vp), (char *)&V_disk(vp),
142                   sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
143     if (!ec) {
144         struct IndexFileHeader iHead;
145         ec = ReadHdr1(vp->vnodeIndex[vSmall].handle, (char *)&iHead,
146                       sizeof(iHead), SMALLINDEXMAGIC, SMALLINDEXVERSION);
147     }
148     if (!ec) {
149         struct IndexFileHeader iHead;
150         ec = ReadHdr1(vp->vnodeIndex[vLarge].handle, (char *)&iHead,
151                       sizeof(iHead), LARGEINDEXMAGIC, LARGEINDEXVERSION);
152     }
153 #ifdef AFS_NAMEI_ENV
154     if (!ec) {
155         struct versionStamp stamp;
156         ec = ReadHdr1(V_linkHandle(vp), (char *)&stamp, sizeof(stamp),
157                       LINKTABLEMAGIC, LINKTABLEVERSION);
158     }
159 #endif
160     if (ec)
161         return (Volume *) 0;
162     return vp;
163 }
164
165
166 static int
167 handleit(struct cmd_syndesc *as, void *arock)
168 {
169     register struct cmd_item *ti;
170     int err = 0;
171     afs_uint32 volumeId = 0;
172     char *partName = 0;
173     char *fileName = NULL;
174     struct DiskPartition64 *partP = NULL;
175     char name1[128];
176     char tmpPartName[20];
177     int fromtime = 0;
178     afs_int32 code;
179
180
181 #ifndef AFS_NT40_ENV
182 #if 0
183     if (geteuid() != 0) {
184         fprintf(stderr, "voldump must be run as root; sorry\n");
185         exit(1);
186     }
187 #endif
188 #endif
189
190     if ((ti = as->parms[0].items))
191         partName = ti->data;
192     if ((ti = as->parms[1].items))
193         volumeId = (afs_uint32)atoi(ti->data);
194     if ((ti = as->parms[2].items))
195         fileName = ti->data;
196     if ((ti = as->parms[3].items))
197         verbose = 1;
198     if (as->parms[4].items && strcmp(as->parms[4].items->data, "0")) {
199         code = ktime_DateToInt32(as->parms[4].items->data, &fromtime);
200         if (code) {
201             fprintf(STDERR, "failed to parse date '%s' (error=%d))\n",
202                 as->parms[4].items->data, code);
203                 return code;
204         }
205     }
206
207     DInit(10);
208
209     err = VAttachPartitions();
210     if (err) {
211         fprintf(stderr, "%d partitions had errors during attach.\n", err);
212     }
213
214     if (partName) {
215         if (strlen(partName) == 1) {
216             if (partName[0] >= 'a' && partName[0] <= 'z') {
217                 strcpy(tmpPartName, "/vicepa");
218                 tmpPartName[6] = partName[0];
219                 partP = VGetPartition(tmpPartName, 0);
220             }
221         } else {
222             partP = VGetPartition(partName, 0);
223         }
224         if (!partP) {
225             fprintf(stderr,
226                     "%s is not an AFS partition name on this server.\n",
227                     partName);
228             exit(1);
229         }
230     }
231
232     if (!volumeId) {
233         fprintf(stderr, "Must specify volume id!\n");
234         exit(1);
235     }
236
237     if (!partP) {
238         fprintf(stderr, "must specify vice partition.\n");
239         exit(1);
240     }
241
242     (void)afs_snprintf(name1, sizeof name1, VFORMAT, (unsigned long)volumeId);
243     HandleVolume(partP, name1, fileName, fromtime);
244     return 0;
245 }
246
247 void
248 HandleVolume(struct DiskPartition64 *dp, char *name, char *filename, int fromtime)
249 {
250     struct VolumeHeader header;
251     struct VolumeDiskHeader diskHeader;
252     struct afs_stat status, stat;
253     register int fd;
254     Volume *vp;
255     IHandle_t *ih;
256     char headerName[1024];
257
258     afs_int32 n;
259
260     (void)afs_snprintf(headerName, sizeof headerName, "%s/%s",
261                        VPartitionPath(dp), name);
262     if ((fd = afs_open(headerName, O_RDONLY)) == -1
263         || afs_fstat(fd, &status) == -1) {
264         fprintf(stderr, "Cannot read volume header %s\n", name);
265         close(fd);
266         exit(1);
267     }
268     n = read(fd, &diskHeader, sizeof(diskHeader));
269
270     if (n != sizeof(diskHeader)
271         || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
272         fprintf(stderr, "Error reading volume header %s\n", name);
273         exit(1);
274     }
275     if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
276         fprintf(stderr,
277                 "Volume %s, version number is incorrect; volume needs salvage\n",
278                 name);
279         exit(1);
280     }
281     DiskToVolumeHeader(&header, &diskHeader);
282
283     close(fd);
284     vp = AttachVolume(dp, name, &header);
285     if (!vp) {
286         fprintf(stderr, "Error attaching volume header %s\n", name);
287         exit(1);
288     }
289
290     DoMyVolDump(vp, dp, filename, fromtime);
291 }
292
293
294 int
295 main(int argc, char **argv)
296 {
297     register struct cmd_syndesc *ts;
298     afs_int32 code;
299
300     VInitVolumePackage(volumeUtility, 5, 5, DONT_CONNECT_FS, 0);
301
302     ts = cmd_CreateSyntax(NULL, handleit, NULL,
303                           "Dump a volume to a 'vos dump' format file without using volserver");
304     cmd_AddParm(ts, "-part", CMD_LIST, CMD_OPTIONAL, "AFS partition name");
305     cmd_AddParm(ts, "-volumeid", CMD_LIST, CMD_OPTIONAL, "Volume id");
306     cmd_AddParm(ts, "-file", CMD_LIST, CMD_OPTIONAL, "Dump filename");
307     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL,
308                 "Trace dump progress (very verbose)");
309     cmd_AddParm(ts, "-time", CMD_SINGLE, CMD_OPTIONAL, "dump from time");
310     code = cmd_Dispatch(argc, argv);
311     return code;
312 }
313
314
315
316
317 static int
318 DumpDouble(int dumpfd, char tag, register afs_uint32 value1,
319            register afs_uint32 value2)
320 {
321     int res;
322     char tbuffer[9];
323     register byte *p = (unsigned char *)tbuffer;
324     *p++ = tag;
325     putint32(p, value1);
326     putint32(p, value2);
327
328     res = write(dumpfd, tbuffer, 9);
329     return ((res == 9) ? 0 : VOLSERDUMPERROR);
330 }
331
332 static int
333 DumpInt32(int dumpfd, char tag, register afs_uint32 value)
334 {
335     char tbuffer[5];
336     register byte *p = (unsigned char *)tbuffer;
337     *p++ = tag;
338     putint32(p, value);
339     return ((write(dumpfd, tbuffer, 5) == 5) ? 0 : VOLSERDUMPERROR);
340 }
341
342 static int
343 DumpString(int dumpfd, char tag, register char *s)
344 {
345     register int n;
346     int code = 0;
347     code = write(dumpfd, &tag, 1);
348     if (code != 1)
349         return VOLSERDUMPERROR;
350     n = strlen(s) + 1;
351     code = write(dumpfd, s, n);
352     if (code != n)
353         return VOLSERDUMPERROR;
354     return 0;
355 }
356
357
358 static int
359 DumpArrayInt32(int dumpfd, char tag, register afs_uint32 * array,
360                register int nelem)
361 {
362     char tbuffer[4];
363     register afs_uint32 v;
364     int code = 0;
365     register byte *p = (unsigned char *)tbuffer;
366     *p++ = tag;
367     putshort(p, nelem);
368     code = write(dumpfd, tbuffer, 3);
369     if (code != 3)
370         return VOLSERDUMPERROR;
371     while (nelem--) {
372         p = (unsigned char *)tbuffer;
373         v = *array++;           /*this was register */
374
375         putint32(p, v);
376         code = write(dumpfd, tbuffer, 4);
377         if (code != 4)
378             return VOLSERDUMPERROR;
379     }
380     return 0;
381 }
382
383
384
385
386 static int
387 DumpDumpHeader(int dumpfd, register Volume * vp, afs_int32 fromtime)
388 {
389     int code = 0;
390     afs_int32 dumpTimes[2];
391
392     if (verbose)
393         fprintf(stderr, "dumping dump header\n");
394
395     if (!code)
396         code = DumpDouble(dumpfd, D_DUMPHEADER, DUMPBEGINMAGIC, DUMPVERSION);
397
398     if (!code)
399         code = DumpInt32(dumpfd, 'v', V_id(vp));
400
401     if (!code)
402         code = DumpString(dumpfd, 'n', V_name(vp));
403
404     dumpTimes[0] = fromtime;
405     dumpTimes[1] = V_backupDate(vp);    /* Until the time the clone was made */
406     if (!code)
407         code = DumpArrayInt32(dumpfd, 't', (afs_uint32 *) dumpTimes, 2);
408
409     return code;
410 }
411
412
413 static int
414 DumpEnd(int dumpfd)
415 {
416     return (DumpInt32(dumpfd, D_DUMPEND, DUMPENDMAGIC));
417 }
418
419 static int
420 DumpByte(int dumpfd, char tag, byte value)
421 {
422     char tbuffer[2];
423     register byte *p = (unsigned char *)tbuffer;
424     *p++ = tag;
425     *p = value;
426     return ((write(dumpfd, tbuffer, 2) == 2) ? 0 : VOLSERDUMPERROR);
427 }
428
429 static int
430 DumpTag(int dumpfd, register int tag)
431 {
432     char p;
433
434     p = tag;
435     return ((write(dumpfd, &p, 1) == 1) ? 0 : VOLSERDUMPERROR);
436
437 }
438
439 static int
440 DumpBool(int dumpfd, char tag, unsigned int value)
441 {
442     char tbuffer[2];
443     register byte *p = (unsigned char *)tbuffer;
444     *p++ = tag;
445     *p = value;
446     return ((write(dumpfd, tbuffer, 2) == 2) ? 0 : VOLSERDUMPERROR);
447 }
448
449
450
451 static int
452 DumpVolumeHeader(int dumpfd, register Volume * vp)
453 {
454     int code = 0;
455
456     if (verbose)
457         fprintf(stderr, "dumping volume header\n");
458
459     if (!code)
460         code = DumpTag(dumpfd, D_VOLUMEHEADER);
461     if (!code)
462         code = DumpInt32(dumpfd, 'i', V_id(vp));
463     if (!code)
464         code = DumpInt32(dumpfd, 'v', V_stamp(vp).version);
465     if (!code)
466         code = DumpString(dumpfd, 'n', V_name(vp));
467     if (!code)
468         code = DumpBool(dumpfd, 's', V_inService(vp));
469     if (!code)
470         code = DumpBool(dumpfd, 'b', V_blessed(vp));
471     if (!code)
472         code = DumpInt32(dumpfd, 'u', V_uniquifier(vp));
473     if (!code)
474         code = DumpByte(dumpfd, 't', (byte) V_type(vp));
475     if (!code)
476         code = DumpInt32(dumpfd, 'p', V_parentId(vp));
477     if (!code)
478         code = DumpInt32(dumpfd, 'c', V_cloneId(vp));
479     if (!code)
480         code = DumpInt32(dumpfd, 'q', V_maxquota(vp));
481     if (!code)
482         code = DumpInt32(dumpfd, 'm', V_minquota(vp));
483     if (!code)
484         code = DumpInt32(dumpfd, 'd', V_diskused(vp));
485     if (!code)
486         code = DumpInt32(dumpfd, 'f', V_filecount(vp));
487     if (!code)
488         code = DumpInt32(dumpfd, 'a', V_accountNumber(vp));
489     if (!code)
490         code = DumpInt32(dumpfd, 'o', V_owner(vp));
491     if (!code)
492         code = DumpInt32(dumpfd, 'C', V_creationDate(vp));      /* Rw volume creation date */
493     if (!code)
494         code = DumpInt32(dumpfd, 'A', V_accessDate(vp));
495     if (!code)
496         code = DumpInt32(dumpfd, 'U', V_updateDate(vp));
497     if (!code)
498         code = DumpInt32(dumpfd, 'E', V_expirationDate(vp));
499     if (!code)
500         code = DumpInt32(dumpfd, 'B', V_backupDate(vp));        /* Rw volume backup clone date */
501     if (!code)
502         code = DumpString(dumpfd, 'O', V_offlineMessage(vp));
503
504     /*
505      * We do NOT dump the detailed volume statistics residing in the old
506      * motd field, since we cannot tell from the info in a dump whether
507      * statistics data has been put there.  Instead, we dump a null string,
508      * just as if that was what the motd contained.
509      */
510     if (!code)
511         code = DumpString(dumpfd, 'M', "");
512     if (!code)
513         code =
514             DumpArrayInt32(dumpfd, 'W', (afs_uint32 *) V_weekUse(vp),
515                            sizeof(V_weekUse(vp)) / sizeof(V_weekUse(vp)[0]));
516     if (!code)
517         code = DumpInt32(dumpfd, 'D', V_dayUseDate(vp));
518     if (!code)
519         code = DumpInt32(dumpfd, 'Z', V_dayUse(vp));
520     return code;
521 }
522
523 static int
524 DumpShort(int dumpfd, char tag, unsigned int value)
525 {
526     char tbuffer[3];
527     register byte *p = (unsigned char *)tbuffer;
528     *p++ = tag;
529     *p++ = value >> 8;
530     *p = value;
531     return ((write(dumpfd, tbuffer, 3) == 3) ? 0 : VOLSERDUMPERROR);
532 }
533
534 static int
535 DumpByteString(int dumpfd, char tag, register byte * bs, register int nbytes)
536 {
537     int code = 0;
538
539     code = write(dumpfd, &tag, 1);
540     if (code != 1)
541         return VOLSERDUMPERROR;
542     code = write(dumpfd, (char *)bs, nbytes);
543     if (code != nbytes)
544         return VOLSERDUMPERROR;
545     return 0;
546 }
547
548
549 static int
550 DumpFile(int dumpfd, int vnode, FdHandle_t * handleP,  struct VnodeDiskObject *v)
551 {
552     int code = 0, failed_seek = 0, failed_write = 0;
553     afs_int32 pad = 0;
554     afs_int32 offset = 0;
555     afs_sfsize_t n, nbytes, howMany, howBig;
556     byte *p;
557 #ifndef AFS_NT40_ENV
558     struct afs_stat status;
559 #endif
560     afs_sfsize_t size, tmpsize;
561 #ifdef  AFS_AIX_ENV
562 #include <sys/statfs.h>
563     struct statfs tstatfs;
564 #endif
565
566     if (verbose)
567         fprintf(stderr, "dumping file for vnode %d\n", vnode);
568
569 #ifdef AFS_NT40_ENV
570     howBig = _filelength(handleP->fd_fd);
571     howMany = 4096;
572
573 #else
574     afs_fstat(handleP->fd_fd, &status);
575     howBig = status.st_size;
576
577 #ifdef  AFS_AIX_ENV
578     /* Unfortunately in AIX valuable fields such as st_blksize are 
579      * gone from the stat structure.
580      */
581     fstatfs(handleP->fd_fd, &tstatfs);
582     howMany = tstatfs.f_bsize;
583 #else
584     howMany = status.st_blksize;
585 #endif /* AFS_AIX_ENV */
586 #endif /* AFS_NT40_ENV */
587
588
589     size = FDH_SIZE(handleP);
590
591     if (verbose)
592         fprintf(stderr, "  howBig = %u, howMany = %u, fdh size = %u\n",
593                 howBig, howMany, size);
594
595 #ifdef AFS_LARGEFILE_ENV
596     {
597         afs_uint32 hi, lo;
598         SplitInt64(size, hi, lo);
599         if (hi == 0L) {
600             code = DumpInt32(dumpfd, 'f', lo);
601         } else {
602             code = DumpDouble(dumpfd, 'h', hi, lo);
603         }
604     }
605 #else /* !AFS_LARGEFILE_ENV */
606     code = DumpInt32(dumpfd, 'f', size);
607 #endif /* !AFS_LARGEFILE_ENV */
608     if (code) {
609         return VOLSERDUMPERROR;
610     }
611
612     p = (unsigned char *)malloc(howMany);
613     if (!p) {
614         fprintf(stderr, "out of memory!\n");
615         return VOLSERDUMPERROR;
616     }
617
618     /* loop through whole file, while we still have bytes left, and no errors, in chunks of howMany bytes */
619     for (nbytes = size; (nbytes && !failed_write); nbytes -= howMany) {
620         if (nbytes < howMany)
621             howMany = nbytes;
622
623         /* Read the data - unless we know we can't */
624         n = (failed_seek ? 0 : FDH_READ(handleP, p, howMany));
625
626         /* If read any good data and we null padded previously, log the
627          * amount that we had null padded.
628          */
629         if ((n > 0) && pad) {
630             fprintf(stderr, "Null padding file %d bytes at offset %u\n", pad,
631                     offset);
632             pad = 0;
633         }
634
635         /* If didn't read enough data, null padd the rest of the buffer. This
636          * can happen if, for instance, the media has some bad spots. We don't
637          * want to quit the dump, so we start null padding.
638          */
639         if (n < howMany) {
640
641                 if (verbose) fprintf(stderr, "  read %u instead of %u bytes.\n", n, howMany);
642
643             /* Record the read error */
644             if (n < 0) {
645                 n = 0;
646                 fprintf(stderr, "Error %d reading inode %s for vnode %d\n",
647                         errno, PrintInode(NULL, handleP->fd_ih->ih_ino),
648                         vnode);
649             } else if (!pad) {
650                 fprintf(stderr, "Error reading inode %s for vnode %d\n",
651                         PrintInode(NULL, handleP->fd_ih->ih_ino), vnode);
652             }
653
654             /* Pad the rest of the buffer with zeros. Remember offset we started 
655              * padding. Keep total tally of padding.
656              */
657             memset(p + n, 0, howMany - n);
658             if (!pad)
659                 offset = (howBig - nbytes) + n;
660             pad += (howMany - n);
661
662             /* Now seek over the data we could not get. An error here means we
663              * can't do the next read.
664              */
665             failed_seek = FDH_SEEK(handleP, ((size - nbytes) + howMany), SEEK_SET);
666             if (failed_seek != ((size - nbytes) + howMany)) {
667                 if (failed_seek < 0) {
668                     fprintf(stderr,
669                             "Error %d seeking in inode %s for vnode %d\n",
670                             errno, PrintInode(NULL, handleP->fd_ih->ih_ino),
671                             vnode);
672                 } else {
673                     fprintf(stderr,
674                             "Error seeking in inode %s for vnode %d\n",
675                             PrintInode(NULL, handleP->fd_ih->ih_ino), vnode);
676                     failed_seek = -1;
677                 }
678             } else {
679                 failed_seek = 0;
680             }
681         }
682
683         /* Now write the data out */
684         if (write(dumpfd, (char *)p, howMany) != howMany)
685             failed_write = VOLSERDUMPERROR;
686     }
687
688     if (pad) {                  /* Any padding we hadn't reported yet */
689         fprintf(stderr, "Null padding file: %d bytes at offset %u\n", pad,
690                 offset);
691     }
692
693     free(p);
694     return failed_write;
695 }
696
697
698 static int
699 DumpVnode(int dumpfd, struct VnodeDiskObject *v, int volid, int vnodeNumber,
700           int dumpEverything, struct Volume *vp)
701 {
702     int code = 0;
703     IHandle_t *ihP;
704     FdHandle_t *fdP;
705
706     if (verbose)
707         fprintf(stderr, "dumping vnode %d\n", vnodeNumber);
708
709     if (!v || v->type == vNull)
710         return code;
711     if (!code)
712         code = DumpDouble(dumpfd, D_VNODE, vnodeNumber, v->uniquifier);
713     if (!dumpEverything)
714         return code;
715     if (!code)
716         code = DumpByte(dumpfd, 't', (byte) v->type);
717     if (!code)
718         code = DumpShort(dumpfd, 'l', v->linkCount);    /* May not need this */
719     if (!code)
720         code = DumpInt32(dumpfd, 'v', v->dataVersion);
721     if (!code)
722         code = DumpInt32(dumpfd, 'm', v->unixModifyTime);
723     if (!code)
724         code = DumpInt32(dumpfd, 'a', v->author);
725     if (!code)
726         code = DumpInt32(dumpfd, 'o', v->owner);
727     if (!code && v->group)
728         code = DumpInt32(dumpfd, 'g', v->group);        /* default group is 0 */
729     if (!code)
730         code = DumpShort(dumpfd, 'b', v->modeBits);
731     if (!code)
732         code = DumpInt32(dumpfd, 'p', v->parent);
733     if (!code)
734         code = DumpInt32(dumpfd, 's', v->serverModifyTime);
735     if (v->type == vDirectory) {
736         acl_HtonACL(VVnodeDiskACL(v));
737         if (!code)
738             code =
739                 DumpByteString(dumpfd, 'A', (byte *) VVnodeDiskACL(v),
740                                VAclDiskSize(v));
741     }
742
743     if (VNDISK_GET_INO(v)) {
744         IH_INIT(ihP, V_device(vp), V_parentId(vp), VNDISK_GET_INO(v));
745         fdP = IH_OPEN(ihP);
746         if (fdP == NULL) {
747             fprintf(stderr,
748                     "Unable to open inode %s for vnode %u (volume %i); not dumped, error %d\n",
749                     PrintInode(NULL, VNDISK_GET_INO(v)), vnodeNumber, volid,
750                     errno);
751         }
752         else
753         {
754                 if (verbose)
755                     fprintf(stderr, "about to dump inode %s for vnode %u\n",
756                             PrintInode(NULL, VNDISK_GET_INO(v)), vnodeNumber);
757                 code = DumpFile(dumpfd, vnodeNumber, fdP, v);
758                 FDH_CLOSE(fdP);
759         }
760         IH_RELEASE(ihP);
761     }
762
763     if (verbose)
764         fprintf(stderr, "done dumping vnode %d\n", vnodeNumber);
765     return code;
766 }
767
768
769 static int
770 DumpVnodeIndex(int dumpfd, Volume * vp, VnodeClass class, afs_int32 fromtime,
771                int forcedump)
772 {
773     register int code = 0;
774     register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
775     char buf[SIZEOF_LARGEDISKVNODE];
776     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
777     StreamHandle_t *file;
778     FdHandle_t *fdP;
779     int size;
780     int flag;
781     int offset = 0;
782     register int vnodeIndex, nVnodes = 0;
783
784     fdP = IH_OPEN(vp->vnodeIndex[class].handle);
785     file = FDH_FDOPEN(fdP, "r+");
786     size = OS_SIZE(fdP->fd_fd);
787     nVnodes = (size / vcp->diskSize) - 1;
788
789     if (nVnodes > 0) {
790         STREAM_SEEK(file, vcp->diskSize, 0);
791     } else
792         nVnodes = 0;
793     for (vnodeIndex = 0;
794          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1 && !code;
795          nVnodes--, vnodeIndex++, offset += vcp->diskSize) {
796         flag = forcedump || (vnode->serverModifyTime >= fromtime);
797         /* Note:  the >= test is very important since some old volumes may not have
798          * a serverModifyTime.  For an epoch dump, this results in 0>=0 test, which
799          * does dump the file! */
800         if (verbose)
801             fprintf(stderr, "about to dump %s vnode %u (vnode offset = %u)\n",
802                         class == vSmall ? "vSmall" : "vLarge",
803                     bitNumberToVnodeNumber(vnodeIndex, class), offset);
804         if (!code)
805             code =
806                 DumpVnode(dumpfd, vnode, V_id(vp),
807                           bitNumberToVnodeNumber(vnodeIndex, class), flag,
808                           vp);
809     }
810     STREAM_CLOSE(file);
811     FDH_CLOSE(fdP);
812     return code;
813 }
814
815
816
817 /* A partial dump (no dump header) */
818 static int
819 DumpPartial(int dumpfd, register Volume * vp, afs_int32 fromtime,
820             int dumpAllDirs)
821 {
822     int code = 0;
823
824     if (verbose)
825         fprintf(stderr, "about to dump the volume header\n");
826     if (!code)
827         code = DumpVolumeHeader(dumpfd, vp);
828
829     if (verbose)
830         fprintf(stderr, "about to dump the large vnode index\n");
831     if (!code)
832         code = DumpVnodeIndex(dumpfd, vp, vLarge, fromtime, dumpAllDirs);
833
834     if (verbose)
835         fprintf(stderr, "about to dump the small vnode index\n");
836     if (!code)
837         code = DumpVnodeIndex(dumpfd, vp, vSmall, fromtime, 0);
838     return code;
839 }
840
841
842
843 static void
844 DoMyVolDump(Volume * vp, struct DiskPartition64 *dp, char *dumpfile, int fromtime)
845 {
846     int code = 0;
847     int dumpAllDirs = 0;
848     int dumpfd = 0;
849
850     if (dumpfile) {
851         unlink(dumpfile);
852         dumpfd =
853             afs_open(dumpfile, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
854         if (dumpfd < 0) {
855             fprintf(stderr, "Failed to open dump file! Exiting.\n");
856             exit(1);
857         }
858     } else {
859         dumpfd = 1;             /* stdout */
860     }
861
862     if (verbose)
863         fprintf(stderr, "about to dump the dump header\n");
864     if (!code)
865         code = DumpDumpHeader(dumpfd, vp, fromtime);
866
867     if (verbose)
868         fprintf(stderr, "about to dump volume contents\n");
869     if (!code)
870         code = DumpPartial(dumpfd, vp, fromtime, dumpAllDirs);
871
872     if (verbose)
873         fprintf(stderr, "about to dump the dump postamble\n");
874     if (!code)
875         code = DumpEnd(dumpfd);
876
877     if (verbose)
878         fprintf(stderr, "finished dump\n");
879     close(dumpfd);              /* might be closing stdout, no harm */
880 }