mirror of
https://github.com/versity/scoutfs.git
synced 2026-09-08 09:07:30 +00:00
scoutfs: add hard link support
Now that we have the link backrefs let's add support for hard links so we can verify that an inode can have multiple backrefs. (It can.) It's a straight forward refactoring of mknod to let callers either allocate or use existing inodes. We push all the btree item specific work into a function called by mknod and link. The only surprising bit is the small max link count. It's limiting the worst case buffer size for the inode_paths ioctl. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
+74
-17
@@ -341,15 +341,14 @@ static int update_lref_item(struct super_block *sb, struct scoutfs_key *key,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scoutfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
dev_t rdev)
|
||||
static int add_entry_items(struct inode *dir, struct dentry *dentry,
|
||||
struct inode *inode)
|
||||
{
|
||||
struct dentry_info *di = dentry->d_fsdata;
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(dir);
|
||||
DECLARE_SCOUTFS_BTREE_CURSOR(curs);
|
||||
struct inode *inode = NULL;
|
||||
struct scoutfs_dirent *dent;
|
||||
struct dentry_info *di;
|
||||
struct scoutfs_key first;
|
||||
struct scoutfs_key last;
|
||||
struct scoutfs_key key;
|
||||
@@ -358,27 +357,17 @@ static int scoutfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
int ret;
|
||||
u64 h;
|
||||
|
||||
di = alloc_dentry_info(dentry);
|
||||
if (IS_ERR(di))
|
||||
return PTR_ERR(di);
|
||||
/* caller should have allocated the dentry info */
|
||||
if (WARN_ON_ONCE(di == NULL))
|
||||
return -EINVAL;
|
||||
|
||||
if (dentry->d_name.len > SCOUTFS_NAME_LEN)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
ret = scoutfs_hold_trans(sb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = scoutfs_dirty_inode_item(dir);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
inode = scoutfs_new_inode(sb, dir, mode, rdev);
|
||||
if (IS_ERR(inode)) {
|
||||
ret = PTR_ERR(inode);
|
||||
goto out;
|
||||
}
|
||||
|
||||
bytes = dent_bytes(dentry->d_name.len);
|
||||
h = name_hash(dentry->d_name.name, dentry->d_name.len, si->salt);
|
||||
scoutfs_set_key(&first, scoutfs_ino(dir), SCOUTFS_DIRENT_KEY, h);
|
||||
@@ -410,6 +399,35 @@ static int scoutfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
update_dentry_info(di, &key, dent);
|
||||
|
||||
scoutfs_btree_release(&curs);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scoutfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
dev_t rdev)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct inode *inode;
|
||||
struct dentry_info *di;
|
||||
int ret;
|
||||
|
||||
di = alloc_dentry_info(dentry);
|
||||
if (IS_ERR(di))
|
||||
return PTR_ERR(di);
|
||||
|
||||
ret = scoutfs_hold_trans(sb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
inode = scoutfs_new_inode(sb, dir, mode, rdev);
|
||||
if (IS_ERR(inode)) {
|
||||
ret = PTR_ERR(inode);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = add_entry_items(dir, dentry, inode);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
i_size_write(dir, i_size_read(dir) + dentry->d_name.len);
|
||||
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
||||
@@ -445,6 +463,44 @@ static int scoutfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
return scoutfs_mknod(dir, dentry, mode | S_IFDIR, 0);
|
||||
}
|
||||
|
||||
static int scoutfs_link(struct dentry *old_dentry,
|
||||
struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = old_dentry->d_inode;
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct dentry_info *di;
|
||||
int ret;
|
||||
|
||||
if (inode->i_nlink >= SCOUTFS_LINK_MAX)
|
||||
return -EMLINK;
|
||||
|
||||
di = alloc_dentry_info(dentry);
|
||||
if (IS_ERR(di))
|
||||
return PTR_ERR(di);
|
||||
|
||||
ret = scoutfs_hold_trans(sb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = add_entry_items(dir, dentry, inode);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
i_size_write(dir, i_size_read(dir) + dentry->d_name.len);
|
||||
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
||||
inode->i_ctime = dir->i_mtime;
|
||||
inc_nlink(inode);
|
||||
|
||||
scoutfs_update_inode_item(inode);
|
||||
scoutfs_update_inode_item(dir);
|
||||
|
||||
atomic_inc(&inode->i_count);
|
||||
d_instantiate(dentry, inode);
|
||||
out:
|
||||
scoutfs_release_trans(sb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlink removes the entry from its item and removes the item if ours
|
||||
* was the only remaining entry.
|
||||
@@ -707,6 +763,7 @@ const struct inode_operations scoutfs_dir_iops = {
|
||||
.mknod = scoutfs_mknod,
|
||||
.create = scoutfs_create,
|
||||
.mkdir = scoutfs_mkdir,
|
||||
.link = scoutfs_link,
|
||||
.unlink = scoutfs_unlink,
|
||||
.rmdir = scoutfs_unlink,
|
||||
.setxattr = scoutfs_setxattr,
|
||||
|
||||
@@ -215,6 +215,15 @@ struct scoutfs_dirent {
|
||||
|
||||
#define SCOUTFS_NAME_LEN 255
|
||||
|
||||
/*
|
||||
* This is arbitrarily limiting the max size of the single buffer
|
||||
* that's needed in the inode_paths ioctl to return all the paths
|
||||
* that link to an inode. The structures could easily support much
|
||||
* more than this but then we'd need to grow a more thorough interface
|
||||
* for iterating over referring paths. That sounds horrible.
|
||||
*/
|
||||
#define SCOUTFS_LINK_MAX 255
|
||||
|
||||
/*
|
||||
* We only use 31 bits for readdir positions so that we don't confuse
|
||||
* old signed 32bit f_pos applications or those on the other side of
|
||||
|
||||
Reference in New Issue
Block a user