|
@@ -87,6 +87,7 @@ static struct inode *bpf_get_inode(struct super_block *sb,
|
|
|
switch (mode & S_IFMT) {
|
|
|
case S_IFDIR:
|
|
|
case S_IFREG:
|
|
|
+ case S_IFLNK:
|
|
|
break;
|
|
|
default:
|
|
|
return ERR_PTR(-EINVAL);
|
|
@@ -119,6 +120,16 @@ static int bpf_inode_type(const struct inode *inode, enum bpf_type *type)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void bpf_dentry_finalize(struct dentry *dentry, struct inode *inode,
|
|
|
+ struct inode *dir)
|
|
|
+{
|
|
|
+ d_instantiate(dentry, inode);
|
|
|
+ dget(dentry);
|
|
|
+
|
|
|
+ dir->i_mtime = current_time(dir);
|
|
|
+ dir->i_ctime = dir->i_mtime;
|
|
|
+}
|
|
|
+
|
|
|
static int bpf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|
|
{
|
|
|
struct inode *inode;
|
|
@@ -133,9 +144,7 @@ static int bpf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|
|
inc_nlink(inode);
|
|
|
inc_nlink(dir);
|
|
|
|
|
|
- d_instantiate(dentry, inode);
|
|
|
- dget(dentry);
|
|
|
-
|
|
|
+ bpf_dentry_finalize(dentry, inode, dir);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -151,9 +160,7 @@ static int bpf_mkobj_ops(struct inode *dir, struct dentry *dentry,
|
|
|
inode->i_op = iops;
|
|
|
inode->i_private = dentry->d_fsdata;
|
|
|
|
|
|
- d_instantiate(dentry, inode);
|
|
|
- dget(dentry);
|
|
|
-
|
|
|
+ bpf_dentry_finalize(dentry, inode, dir);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -181,13 +188,37 @@ bpf_lookup(struct inode *dir, struct dentry *dentry, unsigned flags)
|
|
|
{
|
|
|
if (strchr(dentry->d_name.name, '.'))
|
|
|
return ERR_PTR(-EPERM);
|
|
|
+
|
|
|
return simple_lookup(dir, dentry, flags);
|
|
|
}
|
|
|
|
|
|
+static int bpf_symlink(struct inode *dir, struct dentry *dentry,
|
|
|
+ const char *target)
|
|
|
+{
|
|
|
+ char *link = kstrdup(target, GFP_USER | __GFP_NOWARN);
|
|
|
+ struct inode *inode;
|
|
|
+
|
|
|
+ if (!link)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ inode = bpf_get_inode(dir->i_sb, dir, S_IRWXUGO | S_IFLNK);
|
|
|
+ if (IS_ERR(inode)) {
|
|
|
+ kfree(link);
|
|
|
+ return PTR_ERR(inode);
|
|
|
+ }
|
|
|
+
|
|
|
+ inode->i_op = &simple_symlink_inode_operations;
|
|
|
+ inode->i_link = link;
|
|
|
+
|
|
|
+ bpf_dentry_finalize(dentry, inode, dir);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static const struct inode_operations bpf_dir_iops = {
|
|
|
.lookup = bpf_lookup,
|
|
|
.mknod = bpf_mkobj,
|
|
|
.mkdir = bpf_mkdir,
|
|
|
+ .symlink = bpf_symlink,
|
|
|
.rmdir = simple_rmdir,
|
|
|
.rename = simple_rename,
|
|
|
.link = simple_link,
|
|
@@ -324,6 +355,8 @@ static void bpf_evict_inode(struct inode *inode)
|
|
|
truncate_inode_pages_final(&inode->i_data);
|
|
|
clear_inode(inode);
|
|
|
|
|
|
+ if (S_ISLNK(inode->i_mode))
|
|
|
+ kfree(inode->i_link);
|
|
|
if (!bpf_inode_type(inode, &type))
|
|
|
bpf_any_put(inode->i_private, type);
|
|
|
}
|