Some filesystems use a kinda-sorta controlled dentry refcount leak to pin

dentries of created objects in dcache (and undo it when removing those).
 Reference is grabbed and not released, but it's not actually _stored_
 anywhere.  That works, but it's hard to follow and verify; among other
 things, we have no way to tell _which_ of the increments is intended
 to be an unpaired one.  Worse, on removal we need to decide whether
 the reference had already been dropped, which can be non-trivial if
 that removal is on umount and we need to figure out if this dentry is
 pinned due to e.g. unlink() not done.  Usually that is handled by using
 kill_litter_super() as ->kill_sb(), but there are open-coded special
 cases of the same (consider e.g. /proc/self).
 
 Things get simpler if we introduce a new dentry flag (DCACHE_PERSISTENT)
 marking those "leaked" dentries.  Having it set claims responsibility
 for +1 in refcount.
 
 The end result this series is aiming for:
 
 * get these unbalanced dget() and dput() replaced with new primitives that
   would, in addition to adjusting refcount, set and clear persistency flag.
 * instead of having kill_litter_super() mess with removing the remaining
   "leaked" references (e.g. for all tmpfs files that hadn't been removed
   prior to umount), have the regular shrink_dcache_for_umount() strip
   DCACHE_PERSISTENT of all dentries, dropping the corresponding
   reference if it had been set.  After that kill_litter_super() becomes
   an equivalent of kill_anon_super().
 
 Doing that in a single step is not feasible - it would affect too many places
 in too many filesystems.  It has to be split into a series.
 
 This work has really started early in 2024; quite a few preliminary pieces
 have already gone into mainline.  This chunk is finally getting to the
 meat of that stuff - infrastructure and most of the conversions to it.
 
 Some pieces are still sitting in the local branches, but the bulk of
 that stuff is here.
 
 Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQqUNBr3gm4hGXdBJlZ7Krx/gZQ6wUCaTEq1wAKCRBZ7Krx/gZQ
 643uAQC1rRslhw5l7OjxEpIYbGG4M+QaadN4Nf5Sr2SuTRaPJQD/W4oj/u4C2eCw
 Dd3q071tqyvm/PXNgN2EEnIaxlFUlwc=
 =rKq+
 -----END PGP SIGNATURE-----

Merge tag 'pull-persistency' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull persistent dentry infrastructure and conversion from Al Viro:
 "Some filesystems use a kinda-sorta controlled dentry refcount leak to
  pin dentries of created objects in dcache (and undo it when removing
  those). A reference is grabbed and not released, but it's not actually
  _stored_ anywhere.

  That works, but it's hard to follow and verify; among other things, we
  have no way to tell _which_ of the increments is intended to be an
  unpaired one. Worse, on removal we need to decide whether the
  reference had already been dropped, which can be non-trivial if that
  removal is on umount and we need to figure out if this dentry is
  pinned due to e.g. unlink() not done. Usually that is handled by using
  kill_litter_super() as ->kill_sb(), but there are open-coded special
  cases of the same (consider e.g. /proc/self).

  Things get simpler if we introduce a new dentry flag
  (DCACHE_PERSISTENT) marking those "leaked" dentries. Having it set
  claims responsibility for +1 in refcount.

  The end result this series is aiming for:

   - get these unbalanced dget() and dput() replaced with new primitives
     that would, in addition to adjusting refcount, set and clear
     persistency flag.

   - instead of having kill_litter_super() mess with removing the
     remaining "leaked" references (e.g. for all tmpfs files that hadn't
     been removed prior to umount), have the regular
     shrink_dcache_for_umount() strip DCACHE_PERSISTENT of all dentries,
     dropping the corresponding reference if it had been set. After that
     kill_litter_super() becomes an equivalent of kill_anon_super().

  Doing that in a single step is not feasible - it would affect too many
  places in too many filesystems. It has to be split into a series.

  This work has really started early in 2024; quite a few preliminary
  pieces have already gone into mainline. This chunk is finally getting
  to the meat of that stuff - infrastructure and most of the conversions
  to it.

  Some pieces are still sitting in the local branches, but the bulk of
  that stuff is here"

* tag 'pull-persistency' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (54 commits)
  d_make_discardable(): warn if given a non-persistent dentry
  kill securityfs_recursive_remove()
  convert securityfs
  get rid of kill_litter_super()
  convert rust_binderfs
  convert nfsctl
  convert rpc_pipefs
  convert hypfs
  hypfs: swich hypfs_create_u64() to returning int
  hypfs: switch hypfs_create_str() to returning int
  hypfs: don't pin dentries twice
  convert gadgetfs
  gadgetfs: switch to simple_remove_by_name()
  convert functionfs
  functionfs: switch to simple_remove_by_name()
  functionfs: fix the open/removal races
  functionfs: need to cancel ->reset_work in ->kill_sb()
  functionfs: don't bother with ffs->ref in ffs_data_{opened,closed}()
  functionfs: don't abuse ffs_data_closed() on fs shutdown
  convert selinuxfs
  ...
This commit is contained in:
Linus Torvalds 2025-12-05 14:36:21 -08:00
commit 7cd122b552
53 changed files with 648 additions and 833 deletions

View File

@ -1327,3 +1327,10 @@ When vfs_mkdir() returns an error, and so both dputs() the original
dentry and doesn't provide a replacement, it also unlocks the parent. dentry and doesn't provide a replacement, it also unlocks the parent.
Consequently the return value from vfs_mkdir() can be passed to Consequently the return value from vfs_mkdir() can be passed to
end_creating() and the parent will be unlocked precisely when necessary. end_creating() and the parent will be unlocked precisely when necessary.
---
**mandatory**
kill_litter_super() is gone; convert to DCACHE_PERSISTENT use (as all
in-tree filesystems have done).

View File

@ -127,7 +127,7 @@ spufs_new_file(struct super_block *sb, struct dentry *dentry,
inode->i_fop = fops; inode->i_fop = fops;
inode->i_size = size; inode->i_size = size;
inode->i_private = SPUFS_I(inode)->i_ctx = get_spu_context(ctx); inode->i_private = SPUFS_I(inode)->i_ctx = get_spu_context(ctx);
d_add(dentry, inode); d_make_persistent(dentry, inode);
out: out:
return ret; return ret;
} }
@ -163,10 +163,9 @@ static int spufs_fill_dir(struct dentry *dir,
return -ENOMEM; return -ENOMEM;
ret = spufs_new_file(dir->d_sb, dentry, files->ops, ret = spufs_new_file(dir->d_sb, dentry, files->ops,
files->mode & mode, files->size, ctx); files->mode & mode, files->size, ctx);
if (ret) {
dput(dentry); dput(dentry);
if (ret)
return ret; return ret;
}
files++; files++;
} }
return 0; return 0;
@ -241,11 +240,10 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
inode_lock(inode); inode_lock(inode);
dget(dentry);
inc_nlink(dir); inc_nlink(dir);
inc_nlink(inode); inc_nlink(inode);
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
if (flags & SPU_CREATE_NOSCHED) if (flags & SPU_CREATE_NOSCHED)
ret = spufs_fill_dir(dentry, spufs_dir_nosched_contents, ret = spufs_fill_dir(dentry, spufs_dir_nosched_contents,
@ -468,10 +466,9 @@ spufs_mkgang(struct inode *dir, struct dentry *dentry, umode_t mode)
inode->i_op = &simple_dir_inode_operations; inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations; inode->i_fop = &simple_dir_operations;
d_instantiate(dentry, inode);
dget(dentry);
inc_nlink(dir); inc_nlink(dir);
inc_nlink(d_inode(dentry)); inc_nlink(inode);
d_make_persistent(dentry, inode);
return ret; return ret;
out_iput: out_iput:
@ -758,7 +755,7 @@ static struct file_system_type spufs_type = {
.name = "spufs", .name = "spufs",
.init_fs_context = spufs_init_fs_context, .init_fs_context = spufs_init_fs_context,
.parameters = spufs_fs_parameters, .parameters = spufs_fs_parameters,
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
}; };
MODULE_ALIAS_FS("spufs"); MODULE_ALIAS_FS("spufs");

View File

@ -22,11 +22,9 @@
extern struct dentry *hypfs_mkdir(struct dentry *parent, const char *name); extern struct dentry *hypfs_mkdir(struct dentry *parent, const char *name);
extern struct dentry *hypfs_create_u64(struct dentry *dir, const char *name, extern int hypfs_create_u64(struct dentry *dir, const char *name, __u64 value);
__u64 value);
extern struct dentry *hypfs_create_str(struct dentry *dir, const char *name, extern int hypfs_create_str(struct dentry *dir, const char *name, char *string);
char *string);
/* LPAR Hypervisor */ /* LPAR Hypervisor */
extern int hypfs_diag_init(void); extern int hypfs_diag_init(void);

View File

@ -203,7 +203,7 @@ static int hypfs_create_cpu_files(struct dentry *cpus_dir, void *cpu_info)
{ {
struct dentry *cpu_dir; struct dentry *cpu_dir;
char buffer[TMP_SIZE]; char buffer[TMP_SIZE];
void *rc; int rc;
snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_get_info_type(), snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_get_info_type(),
cpu_info)); cpu_info));
@ -213,22 +213,21 @@ static int hypfs_create_cpu_files(struct dentry *cpus_dir, void *cpu_info)
rc = hypfs_create_u64(cpu_dir, "mgmtime", rc = hypfs_create_u64(cpu_dir, "mgmtime",
cpu_info__acc_time(diag204_get_info_type(), cpu_info) - cpu_info__acc_time(diag204_get_info_type(), cpu_info) -
cpu_info__lp_time(diag204_get_info_type(), cpu_info)); cpu_info__lp_time(diag204_get_info_type(), cpu_info));
if (IS_ERR(rc)) if (rc)
return PTR_ERR(rc); return rc;
rc = hypfs_create_u64(cpu_dir, "cputime", rc = hypfs_create_u64(cpu_dir, "cputime",
cpu_info__lp_time(diag204_get_info_type(), cpu_info)); cpu_info__lp_time(diag204_get_info_type(), cpu_info));
if (IS_ERR(rc)) if (rc)
return PTR_ERR(rc); return rc;
if (diag204_get_info_type() == DIAG204_INFO_EXT) { if (diag204_get_info_type() == DIAG204_INFO_EXT) {
rc = hypfs_create_u64(cpu_dir, "onlinetime", rc = hypfs_create_u64(cpu_dir, "onlinetime",
cpu_info__online_time(diag204_get_info_type(), cpu_info__online_time(diag204_get_info_type(),
cpu_info)); cpu_info));
if (IS_ERR(rc)) if (rc)
return PTR_ERR(rc); return rc;
} }
diag224_idx2name(cpu_info__ctidx(diag204_get_info_type(), cpu_info), buffer); diag224_idx2name(cpu_info__ctidx(diag204_get_info_type(), cpu_info), buffer);
rc = hypfs_create_str(cpu_dir, "type", buffer); return hypfs_create_str(cpu_dir, "type", buffer);
return PTR_ERR_OR_ZERO(rc);
} }
static void *hypfs_create_lpar_files(struct dentry *systems_dir, void *part_hdr) static void *hypfs_create_lpar_files(struct dentry *systems_dir, void *part_hdr)
@ -263,7 +262,7 @@ static int hypfs_create_phys_cpu_files(struct dentry *cpus_dir, void *cpu_info)
{ {
struct dentry *cpu_dir; struct dentry *cpu_dir;
char buffer[TMP_SIZE]; char buffer[TMP_SIZE];
void *rc; int rc;
snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_get_info_type(), snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_get_info_type(),
cpu_info)); cpu_info));
@ -272,11 +271,10 @@ static int hypfs_create_phys_cpu_files(struct dentry *cpus_dir, void *cpu_info)
return PTR_ERR(cpu_dir); return PTR_ERR(cpu_dir);
rc = hypfs_create_u64(cpu_dir, "mgmtime", rc = hypfs_create_u64(cpu_dir, "mgmtime",
phys_cpu__mgm_time(diag204_get_info_type(), cpu_info)); phys_cpu__mgm_time(diag204_get_info_type(), cpu_info));
if (IS_ERR(rc)) if (rc)
return PTR_ERR(rc); return rc;
diag224_idx2name(phys_cpu__ctidx(diag204_get_info_type(), cpu_info), buffer); diag224_idx2name(phys_cpu__ctidx(diag204_get_info_type(), cpu_info), buffer);
rc = hypfs_create_str(cpu_dir, "type", buffer); return hypfs_create_str(cpu_dir, "type", buffer);
return PTR_ERR_OR_ZERO(rc);
} }
static void *hypfs_create_phys_files(struct dentry *parent_dir, void *phys_hdr) static void *hypfs_create_phys_files(struct dentry *parent_dir, void *phys_hdr)
@ -315,41 +313,25 @@ int hypfs_diag_create_files(struct dentry *root)
return rc; return rc;
systems_dir = hypfs_mkdir(root, "systems"); systems_dir = hypfs_mkdir(root, "systems");
if (IS_ERR(systems_dir)) { if (IS_ERR(systems_dir))
rc = PTR_ERR(systems_dir); return PTR_ERR(systems_dir);
goto err_out;
}
time_hdr = (struct x_info_blk_hdr *)buffer; time_hdr = (struct x_info_blk_hdr *)buffer;
part_hdr = time_hdr + info_blk_hdr__size(diag204_get_info_type()); part_hdr = time_hdr + info_blk_hdr__size(diag204_get_info_type());
for (i = 0; i < info_blk_hdr__npar(diag204_get_info_type(), time_hdr); i++) { for (i = 0; i < info_blk_hdr__npar(diag204_get_info_type(), time_hdr); i++) {
part_hdr = hypfs_create_lpar_files(systems_dir, part_hdr); part_hdr = hypfs_create_lpar_files(systems_dir, part_hdr);
if (IS_ERR(part_hdr)) { if (IS_ERR(part_hdr))
rc = PTR_ERR(part_hdr); return PTR_ERR(part_hdr);
goto err_out;
}
} }
if (info_blk_hdr__flags(diag204_get_info_type(), time_hdr) & if (info_blk_hdr__flags(diag204_get_info_type(), time_hdr) &
DIAG204_LPAR_PHYS_FLG) { DIAG204_LPAR_PHYS_FLG) {
ptr = hypfs_create_phys_files(root, part_hdr); ptr = hypfs_create_phys_files(root, part_hdr);
if (IS_ERR(ptr)) { if (IS_ERR(ptr))
rc = PTR_ERR(ptr); return PTR_ERR(ptr);
goto err_out;
}
} }
hyp_dir = hypfs_mkdir(root, "hyp"); hyp_dir = hypfs_mkdir(root, "hyp");
if (IS_ERR(hyp_dir)) { if (IS_ERR(hyp_dir))
rc = PTR_ERR(hyp_dir); return PTR_ERR(hyp_dir);
goto err_out; return hypfs_create_str(hyp_dir, "type", "LPAR Hypervisor");
}
ptr = hypfs_create_str(hyp_dir, "type", "LPAR Hypervisor");
if (IS_ERR(ptr)) {
rc = PTR_ERR(ptr);
goto err_out;
}
rc = 0;
err_out:
return rc;
} }
/* Diagnose 224 functions */ /* Diagnose 224 functions */

View File

