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