proc: change ->nlink under proc_subdir_lock
Currently gluing PDE into global /proc tree is done under lock, but changing ->nlink is not. Additionally struct proc_dir_entry::nlink is not atomic so updates can be lost. Link: http://lkml.kernel.org/r/20190925202436.GA17388@avx2 Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>alistair/sunxi64-5.5-dsi
parent
3cde287bb4
commit
e06689bf57
|
@ -138,8 +138,12 @@ static int proc_getattr(const struct path *path, struct kstat *stat,
|
||||||
{
|
{
|
||||||
struct inode *inode = d_inode(path->dentry);
|
struct inode *inode = d_inode(path->dentry);
|
||||||
struct proc_dir_entry *de = PDE(inode);
|
struct proc_dir_entry *de = PDE(inode);
|
||||||
if (de && de->nlink)
|
if (de) {
|
||||||
set_nlink(inode, de->nlink);
|
nlink_t nlink = READ_ONCE(de->nlink);
|
||||||
|
if (nlink > 0) {
|
||||||
|
set_nlink(inode, nlink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
generic_fillattr(inode, stat);
|
generic_fillattr(inode, stat);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -362,6 +366,7 @@ struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
|
||||||
write_unlock(&proc_subdir_lock);
|
write_unlock(&proc_subdir_lock);
|
||||||
goto out_free_inum;
|
goto out_free_inum;
|
||||||
}
|
}
|
||||||
|
dir->nlink++;
|
||||||
write_unlock(&proc_subdir_lock);
|
write_unlock(&proc_subdir_lock);
|
||||||
|
|
||||||
return dp;
|
return dp;
|
||||||
|
@ -472,10 +477,7 @@ struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
|
||||||
ent->data = data;
|
ent->data = data;
|
||||||
ent->proc_fops = &proc_dir_operations;
|
ent->proc_fops = &proc_dir_operations;
|
||||||
ent->proc_iops = &proc_dir_inode_operations;
|
ent->proc_iops = &proc_dir_inode_operations;
|
||||||
parent->nlink++;
|
|
||||||
ent = proc_register(parent, ent);
|
ent = proc_register(parent, ent);
|
||||||
if (!ent)
|
|
||||||
parent->nlink--;
|
|
||||||
}
|
}
|
||||||
return ent;
|
return ent;
|
||||||
}
|
}
|
||||||
|
@ -505,10 +507,7 @@ struct proc_dir_entry *proc_create_mount_point(const char *name)
|
||||||
ent->data = NULL;
|
ent->data = NULL;
|
||||||
ent->proc_fops = NULL;
|
ent->proc_fops = NULL;
|
||||||
ent->proc_iops = NULL;
|
ent->proc_iops = NULL;
|
||||||
parent->nlink++;
|
|
||||||
ent = proc_register(parent, ent);
|
ent = proc_register(parent, ent);
|
||||||
if (!ent)
|
|
||||||
parent->nlink--;
|
|
||||||
}
|
}
|
||||||
return ent;
|
return ent;
|
||||||
}
|
}
|
||||||
|
@ -666,8 +665,12 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
|
||||||
len = strlen(fn);
|
len = strlen(fn);
|
||||||
|
|
||||||
de = pde_subdir_find(parent, fn, len);
|
de = pde_subdir_find(parent, fn, len);
|
||||||
if (de)
|
if (de) {
|
||||||
rb_erase(&de->subdir_node, &parent->subdir);
|
rb_erase(&de->subdir_node, &parent->subdir);
|
||||||
|
if (S_ISDIR(de->mode)) {
|
||||||
|
parent->nlink--;
|
||||||
|
}
|
||||||
|
}
|
||||||
write_unlock(&proc_subdir_lock);
|
write_unlock(&proc_subdir_lock);
|
||||||
if (!de) {
|
if (!de) {
|
||||||
WARN(1, "name '%s'\n", name);
|
WARN(1, "name '%s'\n", name);
|
||||||
|
@ -676,9 +679,6 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
|
||||||
|
|
||||||
proc_entry_rundown(de);
|
proc_entry_rundown(de);
|
||||||
|
|
||||||
if (S_ISDIR(de->mode))
|
|
||||||
parent->nlink--;
|
|
||||||
de->nlink = 0;
|
|
||||||
WARN(pde_subdir_first(de),
|
WARN(pde_subdir_first(de),
|
||||||
"%s: removing non-empty directory '%s/%s', leaking at least '%s'\n",
|
"%s: removing non-empty directory '%s/%s', leaking at least '%s'\n",
|
||||||
__func__, de->parent->name, de->name, pde_subdir_first(de)->name);
|
__func__, de->parent->name, de->name, pde_subdir_first(de)->name);
|
||||||
|
@ -714,13 +714,12 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
|
||||||
de = next;
|
de = next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
write_unlock(&proc_subdir_lock);
|
|
||||||
|
|
||||||
proc_entry_rundown(de);
|
|
||||||
next = de->parent;
|
next = de->parent;
|
||||||
if (S_ISDIR(de->mode))
|
if (S_ISDIR(de->mode))
|
||||||
next->nlink--;
|
next->nlink--;
|
||||||
de->nlink = 0;
|
write_unlock(&proc_subdir_lock);
|
||||||
|
|
||||||
|
proc_entry_rundown(de);
|
||||||
if (de == root)
|
if (de == root)
|
||||||
break;
|
break;
|
||||||
pde_put(de);
|
pde_put(de);
|
||||||
|
|
Loading…
Reference in New Issue