@ -19,10 +19,9 @@
#define ATTRIBUTE(dir, name, member) \ #define ATTRIBUTE(dir, name, member) \
do { \ do { \
void *rc; \ int rc = hypfs_create_u64(dir, name, member); \
rc = hypfs_create_u64(dir, name, member); \ if (rc) \
if (IS_ERR(rc)) \ return rc; \
return PTR_ERR(rc); \
} while (0) } while (0)
static int hypfs_vm_create_guest(struct dentry *systems_dir, static int hypfs_vm_create_guest(struct dentry *systems_dir,
@ -85,7 +84,7 @@ static int hypfs_vm_create_guest(struct dentry *systems_dir,
int hypfs_vm_create_files(struct dentry *root) int hypfs_vm_create_files(struct dentry *root)
{ {
struct dentry *dir, *file; struct dentry *dir;
struct diag2fc_data *data; struct diag2fc_data *data;
unsigned int count = 0; unsigned int count = 0;
int rc, i; int rc, i;
@ -100,11 +99,9 @@ int hypfs_vm_create_files(struct dentry *root)
rc = PTR_ERR(dir); rc = PTR_ERR(dir);
goto failed; goto failed;
} }
file = hypfs_create_str(dir, "type", "z/VM Hypervisor"); rc = hypfs_create_str(dir, "type", "z/VM Hypervisor");
if (IS_ERR(file)) { if (rc)
rc = PTR_ERR(file);
goto failed; goto failed;
}
/* physical cpus */ /* physical cpus */
dir = hypfs_mkdir(root, "cpus"); dir = hypfs_mkdir(root, "cpus");
@ -112,11 +109,9 @@ int hypfs_vm_create_files(struct dentry *root)
rc = PTR_ERR(dir); rc = PTR_ERR(dir);
goto failed; goto failed;
} }
file = hypfs_create_u64(dir, "count", data->lcpus); rc = hypfs_create_u64(dir, "count", data->lcpus);
if (IS_ERR(file)) { if (rc)
rc = PTR_ERR(file);
goto failed; goto failed;
}
/* guests */ /* guests */
dir = hypfs_mkdir(root, "systems"); dir = hypfs_mkdir(root, "systems");

View File

@ -60,33 +60,17 @@ static void hypfs_update_update(struct super_block *sb)
static void hypfs_add_dentry(struct dentry *dentry) static void hypfs_add_dentry(struct dentry *dentry)
{ {
if (IS_ROOT(dentry->d_parent)) {
dentry->d_fsdata = hypfs_last_dentry; dentry->d_fsdata = hypfs_last_dentry;
hypfs_last_dentry = dentry; hypfs_last_dentry = dentry;
} }
static void hypfs_remove(struct dentry *dentry)
{
struct dentry *parent;
parent = dentry->d_parent;
inode_lock(d_inode(parent));
if (simple_positive(dentry)) {
if (d_is_dir(dentry))
simple_rmdir(d_inode(parent), dentry);
else
simple_unlink(d_inode(parent), dentry);
}
d_drop(dentry);
dput(dentry);
inode_unlock(d_inode(parent));
} }
static void hypfs_delete_tree(struct dentry *root) static void hypfs_delete_tree(void)
{ {
while (hypfs_last_dentry) { while (hypfs_last_dentry) {
struct dentry *next_dentry; struct dentry *next_dentry = hypfs_last_dentry->d_fsdata;
next_dentry = hypfs_last_dentry->d_fsdata; simple_recursive_removal(hypfs_last_dentry, NULL);
hypfs_remove(hypfs_last_dentry);
hypfs_last_dentry = next_dentry; hypfs_last_dentry = next_dentry;
} }
} }
@ -183,14 +167,14 @@ static ssize_t hypfs_write_iter(struct kiocb *iocb, struct iov_iter *from)
rc = -EBUSY; rc = -EBUSY;
goto out; goto out;
} }
hypfs_delete_tree(sb->s_root); hypfs_delete_tree();
if (machine_is_vm()) if (machine_is_vm())
rc = hypfs_vm_create_files(sb->s_root); rc = hypfs_vm_create_files(sb->s_root);
else else
rc = hypfs_diag_create_files(sb->s_root); rc = hypfs_diag_create_files(sb->s_root);
if (rc) { if (rc) {
pr_err("Updating the hypfs tree failed\n"); pr_err("Updating the hypfs tree failed\n");
hypfs_delete_tree(sb->s_root); hypfs_delete_tree();
goto out; goto out;
} }
hypfs_update_update(sb); hypfs_update_update(sb);
@ -325,13 +309,9 @@ static void hypfs_kill_super(struct super_block *sb)
{ {
struct hypfs_sb_info *sb_info = sb->s_fs_info; struct hypfs_sb_info *sb_info = sb->s_fs_info;
if (sb->s_root) hypfs_last_dentry = NULL;
hypfs_delete_tree(sb->s_root); kill_anon_super(sb);
if (sb_info && sb_info->update_file) kfree(sb_info);
hypfs_remove(sb_info->update_file);
kfree(sb->s_fs_info);
sb->s_fs_info = NULL;
kill_litter_super(sb);
} }
static struct dentry *hypfs_create_file(struct dentry *parent, const char *name, static struct dentry *hypfs_create_file(struct dentry *parent, const char *name,
@ -340,17 +320,13 @@ static struct dentry *hypfs_create_file(struct dentry *parent, const char *name,
struct dentry *dentry; struct dentry *dentry;
struct inode *inode; struct inode *inode;
inode_lock(d_inode(parent)); dentry = simple_start_creating(parent, name);
dentry = lookup_noperm(&QSTR(name), parent); if (IS_ERR(dentry))
if (IS_ERR(dentry)) { return ERR_PTR(-ENOMEM);
dentry = ERR_PTR(-ENOMEM);
goto fail;
}
inode = hypfs_make_inode(parent->d_sb, mode); inode = hypfs_make_inode(parent->d_sb, mode);
if (!inode) { if (!inode) {
dput(dentry); simple_done_creating(dentry);
dentry = ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
goto fail;
} }
if (S_ISREG(mode)) { if (S_ISREG(mode)) {
inode->i_fop = &hypfs_file_ops; inode->i_fop = &hypfs_file_ops;
@ -365,11 +341,9 @@ static struct dentry *hypfs_create_file(struct dentry *parent, const char *name,
} else } else
BUG(); BUG();
inode->i_private = data; inode->i_private = data;
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
dget(dentry); simple_done_creating(dentry);
fail: return dentry; // borrowed
inode_unlock(d_inode(parent));
return dentry;
} }
struct dentry *hypfs_mkdir(struct dentry *parent, const char *name) struct dentry *hypfs_mkdir(struct dentry *parent, const char *name)
@ -397,8 +371,7 @@ static struct dentry *hypfs_create_update_file(struct dentry *dir)
return dentry; return dentry;
} }
struct dentry *hypfs_create_u64(struct dentry *dir, int hypfs_create_u64(struct dentry *dir, const char *name, __u64 value)
const char *name, __u64 value)
{ {
char *buffer; char *buffer;
char tmp[TMP_SIZE]; char tmp[TMP_SIZE];
@ -407,35 +380,34 @@ struct dentry *hypfs_create_u64(struct dentry *dir,
snprintf(tmp, TMP_SIZE, "%llu\n", (unsigned long long int)value); snprintf(tmp, TMP_SIZE, "%llu\n", (unsigned long long int)value);
buffer = kstrdup(tmp, GFP_KERNEL); buffer = kstrdup(tmp, GFP_KERNEL);
if (!buffer) if (!buffer)
return ERR_PTR(-ENOMEM); return -ENOMEM;
dentry = dentry =
hypfs_create_file(dir, name, buffer, S_IFREG | REG_FILE_MODE); hypfs_create_file(dir, name, buffer, S_IFREG | REG_FILE_MODE);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
kfree(buffer); kfree(buffer);
return ERR_PTR(-ENOMEM); return -ENOMEM;
} }
hypfs_add_dentry(dentry); hypfs_add_dentry(dentry);
return dentry; return 0;
} }
struct dentry *hypfs_create_str(struct dentry *dir, int hypfs_create_str(struct dentry *dir, const char *name, char *string)
const char *name, char *string)
{ {
char *buffer; char *buffer;
struct dentry *dentry; struct dentry *dentry;
buffer = kmalloc(strlen(string) + 2, GFP_KERNEL); buffer = kmalloc(strlen(string) + 2, GFP_KERNEL);
if (!buffer) if (!buffer)
return ERR_PTR(-ENOMEM); return -ENOMEM;
sprintf(buffer, "%s\n", string); sprintf(buffer, "%s\n", string);
dentry = dentry =
hypfs_create_file(dir, name, buffer, S_IFREG | REG_FILE_MODE); hypfs_create_file(dir, name, buffer, S_IFREG | REG_FILE_MODE);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
kfree(buffer); kfree(buffer);
return ERR_PTR(-ENOMEM); return -ENOMEM;
} }
hypfs_add_dentry(dentry); hypfs_add_dentry(dentry);
return dentry; return 0;
} }
static const struct file_operations hypfs_file_ops = { static const struct file_operations hypfs_file_ops = {

View File

@ -178,28 +178,17 @@ static int binderfs_binder_device_create(struct inode *ref_inode,
} }
root = sb->s_root; root = sb->s_root;
inode_lock(d_inode(root)); dentry = simple_start_creating(root, req->name);
/* look it up */
dentry = lookup_noperm(&QSTR(req->name), root);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
inode_unlock(d_inode(root));
ret = PTR_ERR(dentry); ret = PTR_ERR(dentry);
goto err; goto err;
} }
if (d_really_is_positive(dentry)) {
/* already exists */
dput(dentry);
inode_unlock(d_inode(root));
ret = -EEXIST;
goto err;
}
inode->i_private = device; inode->i_private = device;
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
fsnotify_create(root->d_inode, dentry); fsnotify_create(root->d_inode, dentry);
inode_unlock(d_inode(root)); simple_done_creating(dentry);
return 0; return 0;
@ -472,37 +461,9 @@ static struct inode *binderfs_make_inode(struct super_block *sb, int mode)
return ret; return ret;
} }
static struct dentry *binderfs_create_dentry(struct dentry *parent,
const char *name)
{
struct dentry *dentry;
dentry = lookup_noperm(&QSTR(name), parent);
if (IS_ERR(dentry))
return dentry;
/* Return error if the file/dir already exists. */
if (d_really_is_positive(dentry)) {
dput(dentry);
return ERR_PTR(-EEXIST);
}
return dentry;
}
void rust_binderfs_remove_file(struct dentry *dentry) void rust_binderfs_remove_file(struct dentry *dentry)
{ {
struct inode *parent_inode; simple_recursive_removal(dentry, NULL);
parent_inode = d_inode(dentry->d_parent);
inode_lock(parent_inode);
if (simple_positive(dentry)) {
dget(dentry);
simple_unlink(parent_inode, dentry);
d_delete(dentry);
dput(dentry);
}
inode_unlock(parent_inode);
} }
static struct dentry *rust_binderfs_create_file(struct dentry *parent, const char *name, static struct dentry *rust_binderfs_create_file(struct dentry *parent, const char *name,
@ -510,31 +471,23 @@ static struct dentry *rust_binderfs_create_file(struct dentry *parent, const cha
void *data) void *data)
{ {
struct dentry *dentry; struct dentry *dentry;
struct inode *new_inode, *parent_inode; struct inode *new_inode;
struct super_block *sb;
parent_inode = d_inode(parent);
inode_lock(parent_inode);
dentry = binderfs_create_dentry(parent, name);
if (IS_ERR(dentry))
goto out;
sb = parent_inode->i_sb;
new_inode = binderfs_make_inode(sb, S_IFREG | 0444);
if (!new_inode) {
dput(dentry);
dentry = ERR_PTR(-ENOMEM);
goto out;
}
new_inode = binderfs_make_inode(parent->d_sb, S_IFREG | 0444);
if (!new_inode)
return ERR_PTR(-ENOMEM);
new_inode->i_fop = fops; new_inode->i_fop = fops;
new_inode->i_private = data; new_inode->i_private = data;
d_instantiate(dentry, new_inode);
fsnotify_create(parent_inode, dentry);
out: dentry = simple_start_creating(parent, name);
inode_unlock(parent_inode); if (IS_ERR(dentry)) {
iput(new_inode);
return dentry;
}
d_make_persistent(dentry, new_inode);
fsnotify_create(parent->d_inode, dentry);
simple_done_creating(dentry);
return dentry; return dentry;
} }
@ -556,34 +509,26 @@ static struct dentry *binderfs_create_dir(struct dentry *parent,
const char *name) const char *name)
{ {
struct dentry *dentry; struct dentry *dentry;
struct inode *new_inode, *parent_inode; struct inode *new_inode;
struct super_block *sb;
parent_inode = d_inode(parent); new_inode = binderfs_make_inode(parent->d_sb, S_IFDIR | 0755);
inode_lock(parent_inode); if (!new_inode)
return ERR_PTR(-ENOMEM);
dentry = binderfs_create_dentry(parent, name);
if (IS_ERR(dentry))
goto out;
sb = parent_inode->i_sb;
new_inode = binderfs_make_inode(sb, S_IFDIR | 0755);
if (!new_inode) {
dput(dentry);
dentry = ERR_PTR(-ENOMEM);
goto out;
}
new_inode->i_fop = &simple_dir_operations; new_inode->i_fop = &simple_dir_operations;
new_inode->i_op = &simple_dir_inode_operations; new_inode->i_op = &simple_dir_inode_operations;
set_nlink(new_inode, 2); dentry = simple_start_creating(parent, name);
d_instantiate(dentry, new_inode); if (IS_ERR(dentry)) {
inc_nlink(parent_inode); iput(new_inode);
fsnotify_mkdir(parent_inode, dentry); return dentry;
}
out: inc_nlink(parent->d_inode);
inode_unlock(parent_inode); set_nlink(new_inode, 2);
d_make_persistent(dentry, new_inode);
fsnotify_mkdir(parent->d_inode, dentry);
simple_done_creating(dentry);
return dentry; return dentry;
} }
@ -802,7 +747,7 @@ static void binderfs_kill_super(struct super_block *sb)
* During inode eviction struct binderfs_info is needed. * During inode eviction struct binderfs_info is needed.
* So first wipe the super_block then free struct binderfs_info. * So first wipe the super_block then free struct binderfs_info.
*/ */
kill_litter_super(sb); kill_anon_super(sb);
if (info && info->ipc_ns) if (info && info->ipc_ns)
put_ipc_ns(info->ipc_ns); put_ipc_ns(info->ipc_ns);

View File

@ -183,28 +183,15 @@ static int binderfs_binder_device_create(struct inode *ref_inode,
} }
root = sb->s_root; root = sb->s_root;
inode_lock(d_inode(root)); dentry = simple_start_creating(root, name);
/* look it up */
dentry = lookup_noperm(&QSTR(name), root);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
inode_unlock(d_inode(root));
ret = PTR_ERR(dentry); ret = PTR_ERR(dentry);
goto err; goto err;
} }
if (d_really_is_positive(dentry)) {
/* already exists */
dput(dentry);
inode_unlock(d_inode(root));
ret = -EEXIST;
goto err;
}
inode->i_private = device; inode->i_private = device;
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
fsnotify_create(root->d_inode, dentry); fsnotify_create(root->d_inode, dentry);
inode_unlock(d_inode(root)); simple_done_creating(dentry);
binder_add_device(device); binder_add_device(device);
@ -410,12 +397,6 @@ static int binderfs_binder_ctl_create(struct super_block *sb)
if (!device) if (!device)
return -ENOMEM; return -ENOMEM;
/* If we have already created a binder-control node, return. */
if (info->control_dentry) {
ret = 0;
goto out;
}
ret = -ENOMEM; ret = -ENOMEM;
inode = new_inode(sb); inode = new_inode(sb);
if (!inode) if (!inode)
@ -451,7 +432,8 @@ static int binderfs_binder_ctl_create(struct super_block *sb)
inode->i_private = device; inode->i_private = device;
info->control_dentry = dentry; info->control_dentry = dentry;
d_add(dentry, inode); d_make_persistent(dentry, inode);
dput(dentry);
return 0; return 0;
@ -481,24 +463,6 @@ static struct inode *binderfs_make_inode(struct super_block *sb, int mode)
return ret; return ret;
} }
static struct dentry *binderfs_create_dentry(struct dentry *parent,
const char *name)
{
struct dentry *dentry;
dentry = lookup_noperm(&QSTR(name), parent);
if (IS_ERR(dentry))
return dentry;
/* Return error if the file/dir already exists. */
if (d_really_is_positive(dentry)) {
dput(dentry);
return ERR_PTR(-EEXIST);
}
return dentry;
}
struct dentry *binderfs_create_file(struct dentry *parent, const char *name, struct dentry *binderfs_create_file(struct dentry *parent, const char *name,
const struct file_operations *fops, const struct file_operations *fops,
void *data) void *data)
@ -508,28 +472,24 @@ struct dentry *binderfs_create_file(struct dentry *parent, const char *name,
struct super_block *sb; struct super_block *sb;
parent_inode = d_inode(parent); parent_inode = d_inode(parent);
inode_lock(parent_inode);
dentry = binderfs_create_dentry(parent, name); dentry = simple_start_creating(parent, name);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
goto out; return dentry;
sb = parent_inode->i_sb; sb = parent_inode->i_sb;
new_inode = binderfs_make_inode(sb, S_IFREG | 0444); new_inode = binderfs_make_inode(sb, S_IFREG | 0444);
if (!new_inode) { if (!new_inode) {
dput(dentry); simple_done_creating(dentry);
dentry = ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
goto out;
} }
new_inode->i_fop = fops; new_inode->i_fop = fops;
new_inode->i_private = data; new_inode->i_private = data;
d_instantiate(dentry, new_inode); d_make_persistent(dentry, new_inode);
fsnotify_create(parent_inode, dentry); fsnotify_create(parent_inode, dentry);
simple_done_creating(dentry);
out: return dentry; // borrowed
inode_unlock(parent_inode);
return dentry;
} }
static struct dentry *binderfs_create_dir(struct dentry *parent, static struct dentry *binderfs_create_dir(struct dentry *parent,
@ -540,30 +500,26 @@ static struct dentry *binderfs_create_dir(struct dentry *parent,
struct super_block *sb; struct super_block *sb;
parent_inode = d_inode(parent); parent_inode = d_inode(parent);
inode_lock(parent_inode);
dentry = binderfs_create_dentry(parent, name); dentry = simple_start_creating(parent, name);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
goto out; return dentry;
sb = parent_inode->i_sb; sb = parent_inode->i_sb;
new_inode = binderfs_make_inode(sb, S_IFDIR | 0755); new_inode = binderfs_make_inode(sb, S_IFDIR | 0755);
if (!new_inode) { if (!new_inode) {
dput(dentry); simple_done_creating(dentry);
dentry = ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
goto out;
} }
new_inode->i_fop = &simple_dir_operations; new_inode->i_fop = &simple_dir_operations;
new_inode->i_op = &simple_dir_inode_operations; new_inode->i_op = &simple_dir_inode_operations;
set_nlink(new_inode, 2); set_nlink(new_inode, 2);
d_instantiate(dentry, new_inode); d_make_persistent(dentry, new_inode);
inc_nlink(parent_inode); inc_nlink(parent_inode);
fsnotify_mkdir(parent_inode, dentry); fsnotify_mkdir(parent_inode, dentry);
simple_done_creating(dentry);
out:
inode_unlock(parent_inode);
return dentry; return dentry;
} }
@ -779,7 +735,7 @@ static void binderfs_kill_super(struct super_block *sb)
* During inode eviction struct binderfs_info is needed. * During inode eviction struct binderfs_info is needed.
* So first wipe the super_block then free struct binderfs_info. * So first wipe the super_block then free struct binderfs_info.
*/ */
kill_litter_super(sb); kill_anon_super(sb);
if (info && info->ipc_ns) if (info && info->ipc_ns)
put_ipc_ns(info->ipc_ns); put_ipc_ns(info->ipc_ns);

View File

@ -70,7 +70,7 @@ static struct file_system_type internal_fs_type = {
#else #else
.init_fs_context = ramfs_init_fs_context, .init_fs_context = ramfs_init_fs_context,
#endif #endif
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
}; };
/* Simply take a ref on the existing mount */ /* Simply take a ref on the existing mount */

View File

