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