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:
Zach Brown
2016-08-17 16:22:00 -07:00
parent 0991622a21
commit 6c12e7c38b
2 changed files with 83 additions and 17 deletions
+74 -17
View File
@@ -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,
+9
View File
@@ -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