From: Michael Meffie Date: Fri, 18 Feb 2011 22:56:15 +0000 (-0500) Subject: volscan: display vnode information X-Git-Tag: sna/patch/volscan-display-vnode-info-20110310 X-Git-Url: http://git.sinenomine.net/patch/volscan-display-vnode-info-20110310?p=openafs.git;a=commitdiff_plain;h=refs%2Ftags%2Fsna%2Fpatch%2Fvolscan-display-vnode-info-20110310 volscan: display vnode information Scan volumes on a fileserver to display vnode meta data and relative paths. --- diff --git a/src/vol/.gitignore b/src/vol/.gitignore index abfec40..277e1b0 100644 --- a/src/vol/.gitignore +++ b/src/vol/.gitignore @@ -7,3 +7,4 @@ /gi /fs_conv_sol26 /fssync-debug +/volscan diff --git a/src/vol/Makefile.in b/src/vol/Makefile.in index a60941e..87bc355 100644 --- a/src/vol/Makefile.in +++ b/src/vol/Makefile.in @@ -32,13 +32,14 @@ VLIBOBJS=vnode.o volume.o vutil.o partition.o fssync-server.o fssync-client.o \ clone.o nuke.o devname.o listinodes.o common.o ihandle.o purge.o \ namei_ops.o salvsync-server.o salvsync-client.o daemon_com.o -OBJECTS=${VLIBOBJS} physio.o vol-salvage.o vol-info.o vol-dump.o vol-bless.o fssync-debug.o +OBJECTS=${VLIBOBJS} physio.o vol-salvage.o vol-info.o vol-scan.o vol-dump.o vol-bless.o fssync-debug.o all: gi \ ${TOP_LIBDIR}/vlib.a \ ${TOP_LIBDIR}/libvlib.a \ salvager \ volinfo \ + volscan \ fssync-debug \ $(XFS_SIZE_CHECK) \ ${TOP_INCDIR}/afs/nfs.h \ @@ -113,7 +114,8 @@ ${TOP_INCDIR}/afs/vol_prototypes.h: vol_prototypes.h # # Installation targets # -install: vlib.a salvager volinfo fssync-debug + +install: vlib.a salvager volinfo volscan fssync-debug ${INSTALL} -d ${DESTDIR}${libdir}/afs ${INSTALL} -d ${DESTDIR}${afssrvlibexecdir} ${INSTALL} -d ${DESTDIR}${afssrvsbindir} @@ -122,6 +124,7 @@ install: vlib.a salvager volinfo fssync-debug ${INSTALL_DATA} vlib.a ${DESTDIR}${libdir}/afs/libvlib.a ${INSTALL_PROGRAM} salvager ${DESTDIR}${afssrvlibexecdir}/salvager ${INSTALL_PROGRAM} volinfo ${DESTDIR}${afssrvsbindir}/volinfo + ${INSTALL_PROGRAM} volscan ${DESTDIR}${afssrvsbindir}/volscan ${INSTALL_PROGRAM} fssync-debug ${DESTDIR}${afssrvsbindir}/fssync-debug ${INSTALL_DATA} ${srcdir}/nfs.h ${DESTDIR}${includedir}/afs/nfs.h ${INSTALL_DATA} ${srcdir}/vnode.h ${DESTDIR}${includedir}/afs/vnode.h @@ -141,7 +144,7 @@ install: vlib.a salvager volinfo fssync-debug ${DESTDIR}${afssrvsbindir}/xfs_size_check ; \ fi -dest: vlib.a salvager volinfo fssync-debug +dest: vlib.a salvager volinfo volscan fssync-debug ${INSTALL} -d ${DEST}/lib/afs ${INSTALL} -d ${DEST}/root.server/usr/afs/bin ${INSTALL} -d ${DEST}/include/afs @@ -149,6 +152,7 @@ dest: vlib.a salvager volinfo fssync-debug ${INSTALL_DATA} vlib.a ${DEST}/lib/afs/libvlib.a ${INSTALL_PROGRAM} salvager ${DEST}/root.server/usr/afs/bin/salvager ${INSTALL_PROGRAM} volinfo ${DEST}/root.server/usr/afs/bin/volinfo + ${INSTALL_PROGRAM} volscan ${DEST}/root.server/usr/afs/bin/volscan ${INSTALL_PROGRAM} fssync-debug ${DEST}/root.server/usr/afs/bin/fssync-debug ${INSTALL_DATA} ${srcdir}/nfs.h ${DEST}/include/afs/nfs.h ${INSTALL_DATA} ${srcdir}/vnode.h ${DEST}/include/afs/vnode.h @@ -226,6 +230,12 @@ fssync-debug: fssync-debug.o physio.o AFS_component_version_number.c ${LIBS} $(AFS_LDRULE) fssync-debug.o physio.o \ ${LIBS} $(LIB_roken) ${XLIBS} +vol-scan.o: vol-scan.c + +volscan: vol-scan.o physio.o ihandle.o ${LIBS} + $(AFS_LDRULE) ${LDFLAGS} -o $@ vol-scan.o physio.o \ + ihandle.o ${LIBS} $(LIB_roken) ${XLIBS} + vol-bless: vol-bless.o physio.o ihandle.o ${LIBS} $(AFS_LDRULE) vol-bless.o physio.o \ ${LIBS} $(LIB_roken) ${XLIBS} @@ -238,7 +248,7 @@ xfs_size_check: xfs_size_check.o # clean: $(RM) -f *.o *.a AFS_component_version_number.c - $(RM) -f ${SCMPROGS} ${STAGEPROGS} core salvager volinfo gi fssync-debug + $(RM) -f ${SCMPROGS} ${STAGEPROGS} core salvager volinfo volscan gi fssync-debug test: cd test; $(MAKE) diff --git a/src/vol/vol-scan.c b/src/vol/vol-scan.c new file mode 100644 index 0000000..4796af5 --- /dev/null +++ b/src/vol/vol-scan.c @@ -0,0 +1,1592 @@ +/* + * 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 + * + * Portions Copyright (c) 2006-2011 Sine Nomine Associates + * + */ + +#include +#include + +#include "nfs.h" +#include "lock.h" +#include "ihandle.h" +#include "vnode.h" +#include "volume.h" +#include "partition.h" +#include "salvage.h" +#include "vol_internal.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef AFS_NT40_ENV +#include +#include +#include +#else +#include +#include +#include +#endif + +#ifdef _AIX +#include +#endif + +#ifndef AFS_NT40_ENV +#include "AFS_component_version_number.c" +#endif + +#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 */ + +#define SYNTAX_ERROR 255 +#define PART_NAME_BUFFER_SIZE 256 +#define VOL_HDR_BUFFER_SIZE 256 +#define OLD_NUM_DEVICES 26 /* vicepa to vicepz */ +#define PLACEHOLDER "--" + +/* Command line options */ +#define P_SYNC 0 /* -sync */ +#define P_PARTITION 1 /* -partition */ +#define P_VOLUMEID 2 /* -volumeid */ +#define P_FIND 3 /* -find */ +#define P_OUTPUT 4 /* -output */ +#define P_DELIM 5 /* -delim */ +#define P_NOHEADER 6 /* -noheader */ + +#define SHOW_RW (1<= max_columns) { + fprintf(stderr, "Too many output columns (%d).\n", numColumns); + return 1; + } + for (i = 0; i < sizeof(columnTable) / sizeof(*columnTable); i++) { + if (!strcmp(columnTable[i].name, name)) { + id = columnTable[i].id; + show_column[numColumns++] = id; + if (id == col_host) { + if (gethostname(hostname, sizeof(hostname))) { + strlcpy(hostname, PLACEHOLDER, sizeof(hostname)); + } + } else if (id == col_mtype || id == col_mcell || id == col_mvol) { + parse_mount = 1; + } + return 0; + } + } + fprintf(stderr, "Unknown output name: '%s'.\n", name); + return 1; +} + +/** + * Print right bits as string. + * + * param[in] rights rights bitmap + */ +static void +PrintRights(int rights) +{ + if (rights & PRSFS_READ) { + printf("r"); + } + if (rights & PRSFS_LOOKUP) { + printf("l"); + } + if (rights & PRSFS_INSERT) { + printf("i"); + } + if (rights & PRSFS_DELETE) { + printf("d"); + } + if (rights & PRSFS_WRITE) { + printf("w"); + } + if (rights & PRSFS_LOCK) { + printf("k"); + } + if (rights & PRSFS_ADMINISTER) { + printf("a"); + } + if (rights & PRSFS_USR0) { + printf("A"); + } + if (rights & PRSFS_USR1) { + printf("B"); + } + if (rights & PRSFS_USR2) { + printf("C"); + } + if (rights & PRSFS_USR3) { + printf("D"); + } + if (rights & PRSFS_USR4) { + printf("E"); + } + if (rights & PRSFS_USR5) { + printf("F"); + } + if (rights & PRSFS_USR6) { + printf("G"); + } + if (rights & PRSFS_USR7) { + printf("H"); + } +} + +/** + * Print the cell name portion of the mount point. + */ +static void +PrintMountCell(struct tstate *state) +{ + if (!state->fieldSep) { + printf(PLACEHOLDER); + } else { + *state->fieldSep = '\0'; + printf("%s", state->target + 1); + *state->fieldSep = ':'; + } +} + +/** + * Print the volume name portion of the mount point. + */ +static void +PrintMountVolume(struct tstate *state) +{ + if (!state->fieldSep) { + printf("%s", state->target + 1); + } else { + printf("%s", state->fieldSep + 1); + } +} + +/** + * Print the path to the namei file. + */ +static void +PrintNamei(Volume * vp, VnodeDiskObject * vnode) +{ +#ifdef AFS_NAMEI_ENV + namei_t name; + IHandle_t *ihP = NULL; + Inode ino; + ino = VNDISK_GET_INO(vnode); + IH_INIT(ihP, V_device(vp), V_parentId(vp), ino); + namei_HandleToName(&name, ihP); + printf("%s", name.n_path); + IH_RELEASE(ihP); +#else + printf(PLACEHOLDER); +#endif +} + +/** + * print the volume partition id + */ +static void +PrintPartitionId(Volume * vp) +{ + char *partition = VPartitionPath(V_partition(vp)); + + if (!strncmp(partition, "/vicep", 6)) { + printf("%s", partition + 6); + } else if (!strncmp(partition, "vicep", 5)) { + printf("%s", partition + 5); + } else { + fprintf(stderr, "Invalid partition for volume id %lu\n", + afs_printable_uint32_lu(V_id(vp))); + printf(PLACEHOLDER); + } +} + +/** + * print the column headers + */ +static void +PrintHeader(void) +{ + int i; + columnId id; + const char *name; + + for (i = 0; i < numColumns; i++) { + if (i) { + printf("%s", delim); + } + id = show_column[i]; + name = columnTable[id].name; + while (*name) { + putchar(toupper(*name++)); + } + } + printf("\n"); +} + +/** + * print each column + */ +static void +PrintColumns(struct tstate *state, emitId emit) +{ + int i; + afs_fsize_t length; + Volume *vp = state->vp; + VnodeDiskObject *vnode = state->vnode; + + for (i = 0; i < numColumns; i++) { + if (i) { + printf("%s", delim); + } + switch (show_column[i]) { + case col_host: + printf("%s", hostname); + break; + case col_type: + printf("%s", EmitString(emit, vnode->type)); + break; + case col_vid: + printf("%lu", afs_printable_uint32_lu(V_id(vp))); + break; + case col_vtype: + printf("%s", volumeTypeString(V_type(vp))); + break; + case col_vname: + printf("%s", V_name(vp)); + break; + case col_part: + printf("%s", VPartitionPath(V_partition(vp))); + break; + case col_partid: + PrintPartitionId(vp); + break; + case col_fid: + printf("%lu.%lu.%lu", + afs_printable_uint32_lu(V_id(vp)), + afs_printable_uint32_lu(state->vnodeNumber), + afs_printable_uint32_lu(vnode->uniquifier)); + break; + case col_path: + if (!state->path) { + LookupPath(state); + } + if (state->path) { + printf("/%s", state->path); + } else { + printf(PLACEHOLDER); + } + break; + case col_target: + assert(vnode->type != vSymlink || *state->target); + printf("%s", + (vnode->type == vSymlink + && !state->isMount) ? state->target : PLACEHOLDER); + break; + case col_mount: + printf("%s", + (vnode->type == vSymlink + && state->isMount) ? state->target : PLACEHOLDER); + break; + case col_mtype: + if (state->isMount) { + assert(state->target); + printf("%c", state->target[0]); + } else { + printf(PLACEHOLDER); + } + break; + case col_mcell: + if (state->isMount) { + PrintMountCell(state); + } else { + printf(PLACEHOLDER); + } + break; + case col_mvol: + if (state->isMount) { + PrintMountVolume(state); + } else { + printf(PLACEHOLDER); + } + break; + case col_aid: + if (state->access) { + printf("%d", state->access->id); + } else { + printf(PLACEHOLDER); + } + break; + case col_arights: + if (state->access) { + if (state->isNegRights) { + printf("-"); + } + PrintRights(state->access->rights); + } else { + printf(PLACEHOLDER); + } + break; + case col_vntype: + printf("%s", VnodeTypeString(vnode->type)); + break; + case col_cloned: + printf("%c", vnode->cloned ? 'y' : 'n'); + break; + case col_mode: + printf("0%o", vnode->modeBits); + break; + case col_links: + printf("%lu", afs_printable_uint32_lu(vnode->linkCount)); + break; + case col_length: + VNDISK_GET_LEN(length, vnode); + printf("%llu", length); + break; + case col_uniq: + printf("%lu", afs_printable_uint32_lu(vnode->uniquifier)); + break; + case col_dv: + printf("%lu", afs_printable_uint32_lu(vnode->dataVersion)); + break; + case col_inode: + printf("%" AFS_UINT64_FMT, VNDISK_GET_INO(vnode)); + break; + case col_namei: + PrintNamei(vp, vnode); + break; + case col_modtime: + printf("%lu", afs_printable_uint32_lu(vnode->unixModifyTime)); + break; + case col_author: + printf("%lu", afs_printable_uint32_lu(vnode->author)); + break; + case col_owner: + printf("%lu", afs_printable_uint32_lu(vnode->owner)); + break; + case col_parent: + printf("%lu", afs_printable_uint32_lu(vnode->parent)); + break; + case col_magic: + printf("0x%08X", vnode->vnodeMagic); + break; + case col_lock: + printf("%lu.%lu", afs_printable_uint32_lu(vnode->lock.lockCount), + afs_printable_uint32_lu(vnode->lock.lockTime)); + break; + case col_smodtime: + printf("%lu", afs_printable_uint32_lu(vnode->serverModifyTime)); + break; + case col_group: + printf("%lu", afs_printable_uint32_lu(vnode->group)); + break; + default: + fprintf(stderr, "Unknown column type: %d (%d)\n", show_column[i], + i); + break; + } + } + printf("\n"); +} + +/** + * print each access entry + */ +static void +PrintAcl(struct tstate *state) +{ + int i; + struct acl_accessList *acl = VVnodeDiskACL(state->vnode); + + /* positive acls */ + for (i = 0; i < acl->positive; i++) { + state->isNegRights = 0; + state->access = acl->entries + i; + state->dirty = 1; + PrintColumns(state, emit_access); + } + /* negative acls */ + for (i = (acl->total - 1); i >= (acl->total - acl->negative); i--) { + state->isNegRights = 1; + state->access = acl->entries + i; + state->dirty = 1; + PrintColumns(state, emit_access); + } +} + +/** + * read header file + */ +static int +ReadHeader(IHandle_t * ih, char *to, int size, u_int magic, u_int version) +{ + struct versionStamp *vsn; + int code; + + vsn = (struct versionStamp *)to; + + code = IH_IREAD(ih, 0, to, size); + if (code != size) { + fprintf(stderr, "Inode %s: Failed to read inode.\n", + PrintInode(NULL, ih->ih_ino)); + return -1; + } + + if (vsn->magic != magic) { + fprintf(stderr, "Inode %s: Bad magic %x (%x):\n", + PrintInode(NULL, ih->ih_ino), vsn->magic, magic); + return -1; + } + + if (version && vsn->version != version) { + fprintf(stderr, "Inode %s: Bad version %x (%x)\n", + PrintInode(NULL, ih->ih_ino), vsn->version, version); + return -1; + } + return 0; +} + +/** + * attach volume + * + * Simplified volume attachment for volscan. + */ +static Volume * +SimpleAttachVolume(struct DiskPartition64 *dp, struct VolumeHeader *header) +{ + Volume *vp; + afs_int32 ec = 0; + + vp = &volume_buffer; + 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; + vp->shuttingDown = 0; + vp->goingOffline = 0; + vp->nUsers = 1; + vp->header = &volume_header_buffer; + ec = ReadHeader(V_diskDataHandle(vp), (char *)&V_disk(vp), + sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION); + if (!ec) { + struct IndexFileHeader iHead; + ec = ReadHeader(vp->vnodeIndex[vSmall].handle, (char *)&iHead, + sizeof(iHead), SMALLINDEXMAGIC, SMALLINDEXVERSION); + } + if (!ec) { + struct IndexFileHeader iHead; + ec = ReadHeader(vp->vnodeIndex[vLarge].handle, (char *)&iHead, + sizeof(iHead), LARGEINDEXMAGIC, LARGEINDEXVERSION); + } +#ifdef AFS_NAMEI_ENV + if (!ec) { + struct versionStamp stamp; + ec = ReadHeader(V_linkHandle(vp), (char *)&stamp, sizeof(stamp), + LINKTABLEMAGIC, LINKTABLEVERSION); + } +#endif + if (ec) + return (Volume *) 0; + return vp; +} + +/** + * OS independent file info. + * + */ +static int +GetFileInfo(FD_t fd, int *size) +{ +#ifdef AFS_NT40_ENV + BY_HANDLE_FILE_INFORMATION fi; + if (!GetFileInformationByHandle(fd, &fi)) { + fprintf(stderr, "GetFileInformationByHandle failed.\n"); + return -1; + } + *size = (int)fi.nFileSizeLow; +#else + struct afs_stat status; + if (afs_fstat(fd, &status) == -1) { + fprintf(stderr, "fstat failed, errno %d\n", errno); + return -1; + } + *size = (int)status.st_size; +#endif + return 0; +} + +/** + * open a vnode index given a volume object and vnode class. + * + * @param[inout] state traversal state object + * @param[in] class vnode class id + * + * @pre state object has a valid volume pointer + * + * @post state object has populated fields for class + * + * @return operation status + * @retval 0 success + */ +static int +OpenIndex(struct tstate *state, VnodeClass class) +{ + afs_int32 diskSize = + (class == vSmall ? SIZEOF_SMALLDISKVNODE : SIZEOF_LARGEDISKVNODE); + int size; + + state->vcp[class] = &VnodeClassInfo[class]; + state->diskSize[class] = diskSize; + state->ih[class] = state->vp->vnodeIndex[class].handle; + + state->fdP[class] = IH_OPEN(state->ih[class]); + if (!state->fdP[class]) { + fprintf(stderr, + "Failed to open index file for volume %lu, index class %d.\n", + afs_printable_uint32_lu(V_id(state->vp)), class); + return -1; + } + + if (class == vLarge) { + state->dir_fdP = IH_OPEN(state->ih[class]); + if (!state->dir_fdP) { + fprintf(stderr, + "Failed to open large index file for volume %lu.\n", + afs_printable_uint32_lu(V_id(state->vp))); + return -1; + } + } + + state->file[class] = FDH_FDOPEN(state->fdP[class], "r"); + if (!state->file[class]) { + fprintf(stderr, "fdopen failed for volume %lu.\n", + afs_printable_uint32_lu(V_id(state->vp))); + return -1; + } + + if (GetFileInfo(state->fdP[class]->fd_fd, &size)) { + return -1; + } + + state->nVnodes[class] = (size / diskSize) - 1; + if (state->nVnodes[class] > 0) { + STREAM_ASEEK(state->file[class], diskSize); + } + + return 0; +} + +/** + * close a vnode index by class. + * + * @param[in] state traversal state object + * @param[in] class vnode class id + * + * @post vnode index handles in state object are destroyed + * + * @return operation status + * @retval 0 success + */ +static int +CloseIndex(struct tstate *state, VnodeClass class) +{ + STREAM_CLOSE(state->file[class]); + FDH_CLOSE(state->fdP[class]); + if (class == vLarge) { + FDH_CLOSE(state->dir_fdP); + } + return 0; +} + +/** + * get the VnodeDiskObject for a directory given its vnode id. + * + * @param[in] state traversal state object + * @param[out] vn vnode disk object to populate + * @param[in] vnid vnode id to read + * + * @post vn contains copy of disk object for vnid + * + * @return operation status + * @retval 0 success + * @retval -1 failure + */ +static int +GetDirVN(struct tstate *state, VnodeDiskObject * vn, VnodeId vnid) +{ + afs_int32 code; + off_t off = vnodeIndexOffset(state->vcp[vLarge], vnid); + + code = FDH_SEEK(state->dir_fdP, off, 0); + if (code == -1) { + fprintf(stderr, "GetDirVN: seek failed for %lu.%lu to offset %llu\n", + afs_printable_uint32_lu(V_id(state->vp)), + afs_printable_uint32_lu(vnid), (long long unsigned)off); + return -1; + } + code = FDH_READ(state->dir_fdP, vn, state->diskSize[vLarge]); + if (code != state->diskSize[vLarge]) { + fprintf(stderr, "GetDirVN: read failed for %lu.%lu at offset %llu\n", + afs_printable_uint32_lu(V_id(state->vp)), + afs_printable_uint32_lu(vnid), (long long unsigned)off); + return -1; + } + return 0; +} + +/** + * perform inverse lookup on a vice directory object to map a fid onto a dirent string. + * + * @param[in] state traversal state object + * @param[out] dirent buffer in which to store dirent string + * @param[in] dirent_len length of dirent buffer + * @param[in] pvn directory VnodeDiskObject + * @param[in] pvnid vnode id of pvn + * @param[in] cvnid vnode id to inverse lookup + * @param[in] cuniq uniquifier to inverse lookup + * + * @post dirent contains string for the (cvnid, cuniq) entry + * + * @return operation status + * @retval 0 success + */ +static int +GetDirEntry(struct tstate *state, char *dirent, size_t dirent_len, + VnodeDiskObject * pvn, VnodeId pvnid, VnodeId cvnid, + afs_uint32 cuniq) +{ + DirHandle dir; + Inode ino; + afs_int32 code; + + ino = VNDISK_GET_INO(pvn); + SetSalvageDirHandle(&dir, V_parentId(state->vp), V_device(state->vp), + ino, &code); + code = InverseLookup(&dir, cvnid, cuniq, dirent, dirent_len); + FidZap(&dir); + if (code) { + fprintf(stderr, "InverseLookup failed with code %d\n", code); + } + return code; +} + +/** + * recursively walk up-tree one directory at a time, stopping when we reach the root vnode. + * + * @param[in] state traversal state object + * @param[out] path path string to be filled in + * @param[in] path_size size of the buffer for path + * @param[in] cvn child vnode's VnodeDiskObject + * @param[in] cvnid child vnode id + * + * @pre + * - state object is fully populated + * - path_to_mount points to a valid buffer of size MAXPATHLEN or greater + * - cvn points to a valid VnodeDiskObject + * - cvnid is valid + * + * @post path contains a valid relative path to the vnode from the volume root + * + * @return operation status + * @retval 0 success + */ +static int +RecurseDirHierarchy(struct tstate *state, char *path, size_t path_size, + VnodeDiskObject * cvn, VnodeId cvnid) +{ + int code; + char vnode_buffer[SIZEOF_LARGEDISKVNODE]; + char scratch[MAXPATHLEN]; + char dirent[MAXPATHLEN]; + struct VnodeDiskObject *pvn = (struct VnodeDiskObject *)vnode_buffer; + + if (!cvn->parent) { + return 0; + } + + GetDirVN(state, pvn, cvn->parent); + code = + GetDirEntry(state, dirent, sizeof(dirent), pvn, cvn->parent, cvnid, + cvn->uniquifier); + if (!code) { + if (strlcpy(scratch, path, sizeof(scratch)) >= sizeof(scratch)) { + return -2; + } + if (strlcpy(path, dirent, path_size) >= path_size) { + return -2; + } + if (scratch[0] != '\0') { + if (strlcat(path, "/", path_size) >= path_size) { + return -2; + } + if (strlcat(path, scratch, path_size) >= path_size) { + return -2; + } + } + } + return (code ? code : RecurseDirHierarchy(state, + path, path_size, + pvn, cvn->parent)); +} + +/** + * wrapper around recursive search of directory up-tree. + * + * @param[in] state traversal state object + * + * @pre + * - state object is fully populated + * - state->vnode cvn points to a valid VnodeDiskObject + * - state->vnodeNumber is valid + * + * @post state->path contains a valid relative path to the vnode from the volume root + * + * @return operation status + * @retval 0 success + */ +static int +LookupPath(struct tstate *state) +{ + int code; + VnodeId vnodeNumber = state->vnodeNumber; + VnodeDiskObject *vnode = state->vnode; + + path_buffer[0] = '\0'; + code = + RecurseDirHierarchy(state, path_buffer, sizeof(path_buffer), vnode, + vnodeNumber); + if (!code) { + state->path = path_buffer; + state->dirty = 1; + } else { + fprintf(stderr, + "Failed to lookup path for fid %lu.%lu.%lu.%lu, error code %d\n", + afs_printable_uint32_lu(V_id(state->vp)), + afs_printable_uint32_lu(vnodeNumber), + afs_printable_uint32_lu(vnode->uniquifier), + afs_printable_uint32_lu(vnode->dataVersion), code); + } + return code; +} + +/** + * Retrieve the symlink target from a symlink vnode and determine if + * this is really a mount point. + * + * @param[in] state traversal state object + */ +static int +GetSymlinkTarget(struct tstate *state, char *target, size_t target_buf_size, + afs_uint32 * target_len) +{ + int code; + VnodeDiskObject *vnode = state->vnode; + + IHandle_t *ihP = NULL; + FdHandle_t *fdP = NULL; + afs_fsize_t fileLength; + ssize_t readLength; + Inode ino; + + assert(vnode->type == vSymlink); + ino = VNDISK_GET_INO(vnode); + VNDISK_GET_LEN(fileLength, vnode); + if (fileLength >= target_buf_size) { + fprintf(stderr, "symlink contents for fid (%lu.%lu.%lu.%lu) exceeds " + "%lu, file length is %llu)!\n", + afs_printable_uint32_lu(V_id(state->vp)), + afs_printable_uint32_lu(state->vnodeNumber), + afs_printable_uint32_lu(vnode->uniquifier), + afs_printable_uint32_lu(vnode->dataVersion), target_buf_size, + fileLength); + return -1; + } + if (!fileLength) { + fprintf(stderr, + "symlink contents for fid (%lu.%lu.%lu.%lu) is empty.\n", + afs_printable_uint32_lu(V_id(state->vp)), + afs_printable_uint32_lu(state->vnodeNumber), + afs_printable_uint32_lu(vnode->uniquifier), + afs_printable_uint32_lu(vnode->dataVersion)); + return -1; + } + IH_INIT(ihP, V_device(state->vp), V_parentId(state->vp), ino); + fdP = IH_OPEN(ihP); + if (fdP == NULL) { + code = -1; + goto cleanup; + } + if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) { + code = -1; + goto cleanup; + } + readLength = FDH_READ(fdP, target, fileLength); + if (readLength != fileLength) { + fprintf(stderr, + "symlink contents for fid (%lu.%lu.%lu.%lu) don't match " + "vnode file length metadata (len=%llu, actual=%lld)!\n", + afs_printable_uint32_lu(V_id(state->vp)), + afs_printable_uint32_lu(state->vnodeNumber), + afs_printable_uint32_lu(vnode->uniquifier), + afs_printable_uint32_lu(vnode->dataVersion), fileLength, + (long long)readLength); + code = -1; + goto cleanup; + } + target[readLength] = 0; + *target_len = (afs_uint32) readLength; + code = 0; + + cleanup: + if (fdP) { + FDH_CLOSE(fdP); + } + if (ihP) { + IH_RELEASE(ihP); + } + return code; +} + +/** + * returns true if vnode is a mount point + * + * Reads the vnode to get the target on the first call. + */ +static int +IsMountPoint(struct tstate *state) +{ + if (state->isMount) { + return 1; + } + if (state->vnode->type != vSymlink) { + return 0; + } + if (state->target && !*state->target) { + return 0; + } + if (!state->target) { + int code; + afs_uint32 len = 0; + state->target = target_buffer; + state->dirty = 1; + + code = + GetSymlinkTarget(state, target_buffer, sizeof(target_buffer), + &len); + if (code) { + state->target[0] = '\0'; + return 0; + } + + if ((len > 1) + && ((state->target[0] == '#') || (state->target[0] == '%')) + && (state->target[len - 1] == '.')) { + state->target[len - 1] = '\0'; /* trim trailing dot */ + if (parse_mount) { + state->fieldSep = strchr(state->target, ':'); + } + state->isMount = 1; + } + } + return state->isMount; +} + +/** + * clear vnode info used during scans + */ +static_inline void +ClearVnodeInfo(struct tstate *state) +{ + state->path = NULL; + state->target = NULL; + state->fieldSep = NULL; + state->access = NULL; + state->isNegRights = 0; + state->isMount = 0; + state->dirty = 0; +} + +/** + * scan the vnode index and dispatch vnodes of interest. + * + */ +static void +ScanIndex(struct tstate *state, VnodeClass class) +{ + afs_int32 diskSize = VnodeClassInfo[class].diskSize; + char vnodebuf[SIZEOF_LARGEDISKVNODE]; + struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)vnodebuf; + int vnodeIndex = 0; + int nVnodes = 0; + int offset = 0; + + nVnodes = state->nVnodes[class]; + ClearVnodeInfo(state); + + for (vnodeIndex = 0; + nVnodes + && STREAM_READ(vnodebuf, diskSize, 1, state->file[class]) == 1; + nVnodes--, vnodeIndex++, offset += diskSize) { + + vnode = (struct VnodeDiskObject *)vnodebuf; + + if (vnode->type > vSymlink) { + fprintf(stderr, "Invalid vnode type %u for vnode %u\n", + vnode->type, state->vnodeNumber); + continue; + } + + state->vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class); + state->vnode = vnode; + + if (vnfilter && (vnfilter & (1 << vnode->type)) + && !IsMountPoint(state)) { + PrintColumns(state, emit_vnode); + } + if (show_mounts && IsMountPoint(state)) { + PrintColumns(state, emit_mount); + } + if (show_acls && (vnode->type == vDirectory)) { + PrintAcl(state); + } + if (state->dirty) { + ClearVnodeInfo(state); + } + } +} + +/** + * Scan the volume vnodes. + * + * Read the volume header and scan the volume indexes. + * + * @param[in] dp partition object + * @param[in] name volume header filename + */ +static int +ScanVolume(struct DiskPartition64 *dp, char *name) +{ + struct VolumeHeader header; + struct VolumeDiskHeader diskHeader; + struct afs_stat status; + int fd; + Volume *vp; + char headerName[1024]; + afs_int32 n; + + afs_snprintf(headerName, sizeof(headerName), "%s/%s", + VPartitionPath(dp), name); + if ((fd = afs_open(headerName, O_RDONLY)) == -1) { + fprintf(stderr, "Cannot open volume header %s on partition %s\n", + name, VPartitionPath(dp)); + return -1; + } + + if (afs_fstat(fd, &status) == -1) { + fprintf(stderr, "Cannot stat volume header %s\n", name); + close(fd); + return -1; + } + + n = read(fd, &diskHeader, sizeof(diskHeader)); + if (n != sizeof(diskHeader) + || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) { + fprintf(stderr, "Error reading volume header %s\n", name); + close(fd); + return -1; + } + if (diskHeader.stamp.version != VOLUMEHEADERVERSION) { + printf + ("Volume %s, version number is incorrect; volume needs salvage\n", + name); + close(fd); + return -1; + } + + DiskToVolumeHeader(&header, &diskHeader); + close(fd); + + if (is_online) { + FSYNC_VolOp(header.id, dp->name, FSYNC_VOL_NEEDVOLUME, V_DUMP, NULL); + } + + vp = SimpleAttachVolume(dp, &header); + if (!vp) { + fprintf(stderr, "Error attaching volume header %s\n", name); + return -1; + } + + if (V_type(vp) < 0 || V_type(vp) >= VOLMAXTYPES) { + fprintf(stderr, "Invalid volume type %d for volume %s\n", V_type(vp), + name); + } else if (volfilter & (1 << V_type(vp))) { + struct tstate state; + + memset((void *)&state, 0, sizeof(struct tstate)); + state.vp = vp; + + if (OpenIndex(&state, vLarge)) { /* needed for GetDirVN */ + goto done; + } + + if (scan_index[vSmall]) { + if (OpenIndex(&state, vSmall)) { + CloseIndex(&state, vLarge); + goto done; + } + } + + if (scan_index[vLarge]) { + ScanIndex(&state, vLarge); + } + if (scan_index[vSmall]) { + ScanIndex(&state, vSmall); + } + + CloseIndex(&state, vLarge); + if (scan_index[vSmall]) { + CloseIndex(&state, vSmall); + } + } + + done: + if (is_online) { + FSYNC_VolOp(header.id, dp->name, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL); + } + + return 0; +} + +/** + * scan the partition for vnodes. + * + * param + */ +static int +ScanPartition(struct DiskPartition64 *partP) +{ + int code; + DIR *dirp; + struct dirent *dp; +#ifdef AFS_NT40_ENV + char pname[64]; + char *p = pname; + (void)sprintf(pname, "%s\\", VPartitionPath(partP)); +#else + char *p = VPartitionPath(partP); +#endif + + if (chdir(p) == -1) { + fprintf(stderr, "Can't chdir to partition %s; giving up\n", p); + code = -1; + return code; + } + if ((dirp = opendir(".")) == NULL) { + fprintf(stderr, "Can't read directory %s; giving up\n", p); + code = -1; + return code; + } + while ((dp = readdir(dirp))) { + p = (char *)strrchr(dp->d_name, '.'); + if (p != NULL && strcmp(p, VHDREXT) == 0) { + code = ScanVolume(partP, dp->d_name); + if (code) { + closedir(dirp); + return code; + } + } + } + closedir(dirp); + return 0; +} + +/** + * scan all partitions present + */ +static int +ScanAllPartitions(void) +{ + int code; + struct DiskPartition64 *partP; + + for (partP = DiskPartitionList; partP; partP = partP->next) { + code = ScanPartition(partP); + if (code) { + return code; + } + } + return 0; +} + + +/** + * Initialize the volscan program. + * + * @param online use fssync connection if true + * + * @return operation success + * @retval 0 success + * @retval -1 error + */ +static int +Init(int online) +{ + if (online) { + if (FSYNC_clientInit() != 0) { + fprintf(stderr, "Failed to connect to fileserver.\n"); + return -1; + } + is_online = 1; + } + DInit(1024); + VInitVnodes(vLarge, 0); + VInitVnodes(vSmall, 0); + if (VAttachPartitions()) { + fprintf(stderr, "Failed to attach partitions.\n"); + return -1; + } + return 0; +} + +/** + * Shutdown connection if present. + */ +static int +Shutdown(void) +{ + if (is_online) { + FSYNC_clientFinis(); + } + return 0; +} + +/** + * Convert the partition device number into a partition name. + * + * @param[in] partId partition number, 0 to 254 + * @param[out] partName buffer to hold partition name (e.g. /vicepa) + * @param[in] partNameSize size of partName buffer + * + * @return status + * @retval 0 success + * @retval -1 error, partId is out of range + * @retval -3 error, partition name exceeds partNameSize + */ +static int +GetPartitionName(afs_uint32 partId, char *partName, size_t partNameSize) +{ + if (partId < OLD_NUM_DEVICES) { + if (partNameSize < 8) { + return -2; + } + strlcpy(partName, "/vicep", partNameSize); + partName[6] = partId + 'a'; + partName[7] = '\0'; + return 0; + } + if (partId < VOLMAXPARTS) { + if (partNameSize < 9) { + return -2; + } + strlcpy(partName, "/vicep", partNameSize); + partId -= OLD_NUM_DEVICES; + partName[6] = 'a' + (partId / OLD_NUM_DEVICES); + partName[7] = 'a' + (partId % OLD_NUM_DEVICES); + partName[8] = '\0'; + return 0; + } + return -1; +} + +/** + * Setup and run volume scanner. + * + * @param[in] as command line parameters + * @param[in] arock opaque data (not used) + * + * @return status + * @retval 0 success + * @retval 255 error + */ +static int +Scan(struct cmd_syndesc *as, void *arock) +{ + int code; + struct cmd_item *ti; + afs_uint32 volumeid = 0; + char partName[PART_NAME_BUFFER_SIZE] = "\0"; + char volumeHeaderName[VOL_HDR_BUFFER_SIZE] = "\0"; + struct DiskPartition64 *dp = NULL; + + if (as->parms[P_VOLUMEID].items && !as->parms[P_PARTITION].items) { + fprintf(stderr, + "Must specify -partition with the -volumeid argument.\n"); + return SYNTAX_ERROR; + } + + /* -partition */ + ti = as->parms[P_PARTITION].items; + if (ti) { + afs_uint32 partId = volutil_GetPartitionID(ti->data); + if (partId == -1) { + fprintf(stderr, "Could not parse '%s' as a partition name.\n", + ti->data); + return SYNTAX_ERROR; + } + if (GetPartitionName(partId, partName, sizeof(partName))) { + fprintf(stderr, "Could not format '%s' as a partition name.\n", + ti->data); + return SYNTAX_ERROR; + } + } + + /* -volumeid */ + ti = as->parms[P_VOLUMEID].items; + if (ti) { + volumeid = strtoul(ti->data, NULL, 10); + if (!volumeid) { + fprintf(stderr, "-volumeid must be numeric.\n"); + return SYNTAX_ERROR; + } + afs_snprintf(volumeHeaderName, sizeof(volumeHeaderName), VFORMAT, + afs_printable_uint32_lu(volumeid)); + } + + /* which vnode types */ + for (ti = as->parms[P_FIND].items; ti; ti = ti->next) { + if (!strcmp(ti->data, "rw")) { + volfilter |= SHOW_RW; + } else if (!strcmp(ti->data, "ro")) { + volfilter |= SHOW_RO; + } else if (!strcmp(ti->data, "bk")) { + volfilter |= SHOW_BK; + } else if (!strcmp(ti->data, "files")) { + vnfilter |= SHOW_FILE; + } else if (!strcmp(ti->data, "dirs")) { + vnfilter |= SHOW_DIR; + } else if (!strcmp(ti->data, "symlinks")) { + vnfilter |= SHOW_SYMLINK; + } else if (!strcmp(ti->data, "mounts") || !strcmp(ti->data, "mtpt")) { + show_mounts = 1; + } else if (!strcmp(ti->data, "acls") || !strcmp(ti->data, "access")) { + show_acls = 1; + } else { + fprintf(stderr, + "-find must be one or more of: rw ro bk files dirs mounts symlinks acls\n"); + return SYNTAX_ERROR; + } + } + + /* default to show all */ + if (!volfilter) { + volfilter = SHOW_RW | SHOW_RO | SHOW_BK; + } + if (!vnfilter && !show_mounts && !show_acls) { + vnfilter = SHOW_FILE | SHOW_DIR | SHOW_SYMLINK; + show_mounts = 1; + show_acls = 1; + } + + /* which indexes do we need to scan */ + if ((vnfilter & SHOW_DIR) || show_acls) { + scan_index[vLarge] = 1; + } + if ((vnfilter & (SHOW_FILE | SHOW_SYMLINK)) || show_mounts) { + scan_index[vSmall] = 1; + } + + /* user specified output format */ + for (code = 0, ti = as->parms[P_OUTPUT].items; ti; ti = ti->next) { + code |= AddColumn(ti->data); + } + if (code) { + fprintf(stderr, "Valid names: %s\n", columnNames); + return SYNTAX_ERROR; + } + if (!numColumns) { + /* default columns */ + AddColumn("type"); + AddColumn("fid"); + AddColumn("dv"); + AddColumn("length"); + AddColumn("target"); + AddColumn("mount"); + AddColumn("aid"); + AddColumn("arights"); + AddColumn("path"); + } + + /* user specified field delimiter */ + ti = as->parms[P_DELIM].items; + if (ti) { + if (!strcmp(ti->data, "\\t")) { + delim = "\t"; + } else { + delim = ti->data; + } + } + + /* print optional column header line */ + if (!as->parms[P_NOHEADER].items) { + PrintHeader(); + } + + code = Init((as->parms[P_SYNC].items ? 1 : 0)); + if (!code) { + if (!partName[0]) { + code = ScanAllPartitions(); + } else { + dp = VGetPartition(partName, 0); + if (!dp) { + fprintf(stderr, + "%s is not an AFS partition name on this server.\n", + partName); + code = -1; + } else if (!volumeHeaderName[0]) { + code = ScanPartition(dp); + } else { + code = ScanVolume(dp, volumeHeaderName); + } + } + Shutdown(); + } + return code; +} + +/** + * volscan main function + * + */ +int +main(int argc, char **argv) +{ + struct cmd_syndesc *ts; + afs_int32 code; + +#ifndef AFS_NT40_ENV + if (geteuid() != 0) { + fprintf(stderr, "volscan must be run as root.\n"); + return 1; + } +#endif + + ts = cmd_CreateSyntax(NULL, Scan, NULL, "Scan volumes"); + cmd_AddParm(ts, "-sync", CMD_FLAG, CMD_OPTIONAL, + "sync with running fileserver"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, + "partition to scan"); + cmd_AddParm(ts, "-volumeid", CMD_SINGLE, CMD_OPTIONAL, + "volume id to scan (partition required)"); + cmd_AddParm(ts, "-find", CMD_LIST, CMD_OPTIONAL, + "rw, ro, bk, files, dirs, mounts, symlinks, acls"); + cmd_AddParm(ts, "-output", CMD_LIST, CMD_OPTIONAL, columnNames); + cmd_AddParm(ts, "-delim", CMD_SINGLE, CMD_OPTIONAL, "field delimiter"); + cmd_AddParm(ts, "-noheader", CMD_FLAG, CMD_OPTIONAL, + "do not print header line"); + + code = cmd_Dispatch(argc, argv); + return code; +}