/* * Copyright 2000, International Business Machines Corporation and others. * All Rights Reserved. * * This software has been released under the terms of the IBM Public * License. For details, see the LICENSE file in the top-level source * directory or online at http://www.openafs.org/dl/license10.html */ /* System: VICE-TWO Module: vol-dump.c Institution: The Information Technology Center, Carnegie-Mellon University */ #include #include RCSID ("$Header$"); #include #include #include #include #include #ifdef AFS_NT40_ENV #include #include #include #else #include #include #include #endif #include #include #include #include "nfs.h" #include #include "lock.h" #include "lwp.h" #include #include "ihandle.h" #include "vnode.h" #include "volume.h" #include "partition.h" #include "viceinode.h" #include #include "acl.h" #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef _AIX #include #endif #include #include "volser/volser.h" #include "volser/volint.h" #include "volser/dump.h" #define putint32(p, v) *p++ = v>>24, *p++ = v>>16, *p++ = v>>8, *p++ = v #define putshort(p, v) *p++ = v>>8, *p++ = v #ifdef O_LARGEFILE #define afs_stat stat64 #define afs_fstat fstat64 #define afs_open open64 #else /* !O_LARGEFILE */ #define afs_stat stat #define afs_fstat fstat #define afs_open open #endif /* !O_LARGEFILE */ int VolumeChanged; /* needed by physio - leave alone */ int verbose = 0; /* Forward Declarations */ void HandleVolume(struct DiskPartition64 *partP, char *name, char *filename, int fromtime); Volume *AttachVolume(struct DiskPartition64 *dp, char *volname, register struct VolumeHeader *header); static void DoMyVolDump(Volume * vp, struct DiskPartition64 *dp, char *dumpfile, int fromtime); #ifndef AFS_NT40_ENV #include "AFS_component_version_number.c" #endif char name[VMAXPATHLEN]; int ReadHdr1(IHandle_t * ih, char *to, int size, u_int magic, u_int version) { int bad = 0; int code; code = IH_IREAD(ih, 0, to, size); if (code != size) return -1; return 0; } Volume * AttachVolume(struct DiskPartition64 * dp, char *volname, register struct VolumeHeader * header) { register Volume *vp; afs_int32 ec = 0; vp = (Volume *) calloc(1, sizeof(Volume)); vp->specialStatus = 0; vp->device = dp->device; vp->partition = dp; IH_INIT(vp->vnodeIndex[vLarge].handle, dp->device, header->parent, header->largeVnodeIndex); IH_INIT(vp->vnodeIndex[vSmall].handle, dp->device, header->parent, header->smallVnodeIndex); IH_INIT(vp->diskDataHandle, dp->device, header->parent, header->volumeInfo); IH_INIT(V_linkHandle(vp), dp->device, header->parent, header->linkTable); vp->cacheCheck = 0; /* XXXX */ vp->shuttingDown = 0; vp->goingOffline = 0; vp->nUsers = 1; vp->header = (struct volHeader *)calloc(1, sizeof(*vp->header)); ec = ReadHdr1(V_diskDataHandle(vp), (char *)&V_disk(vp), sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION); if (!ec) { struct IndexFileHeader iHead; ec = ReadHdr1(vp->vnodeIndex[vSmall].handle, (char *)&iHead, sizeof(iHead), SMALLINDEXMAGIC, SMALLINDEXVERSION); } if (!ec) { struct IndexFileHeader iHead; ec = ReadHdr1(vp->vnodeIndex[vLarge].handle, (char *)&iHead, sizeof(iHead), LARGEINDEXMAGIC, LARGEINDEXVERSION); } #ifdef AFS_NAMEI_ENV if (!ec) { struct versionStamp stamp; ec = ReadHdr1(V_linkHandle(vp), (char *)&stamp, sizeof(stamp), LINKTABLEMAGIC, LINKTABLEVERSION); } #endif if (ec) return (Volume *) 0; return vp; } static int handleit(struct cmd_syndesc *as, void *arock) { register struct cmd_item *ti; int err = 0; afs_uint32 volumeId = 0; char *partName = 0; char *fileName = NULL; struct DiskPartition64 *partP = NULL; char name1[128]; char tmpPartName[20]; int fromtime = 0; afs_int32 code; #ifndef AFS_NT40_ENV #if 0 if (geteuid() != 0) { fprintf(stderr, "voldump must be run as root; sorry\n"); exit(1); } #endif #endif if ((ti = as->parms[0].items)) partName = ti->data; if ((ti = as->parms[1].items)) volumeId = (afs_uint32)atoi(ti->data); if ((ti = as->parms[2].items)) fileName = ti->data; if ((ti = as->parms[3].items)) verbose = 1; if (as->parms[4].items && strcmp(as->parms[4].items->data, "0")) { code = ktime_DateToInt32(as->parms[4].items->data, &fromtime); if (code) { fprintf(STDERR, "failed to parse date '%s' (error=%d))\n", as->parms[4].items->data, code); return code; } } DInit(10); err = VAttachPartitions(); if (err) { fprintf(stderr, "%d partitions had errors during attach.\n", err); } if (partName) { if (strlen(partName) == 1) { if (partName[0] >= 'a' && partName[0] <= 'z') { strcpy(tmpPartName, "/vicepa"); tmpPartName[6] = partName[0]; partP = VGetPartition(tmpPartName, 0); } } else { partP = VGetPartition(partName, 0); } if (!partP) { fprintf(stderr, "%s is not an AFS partition name on this server.\n", partName); exit(1); } } if (!volumeId) { fprintf(stderr, "Must specify volume id!\n"); exit(1); } if (!partP) { fprintf(stderr, "must specify vice partition.\n"); exit(1); } (void)afs_snprintf(name1, sizeof name1, VFORMAT, (unsigned long)volumeId); HandleVolume(partP, name1, fileName, fromtime); return 0; } void HandleVolume(struct DiskPartition64 *dp, char *name, char *filename, int fromtime) { struct VolumeHeader header; struct VolumeDiskHeader diskHeader; struct afs_stat status, stat; register int fd; Volume *vp; IHandle_t *ih; char headerName[1024]; afs_int32 n; (void)afs_snprintf(headerName, sizeof headerName, "%s/%s", VPartitionPath(dp), name); if ((fd = afs_open(headerName, O_RDONLY)) == -1 || afs_fstat(fd, &status) == -1) { fprintf(stderr, "Cannot read volume header %s\n", name); close(fd); exit(1); } n = read(fd, &diskHeader, sizeof(diskHeader)); if (n != sizeof(diskHeader) || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) { fprintf(stderr, "Error reading volume header %s\n", name); exit(1); } if (diskHeader.stamp.version != VOLUMEHEADERVERSION) { fprintf(stderr, "Volume %s, version number is incorrect; volume needs salvage\n", name); exit(1); } DiskToVolumeHeader(&header, &diskHeader); close(fd); vp = AttachVolume(dp, name, &header); if (!vp) { fprintf(stderr, "Error attaching volume header %s\n", name); exit(1); } DoMyVolDump(vp, dp, filename, fromtime); } int main(int argc, char **argv) { register struct cmd_syndesc *ts; afs_int32 code; VInitVolumePackage(volumeUtility, 5, 5, DONT_CONNECT_FS, 0); ts = cmd_CreateSyntax(NULL, handleit, NULL, "Dump a volume to a 'vos dump' format file without using volserver"); cmd_AddParm(ts, "-part", CMD_LIST, CMD_OPTIONAL, "AFS partition name"); cmd_AddParm(ts, "-volumeid", CMD_LIST, CMD_OPTIONAL, "Volume id"); cmd_AddParm(ts, "-file", CMD_LIST, CMD_OPTIONAL, "Dump filename"); cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "Trace dump progress (very verbose)"); cmd_AddParm(ts, "-time", CMD_SINGLE, CMD_OPTIONAL, "dump from time"); code = cmd_Dispatch(argc, argv); return code; } static int DumpDouble(int dumpfd, char tag, register afs_uint32 value1, register afs_uint32 value2) { int res; char tbuffer[9]; register byte *p = (unsigned char *)tbuffer; *p++ = tag; putint32(p, value1); putint32(p, value2); res = write(dumpfd, tbuffer, 9); return ((res == 9) ? 0 : VOLSERDUMPERROR); } static int DumpInt32(int dumpfd, char tag, register afs_uint32 value) { char tbuffer[5]; register byte *p = (unsigned char *)tbuffer; *p++ = tag; putint32(p, value); return ((write(dumpfd, tbuffer, 5) == 5) ? 0 : VOLSERDUMPERROR); } static int DumpString(int dumpfd, char tag, register char *s) { register int n; int code = 0; code = write(dumpfd, &tag, 1); if (code != 1) return VOLSERDUMPERROR; n = strlen(s) + 1; code = write(dumpfd, s, n); if (code != n) return VOLSERDUMPERROR; return 0; } static int DumpArrayInt32(int dumpfd, char tag, register afs_uint32 * array, register int nelem) { char tbuffer[4]; register afs_uint32 v; int code = 0; register byte *p = (unsigned char *)tbuffer; *p++ = tag; putshort(p, nelem); code = write(dumpfd, tbuffer, 3); if (code != 3) return VOLSERDUMPERROR; while (nelem--) { p = (unsigned char *)tbuffer; v = *array++; /*this was register */ putint32(p, v); code = write(dumpfd, tbuffer, 4); if (code != 4) return VOLSERDUMPERROR; } return 0; } static int DumpDumpHeader(int dumpfd, register Volume * vp, afs_int32 fromtime) { int code = 0; afs_int32 dumpTimes[2]; if (verbose) fprintf(stderr, "dumping dump header\n"); if (!code) code = DumpDouble(dumpfd, D_DUMPHEADER, DUMPBEGINMAGIC, DUMPVERSION); if (!code) code = DumpInt32(dumpfd, 'v', V_id(vp)); if (!code) code = DumpString(dumpfd, 'n', V_name(vp)); dumpTimes[0] = fromtime; dumpTimes[1] = V_backupDate(vp); /* Until the time the clone was made */ if (!code) code = DumpArrayInt32(dumpfd, 't', (afs_uint32 *) dumpTimes, 2); return code; } static int DumpEnd(int dumpfd) { return (DumpInt32(dumpfd, D_DUMPEND, DUMPENDMAGIC)); } static int DumpByte(int dumpfd, char tag, byte value) { char tbuffer[2]; register byte *p = (unsigned char *)tbuffer; *p++ = tag; *p = value; return ((write(dumpfd, tbuffer, 2) == 2) ? 0 : VOLSERDUMPERROR); } static int DumpTag(int dumpfd, register int tag) { char p; p = tag; return ((write(dumpfd, &p, 1) == 1) ? 0 : VOLSERDUMPERROR); } static int DumpBool(int dumpfd, char tag, unsigned int value) { char tbuffer[2]; register byte *p = (unsigned char *)tbuffer; *p++ = tag; *p = value; return ((write(dumpfd, tbuffer, 2) == 2) ? 0 : VOLSERDUMPERROR); } static int DumpVolumeHeader(int dumpfd, register Volume * vp) { int code = 0; if (verbose) fprintf(stderr, "dumping volume header\n"); if (!code) code = DumpTag(dumpfd, D_VOLUMEHEADER); if (!code) code = DumpInt32(dumpfd, 'i', V_id(vp)); if (!code) code = DumpInt32(dumpfd, 'v', V_stamp(vp).version); if (!code) code = DumpString(dumpfd, 'n', V_name(vp)); if (!code) code = DumpBool(dumpfd, 's', V_inService(vp)); if (!code) code = DumpBool(dumpfd, 'b', V_blessed(vp)); if (!code) code = DumpInt32(dumpfd, 'u', V_uniquifier(vp)); if (!code) code = DumpByte(dumpfd, 't', (byte) V_type(vp)); if (!code) code = DumpInt32(dumpfd, 'p', V_parentId(vp)); if (!code) code = DumpInt32(dumpfd, 'c', V_cloneId(vp)); if (!code) code = DumpInt32(dumpfd, 'q', V_maxquota(vp)); if (!code) code = DumpInt32(dumpfd, 'm', V_minquota(vp)); if (!code) code = DumpInt32(dumpfd, 'd', V_diskused(vp)); if (!code) code = DumpInt32(dumpfd, 'f', V_filecount(vp)); if (!code) code = DumpInt32(dumpfd, 'a', V_accountNumber(vp)); if (!code) code = DumpInt32(dumpfd, 'o', V_owner(vp)); if (!code) code = DumpInt32(dumpfd, 'C', V_creationDate(vp)); /* Rw volume creation date */ if (!code) code = DumpInt32(dumpfd, 'A', V_accessDate(vp)); if (!code) code = DumpInt32(dumpfd, 'U', V_updateDate(vp)); if (!code) code = DumpInt32(dumpfd, 'E', V_expirationDate(vp)); if (!code) code = DumpInt32(dumpfd, 'B', V_backupDate(vp)); /* Rw volume backup clone date */ if (!code) code = DumpString(dumpfd, 'O', V_offlineMessage(vp)); /* * We do NOT dump the detailed volume statistics residing in the old * motd field, since we cannot tell from the info in a dump whether * statistics data has been put there. Instead, we dump a null string, * just as if that was what the motd contained. */ if (!code) code = DumpString(dumpfd, 'M', ""); if (!code) code = DumpArrayInt32(dumpfd, 'W', (afs_uint32 *) V_weekUse(vp), sizeof(V_weekUse(vp)) / sizeof(V_weekUse(vp)[0])); if (!code) code = DumpInt32(dumpfd, 'D', V_dayUseDate(vp)); if (!code) code = DumpInt32(dumpfd, 'Z', V_dayUse(vp)); return code; } static int DumpShort(int dumpfd, char tag, unsigned int value) { char tbuffer[3]; register byte *p = (unsigned char *)tbuffer; *p++ = tag; *p++ = value >> 8; *p = value; return ((write(dumpfd, tbuffer, 3) == 3) ? 0 : VOLSERDUMPERROR); } static int DumpByteString(int dumpfd, char tag, register byte * bs, register int nbytes) { int code = 0; code = write(dumpfd, &tag, 1); if (code != 1) return VOLSERDUMPERROR; code = write(dumpfd, (char *)bs, nbytes); if (code != nbytes) return VOLSERDUMPERROR; return 0; } static int DumpFile(int dumpfd, int vnode, FdHandle_t * handleP, struct VnodeDiskObject *v) { int code = 0, failed_seek = 0, failed_write = 0; afs_int32 pad = 0; afs_int32 offset = 0; afs_sfsize_t n, nbytes, howMany, howBig; byte *p; #ifndef AFS_NT40_ENV struct afs_stat status; #endif afs_sfsize_t size, tmpsize; #ifdef AFS_AIX_ENV #include struct statfs tstatfs; #endif if (verbose) fprintf(stderr, "dumping file for vnode %d\n", vnode); #ifdef AFS_NT40_ENV howBig = _filelength(handleP->fd_fd); howMany = 4096; #else afs_fstat(handleP->fd_fd, &status); howBig = status.st_size; #ifdef AFS_AIX_ENV /* Unfortunately in AIX valuable fields such as st_blksize are * gone from the stat structure. */ fstatfs(handleP->fd_fd, &tstatfs); howMany = tstatfs.f_bsize; #else howMany = status.st_blksize; #endif /* AFS_AIX_ENV */ #endif /* AFS_NT40_ENV */ size = FDH_SIZE(handleP); if (verbose) fprintf(stderr, " howBig = %u, howMany = %u, fdh size = %u\n", howBig, howMany, size); #ifdef AFS_LARGEFILE_ENV { afs_uint32 hi, lo; SplitInt64(size, hi, lo); if (hi == 0L) { code = DumpInt32(dumpfd, 'f', lo); } else { code = DumpDouble(dumpfd, 'h', hi, lo); } } #else /* !AFS_LARGEFILE_ENV */ code = DumpInt32(dumpfd, 'f', size); #endif /* !AFS_LARGEFILE_ENV */ if (code) { return VOLSERDUMPERROR; } p = (unsigned char *)malloc(howMany); if (!p) { fprintf(stderr, "out of memory!\n"); return VOLSERDUMPERROR; } /* loop through whole file, while we still have bytes left, and no errors, in chunks of howMany bytes */ for (nbytes = size; (nbytes && !failed_write); nbytes -= howMany) { if (nbytes < howMany) howMany = nbytes; /* Read the data - unless we know we can't */ n = (failed_seek ? 0 : FDH_READ(handleP, p, howMany)); /* If read any good data and we null padded previously, log the * amount that we had null padded. */ if ((n > 0) && pad) { fprintf(stderr, "Null padding file %d bytes at offset %u\n", pad, offset); pad = 0; } /* If didn't read enough data, null padd the rest of the buffer. This * can happen if, for instance, the media has some bad spots. We don't * want to quit the dump, so we start null padding. */ if (n < howMany) { if (verbose) fprintf(stderr, " read %u instead of %u bytes.\n", n, howMany); /* Record the read error */ if (n < 0) { n = 0; fprintf(stderr, "Error %d reading inode %s for vnode %d\n", errno, PrintInode(NULL, handleP->fd_ih->ih_ino), vnode); } else if (!pad) { fprintf(stderr, "Error reading inode %s for vnode %d\n", PrintInode(NULL, handleP->fd_ih->ih_ino), vnode); } /* Pad the rest of the buffer with zeros. Remember offset we started * padding. Keep total tally of padding. */ memset(p + n, 0, howMany - n); if (!pad) offset = (howBig - nbytes) + n; pad += (howMany - n); /* Now seek over the data we could not get. An error here means we * can't do the next read. */ failed_seek = FDH_SEEK(handleP, ((size - nbytes) + howMany), SEEK_SET); if (failed_seek != ((size - nbytes) + howMany)) { if (failed_seek < 0) { fprintf(stderr, "Error %d seeking in inode %s for vnode %d\n", errno, PrintInode(NULL, handleP->fd_ih->ih_ino), vnode); } else { fprintf(stderr, "Error seeking in inode %s for vnode %d\n", PrintInode(NULL, handleP->fd_ih->ih_ino), vnode); failed_seek = -1; } } else { failed_seek = 0; } } /* Now write the data out */ if (write(dumpfd, (char *)p, howMany) != howMany) failed_write = VOLSERDUMPERROR; } if (pad) { /* Any padding we hadn't reported yet */ fprintf(stderr, "Null padding file: %d bytes at offset %u\n", pad, offset); } free(p); return failed_write; } static int DumpVnode(int dumpfd, struct VnodeDiskObject *v, int volid, int vnodeNumber, int dumpEverything, struct Volume *vp) { int code = 0; IHandle_t *ihP; FdHandle_t *fdP; if (verbose) fprintf(stderr, "dumping vnode %d\n", vnodeNumber); if (!v || v->type == vNull) return code; if (!code) code = DumpDouble(dumpfd, D_VNODE, vnodeNumber, v->uniquifier); if (!dumpEverything) return code; if (!code) code = DumpByte(dumpfd, 't', (byte) v->type); if (!code) code = DumpShort(dumpfd, 'l', v->linkCount); /* May not need this */ if (!code) code = DumpInt32(dumpfd, 'v', v->dataVersion); if (!code) code = DumpInt32(dumpfd, 'm', v->unixModifyTime); if (!code) code = DumpInt32(dumpfd, 'a', v->author); if (!code) code = DumpInt32(dumpfd, 'o', v->owner); if (!code && v->group) code = DumpInt32(dumpfd, 'g', v->group); /* default group is 0 */ if (!code) code = DumpShort(dumpfd, 'b', v->modeBits); if (!code) code = DumpInt32(dumpfd, 'p', v->parent); if (!code) code = DumpInt32(dumpfd, 's', v->serverModifyTime); if (v->type == vDirectory) { acl_HtonACL(VVnodeDiskACL(v)); if (!code) code = DumpByteString(dumpfd, 'A', (byte *) VVnodeDiskACL(v), VAclDiskSize(v)); } if (VNDISK_GET_INO(v)) { IH_INIT(ihP, V_device(vp), V_parentId(vp), VNDISK_GET_INO(v)); fdP = IH_OPEN(ihP); if (fdP == NULL) { fprintf(stderr, "Unable to open inode %s for vnode %u (volume %i); not dumped, error %d\n", PrintInode(NULL, VNDISK_GET_INO(v)), vnodeNumber, volid, errno); } else { if (verbose) fprintf(stderr, "about to dump inode %s for vnode %u\n", PrintInode(NULL, VNDISK_GET_INO(v)), vnodeNumber); code = DumpFile(dumpfd, vnodeNumber, fdP, v); FDH_CLOSE(fdP); } IH_RELEASE(ihP); } if (verbose) fprintf(stderr, "done dumping vnode %d\n", vnodeNumber); return code; } static int DumpVnodeIndex(int dumpfd, Volume * vp, VnodeClass class, afs_int32 fromtime, int forcedump) { register int code = 0; register struct VnodeClassInfo *vcp = &VnodeClassInfo[class]; char buf[SIZEOF_LARGEDISKVNODE]; struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf; StreamHandle_t *file; FdHandle_t *fdP; int size; int flag; int offset = 0; register int vnodeIndex, nVnodes = 0; fdP = IH_OPEN(vp->vnodeIndex[class].handle); file = FDH_FDOPEN(fdP, "r+"); size = OS_SIZE(fdP->fd_fd); nVnodes = (size / vcp->diskSize) - 1; if (nVnodes > 0) { STREAM_SEEK(file, vcp->diskSize, 0); } else nVnodes = 0; for (vnodeIndex = 0; nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1 && !code; nVnodes--, vnodeIndex++, offset += vcp->diskSize) { flag = forcedump || (vnode->serverModifyTime >= fromtime); /* Note: the >= test is very important since some old volumes may not have * a serverModifyTime. For an epoch dump, this results in 0>=0 test, which * does dump the file! */ if (verbose) fprintf(stderr, "about to dump %s vnode %u (vnode offset = %u)\n", class == vSmall ? "vSmall" : "vLarge", bitNumberToVnodeNumber(vnodeIndex, class), offset); if (!code) code = DumpVnode(dumpfd, vnode, V_id(vp), bitNumberToVnodeNumber(vnodeIndex, class), flag, vp); } STREAM_CLOSE(file); FDH_CLOSE(fdP); return code; } /* A partial dump (no dump header) */ static int DumpPartial(int dumpfd, register Volume * vp, afs_int32 fromtime, int dumpAllDirs) { int code = 0; if (verbose) fprintf(stderr, "about to dump the volume header\n"); if (!code) code = DumpVolumeHeader(dumpfd, vp); if (verbose) fprintf(stderr, "about to dump the large vnode index\n"); if (!code) code = DumpVnodeIndex(dumpfd, vp, vLarge, fromtime, dumpAllDirs); if (verbose) fprintf(stderr, "about to dump the small vnode index\n"); if (!code) code = DumpVnodeIndex(dumpfd, vp, vSmall, fromtime, 0); return code; } static void DoMyVolDump(Volume * vp, struct DiskPartition64 *dp, char *dumpfile, int fromtime) { int code = 0; int dumpAllDirs = 0; int dumpfd = 0; if (dumpfile) { unlink(dumpfile); dumpfd = afs_open(dumpfile, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR); if (dumpfd < 0) { fprintf(stderr, "Failed to open dump file! Exiting.\n"); exit(1); } } else { dumpfd = 1; /* stdout */ } if (verbose) fprintf(stderr, "about to dump the dump header\n"); if (!code) code = DumpDumpHeader(dumpfd, vp, fromtime); if (verbose) fprintf(stderr, "about to dump volume contents\n"); if (!code) code = DumpPartial(dumpfd, vp, fromtime, dumpAllDirs); if (verbose) fprintf(stderr, "about to dump the dump postamble\n"); if (!code) code = DumpEnd(dumpfd); if (verbose) fprintf(stderr, "finished dump\n"); close(dumpfd); /* might be closing stdout, no harm */ }