@ -97,13 +97,11 @@ static const struct super_operations ibmasmfs_s_ops = {
.drop_inode = inode_just_drop, .drop_inode = inode_just_drop,
}; };
static const struct file_operations *ibmasmfs_dir_ops = &simple_dir_operations;
static struct file_system_type ibmasmfs_type = { static struct file_system_type ibmasmfs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "ibmasmfs", .name = "ibmasmfs",
.init_fs_context = ibmasmfs_init_fs_context, .init_fs_context = ibmasmfs_init_fs_context,
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
}; };
MODULE_ALIAS_FS("ibmasmfs"); MODULE_ALIAS_FS("ibmasmfs");
@ -122,7 +120,7 @@ static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc)
return -ENOMEM; return -ENOMEM;
root->i_op = &simple_dir_inode_operations; root->i_op = &simple_dir_inode_operations;
root->i_fop = ibmasmfs_dir_ops; root->i_fop = &simple_dir_operations;
sb->s_root = d_make_root(root); sb->s_root = d_make_root(root);
if (!sb->s_root) if (!sb->s_root)
@ -144,7 +142,7 @@ static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode)
return ret; return ret;
} }
static struct dentry *ibmasmfs_create_file(struct dentry *parent, static int ibmasmfs_create_file(struct dentry *parent,
const char *name, const char *name,
const struct file_operations *fops, const struct file_operations *fops,
void *data, void *data,
@ -155,19 +153,20 @@ static struct dentry *ibmasmfs_create_file(struct dentry *parent,
dentry = d_alloc_name(parent, name); dentry = d_alloc_name(parent, name);
if (!dentry) if (!dentry)
return NULL; return -ENOMEM;
inode = ibmasmfs_make_inode(parent->d_sb, S_IFREG | mode); inode = ibmasmfs_make_inode(parent->d_sb, S_IFREG | mode);
if (!inode) { if (!inode) {
dput(dentry); dput(dentry);
return NULL; return -ENOMEM;
} }
inode->i_fop = fops; inode->i_fop = fops;
inode->i_private = data; inode->i_private = data;
d_add(dentry, inode); d_make_persistent(dentry, inode);
return dentry; dput(dentry);
return 0;
} }
static struct dentry *ibmasmfs_create_dir(struct dentry *parent, static struct dentry *ibmasmfs_create_dir(struct dentry *parent,
@ -187,10 +186,11 @@ static struct dentry *ibmasmfs_create_dir(struct dentry *parent,
} }
inode->i_op = &simple_dir_inode_operations; inode->i_op = &simple_dir_inode_operations;
inode->i_fop = ibmasmfs_dir_ops; inode->i_fop = &simple_dir_operations;
d_add(dentry, inode); d_make_persistent(dentry, inode);
return dentry; dput(dentry);
return dentry; // borrowed
} }
int ibmasmfs_register(void) int ibmasmfs_register(void)

View File

