2 * Copyright 2000, International Business Machines Corporation and others.
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
13 Institution: The Information Technology Center, Carnegie-Mellon University
17 #include <afsconfig.h>
18 #include <afs/param.h>
20 #ifdef IGNORE_SOME_GCC_WARNINGS
21 # pragma GCC diagnostic warning "-Wformat"
30 #include <rx/rx_queue.h>
31 #include <afs/afsint.h>
33 #include <afs/errors.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>
45 #include <afs/com_err.h>
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
54 int VolumeChanged
; /* needed by physio - leave alone */
57 /* Forward Declarations */
58 static void HandleVolume(struct DiskPartition64
*partP
, char *name
,
59 char *filename
, int fromtime
);
60 static Volume
*AttachVolume(struct DiskPartition64
*dp
, char *volname
,
61 struct VolumeHeader
*header
);
62 static void DoMyVolDump(Volume
* vp
, struct DiskPartition64
*dp
,
63 char *dumpfile
, int fromtime
);
66 #include "AFS_component_version_number.c"
69 char name
[VMAXPATHLEN
];
73 ReadHdr1(IHandle_t
* ih
, char *to
, int size
, u_int magic
, u_int version
)
77 code
= IH_IREAD(ih
, 0, to
, size
);
86 AttachVolume(struct DiskPartition64
* dp
, char *volname
,
87 struct VolumeHeader
* header
)
92 vp
= (Volume
*) calloc(1, sizeof(Volume
));
93 vp
->specialStatus
= 0;
94 vp
->device
= dp
->device
;
96 IH_INIT(vp
->vnodeIndex
[vLarge
].handle
, dp
->device
, header
->parent
,
97 header
->largeVnodeIndex
);
98 IH_INIT(vp
->vnodeIndex
[vSmall
].handle
, dp
->device
, header
->parent
,
99 header
->smallVnodeIndex
);
100 IH_INIT(vp
->diskDataHandle
, dp
->device
, header
->parent
,
102 IH_INIT(V_linkHandle(vp
), dp
->device
, header
->parent
, header
->linkTable
);
103 vp
->cacheCheck
= 0; /* XXXX */
104 vp
->shuttingDown
= 0;
105 vp
->goingOffline
= 0;
107 vp
->header
= calloc(1, sizeof(*vp
->header
));
108 ec
= ReadHdr1(V_diskDataHandle(vp
), (char *)&V_disk(vp
),
109 sizeof(V_disk(vp
)), VOLUMEINFOMAGIC
, VOLUMEINFOVERSION
);
111 struct IndexFileHeader iHead
;
112 ec
= ReadHdr1(vp
->vnodeIndex
[vSmall
].handle
, (char *)&iHead
,
113 sizeof(iHead
), SMALLINDEXMAGIC
, SMALLINDEXVERSION
);
116 struct IndexFileHeader iHead
;
117 ec
= ReadHdr1(vp
->vnodeIndex
[vLarge
].handle
, (char *)&iHead
,
118 sizeof(iHead
), LARGEINDEXMAGIC
, LARGEINDEXVERSION
);
122 struct versionStamp stamp
;
123 ec
= ReadHdr1(V_linkHandle(vp
), (char *)&stamp
, sizeof(stamp
),
124 LINKTABLEMAGIC
, LINKTABLEVERSION
);
134 handleit(struct cmd_syndesc
*as
, void *arock
)
138 afs_uint32 volumeId
= 0;
140 char *fileName
= NULL
;
141 struct DiskPartition64
*partP
= NULL
;
143 char tmpPartName
[20];
150 if (geteuid() != 0) {
151 fprintf(stderr
, "voldump must be run as root; sorry\n");
157 if ((ti
= as
->parms
[0].items
))
159 if ((ti
= as
->parms
[1].items
))
160 volumeId
= (afs_uint32
)atoi(ti
->data
);
161 if ((ti
= as
->parms
[2].items
))
163 if ((ti
= as
->parms
[3].items
))
165 if (as
->parms
[4].items
&& strcmp(as
->parms
[4].items
->data
, "0")) {
166 code
= ktime_DateToInt32(as
->parms
[4].items
->data
, &fromtime
);
168 fprintf(STDERR
, "failed to parse date '%s' (error=%d))\n",
169 as
->parms
[4].items
->data
, code
);
176 err
= VAttachPartitions();
178 fprintf(stderr
, "%d partitions had errors during attach.\n", err
);
182 if (strlen(partName
) == 1) {
183 if (partName
[0] >= 'a' && partName
[0] <= 'z') {
184 strcpy(tmpPartName
, "/vicepa");
185 tmpPartName
[6] = partName
[0];
186 partP
= VGetPartition(tmpPartName
, 0);
189 partP
= VGetPartition(partName
, 0);
193 "%s is not an AFS partition name on this server.\n",
200 fprintf(stderr
, "Must specify volume id!\n");
205 fprintf(stderr
, "must specify vice partition.\n");
209 snprintf(name1
, sizeof name1
, VFORMAT
, (unsigned long)volumeId
);
210 HandleVolume(partP
, name1
, fileName
, fromtime
);
215 HandleVolume(struct DiskPartition64
*dp
, char *name
, char *filename
, int fromtime
)
217 struct VolumeHeader header
;
218 struct VolumeDiskHeader diskHeader
;
219 struct afs_stat status
;
222 char headerName
[1024];
226 snprintf(headerName
, sizeof headerName
, "%s" OS_DIRSEP
"%s",
227 VPartitionPath(dp
), name
);
228 if ((fd
= afs_open(headerName
, O_RDONLY
)) == -1
229 || afs_fstat(fd
, &status
) == -1) {
230 fprintf(stderr
, "Cannot read volume header %s\n", name
);
234 n
= read(fd
, &diskHeader
, sizeof(diskHeader
));
236 if (n
!= sizeof(diskHeader
)
237 || diskHeader
.stamp
.magic
!= VOLUMEHEADERMAGIC
) {
238 fprintf(stderr
, "Error reading volume header %s\n", name
);
241 if (diskHeader
.stamp
.version
!= VOLUMEHEADERVERSION
) {
243 "Volume %s, version number is incorrect; volume needs salvage\n",
247 DiskToVolumeHeader(&header
, &diskHeader
);
250 vp
= AttachVolume(dp
, name
, &header
);
252 fprintf(stderr
, "Error attaching volume header %s\n", name
);
256 DoMyVolDump(vp
, dp
, filename
, fromtime
);
263 main(int argc
, char **argv
)
265 struct cmd_syndesc
*ts
;
267 VolumePackageOptions opts
;
269 VOptDefaults(volumeUtility
, &opts
);
270 if (VInitVolumePackage2(volumeUtility
, &opts
)) {
271 fprintf(stderr
, "errors encountered initializing volume package, but "
272 "trying to continue anyway\n");
275 ts
= cmd_CreateSyntax(NULL
, handleit
, NULL
, 0,
276 "Dump a volume to a 'vos dump' format file without using volserver");
277 cmd_AddParm(ts
, "-part", CMD_LIST
, CMD_OPTIONAL
, "AFS partition name");
278 cmd_AddParm(ts
, "-volumeid", CMD_LIST
, CMD_OPTIONAL
, "Volume id");
279 cmd_AddParm(ts
, "-file", CMD_LIST
, CMD_OPTIONAL
, "Dump filename");
280 cmd_AddParm(ts
, "-verbose", CMD_FLAG
, CMD_OPTIONAL
,
281 "Trace dump progress (very verbose)");
282 cmd_AddParm(ts
, "-time", CMD_SINGLE
, CMD_OPTIONAL
, "dump from time");
283 code
= cmd_Dispatch(argc
, argv
);
291 DumpDouble(int dumpfd
, char tag
, afs_uint32 value1
,
296 byte
*p
= (unsigned char *)tbuffer
;
298 afs_putint32(p
, value1
);
299 afs_putint32(p
, value2
);
301 res
= write(dumpfd
, tbuffer
, 9);
302 return ((res
== 9) ? 0 : VOLSERDUMPERROR
);
306 DumpInt32(int dumpfd
, char tag
, afs_uint32 value
)
309 byte
*p
= (unsigned char *)tbuffer
;
311 afs_putint32(p
, value
);
312 return ((write(dumpfd
, tbuffer
, 5) == 5) ? 0 : VOLSERDUMPERROR
);
316 DumpString(int dumpfd
, char tag
, char *s
)
320 code
= write(dumpfd
, &tag
, 1);
322 return VOLSERDUMPERROR
;
324 code
= write(dumpfd
, s
, n
);
326 return VOLSERDUMPERROR
;
332 DumpArrayInt32(int dumpfd
, char tag
, afs_uint32
* array
,
338 byte
*p
= (unsigned char *)tbuffer
;
340 afs_putshort(p
, nelem
);
341 code
= write(dumpfd
, tbuffer
, 3);
343 return VOLSERDUMPERROR
;
345 p
= (unsigned char *)tbuffer
;
346 v
= *array
++; /*this was register */
349 code
= write(dumpfd
, tbuffer
, 4);
351 return VOLSERDUMPERROR
;
360 DumpDumpHeader(int dumpfd
, Volume
* vp
, afs_int32 fromtime
)
363 afs_int32 dumpTimes
[2];
366 fprintf(stderr
, "dumping dump header\n");
369 code
= DumpDouble(dumpfd
, D_DUMPHEADER
, DUMPBEGINMAGIC
, DUMPVERSION
);
372 code
= DumpInt32(dumpfd
, 'v', V_id(vp
));
375 code
= DumpString(dumpfd
, 'n', V_name(vp
));
377 dumpTimes
[0] = fromtime
;
378 switch (V_type(vp
)) {
379 case readwriteVolume
:
380 dumpTimes
[1] = V_updateDate(vp
); /* until last update */
383 dumpTimes
[1] = V_creationDate(vp
); /* until clone was updated */
386 /* until backup was made */
387 dumpTimes
[1] = V_backupDate(vp
) != 0 ? V_backupDate(vp
) :
394 code
= DumpArrayInt32(dumpfd
, 't', (afs_uint32
*) dumpTimes
, 2);
403 return (DumpInt32(dumpfd
, D_DUMPEND
, DUMPENDMAGIC
));
407 DumpByte(int dumpfd
, char tag
, byte value
)
410 byte
*p
= (unsigned char *)tbuffer
;
413 return ((write(dumpfd
, tbuffer
, 2) == 2) ? 0 : VOLSERDUMPERROR
);
417 DumpTag(int dumpfd
, int tag
)
422 return ((write(dumpfd
, &p
, 1) == 1) ? 0 : VOLSERDUMPERROR
);
427 DumpBool(int dumpfd
, char tag
, unsigned int value
)
430 byte
*p
= (unsigned char *)tbuffer
;
433 return ((write(dumpfd
, tbuffer
, 2) == 2) ? 0 : VOLSERDUMPERROR
);
439 DumpVolumeHeader(int dumpfd
, Volume
* vp
)
444 fprintf(stderr
, "dumping volume header\n");
447 code
= DumpTag(dumpfd
, D_VOLUMEHEADER
);
449 code
= DumpInt32(dumpfd
, 'i', V_id(vp
));
451 code
= DumpInt32(dumpfd
, 'v', V_stamp(vp
).version
);
453 code
= DumpString(dumpfd
, 'n', V_name(vp
));
455 code
= DumpBool(dumpfd
, 's', V_inService(vp
));
457 code
= DumpBool(dumpfd
, 'b', V_blessed(vp
));
459 code
= DumpInt32(dumpfd
, 'u', V_uniquifier(vp
));
461 code
= DumpByte(dumpfd
, 't', (byte
) V_type(vp
));
463 code
= DumpInt32(dumpfd
, 'p', V_parentId(vp
));
465 code
= DumpInt32(dumpfd
, 'c', V_cloneId(vp
));
467 code
= DumpInt32(dumpfd
, 'q', V_maxquota(vp
));
469 code
= DumpInt32(dumpfd
, 'm', V_minquota(vp
));
471 code
= DumpInt32(dumpfd
, 'd', V_diskused(vp
));
473 code
= DumpInt32(dumpfd
, 'f', V_filecount(vp
));
475 code
= DumpInt32(dumpfd
, 'a', V_accountNumber(vp
));
477 code
= DumpInt32(dumpfd
, 'o', V_owner(vp
));
479 code
= DumpInt32(dumpfd
, 'C', V_creationDate(vp
)); /* Rw volume creation date */
481 code
= DumpInt32(dumpfd
, 'A', V_accessDate(vp
));
483 code
= DumpInt32(dumpfd
, 'U', V_updateDate(vp
));
485 code
= DumpInt32(dumpfd
, 'E', V_expirationDate(vp
));
487 code
= DumpInt32(dumpfd
, 'B', V_backupDate(vp
)); /* Rw volume backup clone date */
489 code
= DumpString(dumpfd
, 'O', V_offlineMessage(vp
));
492 * We do NOT dump the detailed volume statistics residing in the old
493 * motd field, since we cannot tell from the info in a dump whether
494 * statistics data has been put there. Instead, we dump a null string,
495 * just as if that was what the motd contained.
498 code
= DumpString(dumpfd
, 'M', "");
501 DumpArrayInt32(dumpfd
, 'W', (afs_uint32
*) V_weekUse(vp
),
502 sizeof(V_weekUse(vp
)) / sizeof(V_weekUse(vp
)[0]));
504 code
= DumpInt32(dumpfd
, 'D', V_dayUseDate(vp
));
506 code
= DumpInt32(dumpfd
, 'Z', V_dayUse(vp
));
511 DumpShort(int dumpfd
, char tag
, unsigned int value
)
514 byte
*p
= (unsigned char *)tbuffer
;
518 return ((write(dumpfd
, tbuffer
, 3) == 3) ? 0 : VOLSERDUMPERROR
);
522 DumpByteString(int dumpfd
, char tag
, byte
* bs
, int nbytes
)
526 code
= write(dumpfd
, &tag
, 1);
528 return VOLSERDUMPERROR
;
529 code
= write(dumpfd
, (char *)bs
, nbytes
);
531 return VOLSERDUMPERROR
;
537 DumpFile(int dumpfd
, int vnode
, FdHandle_t
* handleP
, struct VnodeDiskObject
*v
)
539 int code
= 0, failed_seek
= 0, failed_write
= 0;
541 afs_foff_t offset
= 0;
542 afs_sfsize_t nbytes
, howBig
;
545 afs_foff_t howFar
= 0;
550 struct afs_stat status
;
552 LARGE_INTEGER fileSize
;
556 #include <sys/statfs.h>
557 struct statfs tstatfs
;
561 fprintf(stderr
, "dumping file for vnode %d\n", vnode
);
564 if (!GetFileSizeEx(handleP
->fd_fd
, &fileSize
)) {
565 Log("DumpFile: GetFileSizeEx returned error code %d on descriptor %d\n", GetLastError(), handleP
->fd_fd
);
566 return VOLSERDUMPERROR
;
568 howBig
= fileSize
.QuadPart
;
572 afs_fstat(handleP
->fd_fd
, &status
);
573 howBig
= status
.st_size
;
576 /* Unfortunately in AIX valuable fields such as st_blksize are
577 * gone from the stat structure.
579 fstatfs(handleP
->fd_fd
, &tstatfs
);
580 howMany
= tstatfs
.f_bsize
;
582 howMany
= status
.st_blksize
;
583 #endif /* AFS_AIX_ENV */
584 #endif /* AFS_NT40_ENV */
587 size
= FDH_SIZE(handleP
);
590 fprintf(stderr
, " howBig = %u, howMany = %u, fdh size = %u\n",
591 (unsigned int) howBig
, (unsigned int) howMany
,
592 (unsigned int) size
);
594 SplitInt64(size
, hi
, lo
);
596 code
= DumpInt32(dumpfd
, 'f', lo
);
598 code
= DumpDouble(dumpfd
, 'h', hi
, lo
);
602 return VOLSERDUMPERROR
;
607 fprintf(stderr
, "out of memory!\n");
608 return VOLSERDUMPERROR
;
611 /* loop through whole file, while we still have bytes left, and no errors, in chunks of howMany bytes */
612 for (nbytes
= size
; (nbytes
&& !failed_write
); nbytes
-= howMany
) {
613 if (nbytes
< howMany
)
616 /* Read the data - unless we know we can't */
617 n
= (failed_seek
? 0 : FDH_PREAD(handleP
, p
, howMany
, howFar
));
620 /* If read any good data and we null padded previously, log the
621 * amount that we had null padded.
623 if ((n
> 0) && pad
) {
624 fprintf(stderr
, "Null padding file %d bytes at offset %lld\n", pad
,
629 /* If didn't read enough data, null padd the rest of the buffer. This
630 * can happen if, for instance, the media has some bad spots. We don't
631 * want to quit the dump, so we start null padding.
635 if (verbose
) fprintf(stderr
, " read %u instead of %u bytes.\n", (unsigned)n
, (unsigned)howMany
);
637 /* Record the read error */
640 fprintf(stderr
, "Error %d reading inode %s for vnode %d\n",
641 errno
, PrintInode(stmp
, handleP
->fd_ih
->ih_ino
),
644 fprintf(stderr
, "Error reading inode %s for vnode %d\n",
645 PrintInode(stmp
, handleP
->fd_ih
->ih_ino
), vnode
);
648 /* Pad the rest of the buffer with zeros. Remember offset we started
649 * padding. Keep total tally of padding.
651 memset(p
+ n
, 0, howMany
- n
);
653 offset
= (howBig
- nbytes
) + n
;
654 pad
+= (howMany
- n
);
656 /* Now seek over the data we could not get. An error here means we
657 * can't do the next read.
659 howFar
= ((size
- nbytes
) + howMany
);
662 /* Now write the data out */
663 if (write(dumpfd
, (char *)p
, howMany
) != howMany
)
664 failed_write
= VOLSERDUMPERROR
;
667 if (pad
) { /* Any padding we hadn't reported yet */
668 fprintf(stderr
, "Null padding file: %d bytes at offset %lld\n", pad
,
678 DumpVnode(int dumpfd
, struct VnodeDiskObject
*v
, VolumeId volid
, int vnodeNumber
,
679 int dumpEverything
, struct Volume
*vp
)
687 fprintf(stderr
, "dumping vnode %d\n", vnodeNumber
);
689 if (!v
|| v
->type
== vNull
)
692 code
= DumpDouble(dumpfd
, D_VNODE
, vnodeNumber
, v
->uniquifier
);
696 code
= DumpByte(dumpfd
, 't', (byte
) v
->type
);
698 code
= DumpShort(dumpfd
, 'l', v
->linkCount
); /* May not need this */
700 code
= DumpInt32(dumpfd
, 'v', v
->dataVersion
);
702 code
= DumpInt32(dumpfd
, 'm', v
->unixModifyTime
);
704 code
= DumpInt32(dumpfd
, 'a', v
->author
);
706 code
= DumpInt32(dumpfd
, 'o', v
->owner
);
707 if (!code
&& v
->group
)
708 code
= DumpInt32(dumpfd
, 'g', v
->group
); /* default group is 0 */
710 code
= DumpShort(dumpfd
, 'b', v
->modeBits
);
712 code
= DumpInt32(dumpfd
, 'p', v
->parent
);
714 code
= DumpInt32(dumpfd
, 's', v
->serverModifyTime
);
715 if (v
->type
== vDirectory
) {
716 code
= acl_HtonACL(VVnodeDiskACL(v
));
718 fprintf(stderr
, "Skipping invalid acl in vnode %u (volume %"AFS_VOLID_FMT
")\n",
719 vnodeNumber
, afs_printable_VolumeId_lu(volid
));
723 DumpByteString(dumpfd
, 'A', (byte
*) VVnodeDiskACL(v
),
727 if (VNDISK_GET_INO(v
)) {
728 IH_INIT(ihP
, V_device(vp
), V_parentId(vp
), VNDISK_GET_INO(v
));
732 "Unable to open inode %s for vnode %u "
733 "(volume %"AFS_VOLID_FMT
"); not dumped, error %d\n",
734 PrintInode(stmp
, VNDISK_GET_INO(v
)), vnodeNumber
,
735 afs_printable_VolumeId_lu(volid
), errno
);
740 fprintf(stderr
, "about to dump inode %s for vnode %u\n",
741 PrintInode(stmp
, VNDISK_GET_INO(v
)), vnodeNumber
);
742 code
= DumpFile(dumpfd
, vnodeNumber
, fdP
, v
);
749 fprintf(stderr
, "done dumping vnode %d\n", vnodeNumber
);
755 DumpVnodeIndex(int dumpfd
, Volume
* vp
, VnodeClass
class, afs_int32 fromtime
,
759 struct VnodeClassInfo
*vcp
= &VnodeClassInfo
[class];
760 char buf
[SIZEOF_LARGEDISKVNODE
];
761 struct VnodeDiskObject
*vnode
= (struct VnodeDiskObject
*)buf
;
762 StreamHandle_t
*file
;
766 afs_foff_t offset
= 0;
767 int vnodeIndex
, nVnodes
= 0;
769 fdP
= IH_OPEN(vp
->vnodeIndex
[class].handle
);
770 file
= FDH_FDOPEN(fdP
, "r+");
771 size
= OS_SIZE(fdP
->fd_fd
);
772 nVnodes
= (size
/ vcp
->diskSize
) - 1;
775 STREAM_ASEEK(file
, vcp
->diskSize
);
779 nVnodes
&& STREAM_READ(vnode
, vcp
->diskSize
, 1, file
) == 1 && !code
;
780 nVnodes
--, vnodeIndex
++, offset
+= vcp
->diskSize
) {
781 flag
= forcedump
|| (vnode
->serverModifyTime
>= fromtime
);
782 /* Note: the >= test is very important since some old volumes may not have
783 * a serverModifyTime. For an epoch dump, this results in 0>=0 test, which
784 * does dump the file! */
786 fprintf(stderr
, "about to dump %s vnode %u (vnode offset = %lld)\n",
787 class == vSmall
? "vSmall" : "vLarge",
788 bitNumberToVnodeNumber(vnodeIndex
, class), (long long)offset
);
791 DumpVnode(dumpfd
, vnode
, V_id(vp
),
792 bitNumberToVnodeNumber(vnodeIndex
, class), flag
,
802 /* A partial dump (no dump header) */
804 DumpPartial(int dumpfd
, Volume
* vp
, afs_int32 fromtime
,
810 fprintf(stderr
, "about to dump the volume header\n");
812 code
= DumpVolumeHeader(dumpfd
, vp
);
815 fprintf(stderr
, "about to dump the large vnode index\n");
817 code
= DumpVnodeIndex(dumpfd
, vp
, vLarge
, fromtime
, dumpAllDirs
);
820 fprintf(stderr
, "about to dump the small vnode index\n");
822 code
= DumpVnodeIndex(dumpfd
, vp
, vSmall
, fromtime
, 0);
829 DoMyVolDump(Volume
* vp
, struct DiskPartition64
*dp
, char *dumpfile
, int fromtime
)
838 afs_open(dumpfile
, O_CREAT
| O_WRONLY
| O_TRUNC
, S_IRUSR
| S_IWUSR
);
840 fprintf(stderr
, "Failed to open dump file: %s. Exiting.\n",
841 afs_error_message(errno
));
845 dumpfd
= 1; /* stdout */
849 fprintf(stderr
, "about to dump the dump header\n");
851 code
= DumpDumpHeader(dumpfd
, vp
, fromtime
);
854 fprintf(stderr
, "about to dump volume contents\n");
856 code
= DumpPartial(dumpfd
, vp
, fromtime
, dumpAllDirs
);
859 fprintf(stderr
, "about to dump the dump postamble\n");
861 code
= DumpEnd(dumpfd
);
864 fprintf(stderr
, "finished dump\n");
865 close(dumpfd
); /* might be closing stdout, no harm */