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