@ -160,8 +160,6 @@ struct ffs_epfile {
struct ffs_data *ffs; struct ffs_data *ffs;
struct ffs_ep *ep; /* P: ffs->eps_lock */ struct ffs_ep *ep; /* P: ffs->eps_lock */
struct dentry *dentry;
/* /*
* Buffer for holding data from partial reads which may happen since * Buffer for holding data from partial reads which may happen since
* were rounding user read requests to a multiple of a max packet size. * were rounding user read requests to a multiple of a max packet size.
@ -271,11 +269,11 @@ struct ffs_desc_helper {
}; };
static int __must_check ffs_epfiles_create(struct ffs_data *ffs); static int __must_check ffs_epfiles_create(struct ffs_data *ffs);
static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count); static void ffs_epfiles_destroy(struct super_block *sb,
struct ffs_epfile *epfiles, unsigned count);
static struct dentry * static int ffs_sb_create_file(struct super_block *sb, const char *name,
ffs_sb_create_file(struct super_block *sb, const char *name, void *data, void *data, const struct file_operations *fops);
const struct file_operations *fops);
/* Devices management *******************************************************/ /* Devices management *******************************************************/
@ -640,13 +638,22 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
static int ffs_ep0_open(struct inode *inode, struct file *file) static int ffs_ep0_open(struct inode *inode, struct file *file)
{ {
struct ffs_data *ffs = inode->i_private; struct ffs_data *ffs = inode->i_sb->s_fs_info;
int ret;
if (ffs->state == FFS_CLOSING) /* Acquire mutex */
return -EBUSY; ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
if (ret < 0)
return ret;
file->private_data = ffs;
ffs_data_opened(ffs); ffs_data_opened(ffs);
if (ffs->state == FFS_CLOSING) {
ffs_data_closed(ffs);
mutex_unlock(&ffs->mutex);
return -EBUSY;
}
mutex_unlock(&ffs->mutex);
file->private_data = ffs;
return stream_open(inode, file); return stream_open(inode, file);
} }
@ -1193,14 +1200,33 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
static int static int
ffs_epfile_open(struct inode *inode, struct file *file) ffs_epfile_open(struct inode *inode, struct file *file)
{ {
struct ffs_epfile *epfile = inode->i_private; struct ffs_data *ffs = inode->i_sb->s_fs_info;
struct ffs_epfile *epfile;
int ret;
if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) /* Acquire mutex */
ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
if (ret < 0)
return ret;
if (!atomic_inc_not_zero(&ffs->opened)) {
mutex_unlock(&ffs->mutex);
return -ENODEV; return -ENODEV;
}
/*
* we want the state to be FFS_ACTIVE; FFS_ACTIVE alone is
* not enough, though - we might have been through FFS_CLOSING
* and back to FFS_ACTIVE, with our file already removed.
*/
epfile = smp_load_acquire(&inode->i_private);
if (unlikely(ffs->state != FFS_ACTIVE || !epfile)) {
mutex_unlock(&ffs->mutex);
ffs_data_closed(ffs);
return -ENODEV;
}
mutex_unlock(&ffs->mutex);
file->private_data = epfile; file->private_data = epfile;
ffs_data_opened(epfile->ffs);
return stream_open(inode, file); return stream_open(inode, file);
} }
@ -1332,7 +1358,7 @@ static void ffs_dmabuf_put(struct dma_buf_attachment *attach)
static int static int
ffs_epfile_release(struct inode *inode, struct file *file) ffs_epfile_release(struct inode *inode, struct file *file)
{ {
struct ffs_epfile *epfile = inode->i_private; struct ffs_epfile *epfile = file->private_data;
struct ffs_dmabuf_priv *priv, *tmp; struct ffs_dmabuf_priv *priv, *tmp;
struct ffs_data *ffs = epfile->ffs; struct ffs_data *ffs = epfile->ffs;
@ -1866,26 +1892,26 @@ ffs_sb_make_inode(struct super_block *sb, void *data,
} }
/* Create "regular" file */ /* Create "regular" file */
static struct dentry *ffs_sb_create_file(struct super_block *sb, static int ffs_sb_create_file(struct super_block *sb, const char *name,
const char *name, void *data, void *data, const struct file_operations *fops)
const struct file_operations *fops)
{ {
struct ffs_data *ffs = sb->s_fs_info; struct ffs_data *ffs = sb->s_fs_info;
struct dentry *dentry; struct dentry *dentry;
struct inode *inode; struct inode *inode;
dentry = d_alloc_name(sb->s_root, name);
if (!dentry)
return NULL;
inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms); inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms);
if (!inode) { if (!inode)
dput(dentry); return -ENOMEM;
return NULL; dentry = simple_start_creating(sb->s_root, name);
if (IS_ERR(dentry)) {
iput(inode);
return PTR_ERR(dentry);
} }
d_add(dentry, inode); d_make_persistent(dentry, inode);
return dentry;
simple_done_creating(dentry);
return 0;
} }
/* Super block */ /* Super block */
@ -1928,10 +1954,7 @@ static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc)
return -ENOMEM; return -ENOMEM;
/* EP0 file */ /* EP0 file */
if (!ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations)) return ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations);
return -ENOMEM;
return 0;
} }
enum { enum {
@ -2071,12 +2094,21 @@ static int ffs_fs_init_fs_context(struct fs_context *fc)
return 0; return 0;
} }
static void ffs_data_reset(struct ffs_data *ffs);
static void static void
ffs_fs_kill_sb(struct super_block *sb) ffs_fs_kill_sb(struct super_block *sb)
{ {
kill_litter_super(sb); kill_anon_super(sb);
if (sb->s_fs_info) if (sb->s_fs_info) {
ffs_data_closed(sb->s_fs_info); struct ffs_data *ffs = sb->s_fs_info;
ffs->state = FFS_CLOSING;
ffs_data_reset(ffs);
// no configfs accesses from that point on,
// so no further schedule_work() is possible
cancel_work_sync(&ffs->reset_work);
ffs_data_put(ffs);
}
} }
static struct file_system_type ffs_fs_type = { static struct file_system_type ffs_fs_type = {
@ -2114,7 +2146,6 @@ static void functionfs_cleanup(void)
/* ffs_data and ffs_function construction and destruction code **************/ /* ffs_data and ffs_function construction and destruction code **************/
static void ffs_data_clear(struct ffs_data *ffs); static void ffs_data_clear(struct ffs_data *ffs);
static void ffs_data_reset(struct ffs_data *ffs);
static void ffs_data_get(struct ffs_data *ffs) static void ffs_data_get(struct ffs_data *ffs)
{ {
@ -2123,7 +2154,6 @@ static void ffs_data_get(struct ffs_data *ffs)
static void ffs_data_opened(struct ffs_data *ffs) static void ffs_data_opened(struct ffs_data *ffs)
{ {
refcount_inc(&ffs->ref);
if (atomic_add_return(1, &ffs->opened) == 1 && if (atomic_add_return(1, &ffs->opened) == 1 &&
ffs->state == FFS_DEACTIVATED) { ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING; ffs->state = FFS_CLOSING;
@ -2148,11 +2178,11 @@ static void ffs_data_put(struct ffs_data *ffs)
static void ffs_data_closed(struct ffs_data *ffs) static void ffs_data_closed(struct ffs_data *ffs)
{ {
if (atomic_dec_and_test(&ffs->opened)) {
if (ffs->no_disconnect) {
struct ffs_epfile *epfiles; struct ffs_epfile *epfiles;
unsigned long flags; unsigned long flags;
if (atomic_dec_and_test(&ffs->opened)) {
if (ffs->no_disconnect) {
ffs->state = FFS_DEACTIVATED; ffs->state = FFS_DEACTIVATED;
spin_lock_irqsave(&ffs->eps_lock, flags); spin_lock_irqsave(&ffs->eps_lock, flags);
epfiles = ffs->epfiles; epfiles = ffs->epfiles;
@ -2161,7 +2191,7 @@ static void ffs_data_closed(struct ffs_data *ffs)
flags); flags);
if (epfiles) if (epfiles)
ffs_epfiles_destroy(epfiles, ffs_epfiles_destroy(ffs->sb, epfiles,
ffs->eps_count); ffs->eps_count);
if (ffs->setup_state == FFS_SETUP_PENDING) if (ffs->setup_state == FFS_SETUP_PENDING)
@ -2171,12 +2201,6 @@ static void ffs_data_closed(struct ffs_data *ffs)
ffs_data_reset(ffs); ffs_data_reset(ffs);
} }
} }
if (atomic_read(&ffs->opened) < 0) {
ffs->state = FFS_CLOSING;
ffs_data_reset(ffs);
}
ffs_data_put(ffs);
} }
static struct ffs_data *ffs_data_new(const char *dev_name) static struct ffs_data *ffs_data_new(const char *dev_name)
@ -2226,7 +2250,7 @@ static void ffs_data_clear(struct ffs_data *ffs)
* copy of epfile will save us from use-after-free. * copy of epfile will save us from use-after-free.
*/ */
if (epfiles) { if (epfiles) {
ffs_epfiles_destroy(epfiles, ffs->eps_count); ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count);
ffs->epfiles = NULL; ffs->epfiles = NULL;
} }
@ -2323,6 +2347,7 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
{ {
struct ffs_epfile *epfile, *epfiles; struct ffs_epfile *epfile, *epfiles;
unsigned i, count; unsigned i, count;
int err;
count = ffs->eps_count; count = ffs->eps_count;
epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL); epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL);
@ -2339,12 +2364,11 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]); sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]);
else else
sprintf(epfile->name, "ep%u", i); sprintf(epfile->name, "ep%u", i);
epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name, err = ffs_sb_create_file(ffs->sb, epfile->name,
epfile, epfile, &ffs_epfile_operations);
&ffs_epfile_operations); if (err) {
if (!epfile->dentry) { ffs_epfiles_destroy(ffs->sb, epfiles, i - 1);
ffs_epfiles_destroy(epfiles, i - 1); return err;
return -ENOMEM;
} }
} }
@ -2352,16 +2376,20 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
return 0; return 0;
} }
static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) static void clear_one(struct dentry *dentry)
{
smp_store_release(&dentry->d_inode->i_private, NULL);
}
static void ffs_epfiles_destroy(struct super_block *sb,
struct ffs_epfile *epfiles, unsigned count)
{ {
struct ffs_epfile *epfile = epfiles; struct ffs_epfile *epfile = epfiles;
struct dentry *root = sb->s_root;
for (; count; --count, ++epfile) { for (; count; --count, ++epfile) {
BUG_ON(mutex_is_locked(&epfile->mutex)); BUG_ON(mutex_is_locked(&epfile->mutex));
if (epfile->dentry) { simple_remove_by_name(root, epfile->name, clear_one);
simple_recursive_removal(epfile->dentry, NULL);
epfile->dentry = NULL;
}
} }
kfree(epfiles); kfree(epfiles);

View File

@ -150,7 +150,6 @@ struct dev_data {
void *buf; void *buf;
wait_queue_head_t wait; wait_queue_head_t wait;
struct super_block *sb; struct super_block *sb;
struct dentry *dentry;
/* except this scratch i/o buffer for ep0 */ /* except this scratch i/o buffer for ep0 */
u8 rbuf[RBUF_SIZE]; u8 rbuf[RBUF_SIZE];
@ -208,7 +207,6 @@ struct ep_data {
struct usb_endpoint_descriptor desc, hs_desc; struct usb_endpoint_descriptor desc, hs_desc;
struct list_head epfiles; struct list_head epfiles;
wait_queue_head_t wait; wait_queue_head_t wait;
struct dentry *dentry;
}; };
static inline void get_ep (struct ep_data *data) static inline void get_ep (struct ep_data *data)
@ -1561,16 +1559,12 @@ static void destroy_ep_files (struct dev_data *dev)
spin_lock_irq (&dev->lock); spin_lock_irq (&dev->lock);
while (!list_empty(&dev->epfiles)) { while (!list_empty(&dev->epfiles)) {
struct ep_data *ep; struct ep_data *ep;
struct dentry *dentry;
/* break link to FS */ /* break link to FS */
ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles); ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles);
list_del_init (&ep->epfiles); list_del_init (&ep->epfiles);
spin_unlock_irq (&dev->lock); spin_unlock_irq (&dev->lock);
dentry = ep->dentry;
ep->dentry = NULL;
/* break link to controller */ /* break link to controller */
mutex_lock(&ep->lock); mutex_lock(&ep->lock);
if (ep->state == STATE_EP_ENABLED) if (ep->state == STATE_EP_ENABLED)
@ -1581,10 +1575,11 @@ static void destroy_ep_files (struct dev_data *dev)
mutex_unlock(&ep->lock); mutex_unlock(&ep->lock);
wake_up (&ep->wait); wake_up (&ep->wait);
put_ep (ep);
/* break link to dcache */ /* break link to dcache */
simple_recursive_removal(dentry, NULL); simple_remove_by_name(dev->sb->s_root, ep->name, NULL);
put_ep (ep);
spin_lock_irq (&dev->lock); spin_lock_irq (&dev->lock);
} }
@ -1592,14 +1587,14 @@ static void destroy_ep_files (struct dev_data *dev)
} }
static struct dentry * static int gadgetfs_create_file (struct super_block *sb, char const *name,
gadgetfs_create_file (struct super_block *sb, char const *name,
void *data, const struct file_operations *fops); void *data, const struct file_operations *fops);
static int activate_ep_files (struct dev_data *dev) static int activate_ep_files (struct dev_data *dev)
{ {
struct usb_ep *ep; struct usb_ep *ep;
struct ep_data *data; struct ep_data *data;
int err;
gadget_for_each_ep (ep, dev->gadget) { gadget_for_each_ep (ep, dev->gadget) {
@ -1622,9 +1617,9 @@ static int activate_ep_files (struct dev_data *dev)
if (!data->req) if (!data->req)
goto enomem1; goto enomem1;
data->dentry = gadgetfs_create_file (dev->sb, data->name, err = gadgetfs_create_file (dev->sb, data->name,
data, &ep_io_operations); data, &ep_io_operations);
if (!data->dentry) if (err)
goto enomem2; goto enomem2;
list_add_tail (&data->epfiles, &dev->epfiles); list_add_tail (&data->epfiles, &dev->epfiles);
} }
@ -1988,25 +1983,27 @@ gadgetfs_make_inode (struct super_block *sb,
/* creates in fs root directory, so non-renamable and non-linkable. /* creates in fs root directory, so non-renamable and non-linkable.
* so inode and dentry are paired, until device reconfig. * so inode and dentry are paired, until device reconfig.
*/ */
static struct dentry * static int gadgetfs_create_file (struct super_block *sb, char const *name,
gadgetfs_create_file (struct super_block *sb, char const *name,
void *data, const struct file_operations *fops) void *data, const struct file_operations *fops)
{ {
struct dentry *dentry; struct dentry *dentry;
struct inode *inode; struct inode *inode;
dentry = d_alloc_name(sb->s_root, name);
if (!dentry)
return NULL;
inode = gadgetfs_make_inode (sb, data, fops, inode = gadgetfs_make_inode (sb, data, fops,
S_IFREG | (default_perm & S_IRWXUGO)); S_IFREG | (default_perm & S_IRWXUGO));
if (!inode) { if (!inode)
dput(dentry); return -ENOMEM;
return NULL;
dentry = simple_start_creating(sb->s_root, name);
if (IS_ERR(dentry)) {
iput(inode);
return PTR_ERR(dentry);
} }
d_add (dentry, inode);
return dentry; d_make_persistent(dentry, inode);
simple_done_creating(dentry);
return 0;
} }
static const struct super_operations gadget_fs_operations = { static const struct super_operations gadget_fs_operations = {
@ -2059,8 +2056,8 @@ gadgetfs_fill_super (struct super_block *sb, struct fs_context *fc)
goto Enomem; goto Enomem;
dev->sb = sb; dev->sb = sb;
dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations); rc = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations);
if (!dev->dentry) { if (rc) {
put_dev(dev); put_dev(dev);
goto Enomem; goto Enomem;
} }
@ -2102,7 +2099,7 @@ static void
gadgetfs_kill_sb (struct super_block *sb) gadgetfs_kill_sb (struct super_block *sb)
{ {
mutex_lock(&sb_mutex); mutex_lock(&sb_mutex);
kill_litter_super (sb); kill_anon_super (sb);
if (the_device) { if (the_device) {
put_dev (the_device); put_dev (the_device);
the_device = NULL; the_device = NULL;

View File

@ -88,7 +88,7 @@ static struct file_system_type xenfs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "xenfs", .name = "xenfs",
.init_fs_context = xenfs_init_fs_context, .init_fs_context = xenfs_init_fs_context,
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
}; };
MODULE_ALIAS_FS("xenfs"); MODULE_ALIAS_FS("xenfs");

View File

@ -55,7 +55,7 @@ void autofs_kill_sb(struct super_block *sb)
} }
pr_debug("shutting down\n"); pr_debug("shutting down\n");
kill_litter_super(sb); kill_anon_super(sb);
if (sbi) if (sbi)
kfree_rcu(sbi, rcu); kfree_rcu(sbi, rcu);
} }

View File

@ -602,9 +602,8 @@ static int autofs_dir_symlink(struct mnt_idmap *idmap,
} }
inode->i_private = cp; inode->i_private = cp;
inode->i_size = size; inode->i_size = size;
d_add(dentry, inode);
dget(dentry); d_make_persistent(dentry, inode);
p_ino = autofs_dentry_ino(dentry->d_parent); p_ino = autofs_dentry_ino(dentry->d_parent);
p_ino->count++; p_ino->count++;
@ -631,12 +630,11 @@ static int autofs_dir_symlink(struct mnt_idmap *idmap,
static int autofs_dir_unlink(struct inode *dir, struct dentry *dentry) static int autofs_dir_unlink(struct inode *dir, struct dentry *dentry)
{ {
struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb);
struct autofs_info *ino = autofs_dentry_ino(dentry);
struct autofs_info *p_ino; struct autofs_info *p_ino;
p_ino = autofs_dentry_ino(dentry->d_parent); p_ino = autofs_dentry_ino(dentry->d_parent);
p_ino->count--; p_ino->count--;
dput(ino->dentry); d_make_discardable(dentry);
d_inode(dentry)->i_size = 0; d_inode(dentry)->i_size = 0;
clear_nlink(d_inode(dentry)); clear_nlink(d_inode(dentry));
@ -718,7 +716,7 @@ static int autofs_dir_rmdir(struct inode *dir, struct dentry *dentry)
p_ino = autofs_dentry_ino(dentry->d_parent); p_ino = autofs_dentry_ino(dentry->d_parent);
p_ino->count--; p_ino->count--;
dput(ino->dentry); d_make_discardable(dentry);
d_inode(dentry)->i_size = 0; d_inode(dentry)->i_size = 0;
clear_nlink(d_inode(dentry)); clear_nlink(d_inode(dentry));
@ -748,12 +746,11 @@ static struct dentry *autofs_dir_mkdir(struct mnt_idmap *idmap,
inode = autofs_get_inode(dir->i_sb, S_IFDIR | mode); inode = autofs_get_inode(dir->i_sb, S_IFDIR | mode);
if (!inode) if (!inode)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
d_add(dentry, inode);
if (sbi->version < 5) if (sbi->version < 5)
autofs_set_leaf_automount_flags(dentry); autofs_set_leaf_automount_flags(dentry);
dget(dentry); d_make_persistent(dentry, inode);
p_ino = autofs_dentry_ino(dentry->d_parent); p_ino = autofs_dentry_ino(dentry->d_parent);
p_ino->count++; p_ino->count++;
inc_nlink(dir); inc_nlink(dir);

View File

@ -765,14 +765,41 @@ static const struct file_operations bm_entry_operations = {
/* /register */ /* /register */
/* add to filesystem */
static int add_entry(Node *e, struct super_block *sb)
{
struct dentry *dentry = simple_start_creating(sb->s_root, e->name);
struct inode *inode;
struct binfmt_misc *misc;
if (IS_ERR(dentry))
return PTR_ERR(dentry);
inode = bm_get_inode(sb, S_IFREG | 0644);
if (unlikely(!inode)) {
simple_done_creating(dentry);
return -ENOMEM;
}
refcount_set(&e->users, 1);
e->dentry = dentry;
inode->i_private = e;
inode->i_fop = &bm_entry_operations;
d_make_persistent(dentry, inode);
misc = i_binfmt_misc(inode);
write_lock(&misc->entries_lock);
list_add(&e->list, &misc->entries);
write_unlock(&misc->entries_lock);
simple_done_creating(dentry);
return 0;
}
static ssize_t bm_register_write(struct file *file, const char __user *buffer, static ssize_t bm_register_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
Node *e; Node *e;
struct inode *inode;
struct super_block *sb = file_inode(file)->i_sb; struct super_block *sb = file_inode(file)->i_sb;
struct dentry *root = sb->s_root, *dentry;
struct binfmt_misc *misc;
int err = 0; int err = 0;
struct file *f = NULL; struct file *f = NULL;
@ -800,39 +827,7 @@ static ssize_t bm_register_write(struct file *file, const char __user *buffer,
e->interp_file = f; e->interp_file = f;
} }
inode_lock(d_inode(root)); err = add_entry(e, sb);
dentry = lookup_noperm(&QSTR(e->name), root);
err = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto out;
err = -EEXIST;
if (d_really_is_positive(dentry))
goto out2;
inode = bm_get_inode(sb, S_IFREG | 0644);
err = -ENOMEM;
if (!inode)
goto out2;
refcount_set(&e->users, 1);
e->dentry = dget(dentry);
inode->i_private = e;
inode->i_fop = &bm_entry_operations;
d_instantiate(dentry, inode);
misc = i_binfmt_misc(inode);
write_lock(&misc->entries_lock);
list_add(&e->list, &misc->entries);
write_unlock(&misc->entries_lock);
err = 0;
out2:
dput(dentry);
out:
inode_unlock(d_inode(root));
if (err) { if (err) {
if (f) { if (f) {
exe_file_allow_write_access(f); exe_file_allow_write_access(f);
@ -1027,7 +1022,7 @@ static struct file_system_type bm_fs_type = {
.name = "binfmt_misc", .name = "binfmt_misc",
.init_fs_context = bm_init_fs_context, .init_fs_context = bm_init_fs_context,
.fs_flags = FS_USERNS_MOUNT, .fs_flags = FS_USERNS_MOUNT,
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
}; };
MODULE_ALIAS_FS("binfmt_misc"); MODULE_ALIAS_FS("binfmt_misc");

View File

@ -400,8 +400,14 @@ static void remove_dir(struct dentry * d)
configfs_remove_dirent(d); configfs_remove_dirent(d);
if (d_really_is_positive(d)) if (d_really_is_positive(d)) {
simple_rmdir(d_inode(parent),d); if (likely(simple_empty(d))) {
__simple_rmdir(d_inode(parent),d);
dput(d);
} else {
pr_warn("remove_dir (%pd): attributes remain", d);
}
}
pr_debug(" o %pd removing done (%d)\n", d, d_count(d)); pr_debug(" o %pd removing done (%d)\n", d, d_count(d));

View File

@ -211,7 +211,8 @@ void configfs_drop_dentry(struct configfs_dirent * sd, struct dentry * parent)
dget_dlock(dentry); dget_dlock(dentry);
__d_drop(dentry); __d_drop(dentry);
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
simple_unlink(d_inode(parent), dentry); __simple_unlink(d_inode(parent), dentry);
dput(dentry);
} else } else
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
} }

View File

@ -116,7 +116,7 @@ static struct file_system_type configfs_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "configfs", .name = "configfs",
.init_fs_context = configfs_init_fs_context, .init_fs_context = configfs_init_fs_context,
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
}; };
MODULE_ALIAS_FS("configfs"); MODULE_ALIAS_FS("configfs");

View File

@ -870,6 +870,24 @@ static inline bool fast_dput(struct dentry *dentry)
return false; return false;
} }
static void finish_dput(struct dentry *dentry)
__releases(dentry->d_lock)
__releases(RCU)
{
while (lock_for_kill(dentry)) {
rcu_read_unlock();
dentry = __dentry_kill(dentry);
if (!dentry)
return;
if (retain_dentry(dentry, true)) {
spin_unlock(&dentry->d_lock);
return;
}
rcu_read_lock();
}
rcu_read_unlock();
spin_unlock(&dentry->d_lock);
}
/* /*
* This is dput * This is dput
@ -907,22 +925,21 @@ void dput(struct dentry *dentry)
rcu_read_unlock(); rcu_read_unlock();
return; return;
} }
while (lock_for_kill(dentry)) { finish_dput(dentry);
rcu_read_unlock();
dentry = __dentry_kill(dentry);
if (!dentry)
return;
if (retain_dentry(dentry, true)) {
spin_unlock(&dentry->d_lock);
return;
}
rcu_read_lock();
}
rcu_read_unlock();
spin_unlock(&dentry->d_lock);
} }
EXPORT_SYMBOL(dput); EXPORT_SYMBOL(dput);
void d_make_discardable(struct dentry *dentry)
{
spin_lock(&dentry->d_lock);
WARN_ON(!(dentry->d_flags & DCACHE_PERSISTENT));
dentry->d_flags &= ~DCACHE_PERSISTENT;
dentry->d_lockref.count--;
rcu_read_lock();
finish_dput(dentry);
}
EXPORT_SYMBOL(d_make_discardable);
static void to_shrink_list(struct dentry *dentry, struct list_head *list) static void to_shrink_list(struct dentry *dentry, struct list_head *list)
__must_hold(&dentry->d_lock) __must_hold(&dentry->d_lock)
{ {
@ -1512,6 +1529,15 @@ static enum d_walk_ret select_collect(void *_data, struct dentry *dentry)
return ret; return ret;
} }
static enum d_walk_ret select_collect_umount(void *_data, struct dentry *dentry)
{
if (dentry->d_flags & DCACHE_PERSISTENT) {
dentry->d_flags &= ~DCACHE_PERSISTENT;
dentry->d_lockref.count--;
}
return select_collect(_data, dentry);
}
static enum d_walk_ret select_collect2(void *_data, struct dentry *dentry) static enum d_walk_ret select_collect2(void *_data, struct dentry *dentry)
{ {
struct select_data *data = _data; struct select_data *data = _data;
@ -1540,18 +1566,20 @@ static enum d_walk_ret select_collect2(void *_data, struct dentry *dentry)
} }
/** /**
* shrink_dcache_parent - prune dcache * shrink_dcache_tree - prune dcache
* @parent: parent of entries to prune * @parent: parent of entries to prune
* @for_umount: true if we want to unpin the persistent ones
* *
* Prune the dcache to remove unused children of the parent dentry. * Prune the dcache to remove unused children of the parent dentry.
*/ */
void shrink_dcache_parent(struct dentry *parent) static void shrink_dcache_tree(struct dentry *parent, bool for_umount)
{ {
for (;;) { for (;;) {
struct select_data data = {.start = parent}; struct select_data data = {.start = parent};
INIT_LIST_HEAD(&data.dispose); INIT_LIST_HEAD(&data.dispose);
d_walk(parent, &data, select_collect); d_walk(parent, &data,
for_umount ? select_collect_umount : select_collect);
if (!list_empty(&data.dispose)) { if (!list_empty(&data.dispose)) {
shrink_dentry_list(&data.dispose); shrink_dentry_list(&data.dispose);
@ -1576,6 +1604,11 @@ void shrink_dcache_parent(struct dentry *parent)
shrink_dentry_list(&data.dispose); shrink_dentry_list(&data.dispose);
} }
} }
void shrink_dcache_parent(struct dentry *parent)
{
shrink_dcache_tree(parent, false);
}
EXPORT_SYMBOL(shrink_dcache_parent); EXPORT_SYMBOL(shrink_dcache_parent);
static enum d_walk_ret umount_check(void *_data, struct dentry *dentry) static enum d_walk_ret umount_check(void *_data, struct dentry *dentry)
@ -1602,7 +1635,7 @@ static enum d_walk_ret umount_check(void *_data, struct dentry *dentry)
static void do_one_tree(struct dentry *dentry) static void do_one_tree(struct dentry *dentry)
{ {
shrink_dcache_parent(dentry); shrink_dcache_tree(dentry, true);
d_walk(dentry, dentry, umount_check); d_walk(dentry, dentry, umount_check);
d_drop(dentry); d_drop(dentry);
dput(dentry); dput(dentry);
@ -1924,7 +1957,6 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
unsigned add_flags = d_flags_for_inode(inode); unsigned add_flags = d_flags_for_inode(inode);
WARN_ON(d_in_lookup(dentry)); WARN_ON(d_in_lookup(dentry));
spin_lock(&dentry->d_lock);
/* /*
* The negative counter only tracks dentries on the LRU. Don't dec if * The negative counter only tracks dentries on the LRU. Don't dec if
* d_lru is on another list. * d_lru is on another list.
@ -1937,7 +1969,6 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
__d_set_inode_and_type(dentry, inode, add_flags); __d_set_inode_and_type(dentry, inode, add_flags);
raw_write_seqcount_end(&dentry->d_seq); raw_write_seqcount_end(&dentry->d_seq);
fsnotify_update_flags(dentry); fsnotify_update_flags(dentry);
spin_unlock(&dentry->d_lock);
} }
/** /**
@ -1961,7 +1992,9 @@ void d_instantiate(struct dentry *entry, struct inode * inode)
if (inode) { if (inode) {
security_d_instantiate(entry, inode); security_d_instantiate(entry, inode);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
spin_lock(&entry->d_lock);
__d_instantiate(entry, inode); __d_instantiate(entry, inode);
spin_unlock(&entry->d_lock);
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
} }
} }
@ -1980,7 +2013,9 @@ void d_instantiate_new(struct dentry *entry, struct inode *inode)
lockdep_annotate_inode_mutex_key(inode); lockdep_annotate_inode_mutex_key(inode);
security_d_instantiate(entry, inode); security_d_instantiate(entry, inode);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
spin_lock(&entry->d_lock);
__d_instantiate(entry, inode); __d_instantiate(entry, inode);
spin_unlock(&entry->d_lock);
WARN_ON(!(inode_state_read(inode) & I_NEW)); WARN_ON(!(inode_state_read(inode) & I_NEW));
inode_state_clear(inode, I_NEW | I_CREATING); inode_state_clear(inode, I_NEW | I_CREATING);
inode_wake_up_bit(inode, __I_NEW); inode_wake_up_bit(inode, __I_NEW);
@ -2742,6 +2777,24 @@ void d_add(struct dentry *entry, struct inode *inode)
} }
EXPORT_SYMBOL(d_add); EXPORT_SYMBOL(d_add);
struct dentry *d_make_persistent(struct dentry *dentry, struct inode *inode)
{
WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias));
WARN_ON(!inode);
security_d_instantiate(dentry, inode);
spin_lock(&inode->i_lock);
spin_lock(&dentry->d_lock);
__d_instantiate(dentry, inode);
dentry->d_flags |= DCACHE_PERSISTENT;
dget_dlock(dentry);
if (d_unhashed(dentry))
__d_rehash(dentry);
spin_unlock(&dentry->d_lock);
spin_unlock(&inode->i_lock);
return dentry;
}
EXPORT_SYMBOL(d_make_persistent);
static void swap_names(struct dentry *dentry, struct dentry *target) static void swap_names(struct dentry *dentry, struct dentry *target)
{ {
if (unlikely(dname_external(target))) { if (unlikely(dname_external(target))) {
@ -3111,26 +3164,6 @@ bool is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
} }
EXPORT_SYMBOL(is_subdir); EXPORT_SYMBOL(is_subdir);
static enum d_walk_ret d_genocide_kill(void *data, struct dentry *dentry)
{
struct dentry *root = data;
if (dentry != root) {
if (d_unhashed(dentry) || !dentry->d_inode)
return D_WALK_SKIP;
if (!(dentry->d_flags & DCACHE_GENOCIDE)) {
dentry->d_flags |= DCACHE_GENOCIDE;
dentry->d_lockref.count--;
}
}
return D_WALK_CONTINUE;
}
void d_genocide(struct dentry *parent)
{
d_walk(parent, parent, d_genocide_kill);
}
void d_mark_tmpfile(struct file *file, struct inode *inode) void d_mark_tmpfile(struct file *file, struct inode *inode)
{ {
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;

View File

@ -329,7 +329,7 @@ static struct file_system_type debug_fs_type = {
.name = "debugfs", .name = "debugfs",
.init_fs_context = debugfs_init_fs_context, .init_fs_context = debugfs_init_fs_context,
.parameters = debugfs_param_specs, .parameters = debugfs_param_specs,
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
}; };
MODULE_ALIAS_FS("debugfs"); MODULE_ALIAS_FS("debugfs");
@ -405,16 +405,15 @@ static struct dentry *debugfs_start_creating(const char *name,
static struct dentry *debugfs_failed_creating(struct dentry *dentry) static struct dentry *debugfs_failed_creating(struct dentry *dentry)
{ {
inode_unlock(d_inode(dentry->d_parent)); simple_done_creating(dentry);
dput(dentry);
simple_release_fs(&debugfs_mount, &debugfs_mount_count); simple_release_fs(&debugfs_mount, &debugfs_mount_count);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
static struct dentry *debugfs_end_creating(struct dentry *dentry) static struct dentry *debugfs_end_creating(struct dentry *dentry)
{ {
inode_unlock(d_inode(dentry->d_parent)); simple_done_creating(dentry);
return dentry; return dentry; // borrowed
} }
static struct dentry *__debugfs_create_file(const char *name, umode_t mode, static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
@ -434,11 +433,6 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return dentry; return dentry;
if (!(debugfs_allow & DEBUGFS_ALLOW_API)) {
debugfs_failed_creating(dentry);
return ERR_PTR(-EPERM);
}
inode = debugfs_get_inode(dentry->d_sb); inode = debugfs_get_inode(dentry->d_sb);
if (unlikely(!inode)) { if (unlikely(!inode)) {
pr_err("out of free dentries, can not create file '%s'\n", pr_err("out of free dentries, can not create file '%s'\n",
@ -456,7 +450,7 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
DEBUGFS_I(inode)->raw = real_fops; DEBUGFS_I(inode)->raw = real_fops;
DEBUGFS_I(inode)->aux = (void *)aux; DEBUGFS_I(inode)->aux = (void *)aux;
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
fsnotify_create(d_inode(dentry->d_parent), dentry); fsnotify_create(d_inode(dentry->d_parent), dentry);
return debugfs_end_creating(dentry); return debugfs_end_creating(dentry);
} }
@ -584,11 +578,6 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return dentry; return dentry;
if (!(debugfs_allow & DEBUGFS_ALLOW_API)) {
debugfs_failed_creating(dentry);
return ERR_PTR(-EPERM);
}
inode = debugfs_get_inode(dentry->d_sb); inode = debugfs_get_inode(dentry->d_sb);
if (unlikely(!inode)) { if (unlikely(!inode)) {
pr_err("out of free dentries, can not create directory '%s'\n", pr_err("out of free dentries, can not create directory '%s'\n",
@ -602,7 +591,7 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
/* directory inodes start off with i_nlink == 2 (for "." entry) */ /* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode); inc_nlink(inode);
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
inc_nlink(d_inode(dentry->d_parent)); inc_nlink(d_inode(dentry->d_parent));
fsnotify_mkdir(d_inode(dentry->d_parent), dentry); fsnotify_mkdir(d_inode(dentry->d_parent), dentry);
return debugfs_end_creating(dentry); return debugfs_end_creating(dentry);
@ -631,11 +620,6 @@ struct dentry *debugfs_create_automount(const char *name,
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return dentry; return dentry;
if (!(debugfs_allow & DEBUGFS_ALLOW_API)) {
debugfs_failed_creating(dentry);
return ERR_PTR(-EPERM);
}
inode = debugfs_get_inode(dentry->d_sb); inode = debugfs_get_inode(dentry->d_sb);
if (unlikely(!inode)) { if (unlikely(!inode)) {
pr_err("out of free dentries, can not create automount '%s'\n", pr_err("out of free dentries, can not create automount '%s'\n",
@ -649,7 +633,7 @@ struct dentry *debugfs_create_automount(const char *name,
DEBUGFS_I(inode)->automount = f; DEBUGFS_I(inode)->automount = f;
/* directory inodes start off with i_nlink == 2 (for "." entry) */ /* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode); inc_nlink(inode);
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
inc_nlink(d_inode(dentry->d_parent)); inc_nlink(d_inode(dentry->d_parent));
fsnotify_mkdir(d_inode(dentry->d_parent), dentry); fsnotify_mkdir(d_inode(dentry->d_parent), dentry);
return debugfs_end_creating(dentry); return debugfs_end_creating(dentry);
@ -704,7 +688,7 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
inode->i_mode = S_IFLNK | S_IRWXUGO; inode->i_mode = S_IFLNK | S_IRWXUGO;
inode->i_op = &debugfs_symlink_inode_operations; inode->i_op = &debugfs_symlink_inode_operations;
inode->i_link = link; inode->i_link = link;
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
return debugfs_end_creating(dentry); return debugfs_end_creating(dentry);
} }
EXPORT_SYMBOL_GPL(debugfs_create_symlink); EXPORT_SYMBOL_GPL(debugfs_create_symlink);

View File

@ -102,7 +102,7 @@ struct pts_fs_info {
struct ida allocated_ptys; struct ida allocated_ptys;
struct pts_mount_opts mount_opts; struct pts_mount_opts mount_opts;
struct super_block *sb; struct super_block *sb;
struct dentry *ptmx_dentry; struct inode *ptmx_inode; // borrowed
}; };
static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb) static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb)
@ -259,7 +259,6 @@ static int devpts_parse_param(struct fs_context *fc, struct fs_parameter *param)
static int mknod_ptmx(struct super_block *sb, struct fs_context *fc) static int mknod_ptmx(struct super_block *sb, struct fs_context *fc)
{ {
int mode; int mode;
int rc = -ENOMEM;
struct dentry *dentry; struct dentry *dentry;
struct inode *inode; struct inode *inode;
struct dentry *root = sb->s_root; struct dentry *root = sb->s_root;
@ -268,18 +267,10 @@ static int mknod_ptmx(struct super_block *sb, struct fs_context *fc)
kuid_t ptmx_uid = current_fsuid(); kuid_t ptmx_uid = current_fsuid();
kgid_t ptmx_gid = current_fsgid(); kgid_t ptmx_gid = current_fsgid();
inode_lock(d_inode(root)); dentry = simple_start_creating(root, "ptmx");
if (IS_ERR(dentry)) {
/* If we have already created ptmx node, return */
if (fsi->ptmx_dentry) {
rc = 0;
goto out;
}
dentry = d_alloc_name(root, "ptmx");
if (!dentry) {
pr_err("Unable to alloc dentry for ptmx node\n"); pr_err("Unable to alloc dentry for ptmx node\n");
goto out; return PTR_ERR(dentry);
} }
/* /*
@ -287,9 +278,9 @@ static int mknod_ptmx(struct super_block *sb, struct fs_context *fc)
*/ */
inode = new_inode(sb); inode = new_inode(sb);
if (!inode) { if (!inode) {
simple_done_creating(dentry);
pr_err("Unable to alloc inode for ptmx node\n"); pr_err("Unable to alloc inode for ptmx node\n");
dput(dentry); return -ENOMEM;
goto out;
} }
inode->i_ino = 2; inode->i_ino = 2;
@ -299,23 +290,18 @@ static int mknod_ptmx(struct super_block *sb, struct fs_context *fc)
init_special_inode(inode, mode, MKDEV(TTYAUX_MAJOR, 2)); init_special_inode(inode, mode, MKDEV(TTYAUX_MAJOR, 2));
inode->i_uid = ptmx_uid; inode->i_uid = ptmx_uid;
inode->i_gid = ptmx_gid; inode->i_gid = ptmx_gid;
fsi->ptmx_inode = inode;
d_add(dentry, inode); d_make_persistent(dentry, inode);
fsi->ptmx_dentry = dentry; simple_done_creating(dentry);
rc = 0;
out: return 0;
inode_unlock(d_inode(root));
return rc;
} }
static void update_ptmx_mode(struct pts_fs_info *fsi) static void update_ptmx_mode(struct pts_fs_info *fsi)
{ {
struct inode *inode; fsi->ptmx_inode->i_mode = S_IFCHR|fsi->mount_opts.ptmxmode;
if (fsi->ptmx_dentry) {
inode = d_inode(fsi->ptmx_dentry);
inode->i_mode = S_IFCHR|fsi->mount_opts.ptmxmode;
}
} }
static int devpts_reconfigure(struct fs_context *fc) static int devpts_reconfigure(struct fs_context *fc)
@ -461,7 +447,7 @@ static void devpts_kill_sb(struct super_block *sb)
if (fsi) if (fsi)
ida_destroy(&fsi->allocated_ptys); ida_destroy(&fsi->allocated_ptys);
kfree(fsi); kfree(fsi);
kill_litter_super(sb); kill_anon_super(sb);
} }
static struct file_system_type devpts_fs_type = { static struct file_system_type devpts_fs_type = {
@ -534,16 +520,15 @@ struct dentry *devpts_pty_new(struct pts_fs_info *fsi, int index, void *priv)
sprintf(s, "%d", index); sprintf(s, "%d", index);
dentry = d_alloc_name(root, s); dentry = d_alloc_name(root, s);
if (dentry) { if (!dentry) {
dentry->d_fsdata = priv;
d_add(dentry, inode);
fsnotify_create(d_inode(root), dentry);
} else {
iput(inode); iput(inode);
dentry = ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
dentry->d_fsdata = priv;
return dentry; d_make_persistent(dentry, inode);
fsnotify_create(d_inode(root), dentry);
dput(dentry);
return dentry; // borrowed
} }
/** /**
@ -573,7 +558,7 @@ void devpts_pty_kill(struct dentry *dentry)
drop_nlink(dentry->d_inode); drop_nlink(dentry->d_inode);
d_drop(dentry); d_drop(dentry);
fsnotify_unlink(d_inode(dentry->d_parent), dentry); fsnotify_unlink(d_inode(dentry->d_parent), dentry);
dput(dentry); /* d_alloc_name() in devpts_pty_new() */ d_make_discardable(dentry);
} }
static int __init init_devpts_fs(void) static int __init init_devpts_fs(void)

