All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Darrick J. Wong" <djwong@kernel.org>
To: aalbersh@kernel.org
Cc: hch@lst.de, linux-xfs@vger.kernel.org
Subject: [PATCH v1.1 4/4] xfs_db: add command to copy directory trees out of filesystems
Date: Tue, 18 Feb 2025 11:58:18 -0800	[thread overview]
Message-ID: <20250218195818.GF21808@frogsfrogsfrogs> (raw)
In-Reply-To: <173888089664.2742734.11946589861684958797.stgit@frogsfrogsfrogs>

From: Darrick J. Wong <djwong@kernel.org>

Aheada of deprecating V4 support in the kernel, let's give people a way
to extract their files from a filesystem without needing to mount.  The
libxfs code won't be removed from the kernel until 2030 and xfsprogs
effectively builds with XFS_SUPPORT_V4=y so that'll give us five years
of releases for archaeologists to draw from.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Andrey Albershteyn <aalbersh@kernel.org>
---
v1.1: tidy up the comments, externs, and xattr name reporting when
things fail, and warn if we lose acls
---
 db/command.h             |    1 
 db/namei.h               |   18 +
 libxfs/libxfs_api_defs.h |    2 
 db/Makefile              |    3 
 db/command.c             |    1 
 db/namei.c               |    5 
 db/rdump.c               | 1054 ++++++++++++++++++++++++++++++++++++++++++++++
 man/man8/xfs_db.8        |   26 +
 8 files changed, 1107 insertions(+), 3 deletions(-)
 create mode 100644 db/namei.h
 create mode 100644 db/rdump.c

diff --git a/db/command.h b/db/command.h
index 2c2926afd7b516..7b1738addff8e1 100644
--- a/db/command.h
+++ b/db/command.h
@@ -36,3 +36,4 @@ extern void		timelimit_init(void);
 extern void		namei_init(void);
 extern void		iunlink_init(void);
 extern void		bmapinflate_init(void);
+void			rdump_init(void);
diff --git a/db/namei.h b/db/namei.h
new file mode 100644
index 00000000000000..05c384bc9a6c35
--- /dev/null
+++ b/db/namei.h
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef DB_NAMEI_H_
+#define DB_NAMEI_H_
+
+int path_walk(xfs_ino_t rootino, const char *path);
+
+typedef int (*dir_emit_t)(struct xfs_trans *tp, struct xfs_inode *dp,
+		xfs_dir2_dataptr_t off, char *name, ssize_t namelen,
+		xfs_ino_t ino, uint8_t dtype, void *private);
+
+int listdir(struct xfs_trans *tp, struct xfs_inode *dp, dir_emit_t dir_emit,
+		void *private);
+
+#endif /* DB_NAMEI_H_ */
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index 530feef2a47db8..14a67c8c24dd7e 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -47,6 +47,7 @@
 #define xfs_attr_leaf_newentsize	libxfs_attr_leaf_newentsize
 #define xfs_attr_namecheck		libxfs_attr_namecheck
 #define xfs_attr_removename		libxfs_attr_removename
+#define xfs_attr_rmtval_get		libxfs_attr_rmtval_get
 #define xfs_attr_set			libxfs_attr_set
 #define xfs_attr_sethash		libxfs_attr_sethash
 #define xfs_attr_sf_firstentry		libxfs_attr_sf_firstentry
@@ -353,6 +354,7 @@
 #define xfs_sb_version_to_features	libxfs_sb_version_to_features
 #define xfs_symlink_blocks		libxfs_symlink_blocks
 #define xfs_symlink_hdr_ok		libxfs_symlink_hdr_ok
+#define xfs_symlink_remote_read		libxfs_symlink_remote_read
 #define xfs_symlink_write_target	libxfs_symlink_write_target
 
 #define xfs_trans_add_item		libxfs_trans_add_item
diff --git a/db/Makefile b/db/Makefile
index 02eeead25b49d0..e36e775eee6021 100644
--- a/db/Makefile
+++ b/db/Makefile
@@ -45,6 +45,7 @@ HFILES = \
 	logformat.h \
 	malloc.h \
 	metadump.h \
+	namei.h \
 	obfuscate.h \
 	output.h \
 	print.h \
@@ -64,7 +65,7 @@ CFILES = $(HFILES:.h=.c) \
 	convert.c \
 	info.c \
 	iunlink.c \
-	namei.c \
+	rdump.c \
 	timelimit.c
 LSRCFILES = xfs_admin.sh xfs_ncheck.sh xfs_metadump.sh
 
diff --git a/db/command.c b/db/command.c
index 1b46c3fec08a0e..15bdabbcb7d728 100644
--- a/db/command.c
+++ b/db/command.c
@@ -145,4 +145,5 @@ init_commands(void)
 	timelimit_init();
 	iunlink_init();
 	bmapinflate_init();
+	rdump_init();
 }
diff --git a/db/namei.c b/db/namei.c
index 6f277a65ed91ac..2586e0591c2357 100644
--- a/db/namei.c
+++ b/db/namei.c
@@ -14,6 +14,7 @@
 #include "fprint.h"
 #include "field.h"
 #include "inode.h"
+#include "namei.h"
 
 /* Path lookup */
 
@@ -144,7 +145,7 @@ path_navigate(
 }
 
 /* Walk a directory path to an inode and set the io cursor to that inode. */
-static int
+int
 path_walk(
 	xfs_ino_t	rootino,
 	const char	*path)
@@ -493,7 +494,7 @@ list_leafdir(
 }
 
 /* Read the directory, display contents. */
