const char *logname, const char *rtname);
int xfrog_geometry(int fd, struct xfs_fsop_geom *fsgeo);
+/*
+ * Structure for recording whatever observations we want about the level of
+ * xfs runtime support for this fd. Right now we only store the fd and fs
+ * geometry.
+ */
+struct xfs_fd {
+ /* ioctl file descriptor */
+ int fd;
+
+ /* filesystem geometry */
+ struct xfs_fsop_geom fsgeom;
+};
+
+/* Static initializers */
+#define XFS_FD_INIT(_fd) { .fd = (_fd), }
+#define XFS_FD_INIT_EMPTY XFS_FD_INIT(-1)
+
+int xfd_prepare_geometry(struct xfs_fd *xfd);
+int xfd_close(struct xfs_fd *xfd);
+
#endif /* _LIBFROG_FSGEOM_H_ */
return errno;
}
+
+/*
+ * Prepare xfs_fd structure for future ioctl operations by computing the xfs
+ * geometry for @xfd->fd. Returns zero or a positive error code.
+ */
+int
+xfd_prepare_geometry(
+ struct xfs_fd *xfd)
+{
+ return xfrog_geometry(xfd->fd, &xfd->fsgeom);
+}
+
+/*
+ * Release any resources associated with this xfs_fd structure. Returns zero
+ * or a positive error code.
+ */
+int
+xfd_close(
+ struct xfs_fd *xfd)
+{
+ int ret = 0;
+
+ if (xfd->fd < 0)
+ return 0;
+
+ ret = close(xfd->fd);
+ xfd->fd = -1;
+ if (ret < 0)
+ return errno;
+
+ return 0;
+}
igrpreq.ocount = &igrplen;
igrp_ino = first_ino;
- error = ioctl(ctx->mnt_fd, XFS_IOC_FSINUMBERS, &igrpreq);
+ error = ioctl(ctx->mnt.fd, XFS_IOC_FSINUMBERS, &igrpreq);
while (!error && igrplen && inogrp.xi_startino < last_ino) {
nr += inogrp.xi_alloccount;
- error = ioctl(ctx->mnt_fd, XFS_IOC_FSINUMBERS, &igrpreq);
+ error = ioctl(ctx->mnt.fd, XFS_IOC_FSINUMBERS, &igrpreq);
}
if (error) {
int ret;
ci = calloc(1, sizeof(struct xfs_count_inodes) +
- (ctx->geo.agcount * sizeof(uint64_t)));
+ (ctx->mnt.fsgeom.agcount * sizeof(uint64_t)));
if (!ci)
return false;
ci->moveon = true;
str_info(ctx, ctx->mntpoint, _("Could not create workqueue."));
goto out_free;
}
- for (agno = 0; agno < ctx->geo.agcount; agno++) {
+ for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) {
ret = workqueue_add(&wq, xfs_count_ag_inodes, agno, ci);
if (ret) {
moveon = false;
}
workqueue_destroy(&wq);
- for (agno = 0; agno < ctx->geo.agcount; agno++)
+ for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++)
*count += ci->counters[agno];
moveon = ci->moveon;
int error;
/* Grab the fstatvfs counters, since it has to report accurately. */
- error = fstatvfs(ctx->mnt_fd, &sfs);
+ error = fstatvfs(ctx->mnt.fd, &sfs);
if (error) {
str_errno(ctx, ctx->mntpoint);
return false;
}
/* Fetch the filesystem counters. */
- error = ioctl(ctx->mnt_fd, XFS_IOC_FSCOUNTS, &fc);
+ error = ioctl(ctx->mnt.fd, XFS_IOC_FSCOUNTS, &fc);
if (error) {
str_errno(ctx, ctx->mntpoint);
return false;
* XFS reserves some blocks to prevent hard ENOSPC, so add those
* blocks back to the free data counts.
*/
- error = ioctl(ctx->mnt_fd, XFS_IOC_GET_RESBLKS, &rb);
+ error = ioctl(ctx->mnt.fd, XFS_IOC_GET_RESBLKS, &rb);
if (error)
str_errno(ctx, ctx->mntpoint);
sfs.f_bfree += rb.resblks_avail;
- *d_blocks = sfs.f_blocks + (ctx->geo.logstart ? ctx->geo.logblocks : 0);
+ *d_blocks = sfs.f_blocks;
+ if (ctx->mnt.fsgeom.logstart > 0)
+ *d_blocks += ctx->mnt.fsgeom.logblocks;
*d_bfree = sfs.f_bfree;
- *r_blocks = ctx->geo.rtblocks;
+ *r_blocks = ctx->mnt.fsgeom.rtblocks;
*r_bfree = fc.freertx;
*f_files = sfs.f_files;
*f_free = sfs.f_ffree;
/* Load the one inode. */
oneino = inogrp->xi_startino + i;
onereq.ubuffer = bs;
- error = ioctl(ctx->mnt_fd, XFS_IOC_FSBULKSTAT_SINGLE,
+ error = ioctl(ctx->mnt.fd, XFS_IOC_FSBULKSTAT_SINGLE,
&onereq);
if (error || bs->bs_ino != inogrp->xi_startino + i) {
memset(bs, 0, sizeof(struct xfs_bstat));
/* Find the inode chunk & alloc mask */
igrp_ino = first_ino;
- error = ioctl(ctx->mnt_fd, XFS_IOC_FSINUMBERS, &igrpreq);
+ error = ioctl(ctx->mnt.fd, XFS_IOC_FSINUMBERS, &igrpreq);
while (!error && igrplen) {
/* Load the inodes. */
ino = inogrp.xi_startino - 1;
*/
if (inogrp.xi_alloccount == 0)
goto igrp_retry;
- error = ioctl(ctx->mnt_fd, XFS_IOC_FSBULKSTAT, &bulkreq);
+ error = ioctl(ctx->mnt.fd, XFS_IOC_FSBULKSTAT, &bulkreq);
if (error)
str_info(ctx, descr, "%s", strerror_r(errno,
buf, DESCR_BUFSZ));
stale_count = 0;
igrp_retry:
- error = ioctl(ctx->mnt_fd, XFS_IOC_FSINUMBERS, &igrpreq);
+ error = ioctl(ctx->mnt.fd, XFS_IOC_FSINUMBERS, &igrpreq);
}
err:
return false;
}
- for (agno = 0; agno < ctx->geo.agcount; agno++) {
+ for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) {
ret = workqueue_add(&wq, xfs_scan_ag_inodes, agno, &si);
if (ret) {
si.moveon = false;
flag = XFS_FSOP_GOING_FLAGS_LOGFLUSH;
str_info(ctx, ctx->mntpoint, _("Shutting down filesystem!"));
- if (ioctl(ctx->mnt_fd, XFS_IOC_GOINGDOWN, &flag))
+ if (ioctl(ctx->mnt.fd, XFS_IOC_GOINGDOWN, &flag))
str_errno(ctx, ctx->mntpoint);
}
if (ctx->datadev)
disk_close(ctx->datadev);
fshandle_destroy();
- if (ctx->mnt_fd >= 0) {
- error = close(ctx->mnt_fd);
- if (error)
- str_errno(ctx, _("closing mountpoint fd"));
- }
+ error = xfd_close(&ctx->mnt);
+ if (error)
+ str_liberror(ctx, error, _("closing mountpoint fd"));
fs_table_destroy();
return true;
* CAP_SYS_ADMIN, which we probably need to do anything fancy
* with the (XFS driver) kernel.
*/
- ctx->mnt_fd = open(ctx->mntpoint, O_RDONLY | O_NOATIME | O_DIRECTORY);
- if (ctx->mnt_fd < 0) {
+ ctx->mnt.fd = open(ctx->mntpoint, O_RDONLY | O_NOATIME | O_DIRECTORY);
+ if (ctx->mnt.fd < 0) {
if (errno == EPERM)
str_info(ctx, ctx->mntpoint,
_("Must be root to run scrub."));
return false;
}
- error = fstat(ctx->mnt_fd, &ctx->mnt_sb);
+ error = fstat(ctx->mnt.fd, &ctx->mnt_sb);
if (error) {
str_errno(ctx, ctx->mntpoint);
return false;
}
- error = fstatvfs(ctx->mnt_fd, &ctx->mnt_sv);
+ error = fstatvfs(ctx->mnt.fd, &ctx->mnt_sv);
if (error) {
str_errno(ctx, ctx->mntpoint);
return false;
}
- error = fstatfs(ctx->mnt_fd, &ctx->mnt_sf);
+ error = fstatfs(ctx->mnt.fd, &ctx->mnt_sf);
if (error) {
str_errno(ctx, ctx->mntpoint);
return false;
}
- if (!platform_test_xfs_fd(ctx->mnt_fd)) {
+ if (!platform_test_xfs_fd(ctx->mnt.fd)) {
str_info(ctx, ctx->mntpoint,
_("Does not appear to be an XFS filesystem!"));
return false;
* This seems to reduce the incidence of stale file handle
* errors when we open things by handle.
*/
- error = syncfs(ctx->mnt_fd);
+ error = syncfs(ctx->mnt.fd);
if (error) {
str_errno(ctx, ctx->mntpoint);
return false;
}
/* Retrieve XFS geometry. */
- error = xfrog_geometry(ctx->mnt_fd, &ctx->geo);
+ error = xfd_prepare_geometry(&ctx->mnt);
if (error) {
str_liberror(ctx, error, _("Retrieving XFS geometry"));
return false;
}
- if (!xfs_action_lists_alloc(ctx->geo.agcount, &ctx->action_lists)) {
+ if (!xfs_action_lists_alloc(ctx->mnt.fsgeom.agcount,
+ &ctx->action_lists)) {
str_error(ctx, ctx->mntpoint, _("Not enough memory."));
return false;
}
- ctx->agblklog = log2_roundup(ctx->geo.agblocks);
- ctx->blocklog = highbit32(ctx->geo.blocksize);
- ctx->inodelog = highbit32(ctx->geo.inodesize);
+ ctx->agblklog = log2_roundup(ctx->mnt.fsgeom.agblocks);
+ ctx->blocklog = highbit32(ctx->mnt.fsgeom.blocksize);
+ ctx->inodelog = highbit32(ctx->mnt.fsgeom.inodesize);
ctx->inopblog = ctx->blocklog - ctx->inodelog;
error = path_to_fshandle(ctx->mntpoint, &ctx->fshandle,
}
/* Did we find the log and rt devices, if they're present? */
- if (ctx->geo.logstart == 0 && ctx->fsinfo.fs_log == NULL) {
+ if (ctx->mnt.fsgeom.logstart == 0 && ctx->fsinfo.fs_log == NULL) {
str_info(ctx, ctx->mntpoint,
_("Unable to find log device path."));
return false;
}
- if (ctx->geo.rtblocks && ctx->fsinfo.fs_rt == NULL) {
+ if (ctx->mnt.fsgeom.rtblocks && ctx->fsinfo.fs_rt == NULL) {
str_info(ctx, ctx->mntpoint,
_("Unable to find realtime device path."));
return false;
if (!moveon)
goto out;
- for (agno = 0; moveon && agno < ctx->geo.agcount; agno++) {
+ for (agno = 0; moveon && agno < ctx->mnt.fsgeom.agcount; agno++) {
ret = workqueue_add(&wq, xfs_scan_ag_metadata, agno, &moveon);
if (ret) {
moveon = false;
struct xfs_bstat *bs,
struct xfs_action_list *alist)
{
- return fn(ctx, bs->bs_ino, bs->bs_gen, ctx->mnt_fd, alist);
+ return fn(ctx, bs->bs_ino, bs->bs_gen, ctx->mnt.fd, alist);
}
struct scrub_inode_ctx {
if (S_ISLNK(bstat->bs_mode)) {
/* Check symlink contents. */
moveon = xfs_scrub_symlink(ctx, bstat->bs_ino,
- bstat->bs_gen, ctx->mnt_fd, &alist);
+ bstat->bs_gen, ctx->mnt.fd, &alist);
} else if (S_ISDIR(bstat->bs_mode)) {
/* Check the directory entries. */
moveon = xfs_scrub_fd(ctx, xfs_scrub_dir, bstat, &alist);
/* Repair anything broken until we fail to make progress. */
do {
- moveon = xfs_action_list_process(ctx, ctx->mnt_fd, alist, flags);
+ moveon = xfs_action_list_process(ctx, ctx->mnt.fd, alist, flags);
if (!moveon) {
*pmoveon = false;
return;
/* Try once more, but this time complain if we can't fix things. */
flags |= ALP_COMPLAIN_IF_UNFIXED;
- moveon = xfs_action_list_process(ctx, ctx->mnt_fd, alist, flags);
+ moveon = xfs_action_list_process(ctx, ctx->mnt.fd, alist, flags);
if (!moveon)
*pmoveon = false;
}
str_error(ctx, ctx->mntpoint, _("Could not create workqueue."));
return false;
}
- for (agno = 0; agno < ctx->geo.agcount; agno++) {
+ for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) {
if (xfs_action_list_length(&ctx->action_lists[agno]) > 0) {
ret = workqueue_add(&wq, xfs_repair_ag, agno, &moveon);
if (ret) {
xfs_agnumber_t agno;
size_t need_fixing = 0;
- for (agno = 0; agno < ctx->geo.agcount; agno++)
+ for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++)
need_fixing += xfs_action_list_length(&ctx->action_lists[agno]);
need_fixing++;
*items = need_fixing;
return false;
/* Retrieve label; quietly bail if we don't support that. */
- error = ioctl(ctx->mnt_fd, FS_IOC_GETFSLABEL, &label);
+ error = ioctl(ctx->mnt.fd, FS_IOC_GETFSLABEL, &label);
if (error) {
if (errno != EOPNOTSUPP && errno != ENOTTY) {
moveon = false;
}
vs.rvp_data = read_verify_pool_init(ctx, ctx->datadev,
- ctx->geo.blocksize, xfs_check_rmap_ioerr,
+ ctx->mnt.fsgeom.blocksize, xfs_check_rmap_ioerr,
scrub_nproc(ctx));
if (!vs.rvp_data) {
str_info(ctx, ctx->mntpoint,
}
if (ctx->logdev) {
vs.rvp_log = read_verify_pool_init(ctx, ctx->logdev,
- ctx->geo.blocksize, xfs_check_rmap_ioerr,
+ ctx->mnt.fsgeom.blocksize, xfs_check_rmap_ioerr,
scrub_nproc(ctx));
if (!vs.rvp_log) {
str_info(ctx, ctx->mntpoint,
}
if (ctx->rtdev) {
vs.rvp_realtime = read_verify_pool_init(ctx, ctx->rtdev,
- ctx->geo.blocksize, xfs_check_rmap_ioerr,
+ ctx->mnt.fsgeom.blocksize, xfs_check_rmap_ioerr,
scrub_nproc(ctx));
if (!vs.rvp_realtime) {
str_info(ctx, ctx->mntpoint,
int error;
/* Flush everything out to disk before we start counting. */
- error = syncfs(ctx->mnt_fd);
+ error = syncfs(ctx->mnt.fd);
if (error) {
str_errno(ctx, ctx->mntpoint);
return false;
xfs_agnumber_t agno,
struct xfs_action_list *alist)
{
- ASSERT(agno < ctx->geo.agcount);
+ ASSERT(agno < ctx->mnt.fsgeom.agcount);
xfs_action_list_splice(&ctx->action_lists[agno], alist);
}
{
bool moveon;
- moveon = xfs_action_list_process(ctx, ctx->mnt_fd, alist,
+ moveon = xfs_action_list_process(ctx, ctx->mnt.fd, alist,
ALP_REPAIR_ONLY | ALP_NOPROGRESS);
if (!moveon)
return moveon;
background_sleep();
/* Check the item. */
- fix = xfs_check_metadata(ctx, ctx->mnt_fd, &meta, false);
+ fix = xfs_check_metadata(ctx, ctx->mnt.fd, &meta, false);
progress_add(1);
switch (fix) {
case CHECK_ABORT:
enum check_outcome fix;
/* Check the item. */
- fix = xfs_check_metadata(ctx, ctx->mnt_fd, &meta, false);
+ fix = xfs_check_metadata(ctx, ctx->mnt.fd, &meta, false);
switch (fix) {
case CHECK_ABORT:
return false;
switch (sc->type) {
case ST_AGHEADER:
case ST_PERAG:
- estimate += ctx->geo.agcount;
+ estimate += ctx->mnt.fsgeom.agcount;
break;
case ST_FS:
estimate++;
if (debug_tweak_on("XFS_SCRUB_NO_KERNEL"))
return false;
if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR") && !injected) {
- inject.fd = ctx->mnt_fd;
+ inject.fd = ctx->mnt.fd;
inject.errtag = XFS_ERRTAG_FORCE_SCRUB_REPAIR;
- error = ioctl(ctx->mnt_fd, XFS_IOC_ERROR_INJECTION, &inject);
+ error = ioctl(ctx->mnt.fd, XFS_IOC_ERROR_INJECTION, &inject);
if (error == 0)
injected = true;
}
meta.sm_type = type;
if (repair)
meta.sm_flags |= XFS_SCRUB_IFLAG_REPAIR;
- error = ioctl(ctx->mnt_fd, XFS_IOC_SCRUB_METADATA, &meta);
+ error = ioctl(ctx->mnt.fd, XFS_IOC_SCRUB_METADATA, &meta);
if (!error)
return true;
switch (errno) {
memcpy(head->fmh_keys, keys, sizeof(struct fsmap) * 2);
head->fmh_count = FSMAP_NR;
- while ((error = ioctl(ctx->mnt_fd, FS_IOC_GETFSMAP, head)) == 0) {
+ while ((error = ioctl(ctx->mnt.fd, FS_IOC_GETFSMAP, head)) == 0) {
for (i = 0, p = head->fmh_recs;
i < head->fmh_entries;
i++, p++) {
off64_t bperag;
bool moveon;
- bperag = (off64_t)ctx->geo.agblocks *
- (off64_t)ctx->geo.blocksize;
+ bperag = (off64_t)ctx->mnt.fsgeom.agblocks *
+ (off64_t)ctx->mnt.fsgeom.blocksize;
snprintf(descr, DESCR_BUFSZ, _("dev %d:%d AG %u fsmap"),
major(ctx->fsinfo.fs_datadev),
}
if (ctx->fsinfo.fs_rt) {
ret = workqueue_add(&wq, xfs_scan_rt_blocks,
- ctx->geo.agcount + 1, &sbx);
+ ctx->mnt.fsgeom.agcount + 1, &sbx);
if (ret) {
sbx.moveon = false;
str_info(ctx, ctx->mntpoint,
}
if (ctx->fsinfo.fs_log) {
ret = workqueue_add(&wq, xfs_scan_log_blocks,
- ctx->geo.agcount + 2, &sbx);
+ ctx->mnt.fsgeom.agcount + 2, &sbx);
if (ret) {
sbx.moveon = false;
str_info(ctx, ctx->mntpoint,
goto out;
}
}
- for (agno = 0; agno < ctx->geo.agcount; agno++) {
+ for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) {
ret = workqueue_add(&wq, xfs_scan_ag_blocks, agno, &sbx);
if (ret) {
sbx.moveon = false;
int error;
range.len = ULLONG_MAX;
- error = ioctl(ctx->mnt_fd, FITRIM, &range);
+ error = ioctl(ctx->mnt.fd, FITRIM, &range);
if (error && errno != EOPNOTSUPP && errno != ENOTTY)
perror(_("fstrim"));
}
#ifndef XFS_SCRUB_XFS_SCRUB_H_
#define XFS_SCRUB_XFS_SCRUB_H_
+#include "fsgeom.h"
+
extern char *progname;
#define _PATH_PROC_MOUNTS "/proc/mounts"
/* How does the user want us to react to errors? */
enum error_action error_action;
- /* fd to filesystem mount point */
- int mnt_fd;
+ /* xfrog context for the mount point */
+ struct xfs_fd mnt;
/* Number of threads for metadata scrubbing */
unsigned int nr_io_threads;
/* XFS specific geometry */
- struct xfs_fsop_geom geo;
struct fs_path fsinfo;
unsigned int agblklog;
unsigned int blocklog;