View File

@ -113,8 +113,7 @@ static int efivarfs_create(struct mnt_idmap *idmap, struct inode *dir,
inode->i_private = var; inode->i_private = var;
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
dget(dentry);
return 0; return 0;
} }
@ -126,9 +125,7 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
if (efivar_entry_delete(var)) if (efivar_entry_delete(var))
return -EINVAL; return -EINVAL;
drop_nlink(d_inode(dentry)); return simple_unlink(dir, dentry);
dput(dentry);
return 0;
}; };
const struct inode_operations efivarfs_dir_inode_operations = { const struct inode_operations efivarfs_dir_inode_operations = {

View File

@ -278,7 +278,8 @@ static int efivarfs_create_dentry(struct super_block *sb, efi_char16_t *name16,
inode->i_private = entry; inode->i_private = entry;
i_size_write(inode, size + sizeof(__u32)); /* attributes + data */ i_size_write(inode, size + sizeof(__u32)); /* attributes + data */
inode_unlock(inode); inode_unlock(inode);
d_add(dentry, inode); d_make_persistent(dentry, inode);
dput(dentry);
return 0; return 0;
@ -522,7 +523,7 @@ static void efivarfs_kill_sb(struct super_block *sb)
struct efivarfs_fs_info *sfi = sb->s_fs_info; struct efivarfs_fs_info *sfi = sb->s_fs_info;
blocking_notifier_chain_unregister(&efivar_ops_nh, &sfi->nb); blocking_notifier_chain_unregister(&efivar_ops_nh, &sfi->nb);
kill_litter_super(sb); kill_anon_super(sb);
kfree(sfi); kfree(sfi);
} }

View File

@ -205,8 +205,7 @@ static const struct file_operations fuse_conn_congestion_threshold_ops = {
static struct dentry *fuse_ctl_add_dentry(struct dentry *parent, static struct dentry *fuse_ctl_add_dentry(struct dentry *parent,
struct fuse_conn *fc, struct fuse_conn *fc,
const char *name, const char *name, int mode,
int mode, int nlink,
const struct inode_operations *iop, const struct inode_operations *iop,
const struct file_operations *fop) const struct file_operations *fop)
{ {
@ -232,10 +231,19 @@ static struct dentry *fuse_ctl_add_dentry(struct dentry *parent,
if (iop) if (iop)
inode->i_op = iop; inode->i_op = iop;
inode->i_fop = fop; inode->i_fop = fop;
set_nlink(inode, nlink); if (S_ISDIR(mode)) {
inc_nlink(d_inode(parent));
inc_nlink(inode);
}
inode->i_private = fc; inode->i_private = fc;
d_add(dentry, inode); d_make_persistent(dentry, inode);
dput(dentry);
/*
* We are returning a borrowed reference here - it's only good while
* fuse_mutex is held. Actually it's d_make_persistent() return
* value...
*/
return dentry; return dentry;
} }
@ -252,22 +260,21 @@ int fuse_ctl_add_conn(struct fuse_conn *fc)
return 0; return 0;
parent = fuse_control_sb->s_root; parent = fuse_control_sb->s_root;
inc_nlink(d_inode(parent));
sprintf(name, "%u", fc->dev); sprintf(name, "%u", fc->dev);
parent = fuse_ctl_add_dentry(parent, fc, name, S_IFDIR | 0500, 2, parent = fuse_ctl_add_dentry(parent, fc, name, S_IFDIR | 0500,
&simple_dir_inode_operations, &simple_dir_inode_operations,
&simple_dir_operations); &simple_dir_operations);
if (!parent) if (!parent)
goto err; goto err;
if (!fuse_ctl_add_dentry(parent, fc, "waiting", S_IFREG | 0400, 1, if (!fuse_ctl_add_dentry(parent, fc, "waiting", S_IFREG | 0400,
NULL, &fuse_ctl_waiting_ops) || NULL, &fuse_ctl_waiting_ops) ||
!fuse_ctl_add_dentry(parent, fc, "abort", S_IFREG | 0200, 1, !fuse_ctl_add_dentry(parent, fc, "abort", S_IFREG | 0200,
NULL, &fuse_ctl_abort_ops) || NULL, &fuse_ctl_abort_ops) ||
!fuse_ctl_add_dentry(parent, fc, "max_background", S_IFREG | 0600, !fuse_ctl_add_dentry(parent, fc, "max_background", S_IFREG | 0600,
1, NULL, &fuse_conn_max_background_ops) || NULL, &fuse_conn_max_background_ops) ||
!fuse_ctl_add_dentry(parent, fc, "congestion_threshold", !fuse_ctl_add_dentry(parent, fc, "congestion_threshold",
S_IFREG | 0600, 1, NULL, S_IFREG | 0600, NULL,
&fuse_conn_congestion_threshold_ops)) &fuse_conn_congestion_threshold_ops))
goto err; goto err;
@ -289,18 +296,13 @@ static void remove_one(struct dentry *dentry)
*/ */
void fuse_ctl_remove_conn(struct fuse_conn *fc) void fuse_ctl_remove_conn(struct fuse_conn *fc)
{ {
struct dentry *dentry;
char name[32]; char name[32];
if (!fuse_control_sb || fc->no_control) if (!fuse_control_sb || fc->no_control)
return; return;
sprintf(name, "%u", fc->dev); sprintf(name, "%u", fc->dev);
dentry = lookup_noperm_positive_unlocked(&QSTR(name), fuse_control_sb->s_root); simple_remove_by_name(fuse_control_sb->s_root, name, remove_one);
if (!IS_ERR(dentry)) {
simple_recursive_removal(dentry, remove_one);
dput(dentry); // paired with lookup_noperm_positive_unlocked()
}
} }
static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fsc) static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fsc)
@ -350,7 +352,7 @@ static void fuse_ctl_kill_sb(struct super_block *sb)
fuse_control_sb = NULL; fuse_control_sb = NULL;
mutex_unlock(&fuse_mutex); mutex_unlock(&fuse_mutex);
kill_litter_super(sb); kill_anon_super(sb);
} }
static struct file_system_type fuse_ctl_fs_type = { static struct file_system_type fuse_ctl_fs_type = {

View File

@ -994,8 +994,7 @@ static int hugetlbfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
if (!inode) if (!inode)
return -ENOSPC; return -ENOSPC;
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
dget(dentry);/* Extra count - pin the dentry in core */
return 0; return 0;
} }
@ -1042,10 +1041,9 @@ static int hugetlbfs_symlink(struct mnt_idmap *idmap,
if (inode) { if (inode) {
int l = strlen(symname)+1; int l = strlen(symname)+1;
error = page_symlink(inode, symname, l); error = page_symlink(inode, symname, l);
if (!error) { if (!error)
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
dget(dentry); else
} else
iput(inode); iput(inode);
} }
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
@ -1502,7 +1500,7 @@ static struct file_system_type hugetlbfs_fs_type = {
.name = "hugetlbfs", .name = "hugetlbfs",
.init_fs_context = hugetlbfs_init_fs_context, .init_fs_context = hugetlbfs_init_fs_context,
.parameters = hugetlb_fs_parameters, .parameters = hugetlb_fs_parameters,
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
.fs_flags = FS_ALLOW_IDMAP, .fs_flags = FS_ALLOW_IDMAP,
}; };

View File