-static int
+int
 listdir(
 	struct xfs_trans	*tp,
 	struct xfs_inode	*dp,
diff --git a/db/rdump.c b/db/rdump.c
new file mode 100644
index 00000000000000..3138c67b6d56b5
--- /dev/null
+++ b/db/rdump.c
@@ -0,0 +1,1054 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "libxfs.h"
+#include "command.h"
+#include "output.h"
+#include "init.h"
+#include "io.h"
+#include "namei.h"
+#include "type.h"
+#include "input.h"
+#include "faddr.h"
+#include "fprint.h"
+#include "field.h"
+#include "inode.h"
+#include "listxattr.h"
+#include <sys/xattr.h>
+#include <linux/xattr.h>
+
+static bool strict_errors;
+
+/* file attributes that we might have lost */
+#define LOST_OWNER		(1U << 0)
+#define LOST_MODE		(1U << 1)
+#define LOST_TIME		(1U << 2)
+#define LOST_SOME_FSXATTR	(1U << 3)
+#define LOST_FSXATTR		(1U << 4)
+#define LOST_XATTR		(1U << 5)
+#define LOST_ACL		(1U << 6)
+
+static unsigned int lost_mask;
+
+static void
+rdump_help(void)
+{
+	dbprintf(_(
+"\n"
+" Recover files out of the filesystem into a directory.\n"
+"\n"
+" Options:\n"
+"   -s      -- Fail on errors when reading content from the filesystem.\n"
+"   paths   -- Copy only these paths.  If no paths are given, copy everything.\n"
+"   destdir -- The destination into which files are recovered.\n"
+	));
+}
+
+struct destdir {
+	int	fd;
+	char	*path;
+	char	*sep;
+};
+
+struct pathbuf {
+	size_t	len;
+	char	path[PATH_MAX + 1];
+};
+
+static int rdump_file(struct xfs_trans *tp, xfs_ino_t ino,
+		const struct destdir *destdir, struct pathbuf *pbuf);
+
+static inline unsigned int xflags2getflags(const struct fsxattr *fa)
+{
+	unsigned int	ret = 0;
+
+	if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE)
+		ret |= FS_IMMUTABLE_FL;
+	if (fa->fsx_xflags & FS_XFLAG_APPEND)
+		ret |= FS_APPEND_FL;
+	if (fa->fsx_xflags & FS_XFLAG_SYNC)
+		ret |= FS_SYNC_FL;
+	if (fa->fsx_xflags & FS_XFLAG_NOATIME)
+		ret |= FS_NOATIME_FL;
+	if (fa->fsx_xflags & FS_XFLAG_NODUMP)
+		ret |= FS_NODUMP_FL;
+	if (fa->fsx_xflags & FS_XFLAG_DAX)
+		ret |= FS_DAX_FL;
+	if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
+		ret |= FS_PROJINHERIT_FL;
+	return ret;
+}
+
+/* Copy common file attributes to this fd */
+static int
+rdump_fileattrs_fd(
+	struct xfs_inode	*ip,
+	const struct destdir	*destdir,
+	const struct pathbuf	*pbuf,
+	int			fd)
+{
+	struct fsxattr		fsxattr = {
+		.fsx_extsize	= ip->i_extsize,
+		.fsx_projid	= ip->i_projid,
+		.fsx_cowextsize	= ip->i_cowextsize,
+		.fsx_xflags	= xfs_ip2xflags(ip),
+	};
+	int			ret;
+
+	ret = fchmod(fd, VFS_I(ip)->i_mode & ~S_IFMT);
+	if (ret) {
+		if (errno == EPERM)
+			lost_mask |= LOST_MODE;
+		else
+			dbprintf(_("%s%s%s: fchmod %s\n"), destdir->path,
+					destdir->sep, pbuf->path,
+					strerror(errno));
+		if (strict_errors)
+			return 1;
+	}
+
+	ret = fchown(fd, i_uid_read(VFS_I(ip)), i_gid_read(VFS_I(ip)));
+	if (ret) {
+		if (errno == EPERM)
+			lost_mask |= LOST_OWNER;
+		else
+			dbprintf(_("%s%s%s: fchown %s\n"), destdir->path,
+					destdir->sep, pbuf->path,
+					strerror(errno));
+		if (strict_errors)
+			return 1;
+	}
+
+	ret = ioctl(fd, XFS_IOC_FSSETXATTR, &fsxattr);
+	if (ret) {
+		unsigned int	getflags = xflags2getflags(&fsxattr);
+
+		/* try to use setflags if the target is not xfs */
+		if (errno == EOPNOTSUPP || errno == ENOTTY) {
+			lost_mask |= LOST_SOME_FSXATTR;
+			ret = ioctl(fd, FS_IOC_SETFLAGS, &getflags);
+		}
+
+		if (errno == EOPNOTSUPP || errno == EPERM || errno == ENOTTY)
+			lost_mask |= LOST_FSXATTR;
+		else
+			dbprintf(_("%s%s%s: fssetxattr %s\n"), destdir->path,
+					destdir->sep, pbuf->path,
+					strerror(errno));
+		if (strict_errors)
+			return 1;
+	}
+
+	return 0;
+}
+
+/* Copy common file attributes to this path */
+static int
+rdump_fileattrs_path(
+	struct xfs_inode	*ip,
+	const struct destdir	*destdir,
+	const struct pathbuf	*pbuf)
+{
+	int			ret;
+
+	ret = fchmodat(destdir->fd, pbuf->path, VFS_I(ip)->i_mode & ~S_IFMT,
+			AT_SYMLINK_NOFOLLOW);
+	if (ret) {
+		/* fchmodat on a symlink is not supported */
+		if (errno == EPERM || errno == EOPNOTSUPP)
+			lost_mask |= LOST_MODE;
+		else
+			dbprintf(_("%s%s%s: fchmodat %s\n"), destdir->path,
+					destdir->sep, pbuf->path,
+					strerror(errno));
+		if (strict_errors)
+			return 1;
+	}
+
+	ret = fchownat(destdir->fd, pbuf->path, i_uid_read(VFS_I(ip)),
+			i_gid_read(VFS_I(ip)), AT_SYMLINK_NOFOLLOW);
+	if (ret) {
+		if (errno == EPERM)
+			lost_mask |= LOST_OWNER;
+		else
+			dbprintf(_("%s%s%s: fchownat %s\n"), destdir->path,
+					destdir->sep, pbuf->path,
+					strerror(errno));
+		if (strict_errors)
+			return 1;
+	}
+
+	/* Cannot copy fsxattrs until setfsxattrat gets merged */
+
+	return 0;
+}
+
+/* Copy access and modification timestamps to this fd. */
+static int
+rdump_timestamps_fd(
+	struct xfs_inode	*ip,
+	const struct destdir	*destdir,
+	const struct pathbuf	*pbuf,
+	int			fd)
+{
+	struct timespec		times[2] = {
+		[0] = {
+			.tv_sec  = inode_get_atime_sec(VFS_I(ip)),
+			.tv_nsec = inode_get_atime_nsec(VFS_I(ip)),
+		},
+		[1] = {
+			.tv_sec  = inode_get_mtime_sec(VFS_I(ip)),
+			.tv_nsec = inode_get_mtime_nsec(VFS_I(ip)),
+		},
+	};
+	int			ret;
+
+	/* Cannot set ctime or btime */
+
+	ret = futimens(fd, times);
+	if (ret) {
+		if (errno == EPERM)
+			lost_mask |= LOST_TIME;
+		else
+			dbprintf(_("%s%s%s: futimens %s\n"), destdir->path,
+					destdir->sep, pbuf->path,
+					strerror(errno));
+		if (strict_errors)
+			return 1;
+	}
+
+	return 0;
+}
+
+/* Copy access and modification timestamps to this path. */
+static int
+rdump_timestamps_path(
+	struct xfs_inode	*ip,
+	const struct destdir	*destdir,
+	const struct pathbuf	*pbuf)
+{
+	struct timespec		times[2] = {
+		[0] = {
+			.tv_sec  = inode_get_atime_sec(VFS_I(ip)),
+			.tv_nsec = inode_get_atime_nsec(VFS_I(ip)),
+		},
+		[1] = {
+			.tv_sec  = inode_get_mtime_sec(VFS_I(ip)),
+			.tv_nsec = inode_get_mtime_nsec(VFS_I(ip)),
+		},
+	};
+	int			ret;
+
+	/* Cannot set ctime or btime */
+
+	ret = utimensat(destdir->fd, pbuf->path, times, AT_SYMLINK_NOFOLLOW);
+	if (ret) {
+		if (errno == EPERM)
+			lost_mask |= LOST_TIME;
+		else
+			dbprintf(_("%s%s%s: utimensat %s\n"), destdir->path,
+					destdir->sep, pbuf->path,
+					strerror(errno));
+		if (strict_errors)
+			return 1;
+	}
+
+	return 0;
+}
+
+struct copyxattr {
+	const struct destdir	*destdir;
+	const struct pathbuf	*pbuf;
+	int			fd;
+	char			name[XATTR_NAME_MAX +
+				     XATTR_SECURITY_PREFIX_LEN + 1];
+	char			value[XATTR_SIZE_MAX];
+};
+
+/*
+ * ACL xattrs can be copied verbatim to another XFS filesystem without issue
+ * because the name of the ondisk xattr is not that of the magic POSIX ACL
+ * xattr.  However, if we're rdumping to another filesystem type, we need to
+ * warn the user that the ACLs most likely won't work because we don't want to
+ * introduce a hard dep on libacl to perform a translation for a foreign fs.
+ */
+static inline bool
+cannot_translate_acl(
+	int			fd,
+	const char		*name,
+	unsigned int		namelen)
+{
+	struct statfs		statfsbuf;
+	int			ret;
+
+	if (namelen == SGI_ACL_FILE_SIZE &&
+	    !strncmp(name, SGI_ACL_FILE, SGI_ACL_FILE_SIZE))
+		return false;
+
+	if (namelen == SGI_ACL_DEFAULT_SIZE &&
+	    !strncmp(name, SGI_ACL_DEFAULT, SGI_ACL_DEFAULT_SIZE))
+		return false;
+
+	ret = fstatfs(fd, &statfsbuf);
+	if (ret)
+		return false;
+
+	return statfsbuf.f_type != XFS_SUPER_MAGIC;
+}
+
+/* Copy one extended attribute */
+static int
+rdump_xattr(
+	struct xfs_trans	*tp,
+	struct xfs_inode	*ip,
+	unsigned int		attr_flags,
+	const unsigned char	*name,
+	unsigned int		namelen,
+	const void		*value,
+	unsigned int		valuelen,
+	void			*priv)
+{
+	struct copyxattr	*cx = priv;
+	const char		*namespace;
+	char			*realname = (char *)name;
+	const size_t		remaining = sizeof(cx->name);
+	ssize_t			added;
+	int			realnamelen = namelen;
+	int			ret;
+
+	switch (attr_flags & XFS_ATTR_NSP_ONDISK_MASK) {
+	case XFS_ATTR_PARENT:
+		return 0;
+	case XFS_ATTR_ROOT:
+		namespace = XATTR_TRUSTED_PREFIX;
+		if (!(lost_mask & LOST_ACL) &&
+		    cannot_translate_acl(cx->fd, (const char *)name, namelen))
+			lost_mask |= LOST_ACL;
+		break;
+	case XFS_ATTR_SECURE:
+		namespace = XATTR_SECURITY_PREFIX;
+		break;
+	case 0: /* user xattrs */
+		namespace = XATTR_USER_PREFIX;
+		break;
+	default:
+		dbprintf(_("%s%s%s: unknown xattr namespace 0x%x\n"),
+				cx->destdir->path, cx->destdir->sep,
+				cx->pbuf->path,
+				attr_flags & XFS_ATTR_NSP_ONDISK_MASK);
+		return strict_errors ? ECANCELED : 0;
+	}
+	added = snprintf(cx->name, remaining, "%s%.*s", namespace,
+			realnamelen, realname);
+	if (added > remaining) {
+		dbprintf(
+ _("%s%s%s: ran out of space formatting xattr name %s%.*s\n"),
+				cx->destdir->path, cx->destdir->sep,
+				cx->pbuf->path, namespace, realnamelen,
+				realname);
+		return strict_errors ? ECANCELED : 0;
+	}
+
+	/* Retrieve xattr value if needed */
+	if (valuelen > 0 && !value) {
+		struct xfs_da_args	args = {
+			.trans		= tp,
+			.dp		= ip,
+			.geo		= mp->m_attr_geo,
+			.owner		= ip->i_ino,
+			.attr_filter	= attr_flags & XFS_ATTR_NSP_ONDISK_MASK,
+			.namelen	= namelen,
+			.name		= name,
+			.value		= cx->value,
+			.valuelen	= valuelen,
+		};
+
+		ret = -libxfs_attr_rmtval_get(&args);
+		if (ret) {
+			dbprintf(_("%s: reading xattr \"%s\" value %s\n"),
+					cx->pbuf->path, cx->name,
+					strerror(ret));
+			return strict_errors ? ECANCELED : 0;
+		}
+
+		value = cx->value;
+	}
+
+	ret = fsetxattr(cx->fd, cx->name, value, valuelen, 0);
+	if (ret) {
+		if (ret == EOPNOTSUPP)
+			lost_mask |= LOST_XATTR;
+		else
+			dbprintf(_("%s%s%s: fsetxattr \"%s\" %s\n"),
+					cx->destdir->path, cx->destdir->sep,
+					cx->pbuf->path, cx->name,
+					strerror(errno));
+		if (strict_errors)
+			return ECANCELED;
+	}
+
+	return 0;
+}
+
+/* Copy extended attributes */
+static int
+rdump_xattrs(
+	struct xfs_trans	*tp,
+	struct xfs_inode	*ip,
+	const struct destdir	*destdir,
+	const struct pathbuf	*pbuf,
+	int			fd)
+{
+	struct copyxattr	*cx;
+	int			ret;
+
+	cx = calloc(1, sizeof(struct copyxattr));
+	if (!cx) {
+		dbprintf(_("%s%s%s: allocating xattr buffer %s\n"),
+				destdir->path, destdir->sep, pbuf->path,
+				strerror(errno));
+		return 1;
+	}
+	cx->destdir = destdir;
+	cx->pbuf = pbuf;
+	cx->fd = fd;
+
+	ret = xattr_walk(tp, ip, rdump_xattr, cx);
+	if (ret && ret != ECANCELED) {
+		dbprintf(_("%s%s%s: listxattr %s\n"), destdir->path,
+				destdir->sep, pbuf->path, strerror(errno));
+		if (strict_errors)
+			return 1;
+	}
+
+	free(cx);
+	return 0;
+}
+
+struct copydirent {
+	const struct destdir	*destdir;
+	struct pathbuf		*pbuf;
+	int			fd;
+};
+
+/* Copy a directory entry. */
+static int
+rdump_dirent(
+	struct xfs_trans	*tp,
+	struct xfs_inode	*dp,
+	xfs_dir2_dataptr_t	off,
+	char			*name,
+	ssize_t			namelen,
+	xfs_ino_t		ino,
+	uint8_t			dtype,
+	void			*private)
+{
+	struct copydirent	*cd = private;
+	size_t			oldlen = cd->pbuf->len;
+	const size_t		remaining = PATH_MAX + 1 - oldlen;
+	ssize_t			added;
+	int			ret;
+
+	/* Negative length means name is null-terminated */
+	if (namelen < 0)
+		namelen = -namelen;
+
+	/* Ignore dot and dotdot */
+	if (namelen == 1 && name[0] == '.')
+		return 0;
+	if (namelen == 2 && name[0] == '.' && name[1] == '.')
+		return 0;
+
+	if (namelen > FILENAME_MAX) {
+		dbprintf(_("%s%s%s: %s\n"),
+				cd->destdir->path, cd->destdir->sep,
+				cd->pbuf->path, strerror(ENAMETOOLONG));
+		return strict_errors ? ECANCELED : 0;
+	}
+
+	added = snprintf(&cd->pbuf->path[oldlen], remaining, "%s%.*s",
+			oldlen ? "/" : "", (int)namelen, name);
+	if (added > remaining) {
+		dbprintf(_("%s%s%s: ran out of space formatting file name\n"),
+				cd->destdir->path, cd->destdir->sep,
+				cd->pbuf->path);
+		return strict_errors ? ECANCELED : 0;
+	}
+
+	cd->pbuf->len += added;
+	ret = rdump_file(tp, ino, cd->destdir, cd->pbuf);
+	cd->pbuf->len = oldlen;
+	cd->pbuf->path[oldlen] = 0;
+	return ret;
+}
+
+/* Copy a directory */
+static int
+rdump_directory(
+	struct xfs_trans	*tp,
+	struct xfs_inode	*dp,
+	const struct destdir	*destdir,
+	struct pathbuf		*pbuf)
+{
+	struct copydirent	*cd;
+	int			ret, ret2;
+
+	cd = calloc(1, sizeof(struct copydirent));
+	if (!cd) {
+		dbprintf(_("%s%s%s: %s\n"), destdir->path, destdir->sep,
+				pbuf->path, strerror(errno));
+		return 1;
+	}
+	cd->destdir = destdir;
+	cd->pbuf = pbuf;
+
+	if (pbuf->len) {
+		/*
+		 * If path is non-empty, we want to create a child somewhere
+		 * underneath the target directory.
+		 */
+		ret = mkdirat(destdir->fd, pbuf->path, 0600);
+		if (ret && errno != EEXIST) {
+			dbprintf(_("%s%s%s: %s\n"), destdir->path,
+					destdir->sep, pbuf->path,
+					strerror(errno));
+			goto out_cd;
+		}
+
+		cd->fd = openat(destdir->fd, pbuf->path,
+				O_RDONLY | O_DIRECTORY);
+		if (cd->fd < 0) {
+			dbprintf(_("%s%s%s: %s\n"), destdir->path,
+					destdir->sep, pbuf->path,
+					strerror(errno));
+			ret = 1;
+			goto out_cd;
+		}
+	} else {
+		/*
+		 * If path is empty, then we're copying the children of a
+		 * directory into the target directory.
+		 */
+		cd->fd = destdir->fd;
+	}
+
+	ret = rdump_fileattrs_fd(dp, destdir, pbuf, cd->fd);
+	if (ret && strict_errors)
+		goto out_close;
+
+	if (xfs_inode_has_attr_fork(dp)) {
+		ret = rdump_xattrs(tp, dp, destdir, pbuf, cd->fd);
+		if (ret && strict_errors)
+			goto out_close;
+	}
+
+	ret = listdir(tp, dp, rdump_dirent, cd);
+	if (ret && ret != ECANCELED) {
+		dbprintf(_("%s%s%s: readdir %s\n"), destdir->path,
+				destdir->sep, pbuf->path, strerror(ret));
+		if (strict_errors)
+			goto out_close;
+	}
+
+	ret = rdump_timestamps_fd(dp, destdir, pbuf, cd->fd);
+	if (ret && strict_errors)
+		goto out_close;
+
+	ret = 0;
+
+out_close:
+	if (cd->fd != destdir->fd) {
+		ret2 = close(cd->fd);
+		if (ret2) {
+			if (!ret)
+				ret = ret2;
+			dbprintf(_("%s%s%s: %s\n"), destdir->path,
+					destdir->sep, pbuf->path,
+					strerror(errno));
+		}
+	}
+
+out_cd:
+	free(cd);
+	return ret;
+}
+
+/* Copy file data */
+static int
+rdump_regfile_data(
+	struct xfs_trans	*tp,
+	struct xfs_inode	*ip,
+	const struct destdir	*destdir,
+	const struct pathbuf	*pbuf,
+	int			fd)
+{
+	struct xfs_bmbt_irec	irec = { };
+	struct xfs_buftarg	*btp;
+	int			nmaps;
+	off_t			pos = 0;
+	const off_t		isize = ip->i_disk_size;
+	int			ret;
+
+	if (XFS_IS_REALTIME_INODE(ip))
+		btp = ip->i_mount->m_rtdev_targp;
+	else
+		btp = ip->i_mount->m_ddev_targp;
+
+	for (;
+	     pos < isize;
+	     pos = XFS_FSB_TO_B(mp, irec.br_startoff + irec.br_blockcount)) {
+		struct xfs_buf	*bp;
+		off_t		buf_pos;
+		off_t		fd_pos;
+		xfs_fileoff_t	off = XFS_B_TO_FSBT(mp, pos);
+		xfs_filblks_t	max_read = XFS_B_TO_FSB(mp, 1048576);
+		xfs_daddr_t	daddr;
+		size_t		count;
+
+		nmaps = 1;
+		ret = -libxfs_bmapi_read(ip, off, max_read, &irec, &nmaps, 0);
+		if (ret) {
+			dbprintf(_("%s: %s\n"), pbuf->path, strerror(ret));
+			if (strict_errors)
+				return 1;
+			continue;
+		}
+		if (!nmaps)
+			break;
+
+		if (!xfs_bmap_is_written_extent(&irec))
+			continue;
+
+		fd_pos = XFS_FSB_TO_B(mp, irec.br_startoff);
+		if (XFS_IS_REALTIME_INODE(ip))
+			daddr =  xfs_rtb_to_daddr(mp, irec.br_startblock);
+		else
+			daddr = XFS_FSB_TO_DADDR(mp, irec.br_startblock);
+
+		ret = -libxfs_buf_read_uncached(btp, daddr,
+				XFS_FSB_TO_BB(mp, irec.br_blockcount), 0, &bp,
+				NULL);
+		if (ret) {
+			dbprintf(_("%s: reading pos 0x%llx %s\n"), pbuf->path,
+					fd_pos, strerror(ret));
+			if (strict_errors)
+				return 1;
+			continue;
+		}
+
+		count = XFS_FSB_TO_B(mp, irec.br_blockcount);
+		if (fd_pos + count > isize)
+			count = isize - fd_pos;
+
+		buf_pos = 0;
+		while (count > 0) {
+			ssize_t	written;
+
+			written = pwrite(fd, bp->b_addr + buf_pos, count,
+					fd_pos);
+			if (written < 0) {
+				libxfs_buf_relse(bp);
+				dbprintf(_("%s%s%s: writing pos 0x%llx %s\n"),
+						destdir->path, destdir->sep,
+						pbuf->path, fd_pos,
+						strerror(errno));
+				return 1;
+			}
+			if (!written) {
+				libxfs_buf_relse(bp);
+				dbprintf(
+ _("%s%s%s: wrote zero at pos 0x%llx %s\n"),
+						destdir->path, destdir->sep,
+						pbuf->path, fd_pos);
+				return 1;
+			}
+
+			fd_pos += written;
+			buf_pos += written;
+			count -= written;
+		}
+
+		libxfs_buf_relse(bp);
+	}
+
+	ret = ftruncate(fd, isize);
+	if (ret) {
+		dbprintf(_("%s%s%s: setting file length 0x%llx %s\n"),
+				destdir->path, destdir->sep, pbuf->path,
+				isize, strerror(errno));
+		return 1;
+	}
+
+	return 0;
+}
+
+/* Copy a regular file */
+static int
+rdump_regfile(
+	struct xfs_trans	*tp,
+	struct xfs_inode	*ip,
+	const struct destdir	*destdir,
+	const struct pathbuf	*pbuf)
+{
+	int			fd;
+	int			ret, ret2;
+
+	fd = openat(destdir->fd, pbuf->path, O_RDWR | O_CREAT | O_TRUNC, 0600);
+	if (fd < 0) {
+		dbprintf(_("%s%s%s: %s\n"), destdir->path, destdir->sep,
+				pbuf->path, strerror(errno));
+		return 1;
+	}
+
+	ret = rdump_fileattrs_fd(ip, destdir, pbuf, fd);
+	if (ret && strict_errors)
+		goto out_close;
+
+	if (xfs_inode_has_attr_fork(ip)) {
+		ret = rdump_xattrs(tp, ip, destdir, pbuf, fd);
+		if (ret && strict_errors)
+			goto out_close;
+	}
+
+	ret = rdump_regfile_data(tp, ip, destdir, pbuf, fd);
+	if (ret && strict_errors)
+		goto out_close;
+
+	ret = rdump_timestamps_fd(ip, destdir, pbuf, fd);
+	if (ret && strict_errors)
+		goto out_close;
+
+out_close:
+	ret2 = close(fd);
+	if (ret2) {
+		if (!ret)
+			ret = ret2;
+		dbprintf(_("%s%s%s: %s\n"), destdir->path, destdir->sep,
+				pbuf->path, strerror(errno));
+		return 1;
+	}
+
+	return ret;
+}
+
+/* Copy a symlink */
+static int
+rdump_symlink(
+	struct xfs_trans	*tp,
+	struct xfs_inode	*ip,
+	const struct destdir	*destdir,
+	const struct pathbuf	*pbuf)
+{
+	char			target[XFS_SYMLINK_MAXLEN + 1];
+	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
+	unsigned int		targetlen = ip->i_disk_size;
+	int			ret;
+
+	if (ifp->if_format == XFS_DINODE_FMT_LOCAL) {
+		memcpy(target, ifp->if_data, targetlen);
+	} else {
+		ret = -libxfs_symlink_remote_read(ip, target);
+		if (ret) {
+			dbprintf(_("%s: %s\n"), pbuf->path, strerror(ret));
+			return strict_errors ? 1 : 0;
+		}
+	}
+	target[targetlen] = 0;
+
+	ret = symlinkat(target, destdir->fd, pbuf->path);
+	if (ret) {
+		dbprintf(_("%s%s%s: %s\n"), destdir->path, destdir->sep,
+				pbuf->path, strerror(errno));
+		return 1;
+	}
+
+	ret = rdump_fileattrs_path(ip, destdir, pbuf);
+	if (ret && strict_errors)
+		goto out;
+
+	ret = rdump_timestamps_path(ip, destdir, pbuf);
+	if (ret && strict_errors)
+		goto out;
+
+	ret = 0;
+out:
+	return ret;
+}
+
+/* Copy a special file */
+static int
+rdump_special(
+	struct xfs_trans	*tp,
+	struct xfs_inode	*ip,
+	const struct destdir	*destdir,
+	const struct pathbuf	*pbuf)
+{
+	const unsigned int	major = IRIX_DEV_MAJOR(VFS_I(ip)->i_rdev);
+	const unsigned int	minor = IRIX_DEV_MINOR(VFS_I(ip)->i_rdev);
+	int			ret;
+
+	ret = mknodat(destdir->fd, pbuf->path, VFS_I(ip)->i_mode & S_IFMT,
+			makedev(major, minor));
+	if (ret) {
+		dbprintf(_("%s%s%s: %s\n"), destdir->path, destdir->sep,
+				pbuf->path, strerror(errno));
+		return 1;
+	}
+
+	ret = rdump_fileattrs_path(ip, destdir, pbuf);
+	if (ret && strict_errors)
+		goto out;
+
+	ret = rdump_timestamps_path(ip, destdir, pbuf);
+	if (ret && strict_errors)
+		goto out;
+
+	ret = 0;
+out:
+	return ret;
+}
+
+/* Dump some kind of file. */
+static int
+rdump_file(
+	struct xfs_trans	*tp,
+	xfs_ino_t		ino,
+	const struct destdir	*destdir,
+	struct pathbuf		*pbuf)
+{
+	struct xfs_inode	*ip;
+	int			ret;
+
+	ret = -libxfs_iget(mp, tp, ino, 0, &ip);
+	if (ret) {
+		dbprintf(_("%s: %s\n"), pbuf->path, strerror(ret));
+		return strict_errors ? ret : 0;
+	}
+
+	switch(VFS_I(ip)->i_mode & S_IFMT) {
+	case S_IFDIR:
+		ret = rdump_directory(tp, ip, destdir, pbuf);
+		break;
+	case S_IFREG:
+		ret = rdump_regfile(tp, ip, destdir, pbuf);
+		break;
+	case S_IFLNK:
+		ret = rdump_symlink(tp, ip, destdir, pbuf);
+		break;
+	default:
+		ret = rdump_special(tp, ip, destdir, pbuf);
+		break;
+	}
+
+	libxfs_irele(ip);
+	return ret;
+}
+
+/* Copy one path out of the filesystem. */
+static int
+rdump_path(
+	struct xfs_mount	*mp,
+	bool			sole_path,
+	const char		*path,
+	const struct destdir	*destdir)
+{
+	struct xfs_trans	*tp;
+	struct pathbuf		*pbuf;
+	const char		*basename;
+	ssize_t			pathlen = strlen(path);
+	int			ret = 1;
+
+	/* Set up destination path data */
+	if (pathlen > PATH_MAX) {
+		dbprintf(_("%s: %s\n"), path, strerror(ENAMETOOLONG));
+		return 1;
+	}
+
+	pbuf = calloc(1, sizeof(struct pathbuf));
+	if (!pbuf) {
+		dbprintf(_("allocating path buf: %s\n"), strerror(errno));
+		return 1;
+	}
+	basename = strrchr(path, '/');
+	if (basename) {
+		pbuf->len = pathlen - (basename + 1 - path);
+		memcpy(pbuf->path, basename + 1, pbuf->len);
+	} else {
+		pbuf->len = pathlen;
+		memcpy(pbuf->path, path, pbuf->len);
+	}
+	pbuf->path[pbuf->len] = 0;
+
+	/* Dump the inode referenced. */
+	if (pathlen) {
+		ret = path_walk(mp->m_sb.sb_rootino, path);
+		if (ret) {
+			dbprintf(_("%s: %s\n"), path, strerror(ret));
+			return 1;
+		}
+
+		if (sole_path) {
+			struct xfs_dinode	*dip = iocur_top->data;
+
+			/*
+			 * If this is the only path to copy out and it's a dir,
+			 * then we can copy the children directly into the
+			 * target.
+			 */
+			if (S_ISDIR(be16_to_cpu(dip->di_mode))) {
+				pbuf->len = 0;
+				pbuf->path[0] = 0;
+			}
+		}
+	} else {
+		set_cur_inode(mp->m_sb.sb_rootino);
+	}
+
+	ret = -libxfs_trans_alloc_empty(mp, &tp);
+	if (ret) {
+		dbprintf(_("allocating state: %s\n"), strerror(ret));
+		goto out_pbuf;
+	}
+
+	ret = rdump_file(tp, iocur_top->ino, destdir, pbuf);
+	libxfs_trans_cancel(tp);
+out_pbuf:
+	free(pbuf);
+	return ret;
+}
+
+static int
+rdump_f(
+	int		argc,
+	char		*argv[])
+{
+	struct destdir	destdir;
+	int		i;
+	int		c;
+	int		ret;
+
+	lost_mask = 0;
+	strict_errors = false;
+	while ((c = getopt(argc, argv, "s")) != -1) {
+		switch (c) {
+		case 's':
+			strict_errors = true;
+			break;
+		default:
+			rdump_help();
+			return 0;
+		}
+	}
+
+	if (argc < optind + 1) {
+		dbprintf(
+ _("Must supply destination directory.\n"));
+		return 0;
+	}
+
+	/* Create and open destination directory */
+	destdir.path = argv[argc - 1];
+	ret = mkdir(destdir.path, 0755);
+	if (ret && errno != EEXIST) {
+		dbprintf(_("%s: %s\n"), destdir.path, strerror(errno));
+		exitcode = 1;
+		return 0;
+	}
+
+	if (destdir.path[0] == 0) {
+		dbprintf(
+ _("Destination dir must be at least one character.\n"));
+		exitcode = 1;
+		return 0;
+	}
+
+	if (destdir.path[strlen(destdir.path) - 1] != '/')
+		destdir.sep = "/";
+	else
+		destdir.sep = "";
+	destdir.fd = open(destdir.path, O_DIRECTORY | O_RDONLY);
+	if (destdir.fd < 0) {
+		dbprintf(_("%s: %s\n"), destdir.path, strerror(errno));
+		exitcode = 1;
+		return 0;
+	}
+
+	if (optind == argc - 1) {
+		/* no dirs given, just do the whole fs */
+		push_cur();
+		ret = rdump_path(mp, false, "", &destdir);
+		pop_cur();
+		if (ret)
+			exitcode = 1;
+		goto out_close;
+	}
+
+	for (i = optind; i < argc - 1; i++) {
+		size_t	len = strlen(argv[i]);
+
+		/* trim trailing slashes */
+		while (len && argv[i][len - 1] == '/')
+			len--;
+		argv[i][len] = 0;
+
+		push_cur();
+		ret = rdump_path(mp, argc == optind + 2, argv[i], &destdir);
+		pop_cur();
+
+		if (ret) {
+			exitcode = 1;
+			if (strict_errors)
+				break;
+		}
+	}
+
+out_close:
+	ret = close(destdir.fd);
+	if (ret) {
+		dbprintf(_("%s: %s\n"), destdir.path, strerror(errno));
+		exitcode = 1;
+	}
+
+	if (lost_mask & LOST_OWNER)
+		dbprintf(_("%s: some uid/gid could not be set\n"),
+				destdir.path);
+	if (lost_mask & LOST_MODE)
+		dbprintf(_("%s: some file modes could not be set\n"),
+				destdir.path);
+	if (lost_mask & LOST_TIME)
+		dbprintf(_("%s: some timestamps could not be set\n"),
+				destdir.path);
+	if (lost_mask & LOST_SOME_FSXATTR)
+		dbprintf(_("%s: some xfs file attr bits could not be set\n"),
+				destdir.path);
+	if (lost_mask & LOST_FSXATTR)
+		dbprintf(_("%s: some xfs file attrs could not be set\n"),
+				destdir.path);
+	if (lost_mask & LOST_XATTR)
+		dbprintf(_("%s: some extended xattrs could not be set\n"),
+				destdir.path);
+	if (lost_mask & LOST_ACL)
+		dbprintf(_("%s: some ACLs could not be translated\n"),
+				destdir.path);
+
+	return 0;
+}
+
+static struct cmdinfo rdump_cmd = {
+	.name		= "rdump",
+	.cfunc		= rdump_f,
+	.argmin		= 0,
+	.argmax		= -1,
+	.canpush	= 0,
+	.args		= "[-s] [paths...] dest_directory",
+	.help		= rdump_help,
+};
+
+void
+rdump_init(void)
+{
+	rdump_cmd.oneline = _("recover files out of a filesystem");
+	add_command(&rdump_cmd);
+}
diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
index 08f38f37ca01cc..2a9322560584b0 100644
--- a/man/man8/xfs_db.8
+++ b/man/man8/xfs_db.8
@@ -1132,6 +1132,32 @@ .SH COMMANDS
 Exit
 .BR xfs_db .
 .TP
