summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2025-07-08 16:38:10 -0400
committerKent Overstreet <kent.overstreet@linux.dev>2025-07-12 19:49:41 -0400
commitd0e71900a3b3f65850a58141f814a38a4eb3ee79 (patch)
treef8287a66ead8c2e763eff804716ae1eb1be7cfb7
parentb02c3e9682fe024f4e2dea2710c772a60fa39c40 (diff)
posix_to_bcachefs: create_or_update_link()
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r--c_src/posix_to_bcachefs.c101
1 files changed, 67 insertions, 34 deletions
diff --git a/c_src/posix_to_bcachefs.c b/c_src/posix_to_bcachefs.c
index bcf960ac..8cb1c7c8 100644
--- a/c_src/posix_to_bcachefs.c
+++ b/c_src/posix_to_bcachefs.c
@@ -14,7 +14,33 @@
#include "libbcachefs/str_hash.h"
#include "libbcachefs/xattr.h"
-void update_inode(struct bch_fs *c,
+static int unlink_and_rm(struct bch_fs *c,
+ subvol_inum dir_inum,
+ struct bch_inode_unpacked *dir,
+ const char *child_name)
+{
+ struct qstr child_name_q = QSTR_INIT(child_name, strlen(child_name));
+
+ struct bch_inode_unpacked child;
+ int ret = bch2_trans_commit_do(c, NULL, NULL,
+ BCH_TRANS_COMMIT_no_enospc,
+ bch2_unlink_trans(trans, dir_inum, dir, &child, &child_name_q, false));
+ bch_err_msg(c, ret, "unlinking %s", child_name);
+ if (ret)
+ return ret;
+
+ if (!(child.bi_flags & BCH_INODE_unlinked))
+ return 0;
+
+ subvol_inum child_inum = dir_inum;
+ child_inum.inum = child.bi_inum;
+
+ ret = bch2_inode_rm(c, child_inum);
+ bch_err_msg(c, ret, "deleting %s", child_name);
+ return ret;
+}
+
+static void update_inode(struct bch_fs *c,
struct bch_inode_unpacked *inode)
{
struct bkey_inode_buf packed;
@@ -28,23 +54,40 @@ void update_inode(struct bch_fs *c,
die("error updating inode: %s", bch2_err_str(ret));
}
-void create_link(struct bch_fs *c,
- struct bch_inode_unpacked *parent,
- const char *name, u64 inum, mode_t mode)
+static int create_or_update_link(struct bch_fs *c,
+ subvol_inum dir_inum,
+ struct bch_inode_unpacked *dir,
+ const char *name, subvol_inum inum, mode_t mode)
{
+ struct bch_hash_info dir_hash = bch2_hash_info_init(c, dir);
+
struct qstr qstr = QSTR(name);
- struct bch_inode_unpacked parent_u;
+ struct bch_inode_unpacked dir_u;
struct bch_inode_unpacked inode;
- int ret = bch2_trans_commit_do(c, NULL, NULL, 0,
- bch2_link_trans(trans,
- (subvol_inum) { 1, parent->bi_inum }, &parent_u,
- (subvol_inum) { 1, inum }, &inode, &qstr));
+ subvol_inum old_inum;
+ int ret = bch2_dirent_lookup(c, dir_inum, &dir_hash, &qstr, &old_inum);
+ if (bch2_err_matches(ret, ENOENT))
+ goto create;
+ if (ret)
+ return ret;
+
+ if (subvol_inum_eq(inum, old_inum))
+ return 0;
+
+ ret = unlink_and_rm(c, dir_inum, dir, name);
if (ret)
- die("error creating hardlink: %s", bch2_err_str(ret));
+ return ret;
+create:
+ ret = bch2_trans_commit_do(c, NULL, NULL, 0,
+ bch2_link_trans(trans,
+ dir_inum, &dir_u,
+ inum, &inode, &qstr));
+ bch_err_msg(c, ret, "error creating hardlink %s", name);
+ return ret;
}
-struct bch_inode_unpacked create_or_update_file(struct bch_fs *c,
+static struct bch_inode_unpacked create_or_update_file(struct bch_fs *c,
subvol_inum dir_inum,
struct bch_inode_unpacked *dir,
const char *name,
@@ -122,7 +165,7 @@ static const struct xattr_handler *xattr_resolve_name(char **name)
return ERR_PTR(-EOPNOTSUPP);
}
-void copy_times(struct bch_fs *c, struct bch_inode_unpacked *dst,
+static void copy_times(struct bch_fs *c, struct bch_inode_unpacked *dst,
struct stat *src)
{
dst->bi_atime = timespec_to_bch2_time(c, src->st_atim);
@@ -130,7 +173,7 @@ void copy_times(struct bch_fs *c, struct bch_inode_unpacked *dst,
dst->bi_ctime = timespec_to_bch2_time(c, src->st_ctim);
}
-void copy_xattrs(struct bch_fs *c, struct bch_inode_unpacked *dst,
+static void copy_xattrs(struct bch_fs *c, struct bch_inode_unpacked *dst,
char *src)
{
struct bch_hash_info hash_info = bch2_hash_info_init(c, dst);
@@ -318,10 +361,10 @@ static void link_data(struct bch_fs *c, struct bch_inode_unpacked *dst,
}
}
-void copy_link(struct bch_fs *c,
- subvol_inum dst_inum,
- struct bch_inode_unpacked *dst,
- char *src)
+static void copy_link(struct bch_fs *c,
+ subvol_inum dst_inum,
+ struct bch_inode_unpacked *dst,
+ char *src)
{
s64 i_sectors_delta = 0;
int ret = bch2_fpunch(c, dst_inum, 0, U64_MAX, &i_sectors_delta);
@@ -622,21 +665,7 @@ static int recursive_remove(struct bch_fs *c,
darray_exit(&child_dirents);
}
- struct qstr d_name = QSTR_INIT(d->d_name, strlen(d->d_name));
-
- ret = bch2_trans_commit_do(c, NULL, NULL,
- BCH_TRANS_COMMIT_no_enospc,
- bch2_unlink_trans(trans, dir_inum, dir, &child, &d_name, false));
- bch_err_msg(c, ret, "unlinking %s", d->d_name);
- if (ret)
- return ret;
-
- if (!(child.bi_flags & BCH_INODE_unlinked))
- return 0;
-
- ret = bch2_inode_rm(c, child_inum);
- bch_err_msg(c, ret, "deleting %s", d->d_name);
- return ret;
+ return unlink_and_rm(c, dir_inum, dir, d->d_name);
}
static int delete_non_matching_dirents(struct bch_fs *c,
@@ -728,12 +757,16 @@ static int copy_dir(struct bch_fs *c,
? genradix_ptr_alloc(&s->hardlinks, stat.st_ino, GFP_KERNEL)
: NULL;
+ subvol_inum dst_dir_inum = { 1, dst->bi_inum };
+
if (dst_inum_p && *dst_inum_p) {
- create_link(c, dst, d->d_name, *dst_inum_p, S_IFREG);
+ ret = create_or_update_link(c, dst_dir_inum, dst, d->d_name,
+ (subvol_inum) { 1, *dst_inum_p }, S_IFREG);
+ if (ret)
+ goto err;
goto next;
}
- subvol_inum dst_dir_inum = { 1, dst->bi_inum };
inode = create_or_update_file(c, dst_dir_inum, dst, d->d_name,
stat.st_uid, stat.st_gid,
stat.st_mode, stat.st_rdev);