@ -230,7 +230,6 @@ extern void shrink_dcache_for_umount(struct super_block *);
extern struct dentry *__d_lookup(const struct dentry *, const struct qstr *); extern struct dentry *__d_lookup(const struct dentry *, const struct qstr *);
extern struct dentry *__d_lookup_rcu(const struct dentry *parent, extern struct dentry *__d_lookup_rcu(const struct dentry *parent,
const struct qstr *name, unsigned *seq); const struct qstr *name, unsigned *seq);
extern void d_genocide(struct dentry *);
/* /*
* pipe.c * pipe.c

View File

@ -630,7 +630,7 @@ static void __simple_recursive_removal(struct dentry *dentry,
if (callback) if (callback)
callback(victim); callback(victim);
fsnotify_delete(inode, d_inode(victim), victim); fsnotify_delete(inode, d_inode(victim), victim);
dput(victim); // unpin it d_make_discardable(victim);
} }
if (victim == dentry) { if (victim == dentry) {
inode_set_mtime_to_ts(inode, inode_set_mtime_to_ts(inode,
@ -655,6 +655,19 @@ void simple_recursive_removal(struct dentry *dentry,
} }
EXPORT_SYMBOL(simple_recursive_removal); EXPORT_SYMBOL(simple_recursive_removal);
void simple_remove_by_name(struct dentry *parent, const char *name,
void (*callback)(struct dentry *))
{
struct dentry *dentry;
dentry = lookup_noperm_positive_unlocked(&QSTR(name), parent);
if (!IS_ERR(dentry)) {
simple_recursive_removal(dentry, callback);
dput(dentry); // paired with lookup_noperm_positive_unlocked()
}
}
EXPORT_SYMBOL(simple_remove_by_name);
/* caller holds parent directory with I_MUTEX_PARENT */ /* caller holds parent directory with I_MUTEX_PARENT */
void locked_recursive_removal(struct dentry *dentry, void locked_recursive_removal(struct dentry *dentry,
void (*callback)(struct dentry *)) void (*callback)(struct dentry *))
@ -752,8 +765,7 @@ int simple_link(struct dentry *old_dentry, struct inode *dir, struct dentry *den
inode_set_ctime_to_ts(dir, inode_set_ctime_current(inode))); inode_set_ctime_to_ts(dir, inode_set_ctime_current(inode)));
inc_nlink(inode); inc_nlink(inode);
ihold(inode); ihold(inode);
dget(dentry); d_make_persistent(dentry, inode);
d_instantiate(dentry, inode);
return 0; return 0;
} }
EXPORT_SYMBOL(simple_link); EXPORT_SYMBOL(simple_link);
@ -779,14 +791,28 @@ int simple_empty(struct dentry *dentry)
} }
EXPORT_SYMBOL(simple_empty); EXPORT_SYMBOL(simple_empty);
int simple_unlink(struct inode *dir, struct dentry *dentry) void __simple_unlink(struct inode *dir, struct dentry *dentry)
{ {
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
inode_set_mtime_to_ts(dir, inode_set_mtime_to_ts(dir,
inode_set_ctime_to_ts(dir, inode_set_ctime_current(inode))); inode_set_ctime_to_ts(dir, inode_set_ctime_current(inode)));
drop_nlink(inode); drop_nlink(inode);
dput(dentry); }
EXPORT_SYMBOL(__simple_unlink);
void __simple_rmdir(struct inode *dir, struct dentry *dentry)
{
drop_nlink(d_inode(dentry));
__simple_unlink(dir, dentry);
drop_nlink(dir);
}
EXPORT_SYMBOL(__simple_rmdir);
int simple_unlink(struct inode *dir, struct dentry *dentry)
{
__simple_unlink(dir, dentry);
d_make_discardable(dentry);
return 0; return 0;
} }
EXPORT_SYMBOL(simple_unlink); EXPORT_SYMBOL(simple_unlink);
@ -796,9 +822,8 @@ int simple_rmdir(struct inode *dir, struct dentry *dentry)
if (!simple_empty(dentry)) if (!simple_empty(dentry))
return -ENOTEMPTY; return -ENOTEMPTY;
drop_nlink(d_inode(dentry)); __simple_rmdir(dir, dentry);
simple_unlink(dir, dentry); d_make_discardable(dentry);
drop_nlink(dir);
return 0; return 0;
} }
EXPORT_SYMBOL(simple_rmdir); EXPORT_SYMBOL(simple_rmdir);
@ -1066,7 +1091,8 @@ int simple_fill_super(struct super_block *s, unsigned long magic,
simple_inode_init_ts(inode); simple_inode_init_ts(inode);
inode->i_fop = files->ops; inode->i_fop = files->ops;
inode->i_ino = i; inode->i_ino = i;
d_add(dentry, inode); d_make_persistent(dentry, inode);
dput(dentry);
} }
return 0; return 0;
} }
@ -2312,3 +2338,11 @@ struct dentry *simple_start_creating(struct dentry *parent, const char *name)
return start_dirop(parent, &qname, LOOKUP_CREATE | LOOKUP_EXCL); return start_dirop(parent, &qname, LOOKUP_CREATE | LOOKUP_EXCL);
} }
EXPORT_SYMBOL(simple_start_creating); EXPORT_SYMBOL(simple_start_creating);
/* parent must have been held exclusive since simple_start_creating() */
void simple_done_creating(struct dentry *child)
{
inode_unlock(child->d_parent->d_inode);
dput(child);
}
EXPORT_SYMBOL(simple_done_creating);

View File

@ -1137,11 +1137,11 @@ static struct dentry *nfsd_mkdir(struct dentry *parent, struct nfsdfs_client *nc
inode->i_private = ncl; inode->i_private = ncl;
kref_get(&ncl->cl_ref); kref_get(&ncl->cl_ref);
} }
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
inc_nlink(dir); inc_nlink(dir);
fsnotify_mkdir(dir, dentry); fsnotify_mkdir(dir, dentry);
inode_unlock(dir); simple_done_creating(dentry);
return dentry; return dentry; // borrowed
} }
#if IS_ENABLED(CONFIG_SUNRPC_GSS) #if IS_ENABLED(CONFIG_SUNRPC_GSS)
@ -1170,9 +1170,9 @@ static void _nfsd_symlink(struct dentry *parent, const char *name,
inode->i_link = (char *)content; inode->i_link = (char *)content;
inode->i_size = strlen(content); inode->i_size = strlen(content);
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
fsnotify_create(dir, dentry); fsnotify_create(dir, dentry);
inode_unlock(dir); simple_done_creating(dentry);
} }
#else #else
static inline void _nfsd_symlink(struct dentry *parent, const char *name, static inline void _nfsd_symlink(struct dentry *parent, const char *name,
@ -1228,11 +1228,11 @@ static int nfsdfs_create_files(struct dentry *root,
kref_get(&ncl->cl_ref); kref_get(&ncl->cl_ref);
inode->i_fop = files->ops; inode->i_fop = files->ops;
inode->i_private = ncl; inode->i_private = ncl;
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
fsnotify_create(dir, dentry); fsnotify_create(dir, dentry);
if (fdentries) if (fdentries)
fdentries[i] = dentry; fdentries[i] = dentry; // borrowed
inode_unlock(dir); simple_done_creating(dentry);
} }
return 0; return 0;
} }
@ -1346,7 +1346,7 @@ static void nfsd_umount(struct super_block *sb)
nfsd_shutdown_threads(net); nfsd_shutdown_threads(net);
kill_litter_super(sb); kill_anon_super(sb);
put_net(net); put_net(net);
} }

View File

@ -441,8 +441,7 @@ static struct dentry *dlmfs_mkdir(struct mnt_idmap * idmap,
ip->ip_conn = conn; ip->ip_conn = conn;
inc_nlink(dir); inc_nlink(dir);
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
dget(dentry); /* Extra count - pin the dentry in core */
status = 0; status = 0;
bail: bail:
@ -480,8 +479,7 @@ static int dlmfs_create(struct mnt_idmap *idmap,
goto bail; goto bail;
} }
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
dget(dentry); /* Extra count - pin the dentry in core */
bail: bail:
return status; return status;
} }
@ -574,7 +572,7 @@ static int dlmfs_init_fs_context(struct fs_context *fc)
static struct file_system_type dlmfs_fs_type = { static struct file_system_type dlmfs_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "ocfs2_dlmfs", .name = "ocfs2_dlmfs",
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
.init_fs_context = dlmfs_init_fs_context, .init_fs_context = dlmfs_init_fs_context,
}; };
MODULE_ALIAS_FS("ocfs2_dlmfs"); MODULE_ALIAS_FS("ocfs2_dlmfs");

View File

@ -3578,14 +3578,12 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx)
return 0; return 0;
if (pos == TGID_OFFSET - 2) { if (pos == TGID_OFFSET - 2) {
struct inode *inode = d_inode(fs_info->proc_self); if (!dir_emit(ctx, "self", 4, self_inum, DT_LNK))
if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK))
return 0; return 0;
ctx->pos = pos = pos + 1; ctx->pos = pos = pos + 1;
} }
if (pos == TGID_OFFSET - 1) { if (pos == TGID_OFFSET - 1) {
struct inode *inode = d_inode(fs_info->proc_thread_self); if (!dir_emit(ctx, "thread-self", 11, thread_self_inum, DT_LNK))
if (!dir_emit(ctx, "thread-self", 11, inode->i_ino, DT_LNK))
return 0; return 0;
ctx->pos = pos = pos + 1; ctx->pos = pos = pos + 1;
} }

View File

@ -373,6 +373,7 @@ static inline void proc_tty_init(void) {}
extern struct proc_dir_entry proc_root; extern struct proc_dir_entry proc_root;
extern void proc_self_init(void); extern void proc_self_init(void);
extern unsigned self_inum, thread_self_inum;
/* /*
* task_[no]mmu.c * task_[no]mmu.c

View File

@ -347,18 +347,12 @@ static void proc_kill_sb(struct super_block *sb)
{ {
struct proc_fs_info *fs_info = proc_sb_info(sb); struct proc_fs_info *fs_info = proc_sb_info(sb);
if (!fs_info) {
kill_anon_super(sb);
return;
}
dput(fs_info->proc_self);
dput(fs_info->proc_thread_self);
kill_anon_super(sb); kill_anon_super(sb);
if (fs_info) {
put_pid_ns(fs_info->pid_ns); put_pid_ns(fs_info->pid_ns);
kfree_rcu(fs_info, rcu); kfree_rcu(fs_info, rcu);
} }
}
static struct file_system_type proc_fs_type = { static struct file_system_type proc_fs_type = {
.name = "proc", .name = "proc",

View File

@ -31,12 +31,11 @@ static const struct inode_operations proc_self_inode_operations = {
.get_link = proc_self_get_link, .get_link = proc_self_get_link,
}; };
static unsigned self_inum __ro_after_init; unsigned self_inum __ro_after_init;
int proc_setup_self(struct super_block *s) int proc_setup_self(struct super_block *s)
{ {
struct inode *root_inode = d_inode(s->s_root); struct inode *root_inode = d_inode(s->s_root);
struct proc_fs_info *fs_info = proc_sb_info(s);
struct dentry *self; struct dentry *self;
int ret = -ENOMEM; int ret = -ENOMEM;
@ -51,18 +50,15 @@ int proc_setup_self(struct super_block *s)
inode->i_uid = GLOBAL_ROOT_UID; inode->i_uid = GLOBAL_ROOT_UID;
inode->i_gid = GLOBAL_ROOT_GID; inode->i_gid = GLOBAL_ROOT_GID;
inode->i_op = &proc_self_inode_operations; inode->i_op = &proc_self_inode_operations;
d_add(self, inode); d_make_persistent(self, inode);
ret = 0; ret = 0;
} else {
dput(self);
} }
dput(self);
} }
inode_unlock(root_inode); inode_unlock(root_inode);
if (ret) if (ret)
pr_err("proc_fill_super: can't allocate /proc/self\n"); pr_err("proc_fill_super: can't allocate /proc/self\n");
else
fs_info->proc_self = self;
return ret; return ret;
} }

View File

@ -31,12 +31,11 @@ static const struct inode_operations proc_thread_self_inode_operations = {
.get_link = proc_thread_self_get_link, .get_link = proc_thread_self_get_link,
}; };
static unsigned thread_self_inum __ro_after_init; unsigned thread_self_inum __ro_after_init;
int proc_setup_thread_self(struct super_block *s) int proc_setup_thread_self(struct super_block *s)
{ {
struct inode *root_inode = d_inode(s->s_root); struct inode *root_inode = d_inode(s->s_root);
struct proc_fs_info *fs_info = proc_sb_info(s);
struct dentry *thread_self; struct dentry *thread_self;
int ret = -ENOMEM; int ret = -ENOMEM;
@ -51,19 +50,15 @@ int proc_setup_thread_self(struct super_block *s)
inode->i_uid = GLOBAL_ROOT_UID; inode->i_uid = GLOBAL_ROOT_UID;
inode->i_gid = GLOBAL_ROOT_GID; inode->i_gid = GLOBAL_ROOT_GID;
inode->i_op = &proc_thread_self_inode_operations; inode->i_op = &proc_thread_self_inode_operations;
d_add(thread_self, inode); d_make_persistent(thread_self, inode);
ret = 0; ret = 0;
} else {
dput(thread_self);
} }
dput(thread_self);
} }
inode_unlock(root_inode); inode_unlock(root_inode);
if (ret) if (ret)
pr_err("proc_fill_super: can't allocate /proc/thread-self\n"); pr_err("proc_fill_super: can't allocate /proc/thread-self\n");
else
fs_info->proc_thread_self = thread_self;
return ret; return ret;
} }

View File

@ -373,7 +373,7 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
if (!dentry) if (!dentry)
return -ENOMEM; return -ENOMEM;
private->dentry = dentry; private->dentry = dentry; // borrowed
private->record = record; private->record = record;
inode->i_size = private->total_size = size; inode->i_size = private->total_size = size;
inode->i_private = private; inode->i_private = private;
@ -382,7 +382,8 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
inode_set_mtime_to_ts(inode, inode_set_mtime_to_ts(inode,
inode_set_ctime_to_ts(inode, record->time)); inode_set_ctime_to_ts(inode, record->time));
d_add(dentry, no_free_ptr(inode)); d_make_persistent(dentry, no_free_ptr(inode));
dput(dentry);
list_add(&(no_free_ptr(private))->list, &records_list); list_add(&(no_free_ptr(private))->list, &records_list);
@ -465,7 +466,7 @@ static void pstore_kill_sb(struct super_block *sb)
guard(mutex)(&pstore_sb_lock); guard(mutex)(&pstore_sb_lock);
WARN_ON(pstore_sb && pstore_sb != sb); WARN_ON(pstore_sb && pstore_sb != sb);
kill_litter_super(sb); kill_anon_super(sb);
pstore_sb = NULL; pstore_sb = NULL;
guard(mutex)(&records_list_lock); guard(mutex)(&records_list_lock);

View File

@ -110,8 +110,7 @@ ramfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
goto out; goto out;
} }
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
dget(dentry); /* Extra count - pin the dentry in core */
error = 0; error = 0;
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
} }
@ -154,8 +153,7 @@ static int ramfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
error = page_symlink(inode, symname, l); error = page_symlink(inode, symname, l);
if (!error) { if (!error) {
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
dget(dentry);
inode_set_mtime_to_ts(dir, inode_set_mtime_to_ts(dir,
inode_set_ctime_current(dir)); inode_set_ctime_current(dir));
} else } else
@ -313,7 +311,7 @@ int ramfs_init_fs_context(struct fs_context *fc)
void ramfs_kill_sb(struct super_block *sb) void ramfs_kill_sb(struct super_block *sb)
{ {
kfree(sb->s_fs_info); kfree(sb->s_fs_info);
kill_litter_super(sb); kill_anon_super(sb);
} }
static struct file_system_type ramfs_fs_type = { static struct file_system_type ramfs_fs_type = {

View File

@ -1292,14 +1292,6 @@ void kill_anon_super(struct super_block *sb)
} }
EXPORT_SYMBOL(kill_anon_super); EXPORT_SYMBOL(kill_anon_super);
void kill_litter_super(struct super_block *sb)
{
if (sb->s_root)
d_genocide(sb->s_root);
kill_anon_super(sb);
}
EXPORT_SYMBOL(kill_litter_super);
int set_anon_super_fc(struct super_block *sb, struct fs_context *fc) int set_anon_super_fc(struct super_block *sb, struct fs_context *fc)
{ {
return set_anon_super(sb, NULL); return set_anon_super(sb, NULL);

View File

@ -757,7 +757,7 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
const struct eventfs_entry *entries, const struct eventfs_entry *entries,
int size, void *data) int size, void *data)
{ {
struct dentry *dentry = tracefs_start_creating(name, parent); struct dentry *dentry;
struct eventfs_root_inode *rei; struct eventfs_root_inode *rei;
struct eventfs_inode *ei; struct eventfs_inode *ei;
struct tracefs_inode *ti; struct tracefs_inode *ti;
@ -768,6 +768,7 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
if (security_locked_down(LOCKDOWN_TRACEFS)) if (security_locked_down(LOCKDOWN_TRACEFS))
return NULL; return NULL;
dentry = tracefs_start_creating(name, parent);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return ERR_CAST(dentry); return ERR_CAST(dentry);
@ -822,7 +823,7 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
* something not worth much. Keeping directory links at 1 * something not worth much. Keeping directory links at 1
* tells userspace not to trust the link number. * tells userspace not to trust the link number.
*/ */
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
/* The dentry of the "events" parent does keep track though */ /* The dentry of the "events" parent does keep track though */
inc_nlink(dentry->d_parent->d_inode); inc_nlink(dentry->d_parent->d_inode);
fsnotify_mkdir(dentry->d_parent->d_inode, dentry); fsnotify_mkdir(dentry->d_parent->d_inode, dentry);
@ -909,5 +910,5 @@ void eventfs_remove_events_dir(struct eventfs_inode *ei)
* and destroyed dynamically. * and destroyed dynamically.
*/ */
d_invalidate(dentry); d_invalidate(dentry);
dput(dentry); d_make_discardable(dentry);
} }

View File

