From 2cd1335257f069d534603c755fa18c4ee2cb4b5e Mon Sep 17 00:00:00 2001 From: Andrew Deason Date: Wed, 27 Jan 2010 15:08:34 -0600 Subject: [PATCH] Abstract /vicepX header traversal The code for examining all of the headers on a vice partition now exists in two places: the VGC scanner, and the salvager. Create the VWalkVolumeHeaders function that contains the common logic so it's in one place, and take a couple of function callbacks for the differences. Change-Id: I12c71b3344ffbd0b22ddf5418f9ff0509734f42b Reviewed-on: http://gerrit.openafs.org/1264 Tested-by: Andrew Deason Reviewed-by: Derrick Brashear --- src/vol/vg_scan.c | 160 +++++--------------- src/vol/vol-salvage.c | 333 ++++++++++++++++++++++++++++-------------- src/vol/vol-salvage.h | 2 +- src/vol/volume.h | 48 ++++++ src/vol/vutil.c | 162 ++++++++++++++++++++ 5 files changed, 472 insertions(+), 233 deletions(-) diff --git a/src/vol/vg_scan.c b/src/vol/vg_scan.c index 99bc6f168..c4080bebb 100644 --- a/src/vol/vg_scan.c +++ b/src/vol/vg_scan.c @@ -206,113 +206,52 @@ _VVGC_scan_table_flush(VVGCache_scan_table_t * tbl, } /** - * read a volume header from disk into a VolumeHeader structure. + * record a volume header found by VWalkVolumeHeaders in a VGC scan table. * - * @param[in] path absolute path to .vol volume header - * @param[out] hdr volume header object + * @param[in] dp the disk partition + * @param[in] name full path to the .vol header (unused) + * @param[in] hdr the header data + * @param[in] last whether this is the last try or not (unused) + * @param[in] rock actually a VVGCache_scan_table_t* to add the volume to * * @return operation status - * @retval 0 success - * @retval ENOENT volume header does not exist - * @retval EINVAL volume header is invalid - * - * @internal + * @retval 0 success + * @retval -1 fatal error adding vol to the scan table */ static int -_VVGC_read_header(const char *path, struct VolumeHeader *hdr) +_VVGC_RecordHeader(struct DiskPartition64 *dp, const char *name, + struct VolumeDiskHeader *hdr, int last, void *rock) { - int fd; int code; - struct VolumeDiskHeader diskHeader; - - fd = afs_open(path, O_RDONLY); - if (fd == -1) { - ViceLog(0, ("_VVGC_read_header: could not open %s; error = %d\n", - path, errno)); - return ENOENT; - } - - code = read(fd, &diskHeader, sizeof(diskHeader)); - close(fd); - if (code != sizeof(diskHeader)) { - ViceLog(0, ("_VVGC_read_header: could not read disk header from %s; error = %d\n", - path, errno)); - return EINVAL; - } + VVGCache_scan_table_t *tbl; + tbl = (VVGCache_scan_table_t *)rock; - if (diskHeader.stamp.magic != VOLUMEHEADERMAGIC) { - ViceLog(0, ("_VVGC_read_header: disk header %s has magic %lu, should " - "be %lu\n", path, - afs_printable_uint32_lu(diskHeader.stamp.magic), - afs_printable_uint32_lu(VOLUMEHEADERMAGIC))); - return EINVAL; + code = _VVGC_scan_table_add(tbl, dp, hdr->id, hdr->parent); + if (code) { + ViceLog(0, ("VVGC_scan_partition: error %d adding volume %s to scan table\n", + code, name)); + return -1; } - - DiskToVolumeHeader(hdr, &diskHeader); return 0; } /** - * determines what to do with a volume header during a VGC scan. + * unlink a faulty volume header found by VWalkVolumeHeaders. * - * @param[in] dp the disk partition object - * @param[in] node_path the absolute path to the header to handle - * @param[out] hdr the header read in from disk - * @param[out] skip 1 if we should skip the header (pretend it doesn't - * exist), 0 otherwise - * - * @return operation status - * @retval 0 success - * @retval -1 internal error beyond just failing to read the header file + * @param[in] dp the disk partition (unused) + * @param[in] name the full path to the .vol header + * @param[in] hdr the header data (unused) + * @param[in] rock unused */ -static int -_VVGC_handle_header(struct DiskPartition64 *dp, const char *node_path, - struct VolumeHeader *hdr, int *skip) +static void +_VVGC_UnlinkHeader(struct DiskPartition64 *dp, const char *name, + struct VolumeDiskHeader *hdr, void *rock) { - int code; - - *skip = 1; - - code = _VVGC_read_header(node_path, hdr); - if (code) { - /* retry while holding a partition write lock, to ensure we're not - * racing a writer/creator of the header */ - - if (code == ENOENT) { - /* Ignore ENOENT; it's as if we never got it from readdir in the - * first place. Other error codes means the header exists, but - * there's something wrong with it. */ - return 0; - } - - code = VPartHeaderLock(dp, WRITE_LOCK); - if (code) { - ViceLog(0, ("_VVGC_handle_header: error acquiring partition " - "write lock while trying to open %s\n", - node_path)); - return -1; - } - code = _VVGC_read_header(node_path, hdr); - VPartHeaderUnlock(dp, WRITE_LOCK); - } - - if (code) { - if (code != ENOENT) { - ViceLog(0, ("_VVGC_scan_partition: %s does not appear to be a " - "legitimate volume header file; deleted\n", - node_path)); - - if (unlink(node_path)) { - ViceLog(0, ("Unable to unlink %s (errno = %d)\n", - node_path, errno)); - } - } - return 0; + ViceLog(0, ("%s is not a legitimate volume header file; deleted\n", name)); + if (unlink(name)) { + ViceLog(0, ("Unable to unlink %s (errno = %d)\n", + name, errno)); } - - /* header is fine; do not skip it, and do not error out */ - *skip = 0; - return 0; } /** @@ -332,13 +271,10 @@ _VVGC_handle_header(struct DiskPartition64 *dp, const char *node_path, static int _VVGC_scan_partition(struct DiskPartition64 * part) { - int code, res, skip; + int code, res; DIR *dirp = NULL; - struct VolumeHeader hdr; - struct dirent *dp; VVGCache_scan_table_t tbl; - char *part_path = NULL, *p; - char node_path[MAXPATHLEN]; + char *part_path = NULL; code = _VVGC_scan_table_init(&tbl); if (code) { @@ -376,36 +312,10 @@ _VVGC_scan_partition(struct DiskPartition64 * part) ViceLog(5, ("VVGC_scan_partition: scanning partition %s for VG cache\n", part_path)); - while ((dp = readdir(dirp))) { - p = strrchr(dp->d_name, '.'); - if (p == NULL || strcmp(p, VHDREXT) != 0) { - continue; - } - snprintf(node_path, - sizeof(node_path), - "%s/%s", - VPartitionPath(part), - dp->d_name); - - res = _VVGC_handle_header(part, node_path, &hdr, &skip); - if (res) { - /* internal error; error out */ - code = -1; - goto done; - } - if (skip) { - continue; - } - - res = _VVGC_scan_table_add(&tbl, - part, - hdr.id, - hdr.parent); - if (res) { - ViceLog(0, ("VVGC_scan_partition: error %d adding volume %s to scan table\n", - res, node_path)); - code = res; - } + code = VWalkVolumeHeaders(part, part_path, _VVGC_RecordHeader, + _VVGC_UnlinkHeader, &tbl); + if (code < 0) { + goto done; } _VVGC_scan_table_flush(&tbl, part); diff --git a/src/vol/vol-salvage.c b/src/vol/vol-salvage.c index 5c90fd16a..bf3c325a7 100644 --- a/src/vol/vol-salvage.c +++ b/src/vol/vol-salvage.c @@ -1361,14 +1361,217 @@ AskVolumeSummary(VolumeId singleVolumeNumber) return code; } +/** + * count how many volume headers are found by VWalkVolumeHeaders. + * + * @param[in] dp the disk partition (unused) + * @param[in] name full path to the .vol header (unused) + * @param[in] hdr the header data (unused) + * @param[in] last whether this is the last try or not (unused) + * @param[in] rock actually an afs_int32*; the running count of how many + * volumes we have found + * + * @retval 0 always + */ +static int +CountHeader(struct DiskPartition64 *dp, const char *name, + struct VolumeDiskHeader *hdr, int last, void *rock) +{ + afs_int32 *nvols = (afs_int32 *)rock; + (*nvols)++; + return 0; +} + +/** + * parameters to pass to the VWalkVolumeHeaders callbacks when recording volume + * data. + */ +struct SalvageScanParams { + VolumeId singleVolumeNumber; /**< 0 for a partition-salvage, otherwise the + * vol id of the VG we're salvaging */ + struct VolumeSummary *vsp; /**< ptr to the current volume summary object + * we're filling in */ + afs_int32 nVolumes; /**< # of vols we've encountered */ + afs_int32 totalVolumes; /**< max # of vols we should encounter (the + * # of vols we've alloc'd memory for) */ +}; + +/** + * records volume summary info found from VWalkVolumeHeaders. + * + * Found volumes are also taken offline if they are in the specific volume + * group we are looking for. + * + * @param[in] dp the disk partition + * @param[in] name full path to the .vol header + * @param[in] hdr the header data + * @param[in] last 1 if this is the last try to read the header, 0 otherwise + * @param[in] rock actually a struct SalvageScanParams*, containing the + * information needed to record the volume summary data + * + * @return operation status + * @retval 0 success + * @retval 1 volume header is mis-named and should be deleted + */ +static int +RecordHeader(struct DiskPartition64 *dp, const char *name, + struct VolumeDiskHeader *hdr, int last, void *rock) +{ + char nameShouldBe[64]; + struct SalvageScanParams *params; + struct VolumeSummary summary; + VolumeId singleVolumeNumber; + + params = (struct SalvageScanParams *)rock; + + singleVolumeNumber = params->singleVolumeNumber; + + DiskToVolumeHeader(&summary.header, hdr); + + if (singleVolumeNumber && summary.header.id == singleVolumeNumber + && summary.header.parent != singleVolumeNumber) { + + if (programType == salvageServer) { +#ifdef SALVSYNC_BUILD_CLIENT + Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n", + summary.header.id, summary.header.parent); + if (SALVSYNC_LinkVolume(summary.header.parent, + summary.header.id, + dp->name, + NULL) != SYNC_OK) { + Log("schedule request failed\n"); + } +#endif + Exit(SALSRV_EXIT_VOLGROUP_LINK); + + } else { + Log("%u is a read-only volume; not salvaged\n", + singleVolumeNumber); + Exit(1); + } + } + + if (!singleVolumeNumber || summary.header.id == singleVolumeNumber + || summary.header.parent == singleVolumeNumber) { + + /* check if the header file is incorrectly named */ + int badname = 0; + const char *base = strrchr(name, '/'); + if (base) { + base++; + } else { + base = name; + } + + (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe, + VFORMAT, afs_printable_uint32_lu(summary.header.id)); + + + if (strcmp(nameShouldBe, base)) { + /* .vol file has wrong name; retry/delete */ + badname = 1; + } + + if (!badname || last) { + /* only offline the volume if the header is good, or if this is + * the last try looking at it; avoid AskOffline'ing the same vol + * multiple times */ + + if (singleVolumeNumber + && summary.header.id != singleVolumeNumber) { + /* don't offline singleVolumeNumber; we already did that + * earlier */ + + AskOffline(summary.header.id, fileSysPartition->name); + } + } + if (badname) { + if (last && !Showmode) { + Log("Volume header file %s is incorrectly named (should be %s " + "not %s); %sdeleted (it will be recreated later, if " + "necessary)\n", name, nameShouldBe, base, + (Testing ? "it would have been " : "")); + } + return 1; + } + + summary.fileName = ToString(base); + params->nVolumes++; + + if (params->nVolumes > params->totalVolumes) { + /* We found more volumes than we found on the first partition walk; + * apparently something created a volume while we were + * partition-salvaging, or we found more than 20 vols when salvaging a + * particular volume. Abort if we detect this, since other programs + * supposed to not touch the partition while it is partition-salvaging, + * and we shouldn't find more than 20 vols in a VG. + */ + Abort("Found %ld vol headers, but should have found at most %ld! " + "Make sure the volserver/fileserver are not running at the " + "same time as a partition salvage\n", + afs_printable_int32_ld(params->nVolumes), + afs_printable_int32_ld(params->totalVolumes)); + } + + memcpy(params->vsp, &summary, sizeof(summary)); + params->vsp++; + } + + return 0; +} + +/** + * possibly unlinks bad volume headers found from VWalkVolumeHeaders. + * + * If the header could not be read in at all, the header is always unlinked. + * If instead RecordHeader said the header was bad (that is, the header file + * is mis-named), we only unlink if we are doing a partition salvage, as + * opposed to salvaging a specific volume group. + * + * @param[in] dp the disk partition + * @param[in] name full path to the .vol header + * @param[in] hdr header data, or NULL if the header could not be read + * @param[in] rock actually a struct SalvageScanParams*, with some information + * about the scan + */ +static void +UnlinkHeader(struct DiskPartition64 *dp, const char *name, + struct VolumeDiskHeader *hdr, void *rock) +{ + struct SalvageScanParams *params; + int dounlink = 0; + + params = (struct SalvageScanParams *)rock; + + if (!hdr) { + /* no header; header is too bogus to read in at all */ + if (!Showmode) { + Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : "")); + } + if (!Testing) { + dounlink = 1; + } + + } else if (!params->singleVolumeNumber) { + /* We were able to read in a header, but RecordHeader said something + * was wrong with it. We only unlink those if we are doing a partition + * salvage. */ + if (!Testing) { + dounlink = 1; + } + } + + if (dounlink && unlink(name)) { + Log("Error %d while trying to unlink %s\n", errno, name); + } +} + void GetVolumeSummary(VolumeId singleVolumeNumber) { - DIR *dirp = NULL; afs_int32 nvols = 0; - struct VolumeSummary *vsp, vs; - struct VolumeDiskHeader diskHeader; - struct dirent *dp; + struct SalvageScanParams params; + int code; if (AskVolumeSummary(singleVolumeNumber) == 0) { /* we successfully got the vol information from the fileserver; no @@ -1376,34 +1579,13 @@ GetVolumeSummary(VolumeId singleVolumeNumber) return; } - /* Get headers from volume directory */ - dirp = opendir(fileSysPath); - if (dirp == NULL) - Abort("Can't read directory %s; not salvaged\n", fileSysPath); if (!singleVolumeNumber) { - while ((dp = readdir(dirp))) { - char *p = dp->d_name; - p = strrchr(dp->d_name, '.'); - if (p != NULL && strcmp(p, VHDREXT) == 0) { - int fd; - char name[64]; - sprintf(name, "%s/%s", fileSysPath, dp->d_name); - if ((fd = afs_open(name, O_RDONLY)) != -1 - && read(fd, (char *)&diskHeader, sizeof(diskHeader)) - == sizeof(diskHeader) - && diskHeader.stamp.magic == VOLUMEHEADERMAGIC) { - DiskToVolumeHeader(&vs.header, &diskHeader); - nvols++; - } - close(fd); - } + /* Count how many volumes we have in /vicepX */ + code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, CountHeader, + NULL, &nvols); + if (code < 0) { + Abort("Can't read directory %s; not salvaged\n", fileSysPath); } -#ifdef AFS_NT40_ENV - closedir(dirp); - dirp = opendir("."); /* No rewinddir for NT */ -#else - rewinddir(dirp); -#endif if (!nvols) nvols = 1; } else { @@ -1413,83 +1595,20 @@ GetVolumeSummary(VolumeId singleVolumeNumber) volumeSummaryp = malloc(nvols * sizeof(struct VolumeSummary)); assert(volumeSummaryp != NULL); - nVolumes = 0; - vsp = volumeSummaryp; - while ((dp = readdir(dirp))) { - char *p = dp->d_name; - p = strrchr(dp->d_name, '.'); - if (p != NULL && strcmp(p, VHDREXT) == 0) { - int error = 0; - int fd; - char name[64]; - sprintf(name, "%s/%s", fileSysPath, dp->d_name); - if ((fd = afs_open(name, O_RDONLY)) == -1 - || read(fd, &diskHeader, sizeof(diskHeader)) - != sizeof(diskHeader) - || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) { - error = 1; - } - close(fd); - if (error) { - if (!singleVolumeNumber) { - if (!Showmode) - Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : "")); - if (!Testing) { - if (unlink(name)) { - Log("Unable to unlink %s (errno = %d)\n", name, errno); - } - } - } - } else { - char nameShouldBe[64]; - DiskToVolumeHeader(&vsp->header, &diskHeader); - if (singleVolumeNumber && vsp->header.id == singleVolumeNumber - && vsp->header.parent != singleVolumeNumber) { - if (programType == salvageServer) { -#ifdef SALVSYNC_BUILD_CLIENT - Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n", - vsp->header.id, vsp->header.parent); - if (SALVSYNC_LinkVolume(vsp->header.parent, - vsp->header.id, - fileSysPartition->name, - NULL) != SYNC_OK) { - Log("schedule request failed\n"); - } -#endif - Exit(SALSRV_EXIT_VOLGROUP_LINK); - } else { - Log("%u is a read-only volume; not salvaged\n", - singleVolumeNumber); - Exit(1); - } - } - if (!singleVolumeNumber - || (vsp->header.id == singleVolumeNumber - || vsp->header.parent == singleVolumeNumber)) { - (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe, - VFORMAT, afs_printable_uint32_lu(vsp->header.id)); - if (singleVolumeNumber - && vsp->header.id != singleVolumeNumber) - AskOffline(vsp->header.id, fileSysPartition->name); - if (strcmp(nameShouldBe, dp->d_name)) { - if (!Showmode) - Log("Volume header file %s is incorrectly named; %sdeleted (it will be recreated later, if necessary)\n", name, (Testing ? "it would have been " : "")); - if (!Testing) { - if (unlink(name)) { - Log("Unable to unlink %s (errno = %d)\n", name, errno); - } - } - } else { - vsp->fileName = ToString(dp->d_name); - nVolumes++; - vsp++; - } - } - } - close(fd); - } + params.singleVolumeNumber = singleVolumeNumber; + params.vsp = volumeSummaryp; + params.nVolumes = 0; + params.totalVolumes = nvols; + + /* walk the partition directory of volume headers and record the info + * about them; unlinking invalid headers */ + code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, RecordHeader, + UnlinkHeader, ¶ms); + if (code < 0) { + Abort("Failed to get volume header summary\n"); } - closedir(dirp); + nVolumes = params.nVolumes; + qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary), CompareVolumes); } @@ -3901,7 +4020,7 @@ Abort(const char *format, ...) } char * -ToString(char *s) +ToString(const char *s) { register char *p; p = (char *)malloc(strlen(s) + 1); diff --git a/src/vol/vol-salvage.h b/src/vol/vol-salvage.h index b0900d356..002e12ba7 100644 --- a/src/vol/vol-salvage.h +++ b/src/vol/vol-salvage.h @@ -220,7 +220,7 @@ extern int canfork; extern void Exit(int code); extern int Fork(void); extern int Wait(char *prog); -extern char *ToString(char *s); +extern char *ToString(const char *s); extern void AskOffline(VolumeId volumeId, char * partition); extern void AskOnline(VolumeId volumeId, char *partition); extern void CheckLogFile(char * log_path); diff --git a/src/vol/volume.h b/src/vol/volume.h index 33b4aeb14..227ec67bc 100644 --- a/src/vol/volume.h +++ b/src/vol/volume.h @@ -877,6 +877,54 @@ extern afs_int32 VCreateVolumeDiskHeader(VolumeDiskHeader_t * hdr, extern afs_int32 VDestroyVolumeDiskHeader(struct DiskPartition64 * dp, VolumeId volid, VolumeId parent); +/** + * VWalkVolumeHeaders header callback. + * + * @param[in] dp disk partition + * @param[in] name full path to the .vol header file + * @param[in] hdr the header data that was read from the .vol header + * @param[in] last 1 if this is the last attempt to read the vol header, 0 + * otherwise. DAFS VWalkVolumeHeaders will retry reading the + * header once, if a non-fatal error occurs when reading the + * header, or if this function returns a positive error code. + * So, if there is a problem, this function will be called + * first with last=0, then with last=1, then the error function + * callback will be called. For non-DAFS, this is always 1. + * @param[in] rock the rock passed to VWalkVolumeHeaders + * + * @return operation status + * @retval 0 success + * @retval negative a fatal error that should stop the walk immediately + * @retval positive an error with the volume header was encountered; the walk + * should continue, but the error function should be called on this + * header + * + * @see VWalkVolumeHeaders + */ +typedef int (*VWalkVolFunc)(struct DiskPartition64 *dp, const char *name, + struct VolumeDiskHeader *hdr, int last, + void *rock); +/** + * VWalkVolumeHeaders error callback. + * + * This is called from VWalkVolumeHeaders when an invalid or otherwise + * problematic volume header is encountered. It is typically implemented as a + * wrapper to unlink the .vol file. + * + * @param[in] dp disk partition + * @param[in] name full path to the .vol header file + * @param[in] hdr header read in from the .vol file, or NULL if it could not + * be read + * @param[in] rock rock passed to VWalkVolumeHeaders + * + * @see VWalkVolumeHeaders + */ +typedef void (*VWalkErrFunc)(struct DiskPartition64 *dp, const char *name, + struct VolumeDiskHeader *hdr, void *rock); +extern int VWalkVolumeHeaders(struct DiskPartition64 *dp, const char *partpath, + VWalkVolFunc volfunc, VWalkErrFunc errfunc, + void *rock); + /* Naive formula relating number of file size to number of 1K blocks in file */ /* Note: we charge 1 block for 0 length files so the user can't store an inifite number of them; for most files, we give him the inode, vnode, diff --git a/src/vol/vutil.c b/src/vol/vutil.c index a28c22aa2..09360e80f 100644 --- a/src/vol/vutil.c +++ b/src/vol/vutil.c @@ -29,6 +29,7 @@ #include #include #endif +#include #include #ifdef AFS_PTHREAD_ENV #include @@ -675,6 +676,167 @@ VDestroyVolumeDiskHeader(struct DiskPartition64 * dp, } #endif /* FSSYNC_BUILD_CLIENT */ +/** + * handle a single vol header as part of VWalkVolumeHeaders. + * + * @param[in] dp disk partition + * @param[in] volfunc function to call when a vol header is successfully read + * @param[in] name full path name to the .vol header + * @param[out] hdr header data read in from the .vol header + * @param[in] locked 1 if the partition headers are locked, 0 otherwise + * @param[in] rock the rock to pass to volfunc + * + * @return operation status + * @retval 0 success + * @retval -1 fatal error, stop scanning + * @retval 1 failed to read header + * @retval 2 volfunc callback indicated error after header read + */ +static int +_VHandleVolumeHeader(struct DiskPartition64 *dp, VWalkVolFunc volfunc, + const char *name, struct VolumeDiskHeader *hdr, + int locked, void *rock) +{ + int error = 0; + int fd; + + if ((fd = afs_open(name, O_RDONLY)) == -1 + || read(fd, hdr, sizeof(*hdr)) + != sizeof(*hdr) + || hdr->stamp.magic != VOLUMEHEADERMAGIC) { + error = 1; + } + + if (fd >= 0) { + close(fd); + } + +#ifdef AFSFS_DEMAND_ATTACH_FS + if (locked) { + VPartHeaderUnlock(dp); + } +#endif /* AFS_DEMAND_ATTACH_FS */ + + if (!error && volfunc) { + /* the volume header seems fine; call the caller-supplied + * 'we-found-a-volume-header' function */ + int last = 1; + +#ifdef AFS_DEMAND_ATTACH_FS + if (!locked) { + last = 0; + } +#endif /* AFS_DEMAND_ATTACH_FS */ + + error = (*volfunc) (dp, name, hdr, last, rock); + if (error < 0) { + return -1; + } + if (error) { + error = 2; + } + } + +#ifdef AFS_DEMAND_ATTACH_FS + if (error && !locked) { + int code; + /* retry reading the volume header under the partition + * header lock, just to be safe and ensure we're not + * racing something rewriting the vol header */ + code = VPartHeaderLock(dp, WRITE_LOCK); + if (code) { + Log("Error acquiring partition write lock when " + "looking at header %s\n", name); + return -1; + } + + return _VHandleVolumeHeader(dp, volfunc, name, hdr, 1, rock); + } +#endif /* AFS_DEMAND_ATTACH_FS */ + + return error; +} + +/** + * walk through the list of volume headers on a partition. + * + * This function looks through all of the .vol headers on a partition, reads in + * each header, and calls the supplied volfunc function on each one. If the + * header cannot be read (or volfunc returns a positive error code), DAFS will + * VPartHeaderExLock() and retry. If that fails, or if we are non-DAFS, errfunc + * will be called (which typically will unlink the problem volume header). + * + * If volfunc returns a negative error code, walking the partition will stop + * and we will return an error immediately. + * + * @param[in] dp partition to walk + * @param[in] partpath the path opendir() + * @param[in] volfunc the function to call when a header is encountered, or + * NULL to just skip over valid headers + * @param[in] errfunc the function to call when a problematic header is + * encountered, or NULL to just skip over bad headers + * @param[in] rock rock for volfunc and errfunc + * + * @see VWalkVolFunc + * @see VWalkErrFunc + * + * @return operation status + * @retval 0 success + * @retval negative fatal error, walk did not finish + */ +int +VWalkVolumeHeaders(struct DiskPartition64 *dp, const char *partpath, + VWalkVolFunc volfunc, VWalkErrFunc errfunc, void *rock) +{ + DIR *dirp = NULL; + struct dirent *dentry = NULL; + int code = 0; + struct VolumeDiskHeader diskHeader; + + dirp = opendir(partpath); + if (!dirp) { + Log("VWalkVolumeHeaders: cannot open directory %s\n", partpath); + code = -1; + goto done; + } + + while ((dentry = readdir(dirp))) { + char *p = dentry->d_name; + p = strrchr(dentry->d_name, '.'); + if (p != NULL && strcmp(p, VHDREXT) == 0) { + char name[VMAXPATHLEN]; + + sprintf(name, "%s/%s", partpath, dentry->d_name); + + code = _VHandleVolumeHeader(dp, volfunc, name, &diskHeader, -1, rock); + if (code < 0) { + /* fatal error, stop walking */ + goto done; + } + if (code && errfunc) { + /* error with header; call the caller-supplied vol error + * function */ + + struct VolumeDiskHeader *hdr = &diskHeader; + if (code == 1) { + /* we failed to read the header at all, so don't pass in + * the header ptr */ + hdr = NULL; + } + (*errfunc) (dp, name, hdr, rock); + } + code = 0; + } + } + done: + if (dirp) { + closedir(dirp); + dirp = NULL; + } + + return code; +} + #ifdef AFS_DEMAND_ATTACH_FS /** -- 2.39.5