From 1724bab8ea7b8b1402b2775ea0281b64590c1106 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 22 Jun 2017 11:35:22 -0700 Subject: [PATCH] scoutfs: store large symlinks in multiple items We're shrinking the max item value size so we need to store symlinks with large target paths in multiple items. The arbitrary max value size defined here will be replaced in the future with the new global maximum value size. Signed-off-by: Zach Brown --- kmod/src/count.h | 5 ++- kmod/src/dir.c | 96 +++++++++++++++++++++++++++++++++-------------- kmod/src/dir.h | 2 +- kmod/src/format.h | 5 ++- kmod/src/inode.c | 2 +- 5 files changed, 77 insertions(+), 33 deletions(-) diff --git a/kmod/src/count.h b/kmod/src/count.h index 921dea89..b763cc2b 100644 --- a/kmod/src/count.h +++ b/kmod/src/count.h @@ -54,9 +54,10 @@ static inline void scoutfs_count_dirents(struct scoutfs_item_count *cnt, static inline void scoutfs_count_sym_target(struct scoutfs_item_count *cnt, unsigned size) { + unsigned nr = DIV_ROUND_UP(size, SCOUTFS_SYMLINK_MAX_VAL_SIZE); - cnt->items += 1; - cnt->keys += sizeof(struct scoutfs_symlink_key); + cnt->items += nr; + cnt->keys += nr * sizeof(struct scoutfs_symlink_key); cnt->vals += size; } diff --git a/kmod/src/dir.c b/kmod/src/dir.c index 0d77d95a..04461e75 100644 --- a/kmod/src/dir.c +++ b/kmod/src/dir.c @@ -634,14 +634,70 @@ out: } static void init_symlink_key(struct scoutfs_key_buf *key, - struct scoutfs_symlink_key *skey, u64 ino) + struct scoutfs_symlink_key *skey, u64 ino, u8 nr) { skey->type = SCOUTFS_SYMLINK_KEY; skey->ino = cpu_to_be64(ino); + skey->nr = nr; scoutfs_key_init(key, skey, sizeof(struct scoutfs_symlink_key)); } +/* + * Operate on all the items that make up a symlink whose target might + * have to be split up into multiple items each with a maximally sized + * value. + * + * returns 0 or -errno from the item calls, particularly including + * EEXIST, EIO, or ENOENT if the item population doesn't match what was + * expected given the op. + * + * The target name can be null for deletion when val isn't used. Size + * still has to be provided to determine the number of items. + */ +enum { + SYM_CREATE = 0, + SYM_LOOKUP, + SYM_DELETE, +}; +static int symlink_item_ops(struct super_block *sb, int op, u64 ino, + const char *target, int size) +{ + struct scoutfs_symlink_key skey; + struct scoutfs_key_buf key; + SCOUTFS_DECLARE_KVEC(val); + unsigned bytes; + unsigned nr; + int ret; + int i; + + if (WARN_ON_ONCE(size <= 0 || size > SCOUTFS_SYMLINK_MAX_SIZE || + op > SYM_DELETE)) + return -EINVAL; + + nr = DIV_ROUND_UP(size, SCOUTFS_SYMLINK_MAX_VAL_SIZE); + for (i = 0; i < nr; i++) { + + init_symlink_key(&key, &skey, ino, i); + bytes = min(size, SCOUTFS_SYMLINK_MAX_VAL_SIZE); + scoutfs_kvec_init(val, (void *)target, bytes); + + if (op == SYM_CREATE) + ret = scoutfs_item_create(sb, &key, val); + else if (op == SYM_LOOKUP) + ret = scoutfs_item_lookup_exact(sb, &key, val, bytes); + else if (op == SYM_DELETE) + ret = scoutfs_item_delete(sb, &key); + if (ret) + break; + + target += SCOUTFS_SYMLINK_MAX_VAL_SIZE; + size -= bytes; + } + + return ret; +} + /* * Full a buffer with the null terminated symlink, point nd at it, and * return it so put_link can free it once the vfs is done. @@ -655,9 +711,6 @@ static void *scoutfs_follow_link(struct dentry *dentry, struct nameidata *nd) struct inode *inode = dentry->d_inode; struct super_block *sb = inode->i_sb; loff_t size = i_size_read(inode); - struct scoutfs_symlink_key skey; - struct scoutfs_key_buf key; - SCOUTFS_DECLARE_KVEC(val); char *path; int ret; @@ -673,14 +726,10 @@ static void *scoutfs_follow_link(struct dentry *dentry, struct nameidata *nd) if (!path) return ERR_PTR(-ENOMEM); - init_symlink_key(&key, &skey, scoutfs_ino(inode)); - scoutfs_kvec_init(val, path, size); + ret = symlink_item_ops(sb, SYM_LOOKUP, scoutfs_ino(inode), path, size); - ret = scoutfs_item_lookup(sb, &key, val); - - /* XXX corruption: missing item, wrong size, not null term */ - if (ret == -ENOENT || - (ret >= 0 && (ret != size || path[size - 1] != '\0'))) + /* XXX corruption: missing items or not null term */ + if (ret == -ENOENT || (ret == 0 && path[size - 1])) ret = -EIO; if (ret < 0) { @@ -711,19 +760,15 @@ const struct inode_operations scoutfs_symlink_iops = { }; /* - * Symlink target paths can be annoyingly huge. We don't want large - * items gumming up the btree so we store relatively rare large paths in - * multiple items. + * Symlink target paths can be annoyingly large. We store relatively + * rare large paths in multiple items. */ static int scoutfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct super_block *sb = dir->i_sb; const int name_len = strlen(symname) + 1; - struct scoutfs_symlink_key skey; - struct scoutfs_key_buf key; struct inode *inode = NULL; - SCOUTFS_DECLARE_KVEC(val); DECLARE_ITEM_COUNT(cnt); int ret; @@ -746,10 +791,8 @@ static int scoutfs_symlink(struct inode *dir, struct dentry *dentry, goto out; } - init_symlink_key(&key, &skey, scoutfs_ino(inode)); - scoutfs_kvec_init(val, (void *)symname, name_len); - - ret = scoutfs_item_create(sb, &key, val); + ret = symlink_item_ops(sb, SYM_CREATE, scoutfs_ino(inode), + symname, name_len); if (ret) goto out; @@ -774,22 +817,19 @@ out: if (!IS_ERR_OR_NULL(inode)) iput(inode); - scoutfs_item_delete(sb, &key); + symlink_item_ops(sb, SYM_DELETE, scoutfs_ino(inode), + NULL, name_len); } scoutfs_release_trans(sb); return ret; } -int scoutfs_symlink_drop(struct super_block *sb, u64 ino) +int scoutfs_symlink_drop(struct super_block *sb, u64 ino, u64 i_size) { - struct scoutfs_symlink_key skey; - struct scoutfs_key_buf key; int ret; - init_symlink_key(&key, &skey, ino); - - ret = scoutfs_item_delete(sb, &key); + ret = symlink_item_ops(sb, SYM_DELETE, ino, NULL, i_size); if (ret == -ENOENT) ret = 0; diff --git a/kmod/src/dir.h b/kmod/src/dir.h index 273d1f54..81b5de4e 100644 --- a/kmod/src/dir.h +++ b/kmod/src/dir.h @@ -19,7 +19,7 @@ int scoutfs_dir_get_backref_path(struct super_block *sb, u64 target_ino, void scoutfs_dir_free_backref_path(struct super_block *sb, struct list_head *list); -int scoutfs_symlink_drop(struct super_block *sb, u64 ino); +int scoutfs_symlink_drop(struct super_block *sb, u64 ino, u64 i_size); int scoutfs_dir_init(void); void scoutfs_dir_exit(void); diff --git a/kmod/src/format.h b/kmod/src/format.h index 3451a04b..fc36d50e 100644 --- a/kmod/src/format.h +++ b/kmod/src/format.h @@ -253,12 +253,15 @@ struct scoutfs_xattr_val_header { __u8 last_part; } __packed; -/* value is the null terminated target path */ +/* size determines nr needed to store full target path in their values */ struct scoutfs_symlink_key { __u8 type; __be64 ino; + __u8 nr; } __packed; +#define SCOUTFS_SYMLINK_MAX_VAL_SIZE 200 + struct scoutfs_betimespec { __be64 sec; __be32 nsec; diff --git a/kmod/src/inode.c b/kmod/src/inode.c index b5774682..c54e21dc 100644 --- a/kmod/src/inode.c +++ b/kmod/src/inode.c @@ -798,7 +798,7 @@ static int __delete_inode(struct super_block *sb, struct scoutfs_key_buf *key, goto out; if (S_ISLNK(mode)) - ret = scoutfs_symlink_drop(sb, ino); + ret = scoutfs_symlink_drop(sb, ino, i_size); else if (S_ISREG(mode)) ret = scoutfs_truncate_extent_items(sb, ino, 0, ~0ULL, false); if (ret)