@ -538,7 +538,7 @@ static struct file_system_type trace_fs_type = {
.name = "tracefs", .name = "tracefs",
.init_fs_context = tracefs_init_fs_context, .init_fs_context = tracefs_init_fs_context,
.parameters = tracefs_param_specs, .parameters = tracefs_param_specs,
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
}; };
MODULE_ALIAS_FS("tracefs"); MODULE_ALIAS_FS("tracefs");
@ -571,16 +571,15 @@ struct dentry *tracefs_start_creating(const char *name, struct dentry *parent)
struct dentry *tracefs_failed_creating(struct dentry *dentry) struct dentry *tracefs_failed_creating(struct dentry *dentry)
{ {
inode_unlock(d_inode(dentry->d_parent)); simple_done_creating(dentry);
dput(dentry);
simple_release_fs(&tracefs_mount, &tracefs_mount_count); simple_release_fs(&tracefs_mount, &tracefs_mount_count);
return NULL; return NULL;
} }
struct dentry *tracefs_end_creating(struct dentry *dentry) struct dentry *tracefs_end_creating(struct dentry *dentry)
{ {
inode_unlock(d_inode(dentry->d_parent)); simple_done_creating(dentry);
return dentry; return dentry; // borrowed
} }
/* Find the inode that this will use for default */ /* Find the inode that this will use for default */
@ -661,7 +660,7 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode,
inode->i_private = data; inode->i_private = data;
inode->i_uid = d_inode(dentry->d_parent)->i_uid; inode->i_uid = d_inode(dentry->d_parent)->i_uid;
inode->i_gid = d_inode(dentry->d_parent)->i_gid; inode->i_gid = d_inode(dentry->d_parent)->i_gid;
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
fsnotify_create(d_inode(dentry->d_parent), dentry); fsnotify_create(d_inode(dentry->d_parent), dentry);
return tracefs_end_creating(dentry); return tracefs_end_creating(dentry);
} }
@ -692,7 +691,7 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent,
/* directory inodes start off with i_nlink == 2 (for "." entry) */ /* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode); inc_nlink(inode);
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
inc_nlink(d_inode(dentry->d_parent)); inc_nlink(d_inode(dentry->d_parent));
fsnotify_mkdir(d_inode(dentry->d_parent), dentry); fsnotify_mkdir(d_inode(dentry->d_parent), dentry);
return tracefs_end_creating(dentry); return tracefs_end_creating(dentry);

View File

@ -198,7 +198,6 @@ enum dentry_flags {
DCACHE_REFERENCED = BIT(6), /* Recently used, don't discard. */ DCACHE_REFERENCED = BIT(6), /* Recently used, don't discard. */
DCACHE_DONTCACHE = BIT(7), /* Purge from memory on final dput() */ DCACHE_DONTCACHE = BIT(7), /* Purge from memory on final dput() */
DCACHE_CANT_MOUNT = BIT(8), DCACHE_CANT_MOUNT = BIT(8),
DCACHE_GENOCIDE = BIT(9),
DCACHE_SHRINK_LIST = BIT(10), DCACHE_SHRINK_LIST = BIT(10),
DCACHE_OP_WEAK_REVALIDATE = BIT(11), DCACHE_OP_WEAK_REVALIDATE = BIT(11),
/* /*
@ -225,6 +224,7 @@ enum dentry_flags {
DCACHE_PAR_LOOKUP = BIT(24), /* being looked up (with parent locked shared) */ DCACHE_PAR_LOOKUP = BIT(24), /* being looked up (with parent locked shared) */
DCACHE_DENTRY_CURSOR = BIT(25), DCACHE_DENTRY_CURSOR = BIT(25),
DCACHE_NORCU = BIT(26), /* No RCU delay for freeing */ DCACHE_NORCU = BIT(26), /* No RCU delay for freeing */
DCACHE_PERSISTENT = BIT(27)
}; };
#define DCACHE_MANAGED_DENTRY \ #define DCACHE_MANAGED_DENTRY \
@ -610,5 +610,7 @@ static inline struct dentry *d_next_sibling(const struct dentry *dentry)
} }
void set_default_d_op(struct super_block *, const struct dentry_operations *); void set_default_d_op(struct super_block *, const struct dentry_operations *);
struct dentry *d_make_persistent(struct dentry *, struct inode *);
void d_make_discardable(struct dentry *dentry);
#endif /* __LINUX_DCACHE_H */ #endif /* __LINUX_DCACHE_H */

View File

