}
/**
- * 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;
}
/**
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) {
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);
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
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 {
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);
}
}
char *
-ToString(char *s)
+ToString(const char *s)
{
register char *p;
p = (char *)malloc(strlen(s) + 1);
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);
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,
#include <sys/file.h>
#include <unistd.h>
#endif
+#include <dirent.h>
#include <sys/stat.h>
#ifdef AFS_PTHREAD_ENV
#include <assert.h>
}
#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
/**