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.
next prev 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox