Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs fixes from Al Viro: "A nasty bug in fs/namespace.c caught by Andrey + a couple of less serious unpleasantness - ecryptfs misc device playing hopeless games with try_module_get() and palinfo procfs support being... not quite correctly done, to be polite." * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: mnt: release locks on error path in do_loopback palinfo fixes procfs: add proc_remove_subtree() ecryptfs: close rmmod race
This commit is contained in:
commit
e8f2b548de
|
@ -849,17 +849,6 @@ static palinfo_entry_t palinfo_entries[]={
|
||||||
|
|
||||||
#define NR_PALINFO_ENTRIES (int) ARRAY_SIZE(palinfo_entries)
|
#define NR_PALINFO_ENTRIES (int) ARRAY_SIZE(palinfo_entries)
|
||||||
|
|
||||||
/*
|
|
||||||
* this array is used to keep track of the proc entries we create. This is
|
|
||||||
* required in the module mode when we need to remove all entries. The procfs code
|
|
||||||
* does not do recursion of deletion
|
|
||||||
*
|
|
||||||
* Notes:
|
|
||||||
* - +1 accounts for the cpuN directory entry in /proc/pal
|
|
||||||
*/
|
|
||||||
#define NR_PALINFO_PROC_ENTRIES (NR_CPUS*(NR_PALINFO_ENTRIES+1))
|
|
||||||
|
|
||||||
static struct proc_dir_entry *palinfo_proc_entries[NR_PALINFO_PROC_ENTRIES];
|
|
||||||
static struct proc_dir_entry *palinfo_dir;
|
static struct proc_dir_entry *palinfo_dir;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -971,60 +960,32 @@ palinfo_read_entry(char *page, char **start, off_t off, int count, int *eof, voi
|
||||||
static void __cpuinit
|
static void __cpuinit
|
||||||
create_palinfo_proc_entries(unsigned int cpu)
|
create_palinfo_proc_entries(unsigned int cpu)
|
||||||
{
|
{
|
||||||
# define CPUSTR "cpu%d"
|
|
||||||
|
|
||||||
pal_func_cpu_u_t f;
|
pal_func_cpu_u_t f;
|
||||||
struct proc_dir_entry **pdir;
|
|
||||||
struct proc_dir_entry *cpu_dir;
|
struct proc_dir_entry *cpu_dir;
|
||||||
int j;
|
int j;
|
||||||
char cpustr[sizeof(CPUSTR)];
|
char cpustr[3+4+1]; /* cpu numbers are up to 4095 on itanic */
|
||||||
|
sprintf(cpustr, "cpu%d", cpu);
|
||||||
|
|
||||||
/*
|
|
||||||
* we keep track of created entries in a depth-first order for
|
|
||||||
* cleanup purposes. Each entry is stored into palinfo_proc_entries
|
|
||||||
*/
|
|
||||||
sprintf(cpustr,CPUSTR, cpu);
|
|
||||||
|
|
||||||
cpu_dir = proc_mkdir(cpustr, palinfo_dir);
|
cpu_dir = proc_mkdir(cpustr, palinfo_dir);
|
||||||
|
if (!cpu_dir)
|
||||||
|
return;
|
||||||
|
|
||||||
f.req_cpu = cpu;
|
f.req_cpu = cpu;
|
||||||
|
|
||||||
/*
|
|
||||||
* Compute the location to store per cpu entries
|
|
||||||
* We dont store the top level entry in this list, but
|
|
||||||
* remove it finally after removing all cpu entries.
|
|
||||||
*/
|
|
||||||
pdir = &palinfo_proc_entries[cpu*(NR_PALINFO_ENTRIES+1)];
|
|
||||||
*pdir++ = cpu_dir;
|
|
||||||
for (j=0; j < NR_PALINFO_ENTRIES; j++) {
|
for (j=0; j < NR_PALINFO_ENTRIES; j++) {
|
||||||
f.func_id = j;
|
f.func_id = j;
|
||||||
*pdir = create_proc_read_entry(
|
create_proc_read_entry(
|
||||||
palinfo_entries[j].name, 0, cpu_dir,
|
palinfo_entries[j].name, 0, cpu_dir,
|
||||||
palinfo_read_entry, (void *)f.value);
|
palinfo_read_entry, (void *)f.value);
|
||||||
pdir++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
remove_palinfo_proc_entries(unsigned int hcpu)
|
remove_palinfo_proc_entries(unsigned int hcpu)
|
||||||
{
|
{
|
||||||
int j;
|
char cpustr[3+4+1]; /* cpu numbers are up to 4095 on itanic */
|
||||||
struct proc_dir_entry *cpu_dir, **pdir;
|
sprintf(cpustr, "cpu%d", hcpu);
|
||||||
|
remove_proc_subtree(cpustr, palinfo_dir);
|
||||||
pdir = &palinfo_proc_entries[hcpu*(NR_PALINFO_ENTRIES+1)];
|
|
||||||
cpu_dir = *pdir;
|
|
||||||
*pdir++=NULL;
|
|
||||||
for (j=0; j < (NR_PALINFO_ENTRIES); j++) {
|
|
||||||
if ((*pdir)) {
|
|
||||||
remove_proc_entry ((*pdir)->name, cpu_dir);
|
|
||||||
*pdir ++= NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cpu_dir) {
|
|
||||||
remove_proc_entry(cpu_dir->name, palinfo_dir);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __cpuinit palinfo_cpu_callback(struct notifier_block *nfb,
|
static int __cpuinit palinfo_cpu_callback(struct notifier_block *nfb,
|
||||||
|
@ -1058,6 +1019,8 @@ palinfo_init(void)
|
||||||
|
|
||||||
printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION);
|
printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION);
|
||||||
palinfo_dir = proc_mkdir("pal", NULL);
|
palinfo_dir = proc_mkdir("pal", NULL);
|
||||||
|
if (!palinfo_dir)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Create palinfo dirs in /proc for all online cpus */
|
/* Create palinfo dirs in /proc for all online cpus */
|
||||||
for_each_online_cpu(i) {
|
for_each_online_cpu(i) {
|
||||||
|
@ -1073,22 +1036,8 @@ palinfo_init(void)
|
||||||
static void __exit
|
static void __exit
|
||||||
palinfo_exit(void)
|
palinfo_exit(void)
|
||||||
{
|
{
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
/* remove all nodes: depth first pass. Could optimize this */
|
|
||||||
for_each_online_cpu(i) {
|
|
||||||
remove_palinfo_proc_entries(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Remove the top level entry finally
|
|
||||||
*/
|
|
||||||
remove_proc_entry(palinfo_dir->name, NULL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Unregister from cpu notifier callbacks
|
|
||||||
*/
|
|
||||||
unregister_hotcpu_notifier(&palinfo_cpu_notifier);
|
unregister_hotcpu_notifier(&palinfo_cpu_notifier);
|
||||||
|
remove_proc_subtree("pal", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(palinfo_init);
|
module_init(palinfo_init);
|
||||||
|
|
|
@ -80,13 +80,6 @@ ecryptfs_miscdev_open(struct inode *inode, struct file *file)
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
mutex_lock(&ecryptfs_daemon_hash_mux);
|
mutex_lock(&ecryptfs_daemon_hash_mux);
|
||||||
rc = try_module_get(THIS_MODULE);
|
|
||||||
if (rc == 0) {
|
|
||||||
rc = -EIO;
|
|
||||||
printk(KERN_ERR "%s: Error attempting to increment module use "
|
|
||||||
"count; rc = [%d]\n", __func__, rc);
|
|
||||||
goto out_unlock_daemon_list;
|
|
||||||
}
|
|
||||||
rc = ecryptfs_find_daemon_by_euid(&daemon);
|
rc = ecryptfs_find_daemon_by_euid(&daemon);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
|
@ -96,7 +89,7 @@ ecryptfs_miscdev_open(struct inode *inode, struct file *file)
|
||||||
if (rc) {
|
if (rc) {
|
||||||
printk(KERN_ERR "%s: Error attempting to spawn daemon; "
|
printk(KERN_ERR "%s: Error attempting to spawn daemon; "
|
||||||
"rc = [%d]\n", __func__, rc);
|
"rc = [%d]\n", __func__, rc);
|
||||||
goto out_module_put_unlock_daemon_list;
|
goto out_unlock_daemon_list;
|
||||||
}
|
}
|
||||||
mutex_lock(&daemon->mux);
|
mutex_lock(&daemon->mux);
|
||||||
if (daemon->flags & ECRYPTFS_DAEMON_MISCDEV_OPEN) {
|
if (daemon->flags & ECRYPTFS_DAEMON_MISCDEV_OPEN) {
|
||||||
|
@ -108,9 +101,6 @@ ecryptfs_miscdev_open(struct inode *inode, struct file *file)
|
||||||
atomic_inc(&ecryptfs_num_miscdev_opens);
|
atomic_inc(&ecryptfs_num_miscdev_opens);
|
||||||
out_unlock_daemon:
|
out_unlock_daemon:
|
||||||
mutex_unlock(&daemon->mux);
|
mutex_unlock(&daemon->mux);
|
||||||
out_module_put_unlock_daemon_list:
|
|
||||||
if (rc)
|
|
||||||
module_put(THIS_MODULE);
|
|
||||||
out_unlock_daemon_list:
|
out_unlock_daemon_list:
|
||||||
mutex_unlock(&ecryptfs_daemon_hash_mux);
|
mutex_unlock(&ecryptfs_daemon_hash_mux);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -147,7 +137,6 @@ ecryptfs_miscdev_release(struct inode *inode, struct file *file)
|
||||||
"bug.\n", __func__, rc);
|
"bug.\n", __func__, rc);
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
module_put(THIS_MODULE);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,6 +460,7 @@ out_free:
|
||||||
|
|
||||||
|
|
||||||
static const struct file_operations ecryptfs_miscdev_fops = {
|
static const struct file_operations ecryptfs_miscdev_fops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
.open = ecryptfs_miscdev_open,
|
.open = ecryptfs_miscdev_open,
|
||||||
.poll = ecryptfs_miscdev_poll,
|
.poll = ecryptfs_miscdev_poll,
|
||||||
.read = ecryptfs_miscdev_read,
|
.read = ecryptfs_miscdev_read,
|
||||||
|
|
|
@ -1690,7 +1690,7 @@ static int do_loopback(struct path *path, const char *old_name,
|
||||||
|
|
||||||
if (IS_ERR(mnt)) {
|
if (IS_ERR(mnt)) {
|
||||||
err = PTR_ERR(mnt);
|
err = PTR_ERR(mnt);
|
||||||
goto out;
|
goto out2;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = graft_tree(mnt, path);
|
err = graft_tree(mnt, path);
|
||||||
|
|
|
@ -755,37 +755,8 @@ void pde_put(struct proc_dir_entry *pde)
|
||||||
free_proc_entry(pde);
|
free_proc_entry(pde);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void entry_rundown(struct proc_dir_entry *de)
|
||||||
* Remove a /proc entry and free it if it's not currently in use.
|
|
||||||
*/
|
|
||||||
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
|
|
||||||
{
|
{
|
||||||
struct proc_dir_entry **p;
|
|
||||||
struct proc_dir_entry *de = NULL;
|
|
||||||
const char *fn = name;
|
|
||||||
unsigned int len;
|
|
||||||
|
|
||||||
spin_lock(&proc_subdir_lock);
|
|
||||||
if (__xlate_proc_name(name, &parent, &fn) != 0) {
|
|
||||||
spin_unlock(&proc_subdir_lock);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
len = strlen(fn);
|
|
||||||
|
|
||||||
for (p = &parent->subdir; *p; p=&(*p)->next ) {
|
|
||||||
if (proc_match(len, fn, *p)) {
|
|
||||||
de = *p;
|
|
||||||
*p = de->next;
|
|
||||||
de->next = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spin_unlock(&proc_subdir_lock);
|
|
||||||
if (!de) {
|
|
||||||
WARN(1, "name '%s'\n", name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock(&de->pde_unload_lock);
|
spin_lock(&de->pde_unload_lock);
|
||||||
/*
|
/*
|
||||||
* Stop accepting new callers into module. If you're
|
* Stop accepting new callers into module. If you're
|
||||||
|
@ -817,6 +788,40 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
|
||||||
spin_lock(&de->pde_unload_lock);
|
spin_lock(&de->pde_unload_lock);
|
||||||
}
|
}
|
||||||
spin_unlock(&de->pde_unload_lock);
|
spin_unlock(&de->pde_unload_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove a /proc entry and free it if it's not currently in use.
|
||||||
|
*/
|
||||||
|
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
|
||||||
|
{
|
||||||
|
struct proc_dir_entry **p;
|
||||||
|
struct proc_dir_entry *de = NULL;
|
||||||
|
const char *fn = name;
|
||||||
|
unsigned int len;
|
||||||
|
|
||||||
|
spin_lock(&proc_subdir_lock);
|
||||||
|
if (__xlate_proc_name(name, &parent, &fn) != 0) {
|
||||||
|
spin_unlock(&proc_subdir_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
len = strlen(fn);
|
||||||
|
|
||||||
|
for (p = &parent->subdir; *p; p=&(*p)->next ) {
|
||||||
|
if (proc_match(len, fn, *p)) {
|
||||||
|
de = *p;
|
||||||
|
*p = de->next;
|
||||||
|
de->next = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&proc_subdir_lock);
|
||||||
|
if (!de) {
|
||||||
|
WARN(1, "name '%s'\n", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry_rundown(de);
|
||||||
|
|
||||||
if (S_ISDIR(de->mode))
|
if (S_ISDIR(de->mode))
|
||||||
parent->nlink--;
|
parent->nlink--;
|
||||||
|
@ -827,3 +832,57 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
|
||||||
pde_put(de);
|
pde_put(de);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(remove_proc_entry);
|
EXPORT_SYMBOL(remove_proc_entry);
|
||||||
|
|
||||||
|
int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
|
||||||
|
{
|
||||||
|
struct proc_dir_entry **p;
|
||||||
|
struct proc_dir_entry *root = NULL, *de, *next;
|
||||||
|
const char *fn = name;
|
||||||
|
unsigned int len;
|
||||||
|
|
||||||
|
spin_lock(&proc_subdir_lock);
|
||||||
|
if (__xlate_proc_name(name, &parent, &fn) != 0) {
|
||||||
|
spin_unlock(&proc_subdir_lock);
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
len = strlen(fn);
|
||||||
|
|
||||||
|
for (p = &parent->subdir; *p; p=&(*p)->next ) {
|
||||||
|
if (proc_match(len, fn, *p)) {
|
||||||
|
root = *p;
|
||||||
|
*p = root->next;
|
||||||
|
root->next = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!root) {
|
||||||
|
spin_unlock(&proc_subdir_lock);
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
de = root;
|
||||||
|
while (1) {
|
||||||
|
next = de->subdir;
|
||||||
|
if (next) {
|
||||||
|
de->subdir = next->next;
|
||||||
|
next->next = NULL;
|
||||||
|
de = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
spin_unlock(&proc_subdir_lock);
|
||||||
|
|
||||||
|
entry_rundown(de);
|
||||||
|
next = de->parent;
|
||||||
|
if (S_ISDIR(de->mode))
|
||||||
|
next->nlink--;
|
||||||
|
de->nlink = 0;
|
||||||
|
if (de == root)
|
||||||
|
break;
|
||||||
|
pde_put(de);
|
||||||
|
|
||||||
|
spin_lock(&proc_subdir_lock);
|
||||||
|
de = next;
|
||||||
|
}
|
||||||
|
pde_put(root);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(remove_proc_subtree);
|
||||||
|
|
|
@ -117,6 +117,7 @@ struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
|
||||||
const struct file_operations *proc_fops,
|
const struct file_operations *proc_fops,
|
||||||
void *data);
|
void *data);
|
||||||
extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent);
|
extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent);
|
||||||
|
extern int remove_proc_subtree(const char *name, struct proc_dir_entry *parent);
|
||||||
|
|
||||||
struct pid_namespace;
|
struct pid_namespace;
|
||||||
|
|
||||||
|
@ -202,6 +203,7 @@ static inline struct proc_dir_entry *proc_create_data(const char *name,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#define remove_proc_entry(name, parent) do {} while (0)
|
#define remove_proc_entry(name, parent) do {} while (0)
|
||||||
|
#define remove_proc_subtree(name, parent) do {} while (0)
|
||||||
|
|
||||||
static inline struct proc_dir_entry *proc_symlink(const char *name,
|
static inline struct proc_dir_entry *proc_symlink(const char *name,
|
||||||
struct proc_dir_entry *parent,const char *dest) {return NULL;}
|
struct proc_dir_entry *parent,const char *dest) {return NULL;}
|
||||||
|
|
Loading…
Reference in New Issue