@ -2310,7 +2310,6 @@ void retire_super(struct super_block *sb);
void generic_shutdown_super(struct super_block *sb); void generic_shutdown_super(struct super_block *sb);
void kill_block_super(struct super_block *sb); void kill_block_super(struct super_block *sb);
void kill_anon_super(struct super_block *sb); void kill_anon_super(struct super_block *sb);
void kill_litter_super(struct super_block *sb);
void deactivate_super(struct super_block *sb); void deactivate_super(struct super_block *sb);
void deactivate_locked_super(struct super_block *sb); void deactivate_locked_super(struct super_block *sb);
int set_anon_super(struct super_block *s, void *data); int set_anon_super(struct super_block *s, void *data);
@ -3191,6 +3190,8 @@ extern int simple_open(struct inode *inode, struct file *file);
extern int simple_link(struct dentry *, struct inode *, struct dentry *); extern int simple_link(struct dentry *, struct inode *, struct dentry *);
extern int simple_unlink(struct inode *, struct dentry *); extern int simple_unlink(struct inode *, struct dentry *);
extern int simple_rmdir(struct inode *, struct dentry *); extern int simple_rmdir(struct inode *, struct dentry *);
extern void __simple_unlink(struct inode *, struct dentry *);
extern void __simple_rmdir(struct inode *, struct dentry *);
void simple_rename_timestamp(struct inode *old_dir, struct dentry *old_dentry, void simple_rename_timestamp(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry); struct inode *new_dir, struct dentry *new_dentry);
extern int simple_rename_exchange(struct inode *old_dir, struct dentry *old_dentry, extern int simple_rename_exchange(struct inode *old_dir, struct dentry *old_dentry,
@ -3200,6 +3201,8 @@ extern int simple_rename(struct mnt_idmap *, struct inode *,
unsigned int); unsigned int);
extern void simple_recursive_removal(struct dentry *, extern void simple_recursive_removal(struct dentry *,
void (*callback)(struct dentry *)); void (*callback)(struct dentry *));
extern void simple_remove_by_name(struct dentry *, const char *,
void (*callback)(struct dentry *));
extern void locked_recursive_removal(struct dentry *, extern void locked_recursive_removal(struct dentry *,
void (*callback)(struct dentry *)); void (*callback)(struct dentry *));
extern int noop_fsync(struct file *, loff_t, loff_t, int); extern int noop_fsync(struct file *, loff_t, loff_t, int);
@ -3229,6 +3232,7 @@ extern int simple_fill_super(struct super_block *, unsigned long,
extern int simple_pin_fs(struct file_system_type *, struct vfsmount **mount, int *count); extern int simple_pin_fs(struct file_system_type *, struct vfsmount **mount, int *count);
extern void simple_release_fs(struct vfsmount **mount, int *count); extern void simple_release_fs(struct vfsmount **mount, int *count);
struct dentry *simple_start_creating(struct dentry *, const char *); struct dentry *simple_start_creating(struct dentry *, const char *);
void simple_done_creating(struct dentry *);
extern ssize_t simple_read_from_buffer(void __user *to, size_t count, extern ssize_t simple_read_from_buffer(void __user *to, size_t count,
loff_t *ppos, const void *from, size_t available); loff_t *ppos, const void *from, size_t available);

View File

@ -66,8 +66,6 @@ enum proc_pidonly {
struct proc_fs_info { struct proc_fs_info {
struct pid_namespace *pid_ns; struct pid_namespace *pid_ns;
struct dentry *proc_self; /* For /proc/self */
struct dentry *proc_thread_self; /* For /proc/thread-self */
kgid_t pid_gid; kgid_t pid_gid;
enum proc_hidepid hide_pid; enum proc_hidepid hide_pid;
enum proc_pidonly pidonly; enum proc_pidonly pidonly;

View File

@ -2257,8 +2257,6 @@ static inline void securityfs_remove(struct dentry *dentry)
#endif #endif
#define securityfs_recursive_remove securityfs_remove
#ifdef CONFIG_BPF_SYSCALL #ifdef CONFIG_BPF_SYSCALL
union bpf_attr; union bpf_attr;
struct bpf_map; struct bpf_map;

View File

@ -508,7 +508,7 @@ static int rootfs_init_fs_context(struct fs_context *fc)
struct file_system_type rootfs_fs_type = { struct file_system_type rootfs_fs_type = {
.name = "rootfs", .name = "rootfs",
.init_fs_context = rootfs_init_fs_context, .init_fs_context = rootfs_init_fs_context,
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
}; };
void __init init_rootfs(void) void __init init_rootfs(void)

View File

@ -599,8 +599,7 @@ static int mqueue_create_attr(struct dentry *dentry, umode_t mode, void *arg)
dir->i_size += DIRENT_SIZE; dir->i_size += DIRENT_SIZE;
simple_inode_init_ts(dir); simple_inode_init_ts(dir);
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
dget(dentry);
return 0; return 0;
out_unlock: out_unlock:
spin_unlock(&mq_lock); spin_unlock(&mq_lock);
@ -617,13 +616,8 @@ static int mqueue_create(struct mnt_idmap *idmap, struct inode *dir,
static int mqueue_unlink(struct inode *dir, struct dentry *dentry) static int mqueue_unlink(struct inode *dir, struct dentry *dentry)
{ {
struct inode *inode = d_inode(dentry);
simple_inode_init_ts(dir);
dir->i_size -= DIRENT_SIZE; dir->i_size -= DIRENT_SIZE;
drop_nlink(inode); return simple_unlink(dir, dentry);
dput(dentry);
return 0;
} }
/* /*
@ -1625,7 +1619,7 @@ static const struct fs_context_operations mqueue_fs_context_ops = {
static struct file_system_type mqueue_fs_type = { static struct file_system_type mqueue_fs_type = {
.name = "mqueue", .name = "mqueue",
.init_fs_context = mqueue_init_fs_context, .init_fs_context = mqueue_init_fs_context,
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
.fs_flags = FS_USERNS_MOUNT, .fs_flags = FS_USERNS_MOUNT,
}; };

View File

@ -144,8 +144,7 @@ static int bpf_inode_type(const struct inode *inode, enum bpf_type *type)
static void bpf_dentry_finalize(struct dentry *dentry, struct inode *inode, static void bpf_dentry_finalize(struct dentry *dentry, struct inode *inode,
struct inode *dir) struct inode *dir)
{ {
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
dget(dentry);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
} }
@ -420,16 +419,12 @@ static int bpf_iter_link_pin_kernel(struct dentry *parent,
struct dentry *dentry; struct dentry *dentry;
int ret; int ret;
inode_lock(parent->d_inode); dentry = simple_start_creating(parent, name);
dentry = lookup_noperm(&QSTR(name), parent); if (IS_ERR(dentry))
if (IS_ERR(dentry)) {
inode_unlock(parent->d_inode);
return PTR_ERR(dentry); return PTR_ERR(dentry);
}
ret = bpf_mkobj_ops(dentry, mode, link, &bpf_link_iops, ret = bpf_mkobj_ops(dentry, mode, link, &bpf_link_iops,
&bpf_iter_fops); &bpf_iter_fops);
dput(dentry); simple_done_creating(dentry);
inode_unlock(parent->d_inode);
return ret; return ret;
} }
@ -1080,7 +1075,7 @@ static void bpf_kill_super(struct super_block *sb)
{ {
struct bpf_mount_opts *opts = sb->s_fs_info; struct bpf_mount_opts *opts = sb->s_fs_info;
kill_litter_super(sb); kill_anon_super(sb);
kfree(opts); kfree(opts);
} }

View File

@ -3878,12 +3878,7 @@ shmem_mknod(struct mnt_idmap *idmap, struct inode *dir,
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
inode_inc_iversion(dir); inode_inc_iversion(dir);
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir)) d_make_persistent(dentry, inode);
d_add(dentry, inode);
else
d_instantiate(dentry, inode);
dget(dentry); /* Extra count - pin the dentry in core */
return error; return error;
out_iput: out_iput:
@ -3944,7 +3939,7 @@ static int shmem_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *dentry) struct dentry *dentry)
{ {
struct inode *inode = d_inode(old_dentry); struct inode *inode = d_inode(old_dentry);
int ret = 0; int ret;
/* /*
* No ordinary (disk based) filesystem counts links as inodes; * No ordinary (disk based) filesystem counts links as inodes;
@ -3956,29 +3951,19 @@ static int shmem_link(struct dentry *old_dentry, struct inode *dir,
if (inode->i_nlink) { if (inode->i_nlink) {
ret = shmem_reserve_inode(inode->i_sb, NULL); ret = shmem_reserve_inode(inode->i_sb, NULL);
if (ret) if (ret)
goto out; return ret;
} }
ret = simple_offset_add(shmem_get_offset_ctx(dir), dentry); ret = simple_offset_add(shmem_get_offset_ctx(dir), dentry);
if (ret) { if (ret) {
if (inode->i_nlink) if (inode->i_nlink)
shmem_free_inode(inode->i_sb, 0); shmem_free_inode(inode->i_sb, 0);
goto out; return ret;
} }
dir->i_size += BOGO_DIRENT_SIZE; dir->i_size += BOGO_DIRENT_SIZE;
inode_set_mtime_to_ts(dir,
inode_set_ctime_to_ts(dir, inode_set_ctime_current(inode)));
inode_inc_iversion(dir); inode_inc_iversion(dir);
inc_nlink(inode); return simple_link(old_dentry, dir, dentry);
ihold(inode); /* New dentry reference */
dget(dentry); /* Extra pinning count for the created dentry */
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
d_add(dentry, inode);
else
d_instantiate(dentry, inode);
out:
return ret;
} }
static int shmem_unlink(struct inode *dir, struct dentry *dentry) static int shmem_unlink(struct inode *dir, struct dentry *dentry)
@ -3991,11 +3976,8 @@ static int shmem_unlink(struct inode *dir, struct dentry *dentry)
simple_offset_remove(shmem_get_offset_ctx(dir), dentry); simple_offset_remove(shmem_get_offset_ctx(dir), dentry);
dir->i_size -= BOGO_DIRENT_SIZE; dir->i_size -= BOGO_DIRENT_SIZE;
inode_set_mtime_to_ts(dir,
inode_set_ctime_to_ts(dir, inode_set_ctime_current(inode)));
inode_inc_iversion(dir); inode_inc_iversion(dir);
drop_nlink(inode); simple_unlink(dir, dentry);
dput(dentry); /* Undo the count from "create" - does all the work */
/* /*
* For now, VFS can't deal with case-insensitive negative dentries, so * For now, VFS can't deal with case-insensitive negative dentries, so
@ -4150,11 +4132,7 @@ static int shmem_symlink(struct mnt_idmap *idmap, struct inode *dir,
dir->i_size += BOGO_DIRENT_SIZE; dir->i_size += BOGO_DIRENT_SIZE;
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
inode_inc_iversion(dir); inode_inc_iversion(dir);
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir)) d_make_persistent(dentry, inode);
d_add(dentry, inode);
else
d_instantiate(dentry, inode);
dget(dentry);
return 0; return 0;
out_remove_offset: out_remove_offset:
@ -5354,7 +5332,7 @@ static struct file_system_type shmem_fs_type = {
#ifdef CONFIG_TMPFS #ifdef CONFIG_TMPFS
.parameters = shmem_fs_parameters, .parameters = shmem_fs_parameters,
#endif #endif
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
.fs_flags = FS_USERNS_MOUNT | FS_ALLOW_IDMAP | FS_MGTIME, .fs_flags = FS_USERNS_MOUNT | FS_ALLOW_IDMAP | FS_MGTIME,
}; };

View File

@ -536,17 +536,16 @@ static int rpc_new_file(struct dentry *parent,
inode = rpc_get_inode(dir->i_sb, S_IFREG | mode); inode = rpc_get_inode(dir->i_sb, S_IFREG | mode);
if (unlikely(!inode)) { if (unlikely(!inode)) {
dput(dentry); simple_done_creating(dentry);
inode_unlock(dir);
return -ENOMEM; return -ENOMEM;
} }
inode->i_ino = iunique(dir->i_sb, 100); inode->i_ino = iunique(dir->i_sb, 100);
if (i_fop) if (i_fop)
inode->i_fop = i_fop; inode->i_fop = i_fop;
rpc_inode_setowner(inode, private); rpc_inode_setowner(inode, private);
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
fsnotify_create(dir, dentry); fsnotify_create(dir, dentry);
inode_unlock(dir); simple_done_creating(dentry);
return 0; return 0;
} }
@ -563,18 +562,17 @@ static struct dentry *rpc_new_dir(struct dentry *parent,
inode = rpc_get_inode(dir->i_sb, S_IFDIR | mode); inode = rpc_get_inode(dir->i_sb, S_IFDIR | mode);
if (unlikely(!inode)) { if (unlikely(!inode)) {
dput(dentry); simple_done_creating(dentry);
inode_unlock(dir);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
inode->i_ino = iunique(dir->i_sb, 100); inode->i_ino = iunique(dir->i_sb, 100);
inc_nlink(dir); inc_nlink(dir);
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
fsnotify_mkdir(dir, dentry); fsnotify_mkdir(dir, dentry);
inode_unlock(dir); simple_done_creating(dentry);
return dentry; return dentry; // borrowed
} }
static int rpc_populate(struct dentry *parent, static int rpc_populate(struct dentry *parent,
@ -657,8 +655,7 @@ int rpc_mkpipe_dentry(struct dentry *parent, const char *name,
inode = rpc_get_inode(dir->i_sb, umode); inode = rpc_get_inode(dir->i_sb, umode);
if (unlikely(!inode)) { if (unlikely(!inode)) {
dput(dentry); simple_done_creating(dentry);
inode_unlock(dir);
err = -ENOMEM; err = -ENOMEM;
goto failed; goto failed;
} }
@ -668,10 +665,10 @@ int rpc_mkpipe_dentry(struct dentry *parent, const char *name,
rpci->private = private; rpci->private = private;
rpci->pipe = pipe; rpci->pipe = pipe;
rpc_inode_setowner(inode, private); rpc_inode_setowner(inode, private);
d_instantiate(dentry, inode); pipe->dentry = dentry; // borrowed
pipe->dentry = dentry; d_make_persistent(dentry, inode);
fsnotify_create(dir, dentry); fsnotify_create(dir, dentry);
inode_unlock(dir); simple_done_creating(dentry);
return 0; return 0;
failed: failed:
@ -1206,7 +1203,7 @@ static void rpc_kill_sb(struct super_block *sb)
sb); sb);
mutex_unlock(&sn->pipefs_sb_lock); mutex_unlock(&sn->pipefs_sb_lock);
out: out:
kill_litter_super(sb); kill_anon_super(sb);
put_net(net); put_net(net);
} }

View File

@ -359,10 +359,15 @@ static void aafs_remove(struct dentry *dentry)
dir = d_inode(dentry->d_parent); dir = d_inode(dentry->d_parent);
dentry = start_removing_dentry(dentry->d_parent, dentry); dentry = start_removing_dentry(dentry->d_parent, dentry);
if (!IS_ERR(dentry) && simple_positive(dentry)) { if (!IS_ERR(dentry) && simple_positive(dentry)) {
if (d_is_dir(dentry)) if (d_is_dir(dentry)) {
simple_rmdir(dir, dentry); if (!WARN_ON(!simple_empty(dentry))) {
else __simple_rmdir(dir, dentry);
simple_unlink(dir, dentry); dput(dentry);
}
} else {
__simple_unlink(dir, dentry);
dput(dentry);
}
d_delete(dentry); d_delete(dentry);
} }
end_removing(dentry); end_removing(dentry);

View File

@ -72,7 +72,7 @@ static struct file_system_type fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "securityfs", .name = "securityfs",
.init_fs_context = securityfs_init_fs_context, .init_fs_context = securityfs_init_fs_context,
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
}; };
/** /**
@ -129,24 +129,19 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
parent = mount->mnt_root; parent = mount->mnt_root;
} }
inode = new_inode(parent->d_sb);
if (unlikely(!inode)) {
dentry = ERR_PTR(-ENOMEM);
goto out;
}
dir = d_inode(parent); dir = d_inode(parent);
inode_lock(dir); dentry = simple_start_creating(parent, name);
dentry = lookup_noperm(&QSTR(name), parent); if (IS_ERR(dentry)) {
if (IS_ERR(dentry)) iput(inode);
goto out; goto out;
if (d_really_is_positive(dentry)) {
error = -EEXIST;
goto out1;
} }
inode = new_inode(dir->i_sb);
if (!inode) {
error = -ENOMEM;
goto out1;
}
inode->i_ino = get_next_ino(); inode->i_ino = get_next_ino();
inode->i_mode = mode; inode->i_mode = mode;
simple_inode_init_ts(inode); simple_inode_init_ts(inode);
@ -162,15 +157,11 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
} else { } else {
inode->i_fop = fops; inode->i_fop = fops;
} }
d_instantiate(dentry, inode); d_make_persistent(dentry, inode);
inode_unlock(dir); simple_done_creating(dentry);
return dentry; return dentry; // borrowed
out1:
dput(dentry);
dentry = ERR_PTR(error);
out: out:
inode_unlock(dir);
if (pinned) if (pinned)
simple_release_fs(&mount, &mount_count); simple_release_fs(&mount, &mount_count);
return dentry; return dentry;

View File

@ -76,7 +76,6 @@ struct selinux_fs_info {
struct dentry *class_dir; struct dentry *class_dir;
unsigned long last_class_ino; unsigned long last_class_ino;
bool policy_opened; bool policy_opened;
struct dentry *policycap_dir;
unsigned long last_ino; unsigned long last_ino;
struct super_block *sb; struct super_block *sb;
}; };
@ -118,7 +117,6 @@ static void selinux_fs_info_free(struct super_block *sb)
#define BOOL_DIR_NAME "booleans" #define BOOL_DIR_NAME "booleans"
#define CLASS_DIR_NAME "class" #define CLASS_DIR_NAME "class"
#define POLICYCAP_DIR_NAME "policy_capabilities"
#define TMPBUFLEN 12 #define TMPBUFLEN 12
static ssize_t sel_read_enforce(struct file *filp, char __user *buf, static ssize_t sel_read_enforce(struct file *filp, char __user *buf,
@ -1211,6 +1209,26 @@ static struct inode *sel_make_inode(struct super_block *sb, umode_t mode)
return ret; return ret;
} }
static struct dentry *sel_attach(struct dentry *parent, const char *name,
struct inode *inode)
{
struct dentry *dentry = d_alloc_name(parent, name);
if (unlikely(!dentry)) {
iput(inode);
return ERR_PTR(-ENOMEM);
}
d_make_persistent(dentry, inode);
dput(dentry);
return dentry;
}
static int sel_attach_file(struct dentry *parent, const char *name,
struct inode *inode)
{
struct dentry *dentry = sel_attach(parent, name, inode);
return PTR_ERR_OR_ZERO(dentry);
}
static ssize_t sel_read_bool(struct file *filep, char __user *buf, static ssize_t sel_read_bool(struct file *filep, char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
@ -1370,8 +1388,7 @@ static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_
*bool_num = num; *bool_num = num;
*bool_pending_names = names; *bool_pending_names = names;
for (i = 0; i < num; i++) { for (i = 0; !ret && i < num; i++) {
struct dentry *dentry;
struct inode *inode; struct inode *inode;
struct inode_security_struct *isec; struct inode_security_struct *isec;
ssize_t len; ssize_t len;
@ -1382,15 +1399,9 @@ static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_
ret = -ENAMETOOLONG; ret = -ENAMETOOLONG;
break; break;
} }
dentry = d_alloc_name(bool_dir, names[i]);
if (!dentry) {
ret = -ENOMEM;
break;
}
inode = sel_make_inode(bool_dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR); inode = sel_make_inode(bool_dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR);
if (!inode) { if (!inode) {
dput(dentry);
ret = -ENOMEM; ret = -ENOMEM;
break; break;
} }
@ -1408,7 +1419,8 @@ static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_
isec->initialized = LABEL_INITIALIZED; isec->initialized = LABEL_INITIALIZED;
inode->i_fop = &sel_bool_ops; inode->i_fop = &sel_bool_ops;
inode->i_ino = i|SEL_BOOL_INO_OFFSET; inode->i_ino = i|SEL_BOOL_INO_OFFSET;
d_add(dentry, inode);
ret = sel_attach_file(bool_dir, names[i], inode);
} }
out: out:
free_page((unsigned long)page); free_page((unsigned long)page);
@ -1593,6 +1605,7 @@ static int sel_make_avc_files(struct dentry *dir)
struct super_block *sb = dir->d_sb; struct super_block *sb = dir->d_sb;
struct selinux_fs_info *fsi = sb->s_fs_info; struct selinux_fs_info *fsi = sb->s_fs_info;
unsigned int i; unsigned int i;
int err = 0;
static const struct tree_descr files[] = { static const struct tree_descr files[] = {
{ "cache_threshold", { "cache_threshold",
&sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR }, &sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR },
@ -1602,26 +1615,20 @@ static int sel_make_avc_files(struct dentry *dir)
#endif #endif
}; };
for (i = 0; i < ARRAY_SIZE(files); i++) { for (i = 0; !err && i < ARRAY_SIZE(files); i++) {
struct inode *inode; struct inode *inode;
struct dentry *dentry;
dentry = d_alloc_name(dir, files[i].name);
if (!dentry)
return -ENOMEM;
inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode);
if (!inode) { if (!inode)
dput(dentry);
return -ENOMEM; return -ENOMEM;
}
inode->i_fop = files[i].ops; inode->i_fop = files[i].ops;
inode->i_ino = ++fsi->last_ino; inode->i_ino = ++fsi->last_ino;
d_add(dentry, inode);
err = sel_attach_file(dir, files[i].name, inode);
} }
return 0; return err;
} }
static int sel_make_ss_files(struct dentry *dir) static int sel_make_ss_files(struct dentry *dir)
@ -1629,30 +1636,25 @@ static int sel_make_ss_files(struct dentry *dir)
struct super_block *sb = dir->d_sb; struct super_block *sb = dir->d_sb;
struct selinux_fs_info *fsi = sb->s_fs_info; struct selinux_fs_info *fsi = sb->s_fs_info;
unsigned int i; unsigned int i;
int err = 0;
static const struct tree_descr files[] = { static const struct tree_descr files[] = {
{ "sidtab_hash_stats", &sel_sidtab_hash_stats_ops, S_IRUGO }, { "sidtab_hash_stats", &sel_sidtab_hash_stats_ops, S_IRUGO },
}; };
for (i = 0; i < ARRAY_SIZE(files); i++) { for (i = 0; !err && i < ARRAY_SIZE(files); i++) {
struct inode *inode; struct inode *inode;
struct dentry *dentry;
dentry = d_alloc_name(dir, files[i].name);
if (!dentry)
return -ENOMEM;
inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode);
if (!inode) { if (!inode)
dput(dentry);
return -ENOMEM; return -ENOMEM;
}
inode->i_fop = files[i].ops; inode->i_fop = files[i].ops;
inode->i_ino = ++fsi->last_ino; inode->i_ino = ++fsi->last_ino;
d_add(dentry, inode);
err = sel_attach_file(dir, files[i].name, inode);
} }
return 0; return err;
} }
static ssize_t sel_read_initcon(struct file *file, char __user *buf, static ssize_t sel_read_initcon(struct file *file, char __user *buf,
@ -1680,30 +1682,25 @@ static const struct file_operations sel_initcon_ops = {
static int sel_make_initcon_files(struct dentry *dir) static int sel_make_initcon_files(struct dentry *dir)
{ {
unsigned int i; unsigned int i;
int err = 0;
for (i = 1; i <= SECINITSID_NUM; i++) { for (i = 1; !err && i <= SECINITSID_NUM; i++) {
struct inode *inode;
struct dentry *dentry;
const char *s = security_get_initial_sid_context(i); const char *s = security_get_initial_sid_context(i);
struct inode *inode;
if (!s) if (!s)
continue; continue;
dentry = d_alloc_name(dir, s);
if (!dentry)
return -ENOMEM;
inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
if (!inode) { if (!inode)
dput(dentry);
return -ENOMEM; return -ENOMEM;
}
inode->i_fop = &sel_initcon_ops; inode->i_fop = &sel_initcon_ops;
inode->i_ino = i|SEL_INITCON_INO_OFFSET; inode->i_ino = i|SEL_INITCON_INO_OFFSET;
d_add(dentry, inode); err = sel_attach_file(dir, s, inode);
} }
return 0; return err;
} }
static inline unsigned long sel_class_to_ino(u16 class) static inline unsigned long sel_class_to_ino(u16 class)
@ -1785,29 +1782,21 @@ static int sel_make_perm_files(struct selinux_policy *newpolicy,
if (rc) if (rc)
return rc; return rc;
for (i = 0; i < nperms; i++) { for (i = 0; !rc && i < nperms; i++) {
struct inode *inode; struct inode *inode;
struct dentry *dentry;
rc = -ENOMEM;
dentry = d_alloc_name(dir, perms[i]);
if (!dentry)
goto out;
rc = -ENOMEM;
inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
if (!inode) { if (!inode) {
dput(dentry); rc = -ENOMEM;
goto out; break;
} }
inode->i_fop = &sel_perm_ops; inode->i_fop = &sel_perm_ops;
/* i+1 since perm values are 1-indexed */ /* i+1 since perm values are 1-indexed */
inode->i_ino = sel_perm_to_ino(classvalue, i + 1); inode->i_ino = sel_perm_to_ino(classvalue, i + 1);
d_add(dentry, inode);
rc = sel_attach_file(dir, perms[i], inode);
} }
rc = 0;
out:
for (i = 0; i < nperms; i++) for (i = 0; i < nperms; i++)
kfree(perms[i]); kfree(perms[i]);
kfree(perms); kfree(perms);
@ -1822,20 +1811,18 @@ static int sel_make_class_dir_entries(struct selinux_policy *newpolicy,
struct selinux_fs_info *fsi = sb->s_fs_info; struct selinux_fs_info *fsi = sb->s_fs_info;
struct dentry *dentry = NULL; struct dentry *dentry = NULL;
struct inode *inode = NULL; struct inode *inode = NULL;
int err;
dentry = d_alloc_name(dir, "index");
if (!dentry)
return -ENOMEM;
inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
if (!inode) { if (!inode)
dput(dentry);
return -ENOMEM; return -ENOMEM;
}
inode->i_fop = &sel_class_ops; inode->i_fop = &sel_class_ops;
inode->i_ino = sel_class_to_ino(index); inode->i_ino = sel_class_to_ino(index);
d_add(dentry, inode);
err = sel_attach_file(dir, "index", inode);
if (err)
return err;
dentry = sel_make_dir(dir, "perms", &fsi->last_class_ino); dentry = sel_make_dir(dir, "perms", &fsi->last_class_ino);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
@ -1883,61 +1870,51 @@ static int sel_make_classes(struct selinux_policy *newpolicy,
return rc; return rc;
} }
static int sel_make_policycap(struct selinux_fs_info *fsi) static int sel_make_policycap(struct dentry *dir)
{ {
struct super_block *sb = dir->d_sb;
unsigned int iter; unsigned int iter;
struct dentry *dentry = NULL;
struct inode *inode = NULL; struct inode *inode = NULL;
int err = 0;
for (iter = 0; !err && iter <= POLICYDB_CAP_MAX; iter++) {
const char *name;
for (iter = 0; iter <= POLICYDB_CAP_MAX; iter++) {
if (iter < ARRAY_SIZE(selinux_policycap_names)) if (iter < ARRAY_SIZE(selinux_policycap_names))
dentry = d_alloc_name(fsi->policycap_dir, name = selinux_policycap_names[iter];
selinux_policycap_names[iter]);
else else
dentry = d_alloc_name(fsi->policycap_dir, "unknown"); name = "unknown";
if (dentry == NULL) inode = sel_make_inode(sb, S_IFREG | 0444);
if (!inode)
return -ENOMEM; return -ENOMEM;
inode = sel_make_inode(fsi->sb, S_IFREG | 0444);
if (inode == NULL) {
dput(dentry);
return -ENOMEM;
}
inode->i_fop = &sel_policycap_ops; inode->i_fop = &sel_policycap_ops;
inode->i_ino = iter | SEL_POLICYCAP_INO_OFFSET; inode->i_ino = iter | SEL_POLICYCAP_INO_OFFSET;
d_add(dentry, inode); err = sel_attach_file(dir, name, inode);
} }
return 0; return err;
} }
static struct dentry *sel_make_dir(struct dentry *dir, const char *name, static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
unsigned long *ino) unsigned long *ino)
{ {
struct dentry *dentry = d_alloc_name(dir, name);
struct inode *inode; struct inode *inode;
if (!dentry)
return ERR_PTR(-ENOMEM);
inode = sel_make_inode(dir->d_sb, S_IFDIR | S_IRUGO | S_IXUGO); inode = sel_make_inode(dir->d_sb, S_IFDIR | S_IRUGO | S_IXUGO);
if (!inode) { if (!inode)
dput(dentry);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
}
inode->i_op = &simple_dir_inode_operations; inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations; inode->i_fop = &simple_dir_operations;
inode->i_ino = ++(*ino); inode->i_ino = ++(*ino);
/* directory inodes start off with i_nlink == 2 (for "." entry) */ /* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode); inc_nlink(inode);
d_add(dentry, inode);
/* bump link count on parent directory, too */ /* bump link count on parent directory, too */
inc_nlink(d_inode(dir)); inc_nlink(d_inode(dir));
return dentry; return sel_attach(dir, name, inode);
} }
static int reject_all(struct mnt_idmap *idmap, struct inode *inode, int mask) static int reject_all(struct mnt_idmap *idmap, struct inode *inode, int mask)
@ -1970,10 +1947,11 @@ static struct dentry *sel_make_swapover_dir(struct super_block *sb,
/* directory inodes start off with i_nlink == 2 (for "." entry) */ /* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode); inc_nlink(inode);
inode_lock(sb->s_root->d_inode); inode_lock(sb->s_root->d_inode);
d_add(dentry, inode); d_make_persistent(dentry, inode);
inc_nlink(sb->s_root->d_inode); inc_nlink(sb->s_root->d_inode);
inode_unlock(sb->s_root->d_inode); inode_unlock(sb->s_root->d_inode);
return dentry; dput(dentry);
return dentry; // borrowed
} }
#define NULL_FILE_NAME "null" #define NULL_FILE_NAME "null"
@ -2025,17 +2003,10 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
goto err; goto err;
} }
ret = -ENOMEM;
dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME);
if (!dentry)
goto err;
ret = -ENOMEM; ret = -ENOMEM;
inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO); inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO);
if (!inode) { if (!inode)
dput(dentry);
goto err; goto err;
}
inode->i_ino = ++fsi->last_ino; inode->i_ino = ++fsi->last_ino;
isec = selinux_inode(inode); isec = selinux_inode(inode);
@ -2044,7 +2015,9 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
isec->initialized = LABEL_INITIALIZED; isec->initialized = LABEL_INITIALIZED;
init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3)); init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3));
d_add(dentry, inode); ret = sel_attach_file(sb->s_root, NULL_FILE_NAME, inode);
if (ret)
goto err;
dentry = sel_make_dir(sb->s_root, "avc", &fsi->last_ino); dentry = sel_make_dir(sb->s_root, "avc", &fsi->last_ino);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
@ -2083,15 +2056,13 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
goto err; goto err;
} }
fsi->policycap_dir = sel_make_dir(sb->s_root, POLICYCAP_DIR_NAME, dentry = sel_make_dir(sb->s_root, "policy_capabilities", &fsi->last_ino);
&fsi->last_ino); if (IS_ERR(dentry)) {
if (IS_ERR(fsi->policycap_dir)) { ret = PTR_ERR(dentry);
ret = PTR_ERR(fsi->policycap_dir);
fsi->policycap_dir = NULL;
goto err; goto err;
} }
ret = sel_make_policycap(fsi); ret = sel_make_policycap(dentry);
if (ret) { if (ret) {
pr_err("SELinux: failed to load policy capabilities\n"); pr_err("SELinux: failed to load policy capabilities\n");
goto err; goto err;
@ -2123,7 +2094,7 @@ static int sel_init_fs_context(struct fs_context *fc)
static void sel_kill_sb(struct super_block *sb) static void sel_kill_sb(struct super_block *sb)
{ {
selinux_fs_info_free(sb); selinux_fs_info_free(sb);
kill_litter_super(sb); kill_anon_super(sb);
} }
static struct file_system_type sel_fs_type = { static struct file_system_type sel_fs_type = {

View File

@ -2960,7 +2960,7 @@ static int smk_init_fs_context(struct fs_context *fc)
static struct file_system_type smk_fs_type = { static struct file_system_type smk_fs_type = {
.name = "smackfs", .name = "smackfs",
.init_fs_context = smk_init_fs_context, .init_fs_context = smk_init_fs_context,
.kill_sb = kill_litter_super, .kill_sb = kill_anon_super,
}; };
static struct vfsmount *smackfs_mount; static struct vfsmount *smackfs_mount;