+.BI "rdump [-s] [" "paths..." "] " "destination_dir"
+Recover the files given by the
+.B path
+arguments by copying them of the filesystem into the directory specified in
+.BR destination_dir .
+
+If the
+.B -s
+option is specified, errors are fatal.
+By default, read errors are ignored in favor of recovering as much data from
+the filesystem as possible.
+
+If zero
+.B paths
+are specified, the entire filesystem is dumped.
+If only one
+.B path
+is specified and it is a directory, the children of that directory will be
+copied directly to the destination.
+If multiple
+.B paths
+are specified, each file is copied into the directory as a new child.
+
+If possible, sparse holes, xfs file attributes, and extended attributes will be
+preserved.
+.TP
 .BI "rgresv [" rgno ]
 Displays the per-rtgroup reservation size, and per-rtgroup
 reservation usage for a given realtime allocation group.

  parent reply	other threads:[~2025-02-18 19:58 UTC|newest]

Thread overview: 225+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-06 22:21 [PATCHBOMB] xfsprogs: all my changes for 6.14 Darrick J. Wong
2025-02-06 22:29 ` [PATCHSET 1/5] xfs_scrub: fixes and cleanups for inode iteration Darrick J. Wong
2025-02-06 22:30   ` [PATCH 01/17] libxfs: unmap xmbuf pages to avoid disaster Darrick J. Wong
2025-02-07  4:10     ` Christoph Hellwig
2025-02-06 22:31   ` [PATCH 02/17] libxfs: mark xmbuf_{un,}map_page static Darrick J. Wong
2025-02-06 22:31   ` [PATCH 03/17] man: document new XFS_BULK_IREQ_METADIR flag to bulkstat Darrick J. Wong
2025-02-07  4:10     ` Christoph Hellwig
2025-02-06 22:31   ` [PATCH 04/17] libfrog: wrap handle construction code Darrick J. Wong
2025-02-07  4:34     ` Christoph Hellwig
2025-02-07  4:49       ` Darrick J. Wong
2025-02-07 17:00         ` Darrick J. Wong
2025-02-13  4:05           ` Christoph Hellwig
2025-02-13  4:20             ` Darrick J. Wong
2025-02-20 21:36     ` [PATCH v1.1] " Darrick J. Wong
2025-02-24 16:58       ` Christoph Hellwig
2025-02-06 22:31   ` [PATCH 05/17] xfs_scrub: don't report data loss in unlinked inodes twice Darrick J. Wong
2025-02-07  4:35     ` Christoph Hellwig
2025-02-07  4:46       ` Darrick J. Wong
2025-02-06 22:32   ` [PATCH 06/17] xfs_scrub: call bulkstat directly if we're only scanning user files Darrick J. Wong
2025-02-07  4:37     ` Christoph Hellwig
2025-02-06 22:32   ` [PATCH 07/17] xfs_scrub: remove flags argument from scrub_scan_all_inodes Darrick J. Wong
2025-02-07  4:38     ` Christoph Hellwig
2025-02-06 22:32   ` [PATCH 08/17] xfs_scrub: selectively re-run bulkstat after re-running inumbers Darrick J. Wong
2025-02-07  4:39     ` Christoph Hellwig
2025-02-06 22:33   ` [PATCH 09/17] xfs_scrub: actually iterate all the bulkstat records Darrick J. Wong
2025-02-07  4:40     ` Christoph Hellwig
2025-02-06 22:33   ` [PATCH 10/17] xfs_scrub: don't double-scan inodes during phase 3 Darrick J. Wong
2025-02-07  4:41     ` Christoph Hellwig
2025-02-06 22:33   ` [PATCH 11/17] xfs_scrub: don't (re)set the bulkstat request icount incorrectly Darrick J. Wong
2025-02-07  4:42     ` Christoph Hellwig
2025-02-06 22:33   ` [PATCH 12/17] xfs_scrub: don't complain if bulkstat fails Darrick J. Wong
2025-02-07  4:42     ` Christoph Hellwig
2025-02-06 22:34   ` [PATCH 13/17] xfs_scrub: return early from bulkstat_for_inumbers if no bulkstat data Darrick J. Wong
2025-02-07  4:43     ` Christoph Hellwig
2025-02-06 22:34   ` [PATCH 14/17] xfs_scrub: don't blow away new inodes in bulkstat_single_step Darrick J. Wong
2025-02-07  4:46     ` Christoph Hellwig
2025-02-07  4:50       ` Darrick J. Wong
2025-02-06 22:34   ` [PATCH 15/17] xfs_scrub: hoist the phase3 bulkstat single stepping code Darrick J. Wong
2025-02-07  4:47     ` Christoph Hellwig
2025-02-06 22:34   ` [PATCH 16/17] xfs_scrub: ignore freed inodes when single-stepping during phase 3 Darrick J. Wong
2025-02-07  4:47     ` Christoph Hellwig
2025-02-06 22:35   ` [PATCH 17/17] xfs_scrub: try harder to fill the bulkstat array with bulkstat() Darrick J. Wong
2025-02-07  4:48     ` Christoph Hellwig
2025-02-06 22:29 ` [PATCHSET 2/5] xfsprogs: new libxfs code from kernel 6.14 Darrick J. Wong
2025-02-06 22:35   ` [PATCH 01/56] xfs: tidy up xfs_iroot_realloc Darrick J. Wong
2025-02-06 22:35   ` [PATCH 02/56] xfs: refactor the inode fork memory allocation functions Darrick J. Wong
2025-02-06 22:35   ` [PATCH 03/56] xfs: make xfs_iroot_realloc take the new numrecs instead of deltas Darrick J. Wong
2025-02-06 22:36   ` [PATCH 04/56] xfs: make xfs_iroot_realloc a bmap btree function Darrick J. Wong
2025-02-06 22:36   ` [PATCH 05/56] xfs: tidy up xfs_bmap_broot_realloc a bit Darrick J. Wong
2025-02-06 22:36   ` [PATCH 06/56] xfs: hoist the node iroot update code out of xfs_btree_new_iroot Darrick J. Wong
2025-02-06 22:36   ` [PATCH 07/56] xfs: hoist the node iroot update code out of xfs_btree_kill_iroot Darrick J. Wong
2025-02-06 22:37   ` [PATCH 08/56] xfs: add some rtgroup inode helpers Darrick J. Wong
2025-02-06 22:37   ` [PATCH 09/56] xfs: prepare to reuse the dquot pointer space in struct xfs_inode Darrick J. Wong
2025-02-06 22:37   ` [PATCH 10/56] xfs: simplify the xfs_rmap_{alloc,free}_extent calling conventions Darrick J. Wong
2025-02-06 22:37   ` [PATCH 11/56] xfs: support storing records in the inode core root Darrick J. Wong
2025-02-06 22:38   ` [PATCH 12/56] xfs: allow inode-based btrees to reserve space in the data device Darrick J. Wong
2025-02-06 22:38   ` [PATCH 13/56] xfs: introduce realtime rmap btree ondisk definitions Darrick J. Wong
2025-02-06 22:38   ` [PATCH 14/56] xfs: realtime rmap btree transaction reservations Darrick J. Wong
2025-02-06 22:39   ` [PATCH 15/56] xfs: add realtime rmap btree operations Darrick J. Wong
2025-02-06 22:39   ` [PATCH 16/56] xfs: prepare rmap functions to deal with rtrmapbt Darrick J. Wong
2025-02-06 22:39   ` [PATCH 17/56] xfs: add a realtime flag to the rmap update log redo items Darrick J. Wong
2025-02-06 22:39   ` [PATCH 18/56] xfs: pretty print metadata file types in error messages Darrick J. Wong
2025-02-06 22:40   ` [PATCH 19/56] xfs: support file data forks containing metadata btrees Darrick J. Wong
2025-02-06 22:40   ` [PATCH 20/56] xfs: add realtime reverse map inode to metadata directory Darrick J. Wong
2025-02-06 22:40   ` [PATCH 21/56] xfs: add metadata reservations for realtime rmap btrees Darrick J. Wong
2025-02-06 22:40   ` [PATCH 22/56] xfs: wire up a new metafile type for the realtime rmap Darrick J. Wong
2025-02-06 22:41   ` [PATCH 23/56] xfs: wire up rmap map and unmap to the realtime rmapbt Darrick J. Wong
2025-02-06 22:41   ` [PATCH 24/56] xfs: create routine to allocate and initialize a realtime rmap btree inode Darrick J. Wong
2025-02-06 22:41   ` [PATCH 25/56] xfs: report realtime rmap btree corruption errors to the health system Darrick J. Wong
2025-02-06 22:41   ` [PATCH 26/56] xfs: scrub the realtime rmapbt Darrick J. Wong
2025-02-06 22:42   ` [PATCH 27/56] xfs: scrub the metadir path of rt rmap btree files Darrick J. Wong
2025-02-06 22:42   ` [PATCH 28/56] xfs: online repair of realtime bitmaps for a realtime group Darrick J. Wong
2025-02-06 22:42   ` [PATCH 29/56] xfs: online repair of the realtime rmap btree Darrick J. Wong
2025-02-06 22:42   ` [PATCH 30/56] xfs: create a shadow rmap btree during realtime rmap repair Darrick J. Wong
2025-02-06 22:43   ` [PATCH 31/56] xfs: namespace the maximum length/refcount symbols Darrick J. Wong
2025-02-06 22:43   ` [PATCH 32/56] xfs: introduce realtime refcount btree ondisk definitions Darrick J. Wong
2025-02-06 22:43   ` [PATCH 33/56] xfs: realtime refcount btree transaction reservations Darrick J. Wong
2025-02-06 22:43   ` [PATCH 34/56] xfs: add realtime refcount btree operations Darrick J. Wong
2025-02-06 22:44   ` [PATCH 35/56] xfs: prepare refcount functions to deal with rtrefcountbt Darrick J. Wong
2025-02-06 22:44   ` [PATCH 36/56] xfs: add a realtime flag to the refcount update log redo items Darrick J. Wong
2025-02-06 22:44   ` [PATCH 37/56] xfs: add realtime refcount btree inode to metadata directory Darrick J. Wong
2025-02-06 22:45   ` [PATCH 38/56] xfs: add metadata reservations for realtime refcount btree Darrick J. Wong
2025-02-06 22:45   ` [PATCH 39/56] xfs: wire up a new metafile type for the realtime refcount Darrick J. Wong
2025-02-06 22:45   ` [PATCH 40/56] xfs: wire up realtime refcount btree cursors Darrick J. Wong
2025-02-06 22:45   ` [PATCH 41/56] xfs: create routine to allocate and initialize a realtime refcount btree inode Darrick J. Wong
2025-02-06 22:46   ` [PATCH 42/56] xfs: update rmap to allow cow staging extents in the rt rmap Darrick J. Wong
2025-02-06 22:46   ` [PATCH 43/56] xfs: compute rtrmap btree max levels when reflink enabled Darrick J. Wong
2025-02-06 22:46   ` [PATCH 44/56] xfs: allow inodes to have the realtime and reflink flags Darrick J. Wong
2025-02-06 22:46   ` [PATCH 45/56] xfs: recover CoW leftovers in the realtime volume Darrick J. Wong
2025-02-06 22:47   ` [PATCH 46/56] xfs: fix xfs_get_extsz_hint behavior with realtime alwayscow files Darrick J. Wong
2025-02-06 22:47   ` [PATCH 47/56] xfs: apply rt extent alignment constraints to CoW extsize hint Darrick J. Wong
2025-02-06 22:47   ` [PATCH 48/56] xfs: enable extent size hints for CoW operations Darrick J. Wong
2025-02-06 22:47   ` [PATCH 49/56] xfs: report realtime refcount btree corruption errors to the health system Darrick J. Wong
2025-02-06 22:48   ` [PATCH 50/56] xfs: scrub the realtime refcount btree Darrick J. Wong
2025-02-06 22:48   ` [PATCH 51/56] xfs: scrub the metadir path of rt refcount btree files Darrick J. Wong
2025-02-06 22:48   ` [PATCH 52/56] xfs: fix the entry condition of exact EOF block allocation optimization Darrick J. Wong
2025-02-06 22:48   ` [PATCH 53/56] xfs: mark xfs_dir_isempty static Darrick J. Wong
2025-02-06 22:49   ` [PATCH 54/56] xfs: remove XFS_ILOG_NONCORE Darrick J. Wong
2025-02-06 22:49   ` [PATCH 55/56] xfs: constify feature checks Darrick J. Wong
2025-02-06 22:49   ` [PATCH 56/56] xfs/libxfs: replace kmalloc() and memcpy() with kmemdup() Darrick J. Wong
2025-02-06 22:30 ` [PATCHSET v6.3 3/5] xfsprogs: realtime reverse-mapping support Darrick J. Wong
2025-02-06 22:49   ` [PATCH 01/27] libxfs: compute the rt rmap btree maxlevels during initialization Darrick J. Wong
2025-02-07  5:07     ` Christoph Hellwig
2025-02-06 22:50   ` [PATCH 02/27] libxfs: add a realtime flag to the rmap update log redo items Darrick J. Wong
2025-02-07  5:08     ` Christoph Hellwig
2025-02-06 22:50   ` [PATCH 03/27] libfrog: enable scrubbing of the realtime rmap Darrick J. Wong
2025-02-07  5:08     ` Christoph Hellwig
2025-02-06 22:50   ` [PATCH 04/27] man: document userspace API changes due to rt rmap Darrick J. Wong
2025-02-07  5:08     ` Christoph Hellwig
2025-02-06 22:51   ` [PATCH 05/27] xfs_db: compute average btree height Darrick J. Wong
2025-02-07  5:09     ` Christoph Hellwig
2025-02-06 22:51   ` [PATCH 06/27] xfs_db: don't abort when bmapping on a non-extents/bmbt fork Darrick J. Wong
2025-02-07  5:09     ` Christoph Hellwig
2025-02-06 22:51   ` [PATCH 07/27] xfs_db: display the realtime rmap btree contents Darrick J. Wong
2025-02-07  5:16     ` Christoph Hellwig
2025-02-06 22:51   ` [PATCH 08/27] xfs_db: support the realtime rmapbt Darrick J. Wong
2025-02-07  5:17     ` Christoph Hellwig
2025-02-06 22:52   ` [PATCH 09/27] xfs_db: copy the realtime rmap btree Darrick J. Wong
2025-02-07  5:17     ` Christoph Hellwig
2025-02-06 22:52   ` [PATCH 10/27] xfs_db: make fsmap query the realtime reverse mapping tree Darrick J. Wong
2025-02-07  5:18     ` Christoph Hellwig
2025-02-06 22:52   ` [PATCH 11/27] xfs_db: add an rgresv command Darrick J. Wong
2025-02-07  5:19     ` Christoph Hellwig
2025-02-07  5:24       ` Darrick J. Wong
2025-02-06 22:52   ` [PATCH 12/27] xfs_spaceman: report health status of the realtime rmap btree Darrick J. Wong
2025-02-07  5:19     ` Christoph Hellwig
2025-02-06 22:53   ` [PATCH 13/27] xfs_repair: tidy up rmap_diffkeys Darrick J. Wong
2025-02-07  5:20     ` Christoph Hellwig
2025-02-06 22:53   ` [PATCH 14/27] xfs_repair: flag suspect long-format btree blocks Darrick J. Wong
2025-02-07  5:21     ` Christoph Hellwig
2025-02-06 22:53   ` [PATCH 15/27] xfs_repair: use realtime rmap btree data to check block types Darrick J. Wong
2025-02-07  5:22     ` Christoph Hellwig
2025-02-06 22:53   ` [PATCH 16/27] xfs_repair: create a new set of incore rmap information for rt groups Darrick J. Wong
2025-02-07  5:22     ` Christoph Hellwig
2025-02-06 22:54   ` [PATCH 17/27] xfs_repair: refactor realtime inode check Darrick J. Wong
2025-02-07  5:22     ` Christoph Hellwig
2025-02-06 22:54   ` [PATCH 18/27] xfs_repair: find and mark the rtrmapbt inodes Darrick J. Wong
2025-02-07  5:33     ` Christoph Hellwig
2025-02-06 22:54   ` [PATCH 19/27] xfs_repair: check existing realtime rmapbt entries against observed rmaps Darrick J. Wong
2025-02-07  5:35     ` Christoph Hellwig
2025-02-06 22:54   ` [PATCH 20/27] xfs_repair: always check realtime file mappings against incore info Darrick J. Wong
2025-02-07  5:35     ` Christoph Hellwig
2025-02-06 22:55   ` [PATCH 21/27] xfs_repair: rebuild the realtime rmap btree Darrick J. Wong
2025-02-07  5:54     ` Christoph Hellwig
2025-02-06 22:55   ` [PATCH 22/27] xfs_repair: check for global free space concerns with default btree slack levels Darrick J. Wong
2025-02-07  5:56     ` Christoph Hellwig
2025-02-06 22:55   ` [PATCH 23/27] xfs_repair: rebuild the bmap btree for realtime files Darrick J. Wong
2025-02-07  5:56     ` Christoph Hellwig
2025-02-06 22:55   ` [PATCH 24/27] xfs_repair: reserve per-AG space while rebuilding rt metadata Darrick J. Wong
2025-02-07  5:57     ` Christoph Hellwig
2025-02-06 22:56   ` [PATCH 25/27] xfs_logprint: report realtime RUIs Darrick J. Wong
2025-02-07  5:58     ` Christoph Hellwig
2025-02-07  5:58     ` Christoph Hellwig
2025-02-06 22:56   ` [PATCH 26/27] mkfs: add some rtgroup inode helpers Darrick J. Wong
2025-02-07  5:59     ` Christoph Hellwig
2025-02-07  6:07       ` Darrick J. Wong
2025-02-06 22:56   ` [PATCH 27/27] mkfs: create the realtime rmap inode Darrick J. Wong
2025-02-07  6:00     ` Christoph Hellwig
2025-02-06 22:30 ` [PATCHSET v6.3 4/5] xfsprogs: reflink on the realtime device Darrick J. Wong
2025-02-06 22:57   ` [PATCH 01/22] libxfs: compute the rt refcount btree maxlevels during initialization Darrick J. Wong
2025-02-13  4:20     ` Christoph Hellwig
2025-02-06 22:57   ` [PATCH 02/22] libxfs: add a realtime flag to the refcount update log redo items Darrick J. Wong
2025-02-13  4:20     ` Christoph Hellwig
2025-02-06 22:57   ` [PATCH 03/22] libxfs: apply rt extent alignment constraints to CoW extsize hint Darrick J. Wong
2025-02-13  4:21     ` Christoph Hellwig
2025-02-06 22:57   ` [PATCH 04/22] libfrog: enable scrubbing of the realtime refcount data Darrick J. Wong
2025-02-13  4:21     ` Christoph Hellwig
2025-02-06 22:58   ` [PATCH 05/22] man: document userspace API changes due to rt reflink Darrick J. Wong
2025-02-13  4:21     ` Christoph Hellwig
2025-02-06 22:58   ` [PATCH 06/22] xfs_db: display the realtime refcount btree contents Darrick J. Wong
2025-02-13  4:22     ` Christoph Hellwig
2025-02-06 22:58   ` [PATCH 07/22] xfs_db: support the realtime refcountbt Darrick J. Wong
2025-02-13  4:22     ` Christoph Hellwig
2025-02-06 22:58   ` [PATCH 08/22] xfs_db: copy the realtime refcount btree Darrick J. Wong
2025-02-13  4:23     ` Christoph Hellwig
2025-02-06 22:59   ` [PATCH 09/22] xfs_db: add rtrefcount reservations to the rgresv command Darrick J. Wong
2025-02-13  4:23     ` Christoph Hellwig
2025-02-06 22:59   ` [PATCH 10/22] xfs_spaceman: report health of the realtime refcount btree Darrick J. Wong
2025-02-13  4:24     ` Christoph Hellwig
2025-02-06 22:59   ` [PATCH 11/22] xfs_repair: allow CoW staging extents in the realtime rmap records Darrick J. Wong
2025-02-13  4:24     ` Christoph Hellwig
2025-02-06 22:59   ` [PATCH 12/22] xfs_repair: use realtime refcount btree data to check block types Darrick J. Wong
2025-02-13  4:24     ` Christoph Hellwig
2025-02-06 23:00   ` [PATCH 13/22] xfs_repair: find and mark the rtrefcountbt inode Darrick J. Wong
2025-02-13  4:25     ` Christoph Hellwig
2025-02-06 23:00   ` [PATCH 14/22] xfs_repair: compute refcount data for the realtime groups Darrick J. Wong
2025-02-13  4:25     ` Christoph Hellwig
2025-02-06 23:00   ` [PATCH 15/22] xfs_repair: check existing realtime refcountbt entries against observed refcounts Darrick J. Wong
2025-02-13  4:26     ` Christoph Hellwig
2025-02-06 23:00   ` [PATCH 16/22] xfs_repair: reject unwritten shared extents Darrick J. Wong
2025-02-13  4:26     ` Christoph Hellwig
2025-02-06 23:01   ` [PATCH 17/22] xfs_repair: rebuild the realtime refcount btree Darrick J. Wong
2025-02-13  4:27     ` Christoph Hellwig
2025-02-06 23:01   ` [PATCH 18/22] xfs_repair: allow realtime files to have the reflink flag set Darrick J. Wong
2025-02-13  4:27     ` Christoph Hellwig
2025-02-06 23:01   ` [PATCH 19/22] xfs_repair: validate CoW extent size hint on rtinherit directories Darrick J. Wong
2025-02-13  4:27     ` Christoph Hellwig
2025-02-06 23:01   ` [PATCH 20/22] xfs_logprint: report realtime CUIs Darrick J. Wong
2025-02-13  4:28     ` Christoph Hellwig
2025-02-06 23:02   ` [PATCH 21/22] mkfs: validate CoW extent size hint when rtinherit is set Darrick J. Wong
2025-02-13  4:28     ` Christoph Hellwig
2025-02-06 23:02   ` [PATCH 22/22] mkfs: enable reflink on the realtime device Darrick J. Wong
2025-02-13  4:28     ` Christoph Hellwig
2025-02-06 22:30 ` [PATCHSET 5/5] xfsprogs: dump fs directory trees Darrick J. Wong
2025-02-06 23:02   ` [PATCH 1/4] xfs_db: pass const pointers when we're not modifying them Darrick J. Wong
2025-02-13  4:29     ` Christoph Hellwig
2025-02-13 13:26     ` Andrey Albershteyn
2025-02-06 23:03   ` [PATCH 2/4] xfs_db: use an empty transaction to try to prevent livelocks in path_navigate Darrick J. Wong
2025-02-13  4:29     ` Christoph Hellwig
2025-02-13 13:26     ` Andrey Albershteyn
2025-02-06 23:03   ` [PATCH 3/4] xfs_db: make listdir more generally useful Darrick J. Wong
2025-02-13  4:30     ` Christoph Hellwig
2025-02-13 13:26     ` Andrey Albershteyn
2025-02-06 23:03   ` [PATCH 4/4] xfs_db: add command to copy directory trees out of filesystems Darrick J. Wong
2025-02-13 13:25     ` Andrey Albershteyn
2025-02-18  8:36     ` Christoph Hellwig
2025-02-18 15:54       ` Darrick J. Wong
2025-02-18 18:04         ` Darrick J. Wong
2025-02-18 19:49           ` Darrick J. Wong
2025-02-19 18:04       ` Eric Sandeen
2025-02-19 18:30         ` Darrick J. Wong
2025-02-20  6:20           ` Christoph Hellwig
2025-02-20  6:19         ` Christoph Hellwig
2025-02-18 19:58     ` Darrick J. Wong [this message]
2025-02-19  5:27       ` [PATCH v1.1 " Christoph Hellwig

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250218195818.GF21808@frogsfrogsfrogs \
    --to=djwong@kernel.org \
    --cc=aalbersh@kernel.org \
    --cc=hch@lst.de \
    --cc=linux-xfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.