Commit 222099f6 authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] dnotify race fix

	A bunch of places dereferences ->d_parent->d_inode with no
protection whatsoever (e.g. on return from read()).  It's an
SMP race on all boxen and pretty wide UP race if we have dnotify
set on parent (race between read() and rename() and similar beasts).

	Patch below is the first one in a series of ->d_parent-related
fixes.  It adds a helper (dnotify_parent(dentry, event)) and converts
places that did inode_dir_notify(dentry->d_parent->d_inode,...) to it.

	Please, apply.  Notice that problem exists in 2.4 and unlike 2.5
there we can't switch to saner API (basically, reporting file events on
file, not on a parent directory).

Some of the further fixes depend on Pat's and Greg's (driverfs and usbdevfs
resp.) patches, so getting them merged would make life easier.  And yes,
some of further chunks (e.g. smbfs ->revalidate() and friends) will also
have to go into 2.4 - they are independent from any threading projects ;-/
parent cb625b80
...@@ -1000,7 +1000,7 @@ asmlinkage ssize_t sys32_pread(unsigned int fd, char * buf, ...@@ -1000,7 +1000,7 @@ asmlinkage ssize_t sys32_pread(unsigned int fd, char * buf,
goto out; goto out;
ret = read(file, buf, count, &pos); ret = read(file, buf, count, &pos);
if (ret > 0) if (ret > 0)
inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_ACCESS); dnotify_parent(file->f_dentry, DN_ACCESS);
out: out:
fput(file); fput(file);
bad_file: bad_file:
...@@ -1032,7 +1032,7 @@ asmlinkage ssize_t sys32_pwrite(unsigned int fd, const char * buf, ...@@ -1032,7 +1032,7 @@ asmlinkage ssize_t sys32_pwrite(unsigned int fd, const char * buf,
ret = write(file, buf, count, &pos); ret = write(file, buf, count, &pos);
if (ret > 0) if (ret > 0)
inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_MODIFY); dnotify_parent(file->f_dentry, DN_MODIFY);
out: out:
fput(file); fput(file);
bad_file: bad_file:
......
...@@ -144,7 +144,7 @@ int notify_change(struct dentry * dentry, struct iattr * attr) ...@@ -144,7 +144,7 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
if (!error) { if (!error) {
unsigned long dn_mask = setattr_mask(ia_valid); unsigned long dn_mask = setattr_mask(ia_valid);
if (dn_mask) if (dn_mask)
inode_dir_notify(dentry->d_parent->d_inode, dn_mask); dnotify_parent(dentry, dn_mask);
} }
return error; return error;
} }
...@@ -189,8 +189,7 @@ asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count) ...@@ -189,8 +189,7 @@ asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count)
} }
} }
if (ret > 0) if (ret > 0)
inode_dir_notify(file->f_dentry->d_parent->d_inode, dnotify_parent(file->f_dentry, DN_ACCESS);
DN_ACCESS);
fput(file); fput(file);
} }
return ret; return ret;
...@@ -216,8 +215,7 @@ asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count) ...@@ -216,8 +215,7 @@ asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count)
} }
} }
if (ret > 0) if (ret > 0)
inode_dir_notify(file->f_dentry->d_parent->d_inode, dnotify_parent(file->f_dentry, DN_MODIFY);
DN_MODIFY);
fput(file); fput(file);
} }
return ret; return ret;
...@@ -321,7 +319,7 @@ static ssize_t do_readv_writev(int type, struct file *file, ...@@ -321,7 +319,7 @@ static ssize_t do_readv_writev(int type, struct file *file,
out_nofree: out_nofree:
/* VERIFY_WRITE actually means a read, as we write to user space */ /* VERIFY_WRITE actually means a read, as we write to user space */
if ((ret + (type == VERIFY_WRITE)) > 0) if ((ret + (type == VERIFY_WRITE)) > 0)
inode_dir_notify(file->f_dentry->d_parent->d_inode, dnotify_parent(file->f_dentry,
(type == VERIFY_WRITE) ? DN_MODIFY : DN_ACCESS); (type == VERIFY_WRITE) ? DN_MODIFY : DN_ACCESS);
return ret; return ret;
} }
...@@ -394,7 +392,7 @@ asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, ...@@ -394,7 +392,7 @@ asmlinkage ssize_t sys_pread(unsigned int fd, char * buf,
goto out; goto out;
ret = read(file, buf, count, &pos); ret = read(file, buf, count, &pos);
if (ret > 0) if (ret > 0)
inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_ACCESS); dnotify_parent(file->f_dentry, DN_ACCESS);
out: out:
fput(file); fput(file);
bad_file: bad_file:
...@@ -426,7 +424,7 @@ asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, ...@@ -426,7 +424,7 @@ asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf,
ret = write(file, buf, count, &pos); ret = write(file, buf, count, &pos);
if (ret > 0) if (ret > 0)
inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_MODIFY); dnotify_parent(file->f_dentry, DN_MODIFY);
out: out:
fput(file); fput(file);
bad_file: bad_file:
......
...@@ -25,3 +25,21 @@ static inline void inode_dir_notify(struct inode *inode, unsigned long event) ...@@ -25,3 +25,21 @@ static inline void inode_dir_notify(struct inode *inode, unsigned long event)
if ((inode)->i_dnotify_mask & (event)) if ((inode)->i_dnotify_mask & (event))
__inode_dir_notify(inode, event); __inode_dir_notify(inode, event);
} }
/*
* This is hopelessly wrong, but unfixable without API changes. At
* least it doesn't oops the kernel...
*/
static inline void dnotify_parent(struct dentry *dentry, unsigned long event)
{
struct dentry *parent;
spin_lock(&dcache_lock);
parent = dentry->d_parent;
if (parent->d_inode->i_dnotify_mask & event) {
dget(parent);
spin_unlock(&dcache_lock);
__inode_dir_notify(parent->d_inode, event);
dput(parent);
} else
spin_unlock(&dcache_lock);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment