mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 04:50:53 +07:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull second set of VFS changes from Al Viro: "Assorted f_pos race fixes, making do_splice_direct() safe to call with i_mutex on parent, O_TMPFILE support, Jeff's locks.c series, ->d_hash/->d_compare calling conventions changes from Linus, misc stuff all over the place." * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (63 commits) Document ->tmpfile() ext4: ->tmpfile() support vfs: export lseek_execute() to modules lseek_execute() doesn't need an inode passed to it block_dev: switch to fixed_size_llseek() cpqphp_sysfs: switch to fixed_size_llseek() tile-srom: switch to fixed_size_llseek() proc_powerpc: switch to fixed_size_llseek() ubi/cdev: switch to fixed_size_llseek() pci/proc: switch to fixed_size_llseek() isapnp: switch to fixed_size_llseek() lpfc: switch to fixed_size_llseek() locks: give the blocked_hash its own spinlock locks: add a new "lm_owner_key" lock operation locks: turn the blocked_list into a hashtable locks: convert fl_link to a hlist_node locks: avoid taking global lock if possible when waking up blocked waiters locks: protect most of the file_lock handling with i_lock locks: encapsulate the fl_link list handling locks: make "added" in __posix_lock_file a bool ...
This commit is contained in:
commit
790eac5640
@ -11,10 +11,8 @@ be able to use diff(1).
|
||||
prototypes:
|
||||
int (*d_revalidate)(struct dentry *, unsigned int);
|
||||
int (*d_weak_revalidate)(struct dentry *, unsigned int);
|
||||
int (*d_hash)(const struct dentry *, const struct inode *,
|
||||
struct qstr *);
|
||||
int (*d_compare)(const struct dentry *, const struct inode *,
|
||||
const struct dentry *, const struct inode *,
|
||||
int (*d_hash)(const struct dentry *, struct qstr *);
|
||||
int (*d_compare)(const struct dentry *, const struct dentry *,
|
||||
unsigned int, const char *, const struct qstr *);
|
||||
int (*d_delete)(struct dentry *);
|
||||
void (*d_release)(struct dentry *);
|
||||
@ -66,6 +64,7 @@ prototypes:
|
||||
int (*atomic_open)(struct inode *, struct dentry *,
|
||||
struct file *, unsigned open_flag,
|
||||
umode_t create_mode, int *opened);
|
||||
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
|
||||
|
||||
locking rules:
|
||||
all may block
|
||||
@ -93,6 +92,7 @@ removexattr: yes
|
||||
fiemap: no
|
||||
update_time: no
|
||||
atomic_open: yes
|
||||
tmpfile: no
|
||||
|
||||
Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
|
||||
victim.
|
||||
@ -344,25 +344,38 @@ prototypes:
|
||||
|
||||
|
||||
locking rules:
|
||||
file_lock_lock may block
|
||||
inode->i_lock may block
|
||||
fl_copy_lock: yes no
|
||||
fl_release_private: maybe no
|
||||
|
||||
----------------------- lock_manager_operations ---------------------------
|
||||
prototypes:
|
||||
int (*lm_compare_owner)(struct file_lock *, struct file_lock *);
|
||||
unsigned long (*lm_owner_key)(struct file_lock *);
|
||||
void (*lm_notify)(struct file_lock *); /* unblock callback */
|
||||
int (*lm_grant)(struct file_lock *, struct file_lock *, int);
|
||||
void (*lm_break)(struct file_lock *); /* break_lease callback */
|
||||
int (*lm_change)(struct file_lock **, int);
|
||||
|
||||
locking rules:
|
||||
file_lock_lock may block
|
||||
lm_compare_owner: yes no
|
||||
lm_notify: yes no
|
||||
lm_grant: no no
|
||||
lm_break: yes no
|
||||
lm_change yes no
|
||||
|
||||
inode->i_lock blocked_lock_lock may block
|
||||
lm_compare_owner: yes[1] maybe no
|
||||
lm_owner_key yes[1] yes no
|
||||
lm_notify: yes yes no
|
||||
lm_grant: no no no
|
||||
lm_break: yes no no
|
||||
lm_change yes no no
|
||||
|
||||
[1]: ->lm_compare_owner and ->lm_owner_key are generally called with
|
||||
*an* inode->i_lock held. It may not be the i_lock of the inode
|
||||
associated with either file_lock argument! This is the case with deadlock
|
||||
detection, since the code has to chase down the owners of locks that may
|
||||
be entirely unrelated to the one on which the lock is being acquired.
|
||||
For deadlock detection however, the blocked_lock_lock is also held. The
|
||||
fact that these locks are held ensures that the file_locks do not
|
||||
disappear out from under you while doing the comparison or generating an
|
||||
owner key.
|
||||
|
||||
--------------------------- buffer_head -----------------------------------
|
||||
prototypes:
|
||||
|
@ -360,6 +360,8 @@ struct inode_operations {
|
||||
int (*removexattr) (struct dentry *, const char *);
|
||||
void (*update_time)(struct inode *, struct timespec *, int);
|
||||
int (*atomic_open)(struct inode *, struct dentry *,
|
||||
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
|
||||
} ____cacheline_aligned;
|
||||
struct file *, unsigned open_flag,
|
||||
umode_t create_mode, int *opened);
|
||||
};
|
||||
@ -472,6 +474,9 @@ otherwise noted.
|
||||
component is negative or needs lookup. Cached positive dentries are
|
||||
still handled by f_op->open().
|
||||
|
||||
tmpfile: called in the end of O_TMPFILE open(). Optional, equivalent to
|
||||
atomically creating, opening and unlinking a file in given directory.
|
||||
|
||||
The Address Space Object
|
||||
========================
|
||||
|
||||
@ -901,10 +906,8 @@ defined:
|
||||
struct dentry_operations {
|
||||
int (*d_revalidate)(struct dentry *, unsigned int);
|
||||
int (*d_weak_revalidate)(struct dentry *, unsigned int);
|
||||
int (*d_hash)(const struct dentry *, const struct inode *,
|
||||
struct qstr *);
|
||||
int (*d_compare)(const struct dentry *, const struct inode *,
|
||||
const struct dentry *, const struct inode *,
|
||||
int (*d_hash)(const struct dentry *, struct qstr *);
|
||||
int (*d_compare)(const struct dentry *, const struct dentry *,
|
||||
unsigned int, const char *, const struct qstr *);
|
||||
int (*d_delete)(const struct dentry *);
|
||||
void (*d_release)(struct dentry *);
|
||||
@ -949,25 +952,24 @@ struct dentry_operations {
|
||||
|
||||
d_hash: called when the VFS adds a dentry to the hash table. The first
|
||||
dentry passed to d_hash is the parent directory that the name is
|
||||
to be hashed into. The inode is the dentry's inode.
|
||||
to be hashed into.
|
||||
|
||||
Same locking and synchronisation rules as d_compare regarding
|
||||
what is safe to dereference etc.
|
||||
|
||||
d_compare: called to compare a dentry name with a given name. The first
|
||||
dentry is the parent of the dentry to be compared, the second is
|
||||
the parent's inode, then the dentry and inode (may be NULL) of the
|
||||
child dentry. len and name string are properties of the dentry to be
|
||||
compared. qstr is the name to compare it with.
|
||||
the child dentry. len and name string are properties of the dentry
|
||||
to be compared. qstr is the name to compare it with.
|
||||
|
||||
Must be constant and idempotent, and should not take locks if
|
||||
possible, and should not or store into the dentry or inodes.
|
||||
Should not dereference pointers outside the dentry or inodes without
|
||||
possible, and should not or store into the dentry.
|
||||
Should not dereference pointers outside the dentry without
|
||||
lots of care (eg. d_parent, d_inode, d_name should not be used).
|
||||
|
||||
However, our vfsmount is pinned, and RCU held, so the dentries and
|
||||
inodes won't disappear, neither will our sb or filesystem module.
|
||||
->i_sb and ->d_sb may be used.
|
||||
->d_sb may be used.
|
||||
|
||||
It is a tricky calling convention because it needs to be called under
|
||||
"rcu-walk", ie. without any locks or references on things.
|
||||
|
@ -32,6 +32,7 @@
|
||||
#define O_SYNC (__O_SYNC|O_DSYNC)
|
||||
|
||||
#define O_PATH 040000000
|
||||
#define O_TMPFILE 0100000000
|
||||
|
||||
#define F_GETLK 7
|
||||
#define F_SETLK 8
|
||||
|
@ -101,7 +101,7 @@ static void show_faulting_vma(unsigned long address, char *buf)
|
||||
if (file) {
|
||||
struct path *path = &file->f_path;
|
||||
nm = d_path(path, buf, PAGE_SIZE - 1);
|
||||
inode = vma->vm_file->f_path.dentry->d_inode;
|
||||
inode = file_inode(vma->vm_file);
|
||||
dev = inode->i_sb->s_dev;
|
||||
ino = inode->i_ino;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#define O_INVISIBLE 004000000 /* invisible I/O, for DMAPI/XDSM */
|
||||
|
||||
#define O_PATH 020000000
|
||||
#define O_TMPFILE 040000000
|
||||
|
||||
#define F_GETLK64 8
|
||||
#define F_SETLK64 9
|
||||
|
@ -29,25 +29,9 @@
|
||||
|
||||
#ifdef CONFIG_PPC64
|
||||
|
||||
static loff_t page_map_seek( struct file *file, loff_t off, int whence)
|
||||
static loff_t page_map_seek(struct file *file, loff_t off, int whence)
|
||||
{
|
||||
loff_t new;
|
||||
switch(whence) {
|
||||
case 0:
|
||||
new = off;
|
||||
break;
|
||||
case 1:
|
||||
new = file->f_pos + off;
|
||||
break;
|
||||
case 2:
|
||||
new = PAGE_SIZE + off;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if ( new < 0 || new > PAGE_SIZE )
|
||||
return -EINVAL;
|
||||
return (file->f_pos = new);
|
||||
return fixed_size_llseek(file, off, whence, PAGE_SIZE);
|
||||
}
|
||||
|
||||
static ssize_t page_map_read( struct file *file, char __user *buf, size_t nbytes,
|
||||
|
@ -35,6 +35,7 @@
|
||||
#define O_SYNC (__O_SYNC|O_DSYNC)
|
||||
|
||||
#define O_PATH 0x1000000
|
||||
#define O_TMPFILE 0x2000000
|
||||
|
||||
#define F_GETOWN 5 /* for sockets. */
|
||||
#define F_SETOWN 6 /* for sockets. */
|
||||
|
@ -98,32 +98,8 @@ static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector)
|
||||
static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
|
||||
{
|
||||
struct ps3_storage_device *dev = ps3flash_dev;
|
||||
loff_t res;
|
||||
|
||||
mutex_lock(&file->f_mapping->host->i_mutex);
|
||||
switch (origin) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
offset += file->f_pos;
|
||||
break;
|
||||
case 2:
|
||||
offset += dev->regions[dev->region_idx].size*dev->blk_size;
|
||||
break;
|
||||
default:
|
||||
offset = -1;
|
||||
}
|
||||
if (offset < 0) {
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
file->f_pos = offset;
|
||||
res = file->f_pos;
|
||||
|
||||
out:
|
||||
mutex_unlock(&file->f_mapping->host->i_mutex);
|
||||
return res;
|
||||
return generic_file_llseek_size(file, offset, origin, MAX_LFS_FILESIZE,
|
||||
dev->regions[dev->region_idx].size*dev->blk_size);
|
||||
}
|
||||
|
||||
static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf,
|
||||
|
@ -273,32 +273,10 @@ static ssize_t srom_write(struct file *filp, const char __user *buf,
|
||||
}
|
||||
|
||||
/* Provide our own implementation so we can use srom->total_size. */
|
||||
loff_t srom_llseek(struct file *filp, loff_t offset, int origin)
|
||||
loff_t srom_llseek(struct file *file, loff_t offset, int origin)
|
||||
{
|
||||
struct srom_dev *srom = filp->private_data;
|
||||
|
||||
if (mutex_lock_interruptible(&srom->lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
switch (origin) {
|
||||
case SEEK_END:
|
||||
offset += srom->total_size;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
offset += filp->f_pos;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset < 0 || offset > srom->total_size) {
|
||||
offset = -EINVAL;
|
||||
} else {
|
||||
filp->f_pos = offset;
|
||||
filp->f_version = 0;
|
||||
}
|
||||
|
||||
mutex_unlock(&srom->lock);
|
||||
|
||||
return offset;
|
||||
struct srom_dev *srom = file->private_data;
|
||||
return fixed_size_llseek(file, offset, origin, srom->total_size);
|
||||
}
|
||||
|
||||
static ssize_t total_show(struct device *dev,
|
||||
|
@ -55,25 +55,7 @@ struct mtd_file_info {
|
||||
static loff_t mtdchar_lseek(struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
struct mtd_file_info *mfi = file->private_data;
|
||||
struct mtd_info *mtd = mfi->mtd;
|
||||
|
||||
switch (orig) {
|
||||
case SEEK_SET:
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
offset += file->f_pos;
|
||||
break;
|
||||
case SEEK_END:
|
||||
offset += mtd->size;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (offset >= 0 && offset <= mtd->size)
|
||||
return file->f_pos = offset;
|
||||
|
||||
return -EINVAL;
|
||||
return fixed_size_llseek(file, offset, orig, mfi->mtd->size);
|
||||
}
|
||||
|
||||
static int count;
|
||||
|
@ -155,7 +155,6 @@ static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin)
|
||||
{
|
||||
struct ubi_volume_desc *desc = file->private_data;
|
||||
struct ubi_volume *vol = desc->vol;
|
||||
loff_t new_offset;
|
||||
|
||||
if (vol->updating) {
|
||||
/* Update is in progress, seeking is prohibited */
|
||||
@ -163,30 +162,7 @@ static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
switch (origin) {
|
||||
case 0: /* SEEK_SET */
|
||||
new_offset = offset;
|
||||
break;
|
||||
case 1: /* SEEK_CUR */
|
||||
new_offset = file->f_pos + offset;
|
||||
break;
|
||||
case 2: /* SEEK_END */
|
||||
new_offset = vol->used_bytes + offset;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (new_offset < 0 || new_offset > vol->used_bytes) {
|
||||
ubi_err("bad seek %lld", new_offset);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dbg_gen("seek volume %d, offset %lld, origin %d, new offset %lld",
|
||||
vol->vol_id, offset, origin, new_offset);
|
||||
|
||||
file->f_pos = new_offset;
|
||||
return new_offset;
|
||||
return fixed_size_llseek(file, offset, origin, vol->used_bytes);
|
||||
}
|
||||
|
||||
static int vol_cdev_fsync(struct file *file, loff_t start, loff_t end,
|
||||
|
@ -230,32 +230,12 @@ bnad_debugfs_open_drvinfo(struct inode *inode, struct file *file)
|
||||
static loff_t
|
||||
bnad_debugfs_lseek(struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
loff_t pos = file->f_pos;
|
||||
struct bnad_debug_info *debug = file->private_data;
|
||||
|
||||
if (!debug)
|
||||
return -EINVAL;
|
||||
|
||||
switch (orig) {
|
||||
case 0:
|
||||
file->f_pos = offset;
|
||||
break;
|
||||
case 1:
|
||||
file->f_pos += offset;
|
||||
break;
|
||||
case 2:
|
||||
file->f_pos = debug->buffer_len + offset;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (file->f_pos < 0 || file->f_pos > debug->buffer_len) {
|
||||
file->f_pos = pos;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return file->f_pos;
|
||||
return fixed_size_llseek(file, offset, orig, debug->buffer_len);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
|
@ -1056,7 +1056,7 @@ static ssize_t dev_mem_read(struct file *file,
|
||||
return -EINVAL;
|
||||
|
||||
memset(&part, 0, sizeof(part));
|
||||
part.mem.start = file->f_pos;
|
||||
part.mem.start = *ppos;
|
||||
part.mem.size = bytes;
|
||||
|
||||
buf = kmalloc(bytes, GFP_KERNEL);
|
||||
@ -1137,7 +1137,7 @@ static ssize_t dev_mem_write(struct file *file, const char __user *user_buf,
|
||||
return -EINVAL;
|
||||
|
||||
memset(&part, 0, sizeof(part));
|
||||
part.mem.start = file->f_pos;
|
||||
part.mem.start = *ppos;
|
||||
part.mem.size = bytes;
|
||||
|
||||
buf = kmalloc(bytes, GFP_KERNEL);
|
||||
|
@ -31,20 +31,9 @@
|
||||
|
||||
#define EISA_EEPROM_MINOR 241
|
||||
|
||||
static loff_t eisa_eeprom_llseek(struct file *file, loff_t offset, int origin )
|
||||
static loff_t eisa_eeprom_llseek(struct file *file, loff_t offset, int origin)
|
||||
{
|
||||
switch (origin) {
|
||||
case 0:
|
||||
/* nothing to do */
|
||||
break;
|
||||
case 1:
|
||||
offset += file->f_pos;
|
||||
break;
|
||||
case 2:
|
||||
offset += HPEE_MAX_LENGTH;
|
||||
break;
|
||||
}
|
||||
return (offset >= 0 && offset < HPEE_MAX_LENGTH) ? (file->f_pos = offset) : -EINVAL;
|
||||
return fixed_size_llseek(file, offset, origin, HPEE_MAX_LENGTH);
|
||||
}
|
||||
|
||||
static ssize_t eisa_eeprom_read(struct file * file,
|
||||
|
@ -167,26 +167,8 @@ static int open(struct inode *inode, struct file *file)
|
||||
|
||||
static loff_t lseek(struct file *file, loff_t off, int whence)
|
||||
{
|
||||
struct ctrl_dbg *dbg;
|
||||
loff_t new = -1;
|
||||
|
||||
mutex_lock(&cpqphp_mutex);
|
||||
dbg = file->private_data;
|
||||
|
||||
switch (whence) {
|
||||
case 0:
|
||||
new = off;
|
||||
break;
|
||||
case 1:
|
||||
new = file->f_pos + off;
|
||||
break;
|
||||
}
|
||||
if (new < 0 || new > dbg->size) {
|
||||
mutex_unlock(&cpqphp_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_unlock(&cpqphp_mutex);
|
||||
return (file->f_pos = new);
|
||||
struct ctrl_dbg *dbg = file->private_data;
|
||||
return fixed_size_llseek(file, off, whence, dbg->size);
|
||||
}
|
||||
|
||||
static ssize_t read(struct file *file, char __user *buf,
|
||||
|
@ -20,27 +20,8 @@ static int proc_initialized; /* = 0 */
|
||||
static loff_t
|
||||
proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
|
||||
{
|
||||
loff_t new = -1;
|
||||
struct inode *inode = file_inode(file);
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
switch (whence) {
|
||||
case 0:
|
||||
new = off;
|
||||
break;
|
||||
case 1:
|
||||
new = file->f_pos + off;
|
||||
break;
|
||||
case 2:
|
||||
new = inode->i_size + off;
|
||||
break;
|
||||
}
|
||||
if (new < 0 || new > inode->i_size)
|
||||
new = -EINVAL;
|
||||
else
|
||||
file->f_pos = new;
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return new;
|
||||
struct pci_dev *dev = PDE_DATA(file_inode(file));
|
||||
return fixed_size_llseek(file, off, whence, dev->cfg_size);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
|
@ -29,27 +29,7 @@ static struct proc_dir_entry *isapnp_proc_bus_dir = NULL;
|
||||
|
||||
static loff_t isapnp_proc_bus_lseek(struct file *file, loff_t off, int whence)
|
||||
{
|
||||
loff_t new = -1;
|
||||
struct inode *inode = file_inode(file);
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
switch (whence) {
|
||||
case 0:
|
||||
new = off;
|
||||
break;
|
||||
case 1:
|
||||
new = file->f_pos + off;
|
||||
break;
|
||||
case 2:
|
||||
new = 256 + off;
|
||||
break;
|
||||
}
|
||||
if (new < 0 || new > 256)
|
||||
new = -EINVAL;
|
||||
else
|
||||
file->f_pos = new;
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return new;
|
||||
return fixed_size_llseek(file, off, whence, 256);
|
||||
}
|
||||
|
||||
static ssize_t isapnp_proc_bus_read(struct file *file, char __user * buf,
|
||||
|
@ -173,31 +173,9 @@ bfad_debugfs_open_reg(struct inode *inode, struct file *file)
|
||||
static loff_t
|
||||
bfad_debugfs_lseek(struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
struct bfad_debug_info *debug;
|
||||
loff_t pos = file->f_pos;
|
||||
|
||||
debug = file->private_data;
|
||||
|
||||
switch (orig) {
|
||||
case 0:
|
||||
file->f_pos = offset;
|
||||
break;
|
||||
case 1:
|
||||
file->f_pos += offset;
|
||||
break;
|
||||
case 2:
|
||||
file->f_pos = debug->buffer_len + offset;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (file->f_pos < 0 || file->f_pos > debug->buffer_len) {
|
||||
file->f_pos = pos;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return file->f_pos;
|
||||
struct bfad_debug_info *debug = file->private_data;
|
||||
return fixed_size_llseek(file, offset, orig,
|
||||
debug->buffer_len);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
|
@ -164,20 +164,8 @@ static loff_t fnic_trace_debugfs_lseek(struct file *file,
|
||||
int howto)
|
||||
{
|
||||
fnic_dbgfs_t *fnic_dbg_prt = file->private_data;
|
||||
loff_t pos = -1;
|
||||
|
||||
switch (howto) {
|
||||
case 0:
|
||||
pos = offset;
|
||||
break;
|
||||
case 1:
|
||||
pos = file->f_pos + offset;
|
||||
break;
|
||||
case 2:
|
||||
pos = fnic_dbg_prt->buffer_len + offset;
|
||||
}
|
||||
return (pos < 0 || pos > fnic_dbg_prt->buffer_len) ?
|
||||
-EINVAL : (file->f_pos = pos);
|
||||
return fixed_size_llseek(file, offset, howto,
|
||||
fnic_dbg_prt->buffer_len);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1165,22 +1165,8 @@ lpfc_debugfs_nodelist_open(struct inode *inode, struct file *file)
|
||||
static loff_t
|
||||
lpfc_debugfs_lseek(struct file *file, loff_t off, int whence)
|
||||
{
|
||||
struct lpfc_debug *debug;
|
||||
loff_t pos = -1;
|
||||
|
||||
debug = file->private_data;
|
||||
|
||||
switch (whence) {
|
||||
case 0:
|
||||
pos = off;
|
||||
break;
|
||||
case 1:
|
||||
pos = file->f_pos + off;
|
||||
break;
|
||||
case 2:
|
||||
pos = debug->len + off;
|
||||
}
|
||||
return (pos < 0 || pos > debug->len) ? -EINVAL : (file->f_pos = pos);
|
||||
struct lpfc_debug *debug = file->private_data;
|
||||
return fixed_size_llseek(file, off, whence, debug->len);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -696,7 +696,7 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
ret = -EBADF;
|
||||
break;
|
||||
}
|
||||
if (!(in_egroup_p(file->f_dentry->d_inode->i_gid) ||
|
||||
if (!(in_egroup_p(file_inode(file)->i_gid) ||
|
||||
capable(CAP_SYSLOG))) {
|
||||
ret = -EPERM;
|
||||
break;
|
||||
|
@ -2329,9 +2329,6 @@ static int comedi_close(struct inode *inode, struct file *file)
|
||||
|
||||
mutex_unlock(&dev->mutex);
|
||||
|
||||
if (file->f_flags & FASYNC)
|
||||
comedi_fasync(-1, file, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -188,22 +188,7 @@ static loff_t vcs_lseek(struct file *file, loff_t offset, int orig)
|
||||
console_unlock();
|
||||
if (size < 0)
|
||||
return size;
|
||||
switch (orig) {
|
||||
default:
|
||||
return -EINVAL;
|
||||
case 2:
|
||||
offset += size;
|
||||
break;
|
||||
case 1:
|
||||
offset += file->f_pos;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
if (offset < 0 || offset > size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
file->f_pos = offset;
|
||||
return file->f_pos;
|
||||
return fixed_size_llseek(file, offset, orig, size);
|
||||
}
|
||||
|
||||
|
||||
|
@ -21,27 +21,7 @@
|
||||
static loff_t
|
||||
proc_bus_zorro_lseek(struct file *file, loff_t off, int whence)
|
||||
{
|
||||
loff_t new = -1;
|
||||
struct inode *inode = file_inode(file);
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
switch (whence) {
|
||||
case 0:
|
||||
new = off;
|
||||
break;
|
||||
case 1:
|
||||
new = file->f_pos + off;
|
||||
break;
|
||||
case 2:
|
||||
new = sizeof(struct ConfigDev) + off;
|
||||
break;
|
||||
}
|
||||
if (new < 0 || new > sizeof(struct ConfigDev))
|
||||
new = -EINVAL;
|
||||
else
|
||||
file->f_pos = new;
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return new;
|
||||
return fixed_size_llseek(file, off, whence, sizeof(struct ConfigDev));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
|
@ -191,8 +191,7 @@ const struct file_operations adfs_dir_operations = {
|
||||
};
|
||||
|
||||
static int
|
||||
adfs_hash(const struct dentry *parent, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
adfs_hash(const struct dentry *parent, struct qstr *qstr)
|
||||
{
|
||||
const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen;
|
||||
const unsigned char *name;
|
||||
@ -228,8 +227,7 @@ adfs_hash(const struct dentry *parent, const struct inode *inode,
|
||||
* requirements of the underlying filesystem.
|
||||
*/
|
||||
static int
|
||||
adfs_compare(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
adfs_compare(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
int i;
|
||||
|
@ -13,18 +13,12 @@
|
||||
typedef int (*toupper_t)(int);
|
||||
|
||||
static int affs_toupper(int ch);
|
||||
static int affs_hash_dentry(const struct dentry *,
|
||||
const struct inode *, struct qstr *);
|
||||
static int affs_compare_dentry(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
static int affs_hash_dentry(const struct dentry *, struct qstr *);
|
||||
static int affs_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name);
|
||||
static int affs_intl_toupper(int ch);
|
||||
static int affs_intl_hash_dentry(const struct dentry *,
|
||||
const struct inode *, struct qstr *);
|
||||
static int affs_intl_compare_dentry(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
static int affs_intl_hash_dentry(const struct dentry *, struct qstr *);
|
||||
static int affs_intl_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name);
|
||||
|
||||
const struct dentry_operations affs_dentry_operations = {
|
||||
@ -86,14 +80,12 @@ __affs_hash_dentry(struct qstr *qstr, toupper_t toupper)
|
||||
}
|
||||
|
||||
static int
|
||||
affs_hash_dentry(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
affs_hash_dentry(const struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
return __affs_hash_dentry(qstr, affs_toupper);
|
||||
}
|
||||
static int
|
||||
affs_intl_hash_dentry(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
affs_intl_hash_dentry(const struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
return __affs_hash_dentry(qstr, affs_intl_toupper);
|
||||
}
|
||||
@ -131,15 +123,13 @@ static inline int __affs_compare_dentry(unsigned int len,
|
||||
}
|
||||
|
||||
static int
|
||||
affs_compare_dentry(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
affs_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
return __affs_compare_dentry(len, str, name, affs_toupper);
|
||||
}
|
||||
static int
|
||||
affs_intl_compare_dentry(const struct dentry *parent,const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
affs_intl_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
return __affs_compare_dentry(len, str, name, affs_intl_toupper);
|
||||
|
@ -252,7 +252,8 @@ static void afs_defer_unlock(struct afs_vnode *vnode, struct key *key)
|
||||
*/
|
||||
static int afs_do_setlk(struct file *file, struct file_lock *fl)
|
||||
{
|
||||
struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host);
|
||||
struct inode *inode = file_inode(file);
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
afs_lock_type_t type;
|
||||
struct key *key = file->private_data;
|
||||
int ret;
|
||||
@ -273,7 +274,7 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
|
||||
|
||||
type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
|
||||
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
|
||||
/* make sure we've got a callback on this file and that our view of the
|
||||
* data version is up to date */
|
||||
@ -420,7 +421,7 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
|
||||
afs_vnode_fetch_status(vnode, NULL, key);
|
||||
|
||||
error:
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
|
2
fs/aio.c
2
fs/aio.c
@ -39,6 +39,8 @@
|
||||
#include <asm/kmap_types.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#define AIO_RING_MAGIC 0xa10a10a1
|
||||
#define AIO_RING_COMPAT_FEATURES 1
|
||||
#define AIO_RING_INCOMPAT_FEATURES 0
|
||||
|
@ -325,31 +325,10 @@ static int blkdev_write_end(struct file *file, struct address_space *mapping,
|
||||
static loff_t block_llseek(struct file *file, loff_t offset, int whence)
|
||||
{
|
||||
struct inode *bd_inode = file->f_mapping->host;
|
||||
loff_t size;
|
||||
loff_t retval;
|
||||
|
||||
mutex_lock(&bd_inode->i_mutex);
|
||||
size = i_size_read(bd_inode);
|
||||
|
||||
retval = -EINVAL;
|
||||
switch (whence) {
|
||||
case SEEK_END:
|
||||
offset += size;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
offset += file->f_pos;
|
||||
case SEEK_SET:
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
if (offset >= 0 && offset <= size) {
|
||||
if (offset != file->f_pos) {
|
||||
file->f_pos = offset;
|
||||
}
|
||||
retval = offset;
|
||||
}
|
||||
out:
|
||||
retval = fixed_size_llseek(file, offset, whence, i_size_read(bd_inode));
|
||||
mutex_unlock(&bd_inode->i_mutex);
|
||||
return retval;
|
||||
}
|
||||
|
@ -2425,20 +2425,7 @@ static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int whence)
|
||||
}
|
||||
}
|
||||
|
||||
if (offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET)) {
|
||||
offset = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (offset > inode->i_sb->s_maxbytes) {
|
||||
offset = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Special lock needed here? */
|
||||
if (offset != file->f_pos) {
|
||||
file->f_pos = offset;
|
||||
file->f_version = 0;
|
||||
}
|
||||
offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
|
||||
out:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return offset;
|
||||
|
@ -3881,7 +3881,7 @@ static long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg)
|
||||
|
||||
static long btrfs_ioctl_quota_rescan(struct file *file, void __user *arg)
|
||||
{
|
||||
struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
|
||||
struct btrfs_root *root = BTRFS_I(file_inode(file))->root;
|
||||
struct btrfs_ioctl_quota_rescan_args *qsa;
|
||||
int ret;
|
||||
|
||||
@ -3914,7 +3914,7 @@ static long btrfs_ioctl_quota_rescan(struct file *file, void __user *arg)
|
||||
|
||||
static long btrfs_ioctl_quota_rescan_status(struct file *file, void __user *arg)
|
||||
{
|
||||
struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
|
||||
struct btrfs_root *root = BTRFS_I(file_inode(file))->root;
|
||||
struct btrfs_ioctl_quota_rescan_args *qsa;
|
||||
int ret = 0;
|
||||
|
||||
@ -4020,7 +4020,7 @@ static long btrfs_ioctl_set_received_subvol(struct file *file,
|
||||
|
||||
static int btrfs_ioctl_get_fslabel(struct file *file, void __user *arg)
|
||||
{
|
||||
struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
|
||||
struct btrfs_root *root = BTRFS_I(file_inode(file))->root;
|
||||
const char *label = root->fs_info->super_copy->label;
|
||||
size_t len = strnlen(label, BTRFS_LABEL_SIZE);
|
||||
int ret;
|
||||
@ -4039,7 +4039,7 @@ static int btrfs_ioctl_get_fslabel(struct file *file, void __user *arg)
|
||||
|
||||
static int btrfs_ioctl_set_fslabel(struct file *file, void __user *arg)
|
||||
{
|
||||
struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
|
||||
struct btrfs_root *root = BTRFS_I(file_inode(file))->root;
|
||||
struct btrfs_super_block *super_block = root->fs_info->super_copy;
|
||||
struct btrfs_trans_handle *trans;
|
||||
char label[BTRFS_LABEL_SIZE];
|
||||
|
@ -866,16 +866,7 @@ static loff_t ceph_llseek(struct file *file, loff_t offset, int whence)
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset < 0 || offset > inode->i_sb->s_maxbytes) {
|
||||
offset = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Special lock needed here? */
|
||||
if (offset != file->f_pos) {
|
||||
file->f_pos = offset;
|
||||
file->f_version = 0;
|
||||
}
|
||||
offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
|
||||
|
||||
out:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
@ -192,7 +192,7 @@ void ceph_count_locks(struct inode *inode, int *fcntl_count, int *flock_count)
|
||||
|
||||
/**
|
||||
* Encode the flock and fcntl locks for the given inode into the ceph_filelock
|
||||
* array. Must be called with lock_flocks() already held.
|
||||
* array. Must be called with inode->i_lock already held.
|
||||
* If we encounter more of a specific lock type than expected, return -ENOSPC.
|
||||
*/
|
||||
int ceph_encode_locks_to_buffer(struct inode *inode,
|
||||
|
@ -2481,20 +2481,20 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap,
|
||||
struct ceph_filelock *flocks;
|
||||
|
||||
encode_again:
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
ceph_count_locks(inode, &num_fcntl_locks, &num_flock_locks);
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
flocks = kmalloc((num_fcntl_locks+num_flock_locks) *
|
||||
sizeof(struct ceph_filelock), GFP_NOFS);
|
||||
if (!flocks) {
|
||||
err = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
err = ceph_encode_locks_to_buffer(inode, flocks,
|
||||
num_fcntl_locks,
|
||||
num_flock_locks);
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
if (err) {
|
||||
kfree(flocks);
|
||||
if (err == -ENOSPC)
|
||||
|
@ -765,7 +765,7 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int whence)
|
||||
|
||||
static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
|
||||
{
|
||||
/* note that this is called by vfs setlease with lock_flocks held
|
||||
/* note that this is called by vfs setlease with i_lock held
|
||||
to protect *lease from going away */
|
||||
struct inode *inode = file_inode(file);
|
||||
struct cifsFileInfo *cfile = file->private_data;
|
||||
|
@ -822,8 +822,7 @@ const struct dentry_operations cifs_dentry_ops = {
|
||||
/* d_delete: cifs_d_delete, */ /* not needed except for debugging */
|
||||
};
|
||||
|
||||
static int cifs_ci_hash(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *q)
|
||||
static int cifs_ci_hash(const struct dentry *dentry, struct qstr *q)
|
||||
{
|
||||
struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls;
|
||||
unsigned long hash;
|
||||
@ -838,12 +837,10 @@ static int cifs_ci_hash(const struct dentry *dentry, const struct inode *inode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cifs_ci_compare(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
static int cifs_ci_compare(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
struct nls_table *codepage = CIFS_SB(pinode->i_sb)->local_nls;
|
||||
struct nls_table *codepage = CIFS_SB(parent->d_sb)->local_nls;
|
||||
|
||||
if ((name->len == len) &&
|
||||
(nls_strnicmp(codepage, name->name, str, len) == 0))
|
||||
|
@ -999,7 +999,7 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock)
|
||||
rc = wait_event_interruptible(flock->fl_wait, !flock->fl_next);
|
||||
if (!rc)
|
||||
goto try_again;
|
||||
locks_delete_block(flock);
|
||||
posix_unblock_lock(flock);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -1092,6 +1092,7 @@ struct lock_to_push {
|
||||
static int
|
||||
cifs_push_posix_locks(struct cifsFileInfo *cfile)
|
||||
{
|
||||
struct inode *inode = cfile->dentry->d_inode;
|
||||
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
||||
struct file_lock *flock, **before;
|
||||
unsigned int count = 0, i = 0;
|
||||
@ -1102,12 +1103,12 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
lock_flocks();
|
||||
cifs_for_each_lock(cfile->dentry->d_inode, before) {
|
||||
spin_lock(&inode->i_lock);
|
||||
cifs_for_each_lock(inode, before) {
|
||||
if ((*before)->fl_flags & FL_POSIX)
|
||||
count++;
|
||||
}
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
INIT_LIST_HEAD(&locks_to_send);
|
||||
|
||||
@ -1126,8 +1127,8 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
|
||||
}
|
||||
|
||||
el = locks_to_send.next;
|
||||
lock_flocks();
|
||||
cifs_for_each_lock(cfile->dentry->d_inode, before) {
|
||||
spin_lock(&inode->i_lock);
|
||||
cifs_for_each_lock(inode, before) {
|
||||
flock = *before;
|
||||
if ((flock->fl_flags & FL_POSIX) == 0)
|
||||
continue;
|
||||
@ -1152,7 +1153,7 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
|
||||
lck->offset = flock->fl_start;
|
||||
el = el->next;
|
||||
}
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) {
|
||||
int stored_rc;
|
||||
|
@ -487,13 +487,7 @@ static int coda_venus_readdir(struct file *coda_file, struct dir_context *ctx)
|
||||
|
||||
/* skip null entries */
|
||||
if (vdir->d_fileno && name.len) {
|
||||
/* try to look up this entry in the dcache, that way
|
||||
* userspace doesn't have to worry about breaking
|
||||
* getcwd by having mismatched inode numbers for
|
||||
* internal volume mountpoints. */
|
||||
ino = find_inode_number(de, &name);
|
||||
if (!ino) ino = vdir->d_fileno;
|
||||
|
||||
ino = vdir->d_fileno;
|
||||
type = CDT2DT(vdir->d_type);
|
||||
if (!dir_emit(ctx, name.name, name.len, ino, type))
|
||||
break;
|
||||
|
66
fs/dcache.c
66
fs/dcache.c
@ -1730,7 +1730,7 @@ EXPORT_SYMBOL(d_add_ci);
|
||||
* Do the slow-case of the dentry name compare.
|
||||
*
|
||||
* Unlike the dentry_cmp() function, we need to atomically
|
||||
* load the name, length and inode information, so that the
|
||||
* load the name and length information, so that the
|
||||
* filesystem can rely on them, and can use the 'name' and
|
||||
* 'len' information without worrying about walking off the
|
||||
* end of memory etc.
|
||||
@ -1748,22 +1748,18 @@ enum slow_d_compare {
|
||||
|
||||
static noinline enum slow_d_compare slow_dentry_cmp(
|
||||
const struct dentry *parent,
|
||||
struct inode *inode,
|
||||
struct dentry *dentry,
|
||||
unsigned int seq,
|
||||
const struct qstr *name)
|
||||
{
|
||||
int tlen = dentry->d_name.len;
|
||||
const char *tname = dentry->d_name.name;
|
||||
struct inode *i = dentry->d_inode;
|
||||
|
||||
if (read_seqcount_retry(&dentry->d_seq, seq)) {
|
||||
cpu_relax();
|
||||
return D_COMP_SEQRETRY;
|
||||
}
|
||||
if (parent->d_op->d_compare(parent, inode,
|
||||
dentry, i,
|
||||
tlen, tname, name))
|
||||
if (parent->d_op->d_compare(parent, dentry, tlen, tname, name))
|
||||
return D_COMP_NOMATCH;
|
||||
return D_COMP_OK;
|
||||
}
|
||||
@ -1773,7 +1769,6 @@ static noinline enum slow_d_compare slow_dentry_cmp(
|
||||
* @parent: parent dentry
|
||||
* @name: qstr of name we wish to find
|
||||
* @seqp: returns d_seq value at the point where the dentry was found
|
||||
* @inode: returns dentry->d_inode when the inode was found valid.
|
||||
* Returns: dentry, or NULL
|
||||
*
|
||||
* __d_lookup_rcu is the dcache lookup function for rcu-walk name
|
||||
@ -1800,7 +1795,7 @@ static noinline enum slow_d_compare slow_dentry_cmp(
|
||||
*/
|
||||
struct dentry *__d_lookup_rcu(const struct dentry *parent,
|
||||
const struct qstr *name,
|
||||
unsigned *seqp, struct inode *inode)
|
||||
unsigned *seqp)
|
||||
{
|
||||
u64 hashlen = name->hash_len;
|
||||
const unsigned char *str = name->name;
|
||||
@ -1834,11 +1829,10 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent,
|
||||
seqretry:
|
||||
/*
|
||||
* The dentry sequence count protects us from concurrent
|
||||
* renames, and thus protects inode, parent and name fields.
|
||||
* renames, and thus protects parent and name fields.
|
||||
*
|
||||
* The caller must perform a seqcount check in order
|
||||
* to do anything useful with the returned dentry,
|
||||
* including using the 'd_inode' pointer.
|
||||
* to do anything useful with the returned dentry.
|
||||
*
|
||||
* NOTE! We do a "raw" seqcount_begin here. That means that
|
||||
* we don't wait for the sequence count to stabilize if it
|
||||
@ -1852,12 +1846,12 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent,
|
||||
continue;
|
||||
if (d_unhashed(dentry))
|
||||
continue;
|
||||
*seqp = seq;
|
||||
|
||||
if (unlikely(parent->d_flags & DCACHE_OP_COMPARE)) {
|
||||
if (dentry->d_name.hash != hashlen_hash(hashlen))
|
||||
continue;
|
||||
switch (slow_dentry_cmp(parent, inode, dentry, seq, name)) {
|
||||
*seqp = seq;
|
||||
switch (slow_dentry_cmp(parent, dentry, seq, name)) {
|
||||
case D_COMP_OK:
|
||||
return dentry;
|
||||
case D_COMP_NOMATCH:
|
||||
@ -1869,6 +1863,7 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent,
|
||||
|
||||
if (dentry->d_name.hash_len != hashlen)
|
||||
continue;
|
||||
*seqp = seq;
|
||||
if (!dentry_cmp(dentry, str, hashlen_len(hashlen)))
|
||||
return dentry;
|
||||
}
|
||||
@ -1966,9 +1961,7 @@ struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name)
|
||||
if (parent->d_flags & DCACHE_OP_COMPARE) {
|
||||
int tlen = dentry->d_name.len;
|
||||
const char *tname = dentry->d_name.name;
|
||||
if (parent->d_op->d_compare(parent, parent->d_inode,
|
||||
dentry, dentry->d_inode,
|
||||
tlen, tname, name))
|
||||
if (parent->d_op->d_compare(parent, dentry, tlen, tname, name))
|
||||
goto next;
|
||||
} else {
|
||||
if (dentry->d_name.len != len)
|
||||
@ -2005,7 +1998,7 @@ struct dentry *d_hash_and_lookup(struct dentry *dir, struct qstr *name)
|
||||
*/
|
||||
name->hash = full_name_hash(name->name, name->len);
|
||||
if (dir->d_flags & DCACHE_OP_HASH) {
|
||||
int err = dir->d_op->d_hash(dir, dir->d_inode, name);
|
||||
int err = dir->d_op->d_hash(dir, name);
|
||||
if (unlikely(err < 0))
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
@ -2975,34 +2968,21 @@ void d_genocide(struct dentry *root)
|
||||
goto again;
|
||||
}
|
||||
|
||||
/**
|
||||
* find_inode_number - check for dentry with name
|
||||
* @dir: directory to check
|
||||
* @name: Name to find.
|
||||
*
|
||||
* Check whether a dentry already exists for the given name,
|
||||
* and return the inode number if it has an inode. Otherwise
|
||||
* 0 is returned.
|
||||
*
|
||||
* This routine is used to post-process directory listings for
|
||||
* filesystems using synthetic inode numbers, and is necessary
|
||||
* to keep getcwd() working.
|
||||
*/
|
||||
|
||||
ino_t find_inode_number(struct dentry *dir, struct qstr *name)
|
||||
void d_tmpfile(struct dentry *dentry, struct inode *inode)
|
||||
{
|
||||
struct dentry * dentry;
|
||||
ino_t ino = 0;
|
||||
|
||||
dentry = d_hash_and_lookup(dir, name);
|
||||
if (!IS_ERR_OR_NULL(dentry)) {
|
||||
if (dentry->d_inode)
|
||||
ino = dentry->d_inode->i_ino;
|
||||
dput(dentry);
|
||||
}
|
||||
return ino;
|
||||
inode_dec_link_count(inode);
|
||||
BUG_ON(dentry->d_name.name != dentry->d_iname ||
|
||||
!hlist_unhashed(&dentry->d_alias) ||
|
||||
!d_unlinked(dentry));
|
||||
spin_lock(&dentry->d_parent->d_lock);
|
||||
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
dentry->d_name.len = sprintf(dentry->d_iname, "#%llu",
|
||||
(unsigned long long)inode->i_ino);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&dentry->d_parent->d_lock);
|
||||
d_instantiate(dentry, inode);
|
||||
}
|
||||
EXPORT_SYMBOL(find_inode_number);
|
||||
EXPORT_SYMBOL(d_tmpfile);
|
||||
|
||||
static __initdata unsigned long dhash_entries;
|
||||
static int __init set_dhash_entries(char *str)
|
||||
|
@ -2243,12 +2243,11 @@ int ecryptfs_encrypt_and_encode_filename(
|
||||
*/
|
||||
int ecryptfs_decode_and_decrypt_filename(char **plaintext_name,
|
||||
size_t *plaintext_name_size,
|
||||
struct dentry *ecryptfs_dir_dentry,
|
||||
struct super_block *sb,
|
||||
const char *name, size_t name_size)
|
||||
{
|
||||
struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
|
||||
&ecryptfs_superblock_to_private(
|
||||
ecryptfs_dir_dentry->d_sb)->mount_crypt_stat;
|
||||
&ecryptfs_superblock_to_private(sb)->mount_crypt_stat;
|
||||
char *decoded_name;
|
||||
size_t decoded_name_size;
|
||||
size_t packet_size;
|
||||
|
@ -575,7 +575,7 @@ int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry,
|
||||
struct inode *ecryptfs_inode);
|
||||
int ecryptfs_decode_and_decrypt_filename(char **decrypted_name,
|
||||
size_t *decrypted_name_size,
|
||||
struct dentry *ecryptfs_dentry,
|
||||
struct super_block *sb,
|
||||
const char *name, size_t name_size);
|
||||
int ecryptfs_fill_zeros(struct file *file, loff_t new_length);
|
||||
int ecryptfs_encrypt_and_encode_filename(
|
||||
|
@ -70,7 +70,7 @@ static ssize_t ecryptfs_read_update_atime(struct kiocb *iocb,
|
||||
struct ecryptfs_getdents_callback {
|
||||
struct dir_context ctx;
|
||||
struct dir_context *caller;
|
||||
struct dentry *dentry;
|
||||
struct super_block *sb;
|
||||
int filldir_called;
|
||||
int entries_written;
|
||||
};
|
||||
@ -88,7 +88,7 @@ ecryptfs_filldir(void *dirent, const char *lower_name, int lower_namelen,
|
||||
|
||||
buf->filldir_called++;
|
||||
rc = ecryptfs_decode_and_decrypt_filename(&name, &name_size,
|
||||
buf->dentry, lower_name,
|
||||
buf->sb, lower_name,
|
||||
lower_namelen);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "%s: Error attempting to decode and decrypt "
|
||||
@ -114,15 +114,14 @@ static int ecryptfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
int rc;
|
||||
struct file *lower_file;
|
||||
struct inode *inode;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct ecryptfs_getdents_callback buf = {
|
||||
.ctx.actor = ecryptfs_filldir,
|
||||
.caller = ctx,
|
||||
.dentry = file->f_path.dentry
|
||||
.sb = inode->i_sb,
|
||||
};
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
lower_file->f_pos = ctx->pos;
|
||||
inode = file_inode(file);
|
||||
rc = iterate_dir(lower_file, &buf.ctx);
|
||||
ctx->pos = buf.ctx.pos;
|
||||
if (rc < 0)
|
||||
|
@ -679,7 +679,7 @@ static int ecryptfs_readlink_lower(struct dentry *dentry, char **buf,
|
||||
set_fs(old_fs);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
rc = ecryptfs_decode_and_decrypt_filename(buf, bufsiz, dentry,
|
||||
rc = ecryptfs_decode_and_decrypt_filename(buf, bufsiz, dentry->d_sb,
|
||||
lower_buf, rc);
|
||||
out:
|
||||
kfree(lower_buf);
|
||||
|
@ -45,8 +45,8 @@ static struct super_block *efivarfs_sb;
|
||||
* So we need to perform a case-sensitive match on part 1 and a
|
||||
* case-insensitive match on part 2.
|
||||
*/
|
||||
static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
static int efivarfs_d_compare(const struct dentry *parent,
|
||||
const struct dentry *dentry,
|
||||
unsigned int len, const char *str,
|
||||
const struct qstr *name)
|
||||
{
|
||||
@ -63,8 +63,7 @@ static int efivarfs_d_compare(const struct dentry *parent, const struct inode *p
|
||||
return strncasecmp(name->name + guid, str + guid, EFI_VARIABLE_GUID_LEN);
|
||||
}
|
||||
|
||||
static int efivarfs_d_hash(const struct dentry *dentry,
|
||||
const struct inode *inode, struct qstr *qstr)
|
||||
static int efivarfs_d_hash(const struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
unsigned long hash = init_name_hash();
|
||||
const unsigned char *s = qstr->name;
|
||||
@ -108,7 +107,7 @@ static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
|
||||
q.name = name;
|
||||
q.len = strlen(name);
|
||||
|
||||
err = efivarfs_d_hash(NULL, NULL, &q);
|
||||
err = efivarfs_d_hash(NULL, &q);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
|
10
fs/exec.c
10
fs/exec.c
@ -110,13 +110,14 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
|
||||
static const struct open_flags uselib_flags = {
|
||||
.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
|
||||
.acc_mode = MAY_READ | MAY_EXEC | MAY_OPEN,
|
||||
.intent = LOOKUP_OPEN
|
||||
.intent = LOOKUP_OPEN,
|
||||
.lookup_flags = LOOKUP_FOLLOW,
|
||||
};
|
||||
|
||||
if (IS_ERR(tmp))
|
||||
goto out;
|
||||
|
||||
file = do_filp_open(AT_FDCWD, tmp, &uselib_flags, LOOKUP_FOLLOW);
|
||||
file = do_filp_open(AT_FDCWD, tmp, &uselib_flags);
|
||||
putname(tmp);
|
||||
error = PTR_ERR(file);
|
||||
if (IS_ERR(file))
|
||||
@ -756,10 +757,11 @@ struct file *open_exec(const char *name)
|
||||
static const struct open_flags open_exec_flags = {
|
||||
.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
|
||||
.acc_mode = MAY_EXEC | MAY_OPEN,
|
||||
.intent = LOOKUP_OPEN
|
||||
.intent = LOOKUP_OPEN,
|
||||
.lookup_flags = LOOKUP_FOLLOW,
|
||||
};
|
||||
|
||||
file = do_filp_open(AT_FDCWD, &tmp, &open_exec_flags, LOOKUP_FOLLOW);
|
||||
file = do_filp_open(AT_FDCWD, &tmp, &open_exec_flags);
|
||||
if (IS_ERR(file))
|
||||
goto out;
|
||||
|
||||
|
@ -119,6 +119,29 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, umode_t mode
|
||||
return ext2_add_nondir(dentry, inode);
|
||||
}
|
||||
|
||||
static int ext2_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
struct inode *inode = ext2_new_inode(dir, mode, NULL);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
|
||||
inode->i_op = &ext2_file_inode_operations;
|
||||
if (ext2_use_xip(inode->i_sb)) {
|
||||
inode->i_mapping->a_ops = &ext2_aops_xip;
|
||||
inode->i_fop = &ext2_xip_file_operations;
|
||||
} else if (test_opt(inode->i_sb, NOBH)) {
|
||||
inode->i_mapping->a_ops = &ext2_nobh_aops;
|
||||
inode->i_fop = &ext2_file_operations;
|
||||
} else {
|
||||
inode->i_mapping->a_ops = &ext2_aops;
|
||||
inode->i_fop = &ext2_file_operations;
|
||||
}
|
||||
mark_inode_dirty(inode);
|
||||
d_tmpfile(dentry, inode);
|
||||
unlock_new_inode(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext2_mknod (struct inode * dir, struct dentry *dentry, umode_t mode, dev_t rdev)
|
||||
{
|
||||
struct inode * inode;
|
||||
@ -398,6 +421,7 @@ const struct inode_operations ext2_dir_inode_operations = {
|
||||
#endif
|
||||
.setattr = ext2_setattr,
|
||||
.get_acl = ext2_get_acl,
|
||||
.tmpfile = ext2_tmpfile,
|
||||
};
|
||||
|
||||
const struct inode_operations ext2_special_inode_operations = {
|
||||
|
@ -1759,6 +1759,45 @@ static int ext3_mknod (struct inode * dir, struct dentry *dentry,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ext3_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
handle_t *handle;
|
||||
struct inode *inode;
|
||||
int err, retries = 0;
|
||||
|
||||
dquot_initialize(dir);
|
||||
|
||||
retry:
|
||||
handle = ext3_journal_start(dir, EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) +
|
||||
4 + EXT3_XATTR_TRANS_BLOCKS);
|
||||
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
|
||||
inode = ext3_new_inode (handle, dir, NULL, mode);
|
||||
err = PTR_ERR(inode);
|
||||
if (!IS_ERR(inode)) {
|
||||
inode->i_op = &ext3_file_inode_operations;
|
||||
inode->i_fop = &ext3_file_operations;
|
||||
ext3_set_aops(inode);
|
||||
err = ext3_orphan_add(handle, inode);
|
||||
if (err)
|
||||
goto err_drop_inode;
|
||||
mark_inode_dirty(inode);
|
||||
d_tmpfile(dentry, inode);
|
||||
unlock_new_inode(inode);
|
||||
}
|
||||
ext3_journal_stop(handle);
|
||||
if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
|
||||
goto retry;
|
||||
return err;
|
||||
err_drop_inode:
|
||||
ext3_journal_stop(handle);
|
||||
unlock_new_inode(inode);
|
||||
iput(inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ext3_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode)
|
||||
{
|
||||
handle_t *handle;
|
||||
@ -2300,7 +2339,7 @@ static int ext3_link (struct dentry * old_dentry,
|
||||
|
||||
retry:
|
||||
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
|
||||
EXT3_INDEX_EXTRA_TRANS_BLOCKS);
|
||||
EXT3_INDEX_EXTRA_TRANS_BLOCKS + 1);
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
|
||||
@ -2314,6 +2353,11 @@ static int ext3_link (struct dentry * old_dentry,
|
||||
err = ext3_add_entry(handle, dentry, inode);
|
||||
if (!err) {
|
||||
ext3_mark_inode_dirty(handle, inode);
|
||||
/* this can happen only for tmpfile being
|
||||
* linked the first time
|
||||
*/
|
||||
if (inode->i_nlink == 1)
|
||||
ext3_orphan_del(handle, inode);
|
||||
d_instantiate(dentry, inode);
|
||||
} else {
|
||||
drop_nlink(inode);
|
||||
@ -2516,6 +2560,7 @@ const struct inode_operations ext3_dir_inode_operations = {
|
||||
.mkdir = ext3_mkdir,
|
||||
.rmdir = ext3_rmdir,
|
||||
.mknod = ext3_mknod,
|
||||
.tmpfile = ext3_tmpfile,
|
||||
.rename = ext3_rename,
|
||||
.setattr = ext3_setattr,
|
||||
#ifdef CONFIG_EXT3_FS_XATTR
|
||||
|
@ -494,17 +494,7 @@ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize)
|
||||
if (dataoff > isize)
|
||||
return -ENXIO;
|
||||
|
||||
if (dataoff < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET))
|
||||
return -EINVAL;
|
||||
if (dataoff > maxsize)
|
||||
return -EINVAL;
|
||||
|
||||
if (dataoff != file->f_pos) {
|
||||
file->f_pos = dataoff;
|
||||
file->f_version = 0;
|
||||
}
|
||||
|
||||
return dataoff;
|
||||
return vfs_setpos(file, dataoff, maxsize);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -580,17 +570,7 @@ static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize)
|
||||
if (holeoff > isize)
|
||||
holeoff = isize;
|
||||
|
||||
if (holeoff < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET))
|
||||
return -EINVAL;
|
||||
if (holeoff > maxsize)
|
||||
return -EINVAL;
|
||||
|
||||
if (holeoff != file->f_pos) {
|
||||
file->f_pos = holeoff;
|
||||
file->f_version = 0;
|
||||
}
|
||||
|
||||
return holeoff;
|
||||
return vfs_setpos(file, holeoff, maxsize);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2296,6 +2296,45 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ext4_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
handle_t *handle;
|
||||
struct inode *inode;
|
||||
int err, retries = 0;
|
||||
|
||||
dquot_initialize(dir);
|
||||
|
||||
retry:
|
||||
inode = ext4_new_inode_start_handle(dir, mode,
|
||||
NULL, 0, NULL,
|
||||
EXT4_HT_DIR,
|
||||
EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) +
|
||||
4 + EXT4_XATTR_TRANS_BLOCKS);
|
||||
handle = ext4_journal_current_handle();
|
||||
err = PTR_ERR(inode);
|
||||
if (!IS_ERR(inode)) {
|
||||
inode->i_op = &ext4_file_inode_operations;
|
||||
inode->i_fop = &ext4_file_operations;
|
||||
ext4_set_aops(inode);
|
||||
err = ext4_orphan_add(handle, inode);
|
||||
if (err)
|
||||
goto err_drop_inode;
|
||||
mark_inode_dirty(inode);
|
||||
d_tmpfile(dentry, inode);
|
||||
unlock_new_inode(inode);
|
||||
}
|
||||
if (handle)
|
||||
ext4_journal_stop(handle);
|
||||
if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
|
||||
goto retry;
|
||||
return err;
|
||||
err_drop_inode:
|
||||
ext4_journal_stop(handle);
|
||||
unlock_new_inode(inode);
|
||||
iput(inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
struct ext4_dir_entry_2 *ext4_init_dot_dotdot(struct inode *inode,
|
||||
struct ext4_dir_entry_2 *de,
|
||||
int blocksize, int csum_size,
|
||||
@ -2903,7 +2942,7 @@ static int ext4_link(struct dentry *old_dentry,
|
||||
retry:
|
||||
handle = ext4_journal_start(dir, EXT4_HT_DIR,
|
||||
(EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
|
||||
EXT4_INDEX_EXTRA_TRANS_BLOCKS));
|
||||
EXT4_INDEX_EXTRA_TRANS_BLOCKS) + 1);
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
|
||||
@ -2917,6 +2956,11 @@ static int ext4_link(struct dentry *old_dentry,
|
||||
err = ext4_add_entry(handle, dentry, inode);
|
||||
if (!err) {
|
||||
ext4_mark_inode_dirty(handle, inode);
|
||||
/* this can happen only for tmpfile being
|
||||
* linked the first time
|
||||
*/
|
||||
if (inode->i_nlink == 1)
|
||||
ext4_orphan_del(handle, inode);
|
||||
d_instantiate(dentry, inode);
|
||||
} else {
|
||||
drop_nlink(inode);
|
||||
@ -3169,6 +3213,7 @@ const struct inode_operations ext4_dir_inode_operations = {
|
||||
.mkdir = ext4_mkdir,
|
||||
.rmdir = ext4_rmdir,
|
||||
.mknod = ext4_mknod,
|
||||
.tmpfile = ext4_tmpfile,
|
||||
.rename = ext4_rename,
|
||||
.setattr = ext4_setattr,
|
||||
.setxattr = generic_setxattr,
|
||||
|
@ -148,8 +148,7 @@ static int msdos_find(struct inode *dir, const unsigned char *name, int len,
|
||||
* that the existing dentry can be used. The msdos fs routines will
|
||||
* return ENOENT or EINVAL as appropriate.
|
||||
*/
|
||||
static int msdos_hash(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
static int msdos_hash(const struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
|
||||
unsigned char msdos_name[MSDOS_NAME];
|
||||
@ -165,8 +164,7 @@ static int msdos_hash(const struct dentry *dentry, const struct inode *inode,
|
||||
* Compare two msdos names. If either of the names are invalid,
|
||||
* we fall back to doing the standard name comparison.
|
||||
*/
|
||||
static int msdos_cmp(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
static int msdos_cmp(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
struct fat_mount_options *options = &MSDOS_SB(parent->d_sb)->options;
|
||||
|
@ -107,8 +107,7 @@ static unsigned int vfat_striptail_len(const struct qstr *qstr)
|
||||
* that the existing dentry can be used. The vfat fs routines will
|
||||
* return ENOENT or EINVAL as appropriate.
|
||||
*/
|
||||
static int vfat_hash(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
static int vfat_hash(const struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
qstr->hash = full_name_hash(qstr->name, vfat_striptail_len(qstr));
|
||||
return 0;
|
||||
@ -120,8 +119,7 @@ static int vfat_hash(const struct dentry *dentry, const struct inode *inode,
|
||||
* that the existing dentry can be used. The vfat fs routines will
|
||||
* return ENOENT or EINVAL as appropriate.
|
||||
*/
|
||||
static int vfat_hashi(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
static int vfat_hashi(const struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io;
|
||||
const unsigned char *name;
|
||||
@ -142,8 +140,7 @@ static int vfat_hashi(const struct dentry *dentry, const struct inode *inode,
|
||||
/*
|
||||
* Case insensitive compare of two vfat names.
|
||||
*/
|
||||
static int vfat_cmpi(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
static int vfat_cmpi(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io;
|
||||
@ -162,8 +159,7 @@ static int vfat_cmpi(const struct dentry *parent, const struct inode *pinode,
|
||||
/*
|
||||
* Case sensitive compare of two vfat names.
|
||||
*/
|
||||
static int vfat_cmp(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
static int vfat_cmp(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
unsigned int alen, blen;
|
||||
|
@ -227,7 +227,7 @@ static void __fput(struct file *file)
|
||||
{
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct vfsmount *mnt = file->f_path.mnt;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct inode *inode = file->f_inode;
|
||||
|
||||
might_sleep();
|
||||
|
||||
|
@ -548,8 +548,7 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos)
|
||||
res = io->bytes < 0 ? io->size : io->bytes;
|
||||
|
||||
if (!is_sync_kiocb(io->iocb)) {
|
||||
struct path *path = &io->iocb->ki_filp->f_path;
|
||||
struct inode *inode = path->dentry->d_inode;
|
||||
struct inode *inode = file_inode(io->iocb->ki_filp);
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
|
||||
|
@ -109,8 +109,7 @@ static int gfs2_drevalidate(struct dentry *dentry, unsigned int flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gfs2_dhash(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *str)
|
||||
static int gfs2_dhash(const struct dentry *dentry, struct qstr *str)
|
||||
{
|
||||
str->hash = gfs2_disk_hash(str->name, str->len);
|
||||
return 0;
|
||||
|
@ -912,7 +912,7 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset,
|
||||
* cluster; until we do, disable leases (by just returning -EINVAL),
|
||||
* unless the administrator has requested purely local locking.
|
||||
*
|
||||
* Locking: called under lock_flocks
|
||||
* Locking: called under i_lock
|
||||
*
|
||||
* Returns: errno
|
||||
*/
|
||||
|
@ -229,13 +229,10 @@ extern int hfs_part_find(struct super_block *, sector_t *, sector_t *);
|
||||
/* string.c */
|
||||
extern const struct dentry_operations hfs_dentry_operations;
|
||||
|
||||
extern int hfs_hash_dentry(const struct dentry *, const struct inode *,
|
||||
struct qstr *);
|
||||
extern int hfs_hash_dentry(const struct dentry *, struct qstr *);
|
||||
extern int hfs_strcmp(const unsigned char *, unsigned int,
|
||||
const unsigned char *, unsigned int);
|
||||
extern int hfs_compare_dentry(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
extern int hfs_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name);
|
||||
|
||||
/* trans.c */
|
||||
|
@ -51,8 +51,7 @@ static unsigned char caseorder[256] = {
|
||||
/*
|
||||
* Hash a string to an integer in a case-independent way
|
||||
*/
|
||||
int hfs_hash_dentry(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *this)
|
||||
int hfs_hash_dentry(const struct dentry *dentry, struct qstr *this)
|
||||
{
|
||||
const unsigned char *name = this->name;
|
||||
unsigned int hash, len = this->len;
|
||||
@ -93,8 +92,7 @@ int hfs_strcmp(const unsigned char *s1, unsigned int len1,
|
||||
* Test for equality of two strings in the HFS filename character ordering.
|
||||
* return 1 on failure and 0 on success
|
||||
*/
|
||||
int hfs_compare_dentry(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
int hfs_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
const unsigned char *n1, *n2;
|
||||
|
@ -495,11 +495,8 @@ int hfsplus_uni2asc(struct super_block *,
|
||||
const struct hfsplus_unistr *, char *, int *);
|
||||
int hfsplus_asc2uni(struct super_block *,
|
||||
struct hfsplus_unistr *, int, const char *, int);
|
||||
int hfsplus_hash_dentry(const struct dentry *dentry,
|
||||
const struct inode *inode, struct qstr *str);
|
||||
int hfsplus_compare_dentry(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str);
|
||||
int hfsplus_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name);
|
||||
|
||||
/* wrapper.c */
|
||||
|
@ -334,8 +334,7 @@ int hfsplus_asc2uni(struct super_block *sb,
|
||||
* Composed unicode characters are decomposed and case-folding is performed
|
||||
* if the appropriate bits are (un)set on the superblock.
|
||||
*/
|
||||
int hfsplus_hash_dentry(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *str)
|
||||
int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str)
|
||||
{
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
const char *astr;
|
||||
@ -386,9 +385,7 @@ int hfsplus_hash_dentry(const struct dentry *dentry, const struct inode *inode,
|
||||
* Composed unicode characters are decomposed and case-folding is performed
|
||||
* if the appropriate bits are (un)set on the superblock.
|
||||
*/
|
||||
int hfsplus_compare_dentry(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
int hfsplus_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
struct super_block *sb = parent->d_sb;
|
||||
|
@ -12,8 +12,7 @@
|
||||
* Note: the dentry argument is the parent dentry.
|
||||
*/
|
||||
|
||||
static int hpfs_hash_dentry(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
static int hpfs_hash_dentry(const struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
unsigned long hash;
|
||||
int i;
|
||||
@ -35,9 +34,7 @@ static int hpfs_hash_dentry(const struct dentry *dentry, const struct inode *ino
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hpfs_compare_dentry(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
static int hpfs_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
unsigned al = len;
|
||||
|
@ -333,8 +333,10 @@ EXPORT_SYMBOL(set_nlink);
|
||||
*/
|
||||
void inc_nlink(struct inode *inode)
|
||||
{
|
||||
if (WARN_ON(inode->i_nlink == 0))
|
||||
if (unlikely(inode->i_nlink == 0)) {
|
||||
WARN_ON(!(inode->i_state & I_LINKABLE));
|
||||
atomic_long_dec(&inode->i_sb->s_remove_count);
|
||||
}
|
||||
|
||||
inode->__i_nlink++;
|
||||
}
|
||||
|
@ -96,11 +96,12 @@ struct open_flags {
|
||||
umode_t mode;
|
||||
int acc_mode;
|
||||
int intent;
|
||||
int lookup_flags;
|
||||
};
|
||||
extern struct file *do_filp_open(int dfd, struct filename *pathname,
|
||||
const struct open_flags *op, int flags);
|
||||
const struct open_flags *op);
|
||||
extern struct file *do_file_open_root(struct dentry *, struct vfsmount *,
|
||||
const char *, const struct open_flags *, int lookup_flags);
|
||||
const char *, const struct open_flags *);
|
||||
|
||||
extern long do_handle_open(int mountdirfd,
|
||||
struct file_handle __user *ufh, int open_flag);
|
||||
@ -130,6 +131,7 @@ extern struct dentry *__d_alloc(struct super_block *, const struct qstr *);
|
||||
* read_write.c
|
||||
*/
|
||||
extern ssize_t __kernel_write(struct file *, const char *, size_t, loff_t *);
|
||||
extern int rw_verify_area(int, struct file *, const loff_t *, size_t);
|
||||
|
||||
/*
|
||||
* splice.c
|
||||
|
@ -28,31 +28,23 @@
|
||||
|
||||
#define BEQUIET
|
||||
|
||||
static int isofs_hashi(const struct dentry *parent, const struct inode *inode,
|
||||
struct qstr *qstr);
|
||||
static int isofs_hash(const struct dentry *parent, const struct inode *inode,
|
||||
struct qstr *qstr);
|
||||
static int isofs_hashi(const struct dentry *parent, struct qstr *qstr);
|
||||
static int isofs_hash(const struct dentry *parent, struct qstr *qstr);
|
||||
static int isofs_dentry_cmpi(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name);
|
||||
static int isofs_dentry_cmp(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name);
|
||||
|
||||
#ifdef CONFIG_JOLIET
|
||||
static int isofs_hashi_ms(const struct dentry *parent, const struct inode *inode,
|
||||
struct qstr *qstr);
|
||||
static int isofs_hash_ms(const struct dentry *parent, const struct inode *inode,
|
||||
struct qstr *qstr);
|
||||
static int isofs_hashi_ms(const struct dentry *parent, struct qstr *qstr);
|
||||
static int isofs_hash_ms(const struct dentry *parent, struct qstr *qstr);
|
||||
static int isofs_dentry_cmpi_ms(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name);
|
||||
static int isofs_dentry_cmp_ms(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name);
|
||||
#endif
|
||||
|
||||
@ -265,30 +257,26 @@ static int isofs_dentry_cmp_common(
|
||||
}
|
||||
|
||||
static int
|
||||
isofs_hash(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
isofs_hash(const struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
return isofs_hash_common(dentry, qstr, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
isofs_hashi(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
isofs_hashi(const struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
return isofs_hashi_common(dentry, qstr, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
isofs_dentry_cmp(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
isofs_dentry_cmp(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
return isofs_dentry_cmp_common(len, str, name, 0, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
isofs_dentry_cmpi(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
isofs_dentry_cmpi(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
return isofs_dentry_cmp_common(len, str, name, 0, 1);
|
||||
@ -296,30 +284,26 @@ isofs_dentry_cmpi(const struct dentry *parent, const struct inode *pinode,
|
||||
|
||||
#ifdef CONFIG_JOLIET
|
||||
static int
|
||||
isofs_hash_ms(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
isofs_hash_ms(const struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
return isofs_hash_common(dentry, qstr, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
isofs_hashi_ms(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
isofs_hashi_ms(const struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
return isofs_hashi_common(dentry, qstr, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
isofs_dentry_cmp_ms(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
isofs_dentry_cmp_ms(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
return isofs_dentry_cmp_common(len, str, name, 1, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
isofs_dentry_cmpi_ms(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
isofs_dentry_cmpi_ms(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
return isofs_dentry_cmp_common(len, str, name, 1, 1);
|
||||
|
@ -37,8 +37,7 @@ isofs_cmp(struct dentry *dentry, const char *compare, int dlen)
|
||||
|
||||
qstr.name = compare;
|
||||
qstr.len = dlen;
|
||||
return dentry->d_op->d_compare(NULL, NULL, NULL, NULL,
|
||||
dentry->d_name.len, dentry->d_name.name, &qstr);
|
||||
return dentry->d_op->d_compare(NULL, NULL, dentry->d_name.len, dentry->d_name.name, &qstr);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1538,8 +1538,7 @@ const struct file_operations jfs_dir_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static int jfs_ci_hash(const struct dentry *dir, const struct inode *inode,
|
||||
struct qstr *this)
|
||||
static int jfs_ci_hash(const struct dentry *dir, struct qstr *this)
|
||||
{
|
||||
unsigned long hash;
|
||||
int i;
|
||||
@ -1552,9 +1551,7 @@ static int jfs_ci_hash(const struct dentry *dir, const struct inode *inode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jfs_ci_compare(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
static int jfs_ci_compare(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
int i, result = 1;
|
||||
|
@ -276,7 +276,7 @@ static int nlmsvc_unlink_block(struct nlm_block *block)
|
||||
dprintk("lockd: unlinking block %p...\n", block);
|
||||
|
||||
/* Remove block from list */
|
||||
status = posix_unblock_lock(block->b_file->f_file, &block->b_call->a_args.lock.fl);
|
||||
status = posix_unblock_lock(&block->b_call->a_args.lock.fl);
|
||||
nlmsvc_remove_block(block);
|
||||
return status;
|
||||
}
|
||||
@ -744,8 +744,20 @@ static int nlmsvc_same_owner(struct file_lock *fl1, struct file_lock *fl2)
|
||||
return fl1->fl_owner == fl2->fl_owner && fl1->fl_pid == fl2->fl_pid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since NLM uses two "keys" for tracking locks, we need to hash them down
|
||||
* to one for the blocked_hash. Here, we're just xor'ing the host address
|
||||
* with the pid in order to create a key value for picking a hash bucket.
|
||||
*/
|
||||
static unsigned long
|
||||
nlmsvc_owner_key(struct file_lock *fl)
|
||||
{
|
||||
return (unsigned long)fl->fl_owner ^ (unsigned long)fl->fl_pid;
|
||||
}
|
||||
|
||||
const struct lock_manager_operations nlmsvc_lock_operations = {
|
||||
.lm_compare_owner = nlmsvc_same_owner,
|
||||
.lm_owner_key = nlmsvc_owner_key,
|
||||
.lm_notify = nlmsvc_notify_blocked,
|
||||
.lm_grant = nlmsvc_grant_deferred,
|
||||
};
|
||||
|
@ -169,7 +169,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
|
||||
|
||||
again:
|
||||
file->f_locks = 0;
|
||||
lock_flocks(); /* protects i_flock list */
|
||||
spin_lock(&inode->i_lock);
|
||||
for (fl = inode->i_flock; fl; fl = fl->fl_next) {
|
||||
if (fl->fl_lmops != &nlmsvc_lock_operations)
|
||||
continue;
|
||||
@ -181,7 +181,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
|
||||
if (match(lockhost, host)) {
|
||||
struct file_lock lock = *fl;
|
||||
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
lock.fl_type = F_UNLCK;
|
||||
lock.fl_start = 0;
|
||||
lock.fl_end = OFFSET_MAX;
|
||||
@ -193,7 +193,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -228,14 +228,14 @@ nlm_file_inuse(struct nlm_file *file)
|
||||
if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
|
||||
return 1;
|
||||
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
for (fl = inode->i_flock; fl; fl = fl->fl_next) {
|
||||
if (fl->fl_lmops == &nlmsvc_lock_operations) {
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
file->f_locks = 0;
|
||||
return 0;
|
||||
}
|
||||
|
281
fs/locks.c
281
fs/locks.c
@ -126,6 +126,7 @@
|
||||
#include <linux/time.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/pid_namespace.h>
|
||||
#include <linux/hashtable.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
@ -153,30 +154,51 @@ int lease_break_time = 45;
|
||||
#define for_each_lock(inode, lockp) \
|
||||
for (lockp = &inode->i_flock; *lockp != NULL; lockp = &(*lockp)->fl_next)
|
||||
|
||||
static LIST_HEAD(file_lock_list);
|
||||
static LIST_HEAD(blocked_list);
|
||||
/*
|
||||
* The global file_lock_list is only used for displaying /proc/locks. Protected
|
||||
* by the file_lock_lock.
|
||||
*/
|
||||
static HLIST_HEAD(file_lock_list);
|
||||
static DEFINE_SPINLOCK(file_lock_lock);
|
||||
|
||||
/*
|
||||
* Protects the two list heads above, plus the inode->i_flock list
|
||||
* The blocked_hash is used to find POSIX lock loops for deadlock detection.
|
||||
* It is protected by blocked_lock_lock.
|
||||
*
|
||||
* We hash locks by lockowner in order to optimize searching for the lock a
|
||||
* particular lockowner is waiting on.
|
||||
*
|
||||
* FIXME: make this value scale via some heuristic? We generally will want more
|
||||
* buckets when we have more lockowners holding locks, but that's a little
|
||||
* difficult to determine without knowing what the workload will look like.
|
||||
*/
|
||||
void lock_flocks(void)
|
||||
{
|
||||
spin_lock(&file_lock_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lock_flocks);
|
||||
#define BLOCKED_HASH_BITS 7
|
||||
static DEFINE_HASHTABLE(blocked_hash, BLOCKED_HASH_BITS);
|
||||
|
||||
void unlock_flocks(void)
|
||||
{
|
||||
spin_unlock(&file_lock_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unlock_flocks);
|
||||
/*
|
||||
* This lock protects the blocked_hash. Generally, if you're accessing it, you
|
||||
* want to be holding this lock.
|
||||
*
|
||||
* In addition, it also protects the fl->fl_block list, and the fl->fl_next
|
||||
* pointer for file_lock structures that are acting as lock requests (in
|
||||
* contrast to those that are acting as records of acquired locks).
|
||||
*
|
||||
* Note that when we acquire this lock in order to change the above fields,
|
||||
* we often hold the i_lock as well. In certain cases, when reading the fields
|
||||
* protected by this lock, we can skip acquiring it iff we already hold the
|
||||
* i_lock.
|
||||
*
|
||||
* In particular, adding an entry to the fl_block list requires that you hold
|
||||
* both the i_lock and the blocked_lock_lock (acquired in that order). Deleting
|
||||
* an entry from the list however only requires the file_lock_lock.
|
||||
*/
|
||||
static DEFINE_SPINLOCK(blocked_lock_lock);
|
||||
|
||||
static struct kmem_cache *filelock_cache __read_mostly;
|
||||
|
||||
static void locks_init_lock_heads(struct file_lock *fl)
|
||||
{
|
||||
INIT_LIST_HEAD(&fl->fl_link);
|
||||
INIT_HLIST_NODE(&fl->fl_link);
|
||||
INIT_LIST_HEAD(&fl->fl_block);
|
||||
init_waitqueue_head(&fl->fl_wait);
|
||||
}
|
||||
@ -210,7 +232,7 @@ void locks_free_lock(struct file_lock *fl)
|
||||
{
|
||||
BUG_ON(waitqueue_active(&fl->fl_wait));
|
||||
BUG_ON(!list_empty(&fl->fl_block));
|
||||
BUG_ON(!list_empty(&fl->fl_link));
|
||||
BUG_ON(!hlist_unhashed(&fl->fl_link));
|
||||
|
||||
locks_release_private(fl);
|
||||
kmem_cache_free(filelock_cache, fl);
|
||||
@ -484,47 +506,108 @@ static int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2)
|
||||
return fl1->fl_owner == fl2->fl_owner;
|
||||
}
|
||||
|
||||
static inline void
|
||||
locks_insert_global_locks(struct file_lock *fl)
|
||||
{
|
||||
spin_lock(&file_lock_lock);
|
||||
hlist_add_head(&fl->fl_link, &file_lock_list);
|
||||
spin_unlock(&file_lock_lock);
|
||||
}
|
||||
|
||||
static inline void
|
||||
locks_delete_global_locks(struct file_lock *fl)
|
||||
{
|
||||
spin_lock(&file_lock_lock);
|
||||
hlist_del_init(&fl->fl_link);
|
||||
spin_unlock(&file_lock_lock);
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
posix_owner_key(struct file_lock *fl)
|
||||
{
|
||||
if (fl->fl_lmops && fl->fl_lmops->lm_owner_key)
|
||||
return fl->fl_lmops->lm_owner_key(fl);
|
||||
return (unsigned long)fl->fl_owner;
|
||||
}
|
||||
|
||||
static inline void
|
||||
locks_insert_global_blocked(struct file_lock *waiter)
|
||||
{
|
||||
hash_add(blocked_hash, &waiter->fl_link, posix_owner_key(waiter));
|
||||
}
|
||||
|
||||
static inline void
|
||||
locks_delete_global_blocked(struct file_lock *waiter)
|
||||
{
|
||||
hash_del(&waiter->fl_link);
|
||||
}
|
||||
|
||||
/* Remove waiter from blocker's block list.
|
||||
* When blocker ends up pointing to itself then the list is empty.
|
||||
*
|
||||
* Must be called with blocked_lock_lock held.
|
||||
*/
|
||||
static void __locks_delete_block(struct file_lock *waiter)
|
||||
{
|
||||
locks_delete_global_blocked(waiter);
|
||||
list_del_init(&waiter->fl_block);
|
||||
list_del_init(&waiter->fl_link);
|
||||
waiter->fl_next = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
void locks_delete_block(struct file_lock *waiter)
|
||||
static void locks_delete_block(struct file_lock *waiter)
|
||||
{
|
||||
lock_flocks();
|
||||
spin_lock(&blocked_lock_lock);
|
||||
__locks_delete_block(waiter);
|
||||
unlock_flocks();
|
||||
spin_unlock(&blocked_lock_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(locks_delete_block);
|
||||
|
||||
/* Insert waiter into blocker's block list.
|
||||
* We use a circular list so that processes can be easily woken up in
|
||||
* the order they blocked. The documentation doesn't require this but
|
||||
* it seems like the reasonable thing to do.
|
||||
*
|
||||
* Must be called with both the i_lock and blocked_lock_lock held. The fl_block
|
||||
* list itself is protected by the file_lock_list, but by ensuring that the
|
||||
* i_lock is also held on insertions we can avoid taking the blocked_lock_lock
|
||||
* in some cases when we see that the fl_block list is empty.
|
||||
*/
|
||||
static void locks_insert_block(struct file_lock *blocker,
|
||||
struct file_lock *waiter)
|
||||
static void __locks_insert_block(struct file_lock *blocker,
|
||||
struct file_lock *waiter)
|
||||
{
|
||||
BUG_ON(!list_empty(&waiter->fl_block));
|
||||
list_add_tail(&waiter->fl_block, &blocker->fl_block);
|
||||
waiter->fl_next = blocker;
|
||||
list_add_tail(&waiter->fl_block, &blocker->fl_block);
|
||||
if (IS_POSIX(blocker))
|
||||
list_add(&waiter->fl_link, &blocked_list);
|
||||
locks_insert_global_blocked(waiter);
|
||||
}
|
||||
|
||||
/* Wake up processes blocked waiting for blocker.
|
||||
* If told to wait then schedule the processes until the block list
|
||||
* is empty, otherwise empty the block list ourselves.
|
||||
/* Must be called with i_lock held. */
|
||||
static void locks_insert_block(struct file_lock *blocker,
|
||||
struct file_lock *waiter)
|
||||
{
|
||||
spin_lock(&blocked_lock_lock);
|
||||
__locks_insert_block(blocker, waiter);
|
||||
spin_unlock(&blocked_lock_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wake up processes blocked waiting for blocker.
|
||||
*
|
||||
* Must be called with the inode->i_lock held!
|
||||
*/
|
||||
static void locks_wake_up_blocks(struct file_lock *blocker)
|
||||
{
|
||||
/*
|
||||
* Avoid taking global lock if list is empty. This is safe since new
|
||||
* blocked requests are only added to the list under the i_lock, and
|
||||
* the i_lock is always held here. Note that removal from the fl_block
|
||||
* list does not require the i_lock, so we must recheck list_empty()
|
||||
* after acquiring the blocked_lock_lock.
|
||||
*/
|
||||
if (list_empty(&blocker->fl_block))
|
||||
return;
|
||||
|
||||
spin_lock(&blocked_lock_lock);
|
||||
while (!list_empty(&blocker->fl_block)) {
|
||||
struct file_lock *waiter;
|
||||
|
||||
@ -536,20 +619,23 @@ static void locks_wake_up_blocks(struct file_lock *blocker)
|
||||
else
|
||||
wake_up(&waiter->fl_wait);
|
||||
}
|
||||
spin_unlock(&blocked_lock_lock);
|
||||
}
|
||||
|
||||
/* Insert file lock fl into an inode's lock list at the position indicated
|
||||
* by pos. At the same time add the lock to the global file lock list.
|
||||
*
|
||||
* Must be called with the i_lock held!
|
||||
*/
|
||||
static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
|
||||
{
|
||||
list_add(&fl->fl_link, &file_lock_list);
|
||||
|
||||
fl->fl_nspid = get_pid(task_tgid(current));
|
||||
|
||||
/* insert into file's list */
|
||||
fl->fl_next = *pos;
|
||||
*pos = fl;
|
||||
|
||||
locks_insert_global_locks(fl);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -557,14 +643,17 @@ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
|
||||
* Wake up processes that are blocked waiting for this lock,
|
||||
* notify the FS that the lock has been cleared and
|
||||
* finally free the lock.
|
||||
*
|
||||
* Must be called with the i_lock held!
|
||||
*/
|
||||
static void locks_delete_lock(struct file_lock **thisfl_p)
|
||||
{
|
||||
struct file_lock *fl = *thisfl_p;
|
||||
|
||||
locks_delete_global_locks(fl);
|
||||
|
||||
*thisfl_p = fl->fl_next;
|
||||
fl->fl_next = NULL;
|
||||
list_del_init(&fl->fl_link);
|
||||
|
||||
if (fl->fl_nspid) {
|
||||
put_pid(fl->fl_nspid);
|
||||
@ -625,8 +714,9 @@ void
|
||||
posix_test_lock(struct file *filp, struct file_lock *fl)
|
||||
{
|
||||
struct file_lock *cfl;
|
||||
struct inode *inode = file_inode(filp);
|
||||
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
for (cfl = file_inode(filp)->i_flock; cfl; cfl = cfl->fl_next) {
|
||||
if (!IS_POSIX(cfl))
|
||||
continue;
|
||||
@ -639,7 +729,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl)
|
||||
fl->fl_pid = pid_vnr(cfl->fl_nspid);
|
||||
} else
|
||||
fl->fl_type = F_UNLCK;
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(posix_test_lock);
|
||||
@ -676,13 +766,14 @@ static struct file_lock *what_owner_is_waiting_for(struct file_lock *block_fl)
|
||||
{
|
||||
struct file_lock *fl;
|
||||
|
||||
list_for_each_entry(fl, &blocked_list, fl_link) {
|
||||
hash_for_each_possible(blocked_hash, fl, fl_link, posix_owner_key(block_fl)) {
|
||||
if (posix_same_owner(fl, block_fl))
|
||||
return fl->fl_next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Must be called with the blocked_lock_lock held! */
|
||||
static int posix_locks_deadlock(struct file_lock *caller_fl,
|
||||
struct file_lock *block_fl)
|
||||
{
|
||||
@ -718,7 +809,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
if (request->fl_flags & FL_ACCESS)
|
||||
goto find_conflict;
|
||||
|
||||
@ -748,9 +839,9 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
|
||||
* give it the opportunity to lock the file.
|
||||
*/
|
||||
if (found) {
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
cond_resched();
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
}
|
||||
|
||||
find_conflict:
|
||||
@ -777,7 +868,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
|
||||
error = 0;
|
||||
|
||||
out:
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
if (new_fl)
|
||||
locks_free_lock(new_fl);
|
||||
return error;
|
||||
@ -791,7 +882,8 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
|
||||
struct file_lock *left = NULL;
|
||||
struct file_lock *right = NULL;
|
||||
struct file_lock **before;
|
||||
int error, added = 0;
|
||||
int error;
|
||||
bool added = false;
|
||||
|
||||
/*
|
||||
* We may need two file_lock structures for this operation,
|
||||
@ -806,7 +898,12 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
|
||||
new_fl2 = locks_alloc_lock();
|
||||
}
|
||||
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
/*
|
||||
* New lock request. Walk all POSIX locks and look for conflicts. If
|
||||
* there are any, either return error or put the request on the
|
||||
* blocker's list of waiters and the global blocked_hash.
|
||||
*/
|
||||
if (request->fl_type != F_UNLCK) {
|
||||
for_each_lock(inode, before) {
|
||||
fl = *before;
|
||||
@ -819,11 +916,17 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
|
||||
error = -EAGAIN;
|
||||
if (!(request->fl_flags & FL_SLEEP))
|
||||
goto out;
|
||||
/*
|
||||
* Deadlock detection and insertion into the blocked
|
||||
* locks list must be done while holding the same lock!
|
||||
*/
|
||||
error = -EDEADLK;
|
||||
if (posix_locks_deadlock(request, fl))
|
||||
goto out;
|
||||
error = FILE_LOCK_DEFERRED;
|
||||
locks_insert_block(fl, request);
|
||||
spin_lock(&blocked_lock_lock);
|
||||
if (likely(!posix_locks_deadlock(request, fl))) {
|
||||
error = FILE_LOCK_DEFERRED;
|
||||
__locks_insert_block(fl, request);
|
||||
}
|
||||
spin_unlock(&blocked_lock_lock);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -845,7 +948,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
|
||||
before = &fl->fl_next;
|
||||
}
|
||||
|
||||
/* Process locks with this owner. */
|
||||
/* Process locks with this owner. */
|
||||
while ((fl = *before) && posix_same_owner(request, fl)) {
|
||||
/* Detect adjacent or overlapping regions (if same lock type)
|
||||
*/
|
||||
@ -880,7 +983,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
|
||||
continue;
|
||||
}
|
||||
request = fl;
|
||||
added = 1;
|
||||
added = true;
|
||||
}
|
||||
else {
|
||||
/* Processing for different lock types is a bit
|
||||
@ -891,7 +994,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
|
||||
if (fl->fl_start > request->fl_end)
|
||||
break;
|
||||
if (request->fl_type == F_UNLCK)
|
||||
added = 1;
|
||||
added = true;
|
||||
if (fl->fl_start < request->fl_start)
|
||||
left = fl;
|
||||
/* If the next lock in the list has a higher end
|
||||
@ -921,7 +1024,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
|
||||
locks_release_private(fl);
|
||||
locks_copy_private(fl, request);
|
||||
request = fl;
|
||||
added = 1;
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
/* Go on to next lock.
|
||||
@ -931,10 +1034,9 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
|
||||
}
|
||||
|
||||
/*
|
||||
* The above code only modifies existing locks in case of
|
||||
* merging or replacing. If new lock(s) need to be inserted
|
||||
* all modifications are done bellow this, so it's safe yet to
|
||||
* bail out.
|
||||
* The above code only modifies existing locks in case of merging or
|
||||
* replacing. If new lock(s) need to be inserted all modifications are
|
||||
* done below this, so it's safe yet to bail out.
|
||||
*/
|
||||
error = -ENOLCK; /* "no luck" */
|
||||
if (right && left == right && !new_fl2)
|
||||
@ -974,7 +1076,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
|
||||
locks_wake_up_blocks(left);
|
||||
}
|
||||
out:
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
/*
|
||||
* Free any unused locks.
|
||||
*/
|
||||
@ -1049,14 +1151,14 @@ int locks_mandatory_locked(struct inode *inode)
|
||||
/*
|
||||
* Search the lock list for this inode for any POSIX locks.
|
||||
*/
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
|
||||
if (!IS_POSIX(fl))
|
||||
continue;
|
||||
if (fl->fl_owner != owner)
|
||||
break;
|
||||
}
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
return fl ? -EAGAIN : 0;
|
||||
}
|
||||
|
||||
@ -1199,7 +1301,7 @@ int __break_lease(struct inode *inode, unsigned int mode)
|
||||
if (IS_ERR(new_fl))
|
||||
return PTR_ERR(new_fl);
|
||||
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
|
||||
time_out_leases(inode);
|
||||
|
||||
@ -1249,11 +1351,11 @@ int __break_lease(struct inode *inode, unsigned int mode)
|
||||
break_time++;
|
||||
}
|
||||
locks_insert_block(flock, new_fl);
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
error = wait_event_interruptible_timeout(new_fl->fl_wait,
|
||||
!new_fl->fl_next, break_time);
|
||||
lock_flocks();
|
||||
__locks_delete_block(new_fl);
|
||||
spin_lock(&inode->i_lock);
|
||||
locks_delete_block(new_fl);
|
||||
if (error >= 0) {
|
||||
if (error == 0)
|
||||
time_out_leases(inode);
|
||||
@ -1270,7 +1372,7 @@ int __break_lease(struct inode *inode, unsigned int mode)
|
||||
}
|
||||
|
||||
out:
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
locks_free_lock(new_fl);
|
||||
return error;
|
||||
}
|
||||
@ -1323,9 +1425,10 @@ EXPORT_SYMBOL(lease_get_mtime);
|
||||
int fcntl_getlease(struct file *filp)
|
||||
{
|
||||
struct file_lock *fl;
|
||||
struct inode *inode = file_inode(filp);
|
||||
int type = F_UNLCK;
|
||||
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
time_out_leases(file_inode(filp));
|
||||
for (fl = file_inode(filp)->i_flock; fl && IS_LEASE(fl);
|
||||
fl = fl->fl_next) {
|
||||
@ -1334,11 +1437,11 @@ int fcntl_getlease(struct file *filp)
|
||||
break;
|
||||
}
|
||||
}
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
return type;
|
||||
}
|
||||
|
||||
int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
|
||||
static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
|
||||
{
|
||||
struct file_lock *fl, **before, **my_before = NULL, *lease;
|
||||
struct dentry *dentry = filp->f_path.dentry;
|
||||
@ -1403,7 +1506,7 @@ int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
|
||||
return error;
|
||||
}
|
||||
|
||||
int generic_delete_lease(struct file *filp, struct file_lock **flp)
|
||||
static int generic_delete_lease(struct file *filp, struct file_lock **flp)
|
||||
{
|
||||
struct file_lock *fl, **before;
|
||||
struct dentry *dentry = filp->f_path.dentry;
|
||||
@ -1428,7 +1531,7 @@ int generic_delete_lease(struct file *filp, struct file_lock **flp)
|
||||
* The (input) flp->fl_lmops->lm_break function is required
|
||||
* by break_lease().
|
||||
*
|
||||
* Called with file_lock_lock held.
|
||||
* Called with inode->i_lock held.
|
||||
*/
|
||||
int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
|
||||
{
|
||||
@ -1497,11 +1600,12 @@ static int __vfs_setlease(struct file *filp, long arg, struct file_lock **lease)
|
||||
|
||||
int vfs_setlease(struct file *filp, long arg, struct file_lock **lease)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
int error;
|
||||
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
error = __vfs_setlease(filp, arg, lease);
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -1519,6 +1623,7 @@ static int do_fcntl_delete_lease(struct file *filp)
|
||||
static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg)
|
||||
{
|
||||
struct file_lock *fl, *ret;
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct fasync_struct *new;
|
||||
int error;
|
||||
|
||||
@ -1532,10 +1637,10 @@ static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg)
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = fl;
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
error = __vfs_setlease(filp, arg, &ret);
|
||||
if (error) {
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
locks_free_lock(fl);
|
||||
goto out_free_fasync;
|
||||
}
|
||||
@ -1552,7 +1657,7 @@ static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg)
|
||||
new = NULL;
|
||||
|
||||
error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
out_free_fasync:
|
||||
if (new)
|
||||
@ -2076,7 +2181,7 @@ void locks_remove_flock(struct file *filp)
|
||||
fl.fl_ops->fl_release_private(&fl);
|
||||
}
|
||||
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
before = &inode->i_flock;
|
||||
|
||||
while ((fl = *before) != NULL) {
|
||||
@ -2094,30 +2199,28 @@ void locks_remove_flock(struct file *filp)
|
||||
}
|
||||
before = &fl->fl_next;
|
||||
}
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* posix_unblock_lock - stop waiting for a file lock
|
||||
* @filp: how the file was opened
|
||||
* @waiter: the lock which was waiting
|
||||
*
|
||||
* lockd needs to block waiting for locks.
|
||||
*/
|
||||
int
|
||||
posix_unblock_lock(struct file *filp, struct file_lock *waiter)
|
||||
posix_unblock_lock(struct file_lock *waiter)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
lock_flocks();
|
||||
spin_lock(&blocked_lock_lock);
|
||||
if (waiter->fl_next)
|
||||
__locks_delete_block(waiter);
|
||||
else
|
||||
status = -ENOENT;
|
||||
unlock_flocks();
|
||||
spin_unlock(&blocked_lock_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(posix_unblock_lock);
|
||||
|
||||
/**
|
||||
@ -2215,7 +2318,7 @@ static int locks_show(struct seq_file *f, void *v)
|
||||
{
|
||||
struct file_lock *fl, *bfl;
|
||||
|
||||
fl = list_entry(v, struct file_lock, fl_link);
|
||||
fl = hlist_entry(v, struct file_lock, fl_link);
|
||||
|
||||
lock_get_status(f, fl, *((loff_t *)f->private), "");
|
||||
|
||||
@ -2229,21 +2332,23 @@ static void *locks_start(struct seq_file *f, loff_t *pos)
|
||||
{
|
||||
loff_t *p = f->private;
|
||||
|
||||
lock_flocks();
|
||||
spin_lock(&file_lock_lock);
|
||||
spin_lock(&blocked_lock_lock);
|
||||
*p = (*pos + 1);
|
||||
return seq_list_start(&file_lock_list, *pos);
|
||||
return seq_hlist_start(&file_lock_list, *pos);
|
||||
}
|
||||
|
||||
static void *locks_next(struct seq_file *f, void *v, loff_t *pos)
|
||||
{
|
||||
loff_t *p = f->private;
|
||||
++*p;
|
||||
return seq_list_next(v, &file_lock_list, pos);
|
||||
return seq_hlist_next(v, &file_lock_list, pos);
|
||||
}
|
||||
|
||||
static void locks_stop(struct seq_file *f, void *v)
|
||||
{
|
||||
unlock_flocks();
|
||||
spin_unlock(&blocked_lock_lock);
|
||||
spin_unlock(&file_lock_lock);
|
||||
}
|
||||
|
||||
static const struct seq_operations locks_seq_operations = {
|
||||
@ -2290,7 +2395,8 @@ int lock_may_read(struct inode *inode, loff_t start, unsigned long len)
|
||||
{
|
||||
struct file_lock *fl;
|
||||
int result = 1;
|
||||
lock_flocks();
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
|
||||
if (IS_POSIX(fl)) {
|
||||
if (fl->fl_type == F_RDLCK)
|
||||
@ -2307,7 +2413,7 @@ int lock_may_read(struct inode *inode, loff_t start, unsigned long len)
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -2330,7 +2436,8 @@ int lock_may_write(struct inode *inode, loff_t start, unsigned long len)
|
||||
{
|
||||
struct file_lock *fl;
|
||||
int result = 1;
|
||||
lock_flocks();
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
|
||||
if (IS_POSIX(fl)) {
|
||||
if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
|
||||
@ -2345,7 +2452,7 @@ int lock_may_write(struct inode *inode, loff_t start, unsigned long len)
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ static int minix_readdir(struct file *file, struct dir_context *ctx)
|
||||
unsigned offset;
|
||||
unsigned long n;
|
||||
|
||||
ctx->pos = pos = (pos + chunk_size-1) & ~(chunk_size-1);
|
||||
ctx->pos = pos = ALIGN(pos, chunk_size);
|
||||
if (pos >= inode->i_size)
|
||||
return 0;
|
||||
|
||||
|
@ -54,6 +54,18 @@ static int minix_mknod(struct inode * dir, struct dentry *dentry, umode_t mode,
|
||||
return error;
|
||||
}
|
||||
|
||||
static int minix_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
int error;
|
||||
struct inode *inode = minix_new_inode(dir, mode, &error);
|
||||
if (inode) {
|
||||
minix_set_inode(inode, 0);
|
||||
mark_inode_dirty(inode);
|
||||
d_tmpfile(dentry, inode);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static int minix_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
bool excl)
|
||||
{
|
||||
@ -254,4 +266,5 @@ const struct inode_operations minix_dir_inode_operations = {
|
||||
.mknod = minix_mknod,
|
||||
.rename = minix_rename,
|
||||
.getattr = minix_getattr,
|
||||
.tmpfile = minix_tmpfile,
|
||||
};
|
||||
|
113
fs/namei.c
113
fs/namei.c
@ -1352,7 +1352,7 @@ static int lookup_fast(struct nameidata *nd,
|
||||
*/
|
||||
if (nd->flags & LOOKUP_RCU) {
|
||||
unsigned seq;
|
||||
dentry = __d_lookup_rcu(parent, &nd->last, &seq, nd->inode);
|
||||
dentry = __d_lookup_rcu(parent, &nd->last, &seq);
|
||||
if (!dentry)
|
||||
goto unlazy;
|
||||
|
||||
@ -1787,8 +1787,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
|
||||
struct dentry *parent = nd->path.dentry;
|
||||
nd->flags &= ~LOOKUP_JUMPED;
|
||||
if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
|
||||
err = parent->d_op->d_hash(parent, nd->inode,
|
||||
&this);
|
||||
err = parent->d_op->d_hash(parent, &this);
|
||||
if (err < 0)
|
||||
break;
|
||||
}
|
||||
@ -2121,7 +2120,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
|
||||
* to use its own hash..
|
||||
*/
|
||||
if (base->d_flags & DCACHE_OP_HASH) {
|
||||
int err = base->d_op->d_hash(base, base->d_inode, &this);
|
||||
int err = base->d_op->d_hash(base, &this);
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
@ -2690,28 +2689,10 @@ static int do_last(struct nameidata *nd, struct path *path,
|
||||
nd->flags &= ~LOOKUP_PARENT;
|
||||
nd->flags |= op->intent;
|
||||
|
||||
switch (nd->last_type) {
|
||||
case LAST_DOTDOT:
|
||||
case LAST_DOT:
|
||||
if (nd->last_type != LAST_NORM) {
|
||||
error = handle_dots(nd, nd->last_type);
|
||||
if (error)
|
||||
return error;
|
||||
/* fallthrough */
|
||||
case LAST_ROOT:
|
||||
error = complete_walk(nd);
|
||||
if (error)
|
||||
return error;
|
||||
audit_inode(name, nd->path.dentry, 0);
|
||||
if (open_flag & O_CREAT) {
|
||||
error = -EISDIR;
|
||||
goto out;
|
||||
}
|
||||
goto finish_open;
|
||||
case LAST_BIND:
|
||||
error = complete_walk(nd);
|
||||
if (error)
|
||||
return error;
|
||||
audit_inode(name, dir, 0);
|
||||
goto finish_open;
|
||||
}
|
||||
|
||||
@ -2841,19 +2822,19 @@ static int do_last(struct nameidata *nd, struct path *path,
|
||||
}
|
||||
nd->inode = inode;
|
||||
/* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */
|
||||
finish_open:
|
||||
error = complete_walk(nd);
|
||||
if (error) {
|
||||
path_put(&save_parent);
|
||||
return error;
|
||||
}
|
||||
audit_inode(name, nd->path.dentry, 0);
|
||||
error = -EISDIR;
|
||||
if ((open_flag & O_CREAT) && S_ISDIR(nd->inode->i_mode))
|
||||
goto out;
|
||||
error = -ENOTDIR;
|
||||
if ((nd->flags & LOOKUP_DIRECTORY) && !can_lookup(nd->inode))
|
||||
goto out;
|
||||
audit_inode(name, nd->path.dentry, 0);
|
||||
finish_open:
|
||||
if (!S_ISREG(nd->inode->i_mode))
|
||||
will_truncate = false;
|
||||
|
||||
@ -2920,6 +2901,67 @@ static int do_last(struct nameidata *nd, struct path *path,
|
||||
goto retry_lookup;
|
||||
}
|
||||
|
||||
static int do_tmpfile(int dfd, struct filename *pathname,
|
||||
struct nameidata *nd, int flags,
|
||||
const struct open_flags *op,
|
||||
struct file *file, int *opened)
|
||||
{
|
||||
static const struct qstr name = QSTR_INIT("/", 1);
|
||||
struct dentry *dentry, *child;
|
||||
struct inode *dir;
|
||||
int error = path_lookupat(dfd, pathname->name,
|
||||
flags | LOOKUP_DIRECTORY, nd);
|
||||
if (unlikely(error))
|
||||
return error;
|
||||
error = mnt_want_write(nd->path.mnt);
|
||||
if (unlikely(error))
|
||||
goto out;
|
||||
/* we want directory to be writable */
|
||||
error = inode_permission(nd->inode, MAY_WRITE | MAY_EXEC);
|
||||
if (error)
|
||||
goto out2;
|
||||
dentry = nd->path.dentry;
|
||||
dir = dentry->d_inode;
|
||||
if (!dir->i_op->tmpfile) {
|
||||
error = -EOPNOTSUPP;
|
||||
goto out2;
|
||||
}
|
||||
child = d_alloc(dentry, &name);
|
||||
if (unlikely(!child)) {
|
||||
error = -ENOMEM;
|
||||
goto out2;
|
||||
}
|
||||
nd->flags &= ~LOOKUP_DIRECTORY;
|
||||
nd->flags |= op->intent;
|
||||
dput(nd->path.dentry);
|
||||
nd->path.dentry = child;
|
||||
error = dir->i_op->tmpfile(dir, nd->path.dentry, op->mode);
|
||||
if (error)
|
||||
goto out2;
|
||||
audit_inode(pathname, nd->path.dentry, 0);
|
||||
error = may_open(&nd->path, op->acc_mode, op->open_flag);
|
||||
if (error)
|
||||
goto out2;
|
||||
file->f_path.mnt = nd->path.mnt;
|
||||
error = finish_open(file, nd->path.dentry, NULL, opened);
|
||||
if (error)
|
||||
goto out2;
|
||||
error = open_check_o_direct(file);
|
||||
if (error) {
|
||||
fput(file);
|
||||
} else if (!(op->open_flag & O_EXCL)) {
|
||||
struct inode *inode = file_inode(file);
|
||||
spin_lock(&inode->i_lock);
|
||||
inode->i_state |= I_LINKABLE;
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
out2:
|
||||
mnt_drop_write(nd->path.mnt);
|
||||
out:
|
||||
path_put(&nd->path);
|
||||
return error;
|
||||
}
|
||||
|
||||
static struct file *path_openat(int dfd, struct filename *pathname,
|
||||
struct nameidata *nd, const struct open_flags *op, int flags)
|
||||
{
|
||||
@ -2935,6 +2977,11 @@ static struct file *path_openat(int dfd, struct filename *pathname,
|
||||
|
||||
file->f_flags = op->open_flag;
|
||||
|
||||
if (unlikely(file->f_flags & O_TMPFILE)) {
|
||||
error = do_tmpfile(dfd, pathname, nd, flags, op, file, &opened);
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = path_init(dfd, pathname->name, flags | LOOKUP_PARENT, nd, &base);
|
||||
if (unlikely(error))
|
||||
goto out;
|
||||
@ -2987,9 +3034,10 @@ static struct file *path_openat(int dfd, struct filename *pathname,
|
||||
}
|
||||
|
||||
struct file *do_filp_open(int dfd, struct filename *pathname,
|
||||
const struct open_flags *op, int flags)
|
||||
const struct open_flags *op)
|
||||
{
|
||||
struct nameidata nd;
|
||||
int flags = op->lookup_flags;
|
||||
struct file *filp;
|
||||
|
||||
filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
|
||||
@ -3001,17 +3049,16 @@ struct file *do_filp_open(int dfd, struct filename *pathname,
|
||||
}
|
||||
|
||||
struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
|
||||
const char *name, const struct open_flags *op, int flags)
|
||||
const char *name, const struct open_flags *op)
|
||||
{
|
||||
struct nameidata nd;
|
||||
struct file *file;
|
||||
struct filename filename = { .name = name };
|
||||
int flags = op->lookup_flags | LOOKUP_ROOT;
|
||||
|
||||
nd.root.mnt = mnt;
|
||||
nd.root.dentry = dentry;
|
||||
|
||||
flags |= LOOKUP_ROOT;
|
||||
|
||||
if (dentry->d_inode->i_op->follow_link && op->intent & LOOKUP_OPEN)
|
||||
return ERR_PTR(-ELOOP);
|
||||
|
||||
@ -3586,12 +3633,18 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
/* Make sure we don't allow creating hardlink to an unlinked file */
|
||||
if (inode->i_nlink == 0)
|
||||
if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE))
|
||||
error = -ENOENT;
|
||||
else if (max_links && inode->i_nlink >= max_links)
|
||||
error = -EMLINK;
|
||||
else
|
||||
error = dir->i_op->link(old_dentry, dir, new_dentry);
|
||||
|
||||
if (!error && (inode->i_state & I_LINKABLE)) {
|
||||
spin_lock(&inode->i_lock);
|
||||
inode->i_state &= ~I_LINKABLE;
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
if (!error)
|
||||
fsnotify_link(dir, inode, new_dentry);
|
||||
|
@ -73,10 +73,8 @@ const struct inode_operations ncp_dir_inode_operations =
|
||||
* Dentry operations routines
|
||||
*/
|
||||
static int ncp_lookup_validate(struct dentry *, unsigned int);
|
||||
static int ncp_hash_dentry(const struct dentry *, const struct inode *,
|
||||
struct qstr *);
|
||||
static int ncp_compare_dentry(const struct dentry *, const struct inode *,
|
||||
const struct dentry *, const struct inode *,
|
||||
static int ncp_hash_dentry(const struct dentry *, struct qstr *);
|
||||
static int ncp_compare_dentry(const struct dentry *, const struct dentry *,
|
||||
unsigned int, const char *, const struct qstr *);
|
||||
static int ncp_delete_dentry(const struct dentry *);
|
||||
|
||||
@ -119,11 +117,19 @@ static inline int ncp_case_sensitive(const struct inode *i)
|
||||
/*
|
||||
* Note: leave the hash unchanged if the directory
|
||||
* is case-sensitive.
|
||||
*
|
||||
* Accessing the parent inode can be racy under RCU pathwalking.
|
||||
* Use ACCESS_ONCE() to make sure we use _one_ particular inode,
|
||||
* the callers will handle races.
|
||||
*/
|
||||
static int
|
||||
ncp_hash_dentry(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *this)
|
||||
ncp_hash_dentry(const struct dentry *dentry, struct qstr *this)
|
||||
{
|
||||
struct inode *inode = ACCESS_ONCE(dentry->d_inode);
|
||||
|
||||
if (!inode)
|
||||
return 0;
|
||||
|
||||
if (!ncp_case_sensitive(inode)) {
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
struct nls_table *t;
|
||||
@ -140,14 +146,24 @@ ncp_hash_dentry(const struct dentry *dentry, const struct inode *inode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Accessing the parent inode can be racy under RCU pathwalking.
|
||||
* Use ACCESS_ONCE() to make sure we use _one_ particular inode,
|
||||
* the callers will handle races.
|
||||
*/
|
||||
static int
|
||||
ncp_compare_dentry(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
ncp_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
struct inode *pinode;
|
||||
|
||||
if (len != name->len)
|
||||
return 1;
|
||||
|
||||
pinode = ACCESS_ONCE(parent->d_inode);
|
||||
if (!pinode)
|
||||
return 1;
|
||||
|
||||
if (ncp_case_sensitive(pinode))
|
||||
return strncmp(str, name->name, len);
|
||||
|
||||
@ -659,8 +675,6 @@ ncp_fill_cache(struct file *file, struct dir_context *ctx,
|
||||
if (!valid)
|
||||
ctl.valid = 0;
|
||||
if (!ctl.filled && (ctl.fpos == ctx->pos)) {
|
||||
if (!ino)
|
||||
ino = find_inode_number(dentry, &qname);
|
||||
if (!ino)
|
||||
ino = iunique(dir->i_sb, 2);
|
||||
ctl.filled = !dir_emit(ctx, qname.name, qname.len,
|
||||
@ -1123,17 +1137,6 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
|
||||
new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
|
||||
|
||||
if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) {
|
||||
/*
|
||||
* fail with EBUSY if there are still references to this
|
||||
* directory.
|
||||
*/
|
||||
dentry_unhash(new_dentry);
|
||||
error = -EBUSY;
|
||||
if (!d_unhashed(new_dentry))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ncp_age_dentry(server, old_dentry);
|
||||
ncp_age_dentry(server, new_dentry);
|
||||
|
||||
|
@ -891,6 +891,10 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
|
||||
if (!server) /* How this could happen? */
|
||||
goto out;
|
||||
|
||||
result = -EPERM;
|
||||
if (IS_DEADDIR(dentry->d_inode))
|
||||
goto out;
|
||||
|
||||
/* ageing the dentry to force validation */
|
||||
ncp_age_dentry(server, dentry);
|
||||
|
||||
|
@ -73,20 +73,20 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
|
||||
if (inode->i_flock == NULL)
|
||||
goto out;
|
||||
|
||||
/* Protect inode->i_flock using the file locks lock */
|
||||
lock_flocks();
|
||||
/* Protect inode->i_flock using the i_lock */
|
||||
spin_lock(&inode->i_lock);
|
||||
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
|
||||
if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
|
||||
continue;
|
||||
if (nfs_file_open_context(fl->fl_file) != ctx)
|
||||
continue;
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
status = nfs4_lock_delegation_recall(fl, state, stateid);
|
||||
if (status < 0)
|
||||
goto out;
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
}
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
@ -1373,13 +1373,13 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_
|
||||
/* Guard against delegation returns and new lock/unlock calls */
|
||||
down_write(&nfsi->rwsem);
|
||||
/* Protect inode->i_flock using the BKL */
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
|
||||
if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
|
||||
continue;
|
||||
if (nfs_file_open_context(fl->fl_file)->state != state)
|
||||
continue;
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
status = ops->recover_lock(state, fl);
|
||||
switch (status) {
|
||||
case 0:
|
||||
@ -1406,9 +1406,9 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_
|
||||
/* kill_proc(fl->fl_pid, SIGLOST, 1); */
|
||||
status = 0;
|
||||
}
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
}
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
out:
|
||||
up_write(&nfsi->rwsem);
|
||||
return status;
|
||||
|
@ -2645,13 +2645,13 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
|
||||
|
||||
list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
|
||||
|
||||
/* only place dl_time is set. protected by lock_flocks*/
|
||||
/* Only place dl_time is set; protected by i_lock: */
|
||||
dp->dl_time = get_seconds();
|
||||
|
||||
nfsd4_cb_recall(dp);
|
||||
}
|
||||
|
||||
/* Called from break_lease() with lock_flocks() held. */
|
||||
/* Called from break_lease() with i_lock held. */
|
||||
static void nfsd_break_deleg_cb(struct file_lock *fl)
|
||||
{
|
||||
struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner;
|
||||
@ -4520,7 +4520,7 @@ check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner)
|
||||
struct inode *inode = filp->fi_inode;
|
||||
int status = 0;
|
||||
|
||||
lock_flocks();
|
||||
spin_lock(&inode->i_lock);
|
||||
for (flpp = &inode->i_flock; *flpp != NULL; flpp = &(*flpp)->fl_next) {
|
||||
if ((*flpp)->fl_owner == (fl_owner_t)lowner) {
|
||||
status = 1;
|
||||
@ -4528,7 +4528,7 @@ check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner)
|
||||
}
|
||||
}
|
||||
out:
|
||||
unlock_flocks();
|
||||
spin_unlock(&inode->i_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -399,9 +399,6 @@ static int fanotify_release(struct inode *ignored, struct file *file)
|
||||
wake_up(&group->fanotify_data.access_waitq);
|
||||
#endif
|
||||
|
||||
if (file->f_flags & FASYNC)
|
||||
fsnotify_fasync(-1, file, 0);
|
||||
|
||||
/* matches the fanotify_init->fsnotify_alloc_group */
|
||||
fsnotify_destroy_group(group);
|
||||
|
||||
|
@ -2646,17 +2646,7 @@ static loff_t ocfs2_file_llseek(struct file *file, loff_t offset, int whence)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET))
|
||||
ret = -EINVAL;
|
||||
if (!ret && offset > inode->i_sb->s_maxbytes)
|
||||
ret = -EINVAL;
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (offset != file->f_pos) {
|
||||
file->f_pos = offset;
|
||||
file->f_version = 0;
|
||||
}
|
||||
offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
|
||||
|
||||
out:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
61
fs/open.c
61
fs/open.c
@ -840,11 +840,15 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
|
||||
if (flags & __O_SYNC)
|
||||
flags |= O_DSYNC;
|
||||
|
||||
/*
|
||||
* If we have O_PATH in the open flag. Then we
|
||||
* cannot have anything other than the below set of flags
|
||||
*/
|
||||
if (flags & O_PATH) {
|
||||
if (flags & O_TMPFILE) {
|
||||
if (!(flags & O_CREAT))
|
||||
return -EINVAL;
|
||||
acc_mode = MAY_OPEN | ACC_MODE(flags);
|
||||
} else if (flags & O_PATH) {
|
||||
/*
|
||||
* If we have O_PATH in the open flag. Then we
|
||||
* cannot have anything other than the below set of flags
|
||||
*/
|
||||
flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH;
|
||||
acc_mode = 0;
|
||||
} else {
|
||||
@ -876,7 +880,8 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
|
||||
lookup_flags |= LOOKUP_DIRECTORY;
|
||||
if (!(flags & O_NOFOLLOW))
|
||||
lookup_flags |= LOOKUP_FOLLOW;
|
||||
return lookup_flags;
|
||||
op->lookup_flags = lookup_flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -893,8 +898,8 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
|
||||
struct file *file_open_name(struct filename *name, int flags, umode_t mode)
|
||||
{
|
||||
struct open_flags op;
|
||||
int lookup = build_open_flags(flags, mode, &op);
|
||||
return do_filp_open(AT_FDCWD, name, &op, lookup);
|
||||
int err = build_open_flags(flags, mode, &op);
|
||||
return err ? ERR_PTR(err) : do_filp_open(AT_FDCWD, name, &op);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -919,37 +924,43 @@ struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt,
|
||||
const char *filename, int flags)
|
||||
{
|
||||
struct open_flags op;
|
||||
int lookup = build_open_flags(flags, 0, &op);
|
||||
int err = build_open_flags(flags, 0, &op);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
if (flags & O_CREAT)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (!filename && (flags & O_DIRECTORY))
|
||||
if (!dentry->d_inode->i_op->lookup)
|
||||
return ERR_PTR(-ENOTDIR);
|
||||
return do_file_open_root(dentry, mnt, filename, &op, lookup);
|
||||
return do_file_open_root(dentry, mnt, filename, &op);
|
||||
}
|
||||
EXPORT_SYMBOL(file_open_root);
|
||||
|
||||
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
|
||||
{
|
||||
struct open_flags op;
|
||||
int lookup = build_open_flags(flags, mode, &op);
|
||||
struct filename *tmp = getname(filename);
|
||||
int fd = PTR_ERR(tmp);
|
||||
int fd = build_open_flags(flags, mode, &op);
|
||||
struct filename *tmp;
|
||||
|
||||
if (!IS_ERR(tmp)) {
|
||||
fd = get_unused_fd_flags(flags);
|
||||
if (fd >= 0) {
|
||||
struct file *f = do_filp_open(dfd, tmp, &op, lookup);
|
||||
if (IS_ERR(f)) {
|
||||
put_unused_fd(fd);
|
||||
fd = PTR_ERR(f);
|
||||
} else {
|
||||
fsnotify_open(f);
|
||||
fd_install(fd, f);
|
||||
}
|
||||
if (fd)
|
||||
return fd;
|
||||
|
||||
tmp = getname(filename);
|
||||
if (IS_ERR(tmp))
|
||||
return PTR_ERR(tmp);
|
||||
|
||||
fd = get_unused_fd_flags(flags);
|
||||
if (fd >= 0) {
|
||||
struct file *f = do_filp_open(dfd, tmp, &op);
|
||||
if (IS_ERR(f)) {
|
||||
put_unused_fd(fd);
|
||||
fd = PTR_ERR(f);
|
||||
} else {
|
||||
fsnotify_open(f);
|
||||
fd_install(fd, f);
|
||||
}
|
||||
putname(tmp);
|
||||
}
|
||||
putname(tmp);
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
105
fs/proc/base.c
105
fs/proc/base.c
@ -1686,41 +1686,29 @@ bool proc_fill_cache(struct file *file, struct dir_context *ctx,
|
||||
instantiate_t instantiate, struct task_struct *task, const void *ptr)
|
||||
{
|
||||
struct dentry *child, *dir = file->f_path.dentry;
|
||||
struct qstr qname = QSTR_INIT(name, len);
|
||||
struct inode *inode;
|
||||
struct qstr qname;
|
||||
ino_t ino = 0;
|
||||
unsigned type = DT_UNKNOWN;
|
||||
unsigned type;
|
||||
ino_t ino;
|
||||
|
||||
qname.name = name;
|
||||
qname.len = len;
|
||||
qname.hash = full_name_hash(name, len);
|
||||
|
||||
child = d_lookup(dir, &qname);
|
||||
child = d_hash_and_lookup(dir, &qname);
|
||||
if (!child) {
|
||||
struct dentry *new;
|
||||
new = d_alloc(dir, &qname);
|
||||
if (new) {
|
||||
child = instantiate(dir->d_inode, new, task, ptr);
|
||||
if (child)
|
||||
dput(new);
|
||||
else
|
||||
child = new;
|
||||
child = d_alloc(dir, &qname);
|
||||
if (!child)
|
||||
goto end_instantiate;
|
||||
if (instantiate(dir->d_inode, child, task, ptr) < 0) {
|
||||
dput(child);
|
||||
goto end_instantiate;
|
||||
}
|
||||
}
|
||||
if (!child || IS_ERR(child) || !child->d_inode)
|
||||
goto end_instantiate;
|
||||
inode = child->d_inode;
|
||||
if (inode) {
|
||||
ino = inode->i_ino;
|
||||
type = inode->i_mode >> 12;
|
||||
}
|
||||
ino = inode->i_ino;
|
||||
type = inode->i_mode >> 12;
|
||||
dput(child);
|
||||
end_instantiate:
|
||||
if (!ino)
|
||||
ino = find_inode_number(dir, &qname);
|
||||
if (!ino)
|
||||
ino = 1;
|
||||
return dir_emit(ctx, name, len, ino, type);
|
||||
|
||||
end_instantiate:
|
||||
return dir_emit(ctx, name, len, 1, DT_UNKNOWN);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CHECKPOINT_RESTORE
|
||||
@ -1846,7 +1834,7 @@ struct map_files_info {
|
||||
unsigned char name[4*sizeof(long)+2]; /* max: %lx-%lx\0 */
|
||||
};
|
||||
|
||||
static struct dentry *
|
||||
static int
|
||||
proc_map_files_instantiate(struct inode *dir, struct dentry *dentry,
|
||||
struct task_struct *task, const void *ptr)
|
||||
{
|
||||
@ -1856,7 +1844,7 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry,
|
||||
|
||||
inode = proc_pid_make_inode(dir->i_sb, task);
|
||||
if (!inode)
|
||||
return ERR_PTR(-ENOENT);
|
||||
return -ENOENT;
|
||||
|
||||
ei = PROC_I(inode);
|
||||
ei->op.proc_get_link = proc_map_files_get_link;
|
||||
@ -1873,7 +1861,7 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry,
|
||||
d_set_d_op(dentry, &tid_map_files_dentry_operations);
|
||||
d_add(dentry, inode);
|
||||
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dentry *proc_map_files_lookup(struct inode *dir,
|
||||
@ -1882,23 +1870,23 @@ static struct dentry *proc_map_files_lookup(struct inode *dir,
|
||||
unsigned long vm_start, vm_end;
|
||||
struct vm_area_struct *vma;
|
||||
struct task_struct *task;
|
||||
struct dentry *result;
|
||||
int result;
|
||||
struct mm_struct *mm;
|
||||
|
||||
result = ERR_PTR(-EPERM);
|
||||
result = -EPERM;
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
goto out;
|
||||
|
||||
result = ERR_PTR(-ENOENT);
|
||||
result = -ENOENT;
|
||||
task = get_proc_task(dir);
|
||||
if (!task)
|
||||
goto out;
|
||||
|
||||
result = ERR_PTR(-EACCES);
|
||||
result = -EACCES;
|
||||
if (!ptrace_may_access(task, PTRACE_MODE_READ))
|
||||
goto out_put_task;
|
||||
|
||||
result = ERR_PTR(-ENOENT);
|
||||
result = -ENOENT;
|
||||
if (dname_to_vma_addr(dentry, &vm_start, &vm_end))
|
||||
goto out_put_task;
|
||||
|
||||
@ -1921,7 +1909,7 @@ static struct dentry *proc_map_files_lookup(struct inode *dir,
|
||||
out_put_task:
|
||||
put_task_struct(task);
|
||||
out:
|
||||
return result;
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
static const struct inode_operations proc_map_files_inode_operations = {
|
||||
@ -2135,13 +2123,12 @@ static const struct file_operations proc_timers_operations = {
|
||||
};
|
||||
#endif /* CONFIG_CHECKPOINT_RESTORE */
|
||||
|
||||
static struct dentry *proc_pident_instantiate(struct inode *dir,
|
||||
static int proc_pident_instantiate(struct inode *dir,
|
||||
struct dentry *dentry, struct task_struct *task, const void *ptr)
|
||||
{
|
||||
const struct pid_entry *p = ptr;
|
||||
struct inode *inode;
|
||||
struct proc_inode *ei;
|
||||
struct dentry *error = ERR_PTR(-ENOENT);
|
||||
|
||||
inode = proc_pid_make_inode(dir->i_sb, task);
|
||||
if (!inode)
|
||||
@ -2160,9 +2147,9 @@ static struct dentry *proc_pident_instantiate(struct inode *dir,
|
||||
d_add(dentry, inode);
|
||||
/* Close the race of the process dying before we return the dentry */
|
||||
if (pid_revalidate(dentry, 0))
|
||||
error = NULL;
|
||||
return 0;
|
||||
out:
|
||||
return error;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static struct dentry *proc_pident_lookup(struct inode *dir,
|
||||
@ -2170,11 +2157,11 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
|
||||
const struct pid_entry *ents,
|
||||
unsigned int nents)
|
||||
{
|
||||
struct dentry *error;
|
||||
int error;
|
||||
struct task_struct *task = get_proc_task(dir);
|
||||
const struct pid_entry *p, *last;
|
||||
|
||||
error = ERR_PTR(-ENOENT);
|
||||
error = -ENOENT;
|
||||
|
||||
if (!task)
|
||||
goto out_no_task;
|
||||
@ -2197,7 +2184,7 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
|
||||
out:
|
||||
put_task_struct(task);
|
||||
out_no_task:
|
||||
return error;
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
static int proc_pident_readdir(struct file *file, struct dir_context *ctx,
|
||||
@ -2780,11 +2767,10 @@ void proc_flush_task(struct task_struct *task)
|
||||
}
|
||||
}
|
||||
|
||||
static struct dentry *proc_pid_instantiate(struct inode *dir,
|
||||
struct dentry * dentry,
|
||||
struct task_struct *task, const void *ptr)
|
||||
static int proc_pid_instantiate(struct inode *dir,
|
||||
struct dentry * dentry,
|
||||
struct task_struct *task, const void *ptr)
|
||||
{
|
||||
struct dentry *error = ERR_PTR(-ENOENT);
|
||||
struct inode *inode;
|
||||
|
||||
inode = proc_pid_make_inode(dir->i_sb, task);
|
||||
@ -2804,14 +2790,14 @@ static struct dentry *proc_pid_instantiate(struct inode *dir,
|
||||
d_add(dentry, inode);
|
||||
/* Close the race of the process dying before we return the dentry */
|
||||
if (pid_revalidate(dentry, 0))
|
||||
error = NULL;
|
||||
return 0;
|
||||
out:
|
||||
return error;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)
|
||||
{
|
||||
struct dentry *result = NULL;
|
||||
int result = 0;
|
||||
struct task_struct *task;
|
||||
unsigned tgid;
|
||||
struct pid_namespace *ns;
|
||||
@ -2832,7 +2818,7 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, unsign
|
||||
result = proc_pid_instantiate(dir, dentry, task, NULL);
|
||||
put_task_struct(task);
|
||||
out:
|
||||
return result;
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2884,21 +2870,21 @@ static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter ite
|
||||
int proc_pid_readdir(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
struct tgid_iter iter;
|
||||
struct pid_namespace *ns;
|
||||
struct pid_namespace *ns = file->f_dentry->d_sb->s_fs_info;
|
||||
loff_t pos = ctx->pos;
|
||||
|
||||
if (pos >= PID_MAX_LIMIT + TGID_OFFSET)
|
||||
return 0;
|
||||
|
||||
if (pos == TGID_OFFSET - 1) {
|
||||
if (!proc_fill_cache(file, ctx, "self", 4, NULL, NULL, NULL))
|
||||
struct inode *inode = ns->proc_self->d_inode;
|
||||
if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK))
|
||||
return 0;
|
||||
iter.tgid = 0;
|
||||
} else {
|
||||
iter.tgid = pos - TGID_OFFSET;
|
||||
}
|
||||
iter.task = NULL;
|
||||
ns = file->f_dentry->d_sb->s_fs_info;
|
||||
for (iter = next_tgid(ns, iter);
|
||||
iter.task;
|
||||
iter.tgid += 1, iter = next_tgid(ns, iter)) {
|
||||
@ -3027,10 +3013,9 @@ static const struct inode_operations proc_tid_base_inode_operations = {
|
||||
.setattr = proc_setattr,
|
||||
};
|
||||
|
||||
static struct dentry *proc_task_instantiate(struct inode *dir,
|
||||
static int proc_task_instantiate(struct inode *dir,
|
||||
struct dentry *dentry, struct task_struct *task, const void *ptr)
|
||||
{
|
||||
struct dentry *error = ERR_PTR(-ENOENT);
|
||||
struct inode *inode;
|
||||
inode = proc_pid_make_inode(dir->i_sb, task);
|
||||
|
||||
@ -3049,14 +3034,14 @@ static struct dentry *proc_task_instantiate(struct inode *dir,
|
||||
d_add(dentry, inode);
|
||||
/* Close the race of the process dying before we return the dentry */
|
||||
if (pid_revalidate(dentry, 0))
|
||||
error = NULL;
|
||||
return 0;
|
||||
out:
|
||||
return error;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)
|
||||
{
|
||||
struct dentry *result = ERR_PTR(-ENOENT);
|
||||
int result = -ENOENT;
|
||||
struct task_struct *task;
|
||||
struct task_struct *leader = get_proc_task(dir);
|
||||
unsigned tid;
|
||||
@ -3086,7 +3071,7 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry
|
||||
out:
|
||||
put_task_struct(leader);
|
||||
out_no_task:
|
||||
return result;
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
/*
|
||||
|
18
fs/proc/fd.c
18
fs/proc/fd.c
@ -167,11 +167,10 @@ static int proc_fd_link(struct dentry *dentry, struct path *path)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct dentry *
|
||||
static int
|
||||
proc_fd_instantiate(struct inode *dir, struct dentry *dentry,
|
||||
struct task_struct *task, const void *ptr)
|
||||
{
|
||||
struct dentry *error = ERR_PTR(-ENOENT);
|
||||
unsigned fd = (unsigned long)ptr;
|
||||
struct proc_inode *ei;
|
||||
struct inode *inode;
|
||||
@ -194,9 +193,9 @@ proc_fd_instantiate(struct inode *dir, struct dentry *dentry,
|
||||
|
||||
/* Close the race of the process dying before we return the dentry */
|
||||
if (tid_fd_revalidate(dentry, 0))
|
||||
error = NULL;
|
||||
return 0;
|
||||
out:
|
||||
return error;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static struct dentry *proc_lookupfd_common(struct inode *dir,
|
||||
@ -204,7 +203,7 @@ static struct dentry *proc_lookupfd_common(struct inode *dir,
|
||||
instantiate_t instantiate)
|
||||
{
|
||||
struct task_struct *task = get_proc_task(dir);
|
||||
struct dentry *result = ERR_PTR(-ENOENT);
|
||||
int result = -ENOENT;
|
||||
unsigned fd = name_to_int(dentry);
|
||||
|
||||
if (!task)
|
||||
@ -216,7 +215,7 @@ static struct dentry *proc_lookupfd_common(struct inode *dir,
|
||||
out:
|
||||
put_task_struct(task);
|
||||
out_no_task:
|
||||
return result;
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
static int proc_readfd_common(struct file *file, struct dir_context *ctx,
|
||||
@ -300,11 +299,10 @@ const struct inode_operations proc_fd_inode_operations = {
|
||||
.setattr = proc_setattr,
|
||||
};
|
||||
|
||||
static struct dentry *
|
||||
static int
|
||||
proc_fdinfo_instantiate(struct inode *dir, struct dentry *dentry,
|
||||
struct task_struct *task, const void *ptr)
|
||||
{
|
||||
struct dentry *error = ERR_PTR(-ENOENT);
|
||||
unsigned fd = (unsigned long)ptr;
|
||||
struct proc_inode *ei;
|
||||
struct inode *inode;
|
||||
@ -324,9 +322,9 @@ proc_fdinfo_instantiate(struct inode *dir, struct dentry *dentry,
|
||||
|
||||
/* Close the race of the process dying before we return the dentry */
|
||||
if (tid_fd_revalidate(dentry, 0))
|
||||
error = NULL;
|
||||
return 0;
|
||||
out:
|
||||
return error;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static struct dentry *
|
||||
|
@ -170,7 +170,7 @@ extern struct dentry *proc_pid_lookup(struct inode *, struct dentry *, unsigned
|
||||
extern loff_t mem_lseek(struct file *, loff_t, int);
|
||||
|
||||
/* Lookups */
|
||||
typedef struct dentry *instantiate_t(struct inode *, struct dentry *,
|
||||
typedef int instantiate_t(struct inode *, struct dentry *,
|
||||
struct task_struct *, const void *);
|
||||
extern bool proc_fill_cache(struct file *, struct dir_context *, const char *, int,
|
||||
instantiate_t, struct task_struct *, const void *);
|
||||
|
@ -187,13 +187,12 @@ static const struct inode_operations proc_ns_link_inode_operations = {
|
||||
.setattr = proc_setattr,
|
||||
};
|
||||
|
||||
static struct dentry *proc_ns_instantiate(struct inode *dir,
|
||||
static int proc_ns_instantiate(struct inode *dir,
|
||||
struct dentry *dentry, struct task_struct *task, const void *ptr)
|
||||
{
|
||||
const struct proc_ns_operations *ns_ops = ptr;
|
||||
struct inode *inode;
|
||||
struct proc_inode *ei;
|
||||
struct dentry *error = ERR_PTR(-ENOENT);
|
||||
|
||||
inode = proc_pid_make_inode(dir->i_sb, task);
|
||||
if (!inode)
|
||||
@ -208,9 +207,9 @@ static struct dentry *proc_ns_instantiate(struct inode *dir,
|
||||
d_add(dentry, inode);
|
||||
/* Close the race of the process dying before we return the dentry */
|
||||
if (pid_revalidate(dentry, 0))
|
||||
error = NULL;
|
||||
return 0;
|
||||
out:
|
||||
return error;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx)
|
||||
@ -248,12 +247,12 @@ const struct file_operations proc_ns_dir_operations = {
|
||||
static struct dentry *proc_ns_dir_lookup(struct inode *dir,
|
||||
struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct dentry *error;
|
||||
int error;
|
||||
struct task_struct *task = get_proc_task(dir);
|
||||
const struct proc_ns_operations **entry, **last;
|
||||
unsigned int len = dentry->d_name.len;
|
||||
|
||||
error = ERR_PTR(-ENOENT);
|
||||
error = -ENOENT;
|
||||
|
||||
if (!task)
|
||||
goto out_no_task;
|
||||
@ -272,7 +271,7 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir,
|
||||
out:
|
||||
put_task_struct(task);
|
||||
out_no_task:
|
||||
return error;
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
const struct inode_operations proc_ns_dir_inode_operations = {
|
||||
|
@ -796,15 +796,16 @@ static int sysctl_is_seen(struct ctl_table_header *p)
|
||||
return res;
|
||||
}
|
||||
|
||||
static int proc_sys_compare(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
static int proc_sys_compare(const struct dentry *parent, const struct dentry *dentry,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
struct ctl_table_header *head;
|
||||
struct inode *inode;
|
||||
|
||||
/* Although proc doesn't have negative dentries, rcu-walk means
|
||||
* that inode here can be NULL */
|
||||
/* AV: can it, indeed? */
|
||||
inode = ACCESS_ONCE(dentry->d_inode);
|
||||
if (!inode)
|
||||
return 1;
|
||||
if (name->len != len)
|
||||
|
@ -41,8 +41,19 @@ static inline int unsigned_offsets(struct file *file)
|
||||
return file->f_mode & FMODE_UNSIGNED_OFFSET;
|
||||
}
|
||||
|
||||
static loff_t lseek_execute(struct file *file, struct inode *inode,
|
||||
loff_t offset, loff_t maxsize)
|
||||
/**
|
||||
* vfs_setpos - update the file offset for lseek
|
||||
* @file: file structure in question
|
||||
* @offset: file offset to seek to
|
||||
* @maxsize: maximum file size
|
||||
*
|
||||
* This is a low-level filesystem helper for updating the file offset to
|
||||
* the value specified by @offset if the given offset is valid and it is
|
||||
* not equal to the current file offset.
|
||||
*
|
||||
* Return the specified offset on success and -EINVAL on invalid offset.
|
||||
*/
|
||||
loff_t vfs_setpos(struct file *file, loff_t offset, loff_t maxsize)
|
||||
{
|
||||
if (offset < 0 && !unsigned_offsets(file))
|
||||
return -EINVAL;
|
||||
@ -55,6 +66,7 @@ static loff_t lseek_execute(struct file *file, struct inode *inode,
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_setpos);
|
||||
|
||||
/**
|
||||
* generic_file_llseek_size - generic llseek implementation for regular files
|
||||
@ -76,8 +88,6 @@ loff_t
|
||||
generic_file_llseek_size(struct file *file, loff_t offset, int whence,
|
||||
loff_t maxsize, loff_t eof)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_END:
|
||||
offset += eof;
|
||||
@ -97,8 +107,7 @@ generic_file_llseek_size(struct file *file, loff_t offset, int whence,
|
||||
* like SEEK_SET.
|
||||
*/
|
||||
spin_lock(&file->f_lock);
|
||||
offset = lseek_execute(file, inode, file->f_pos + offset,
|
||||
maxsize);
|
||||
offset = vfs_setpos(file, file->f_pos + offset, maxsize);
|
||||
spin_unlock(&file->f_lock);
|
||||
return offset;
|
||||
case SEEK_DATA:
|
||||
@ -120,7 +129,7 @@ generic_file_llseek_size(struct file *file, loff_t offset, int whence,
|
||||
break;
|
||||
}
|
||||
|
||||
return lseek_execute(file, inode, offset, maxsize);
|
||||
return vfs_setpos(file, offset, maxsize);
|
||||
}
|
||||
EXPORT_SYMBOL(generic_file_llseek_size);
|
||||
|
||||
@ -144,6 +153,26 @@ loff_t generic_file_llseek(struct file *file, loff_t offset, int whence)
|
||||
}
|
||||
EXPORT_SYMBOL(generic_file_llseek);
|
||||
|
||||
/**
|
||||
* fixed_size_llseek - llseek implementation for fixed-sized devices
|
||||
* @file: file structure to seek on
|
||||
* @offset: file offset to seek to
|
||||
* @whence: type of seek
|
||||
* @size: size of the file
|
||||
*
|
||||
*/
|
||||
loff_t fixed_size_llseek(struct file *file, loff_t offset, int whence, loff_t size)
|
||||
{
|
||||
switch (whence) {
|
||||
case SEEK_SET: case SEEK_CUR: case SEEK_END:
|
||||
return generic_file_llseek_size(file, offset, whence,
|
||||
size, size);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(fixed_size_llseek);
|
||||
|
||||
/**
|
||||
* noop_llseek - No Operation Performed llseek implementation
|
||||
* @file: file structure to seek on
|
||||
@ -296,7 +325,7 @@ SYSCALL_DEFINE5(llseek, unsigned int, fd, unsigned long, offset_high,
|
||||
* them to something that fits in "int" so that others
|
||||
* won't have to do range checks all the time.
|
||||
*/
|
||||
int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count)
|
||||
int rw_verify_area(int read_write, struct file *file, const loff_t *ppos, size_t count)
|
||||
{
|
||||
struct inode *inode;
|
||||
loff_t pos;
|
||||
@ -477,7 +506,8 @@ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
|
||||
if (f.file) {
|
||||
loff_t pos = file_pos_read(f.file);
|
||||
ret = vfs_read(f.file, buf, count, &pos);
|
||||
file_pos_write(f.file, pos);
|
||||
if (ret >= 0)
|
||||
file_pos_write(f.file, pos);
|
||||
fdput(f);
|
||||
}
|
||||
return ret;
|
||||
@ -492,7 +522,8 @@ SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
|
||||
if (f.file) {
|
||||
loff_t pos = file_pos_read(f.file);
|
||||
ret = vfs_write(f.file, buf, count, &pos);
|
||||
file_pos_write(f.file, pos);
|
||||
if (ret >= 0)
|
||||
file_pos_write(f.file, pos);
|
||||
fdput(f);
|
||||
}
|
||||
|
||||
@ -780,7 +811,8 @@ SYSCALL_DEFINE3(readv, unsigned long, fd, const struct iovec __user *, vec,
|
||||
if (f.file) {
|
||||
loff_t pos = file_pos_read(f.file);
|
||||
ret = vfs_readv(f.file, vec, vlen, &pos);
|
||||
file_pos_write(f.file, pos);
|
||||
if (ret >= 0)
|
||||
file_pos_write(f.file, pos);
|
||||
fdput(f);
|
||||
}
|
||||
|
||||
@ -799,7 +831,8 @@ SYSCALL_DEFINE3(writev, unsigned long, fd, const struct iovec __user *, vec,
|
||||
if (f.file) {
|
||||
loff_t pos = file_pos_read(f.file);
|
||||
ret = vfs_writev(f.file, vec, vlen, &pos);
|
||||
file_pos_write(f.file, pos);
|
||||
if (ret >= 0)
|
||||
file_pos_write(f.file, pos);
|
||||
fdput(f);
|
||||
}
|
||||
|
||||
@ -959,7 +992,8 @@ COMPAT_SYSCALL_DEFINE3(readv, unsigned long, fd,
|
||||
return -EBADF;
|
||||
pos = f.file->f_pos;
|
||||
ret = compat_readv(f.file, vec, vlen, &pos);
|
||||
f.file->f_pos = pos;
|
||||
if (ret >= 0)
|
||||
f.file->f_pos = pos;
|
||||
fdput(f);
|
||||
return ret;
|
||||
}
|
||||
@ -1025,7 +1059,8 @@ COMPAT_SYSCALL_DEFINE3(writev, unsigned long, fd,
|
||||
return -EBADF;
|
||||
pos = f.file->f_pos;
|
||||
ret = compat_writev(f.file, vec, vlen, &pos);
|
||||
f.file->f_pos = pos;
|
||||
if (ret >= 0)
|
||||
f.file->f_pos = pos;
|
||||
fdput(f);
|
||||
return ret;
|
||||
}
|
||||
@ -1129,7 +1164,9 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
|
||||
if (in.file->f_flags & O_NONBLOCK)
|
||||
fl = SPLICE_F_NONBLOCK;
|
||||
#endif
|
||||
file_start_write(out.file);
|
||||
retval = do_splice_direct(in.file, &pos, out.file, &out_pos, count, fl);
|
||||
file_end_write(out.file);
|
||||
|
||||
if (retval > 0) {
|
||||
add_rchar(current, retval);
|
||||
|
38
fs/splice.c
38
fs/splice.c
@ -1098,27 +1098,13 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
|
||||
{
|
||||
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *,
|
||||
loff_t *, size_t, unsigned int);
|
||||
int ret;
|
||||
|
||||
if (unlikely(!(out->f_mode & FMODE_WRITE)))
|
||||
return -EBADF;
|
||||
|
||||
if (unlikely(out->f_flags & O_APPEND))
|
||||
return -EINVAL;
|
||||
|
||||
ret = rw_verify_area(WRITE, out, ppos, len);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
|
||||
if (out->f_op && out->f_op->splice_write)
|
||||
splice_write = out->f_op->splice_write;
|
||||
else
|
||||
splice_write = default_file_splice_write;
|
||||
|
||||
file_start_write(out);
|
||||
ret = splice_write(pipe, out, ppos, len, flags);
|
||||
file_end_write(out);
|
||||
return ret;
|
||||
return splice_write(pipe, out, ppos, len, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1307,6 +1293,16 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
|
||||
};
|
||||
long ret;
|
||||
|
||||
if (unlikely(!(out->f_mode & FMODE_WRITE)))
|
||||
return -EBADF;
|
||||
|
||||
if (unlikely(out->f_flags & O_APPEND))
|
||||
return -EINVAL;
|
||||
|
||||
ret = rw_verify_area(WRITE, out, opos, len);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
|
||||
ret = splice_direct_to_actor(in, &sd, direct_splice_actor);
|
||||
if (ret > 0)
|
||||
*ppos = sd.pos;
|
||||
@ -1362,7 +1358,19 @@ static long do_splice(struct file *in, loff_t __user *off_in,
|
||||
offset = out->f_pos;
|
||||
}
|
||||
|
||||
if (unlikely(!(out->f_mode & FMODE_WRITE)))
|
||||
return -EBADF;
|
||||
|
||||
if (unlikely(out->f_flags & O_APPEND))
|
||||
return -EINVAL;
|
||||
|
||||
ret = rw_verify_area(WRITE, out, &offset, len);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
|
||||
file_start_write(out);
|
||||
ret = do_splice_from(ipipe, out, &offset, len, flags);
|
||||
file_end_write(out);
|
||||
|
||||
if (!off_out)
|
||||
out->f_pos = offset;
|
||||
|
@ -27,8 +27,7 @@ static int add_nondir(struct dentry *dentry, struct inode *inode)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sysv_hash(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
static int sysv_hash(const struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
/* Truncate the name in place, avoids having to define a compare
|
||||
function. */
|
||||
|
@ -594,6 +594,29 @@ static int udf_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udf_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct udf_inode_info *iinfo;
|
||||
int err;
|
||||
|
||||
inode = udf_new_inode(dir, mode, &err);
|
||||
if (!inode)
|
||||
return err;
|
||||
|
||||
iinfo = UDF_I(inode);
|
||||
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
|
||||
inode->i_data.a_ops = &udf_adinicb_aops;
|
||||
else
|
||||
inode->i_data.a_ops = &udf_aops;
|
||||
inode->i_op = &udf_file_inode_operations;
|
||||
inode->i_fop = &udf_file_operations;
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
d_tmpfile(dentry, inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udf_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
dev_t rdev)
|
||||
{
|
||||
@ -1311,6 +1334,7 @@ const struct inode_operations udf_dir_inode_operations = {
|
||||
.rmdir = udf_rmdir,
|
||||
.mknod = udf_mknod,
|
||||
.rename = udf_rename,
|
||||
.tmpfile = udf_tmpfile,
|
||||
};
|
||||
const struct inode_operations udf_symlink_inode_operations = {
|
||||
.readlink = generic_readlink,
|
||||
|
@ -1268,8 +1268,7 @@ xfs_seek_data(
|
||||
}
|
||||
|
||||
out:
|
||||
if (offset != file->f_pos)
|
||||
file->f_pos = offset;
|
||||
offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
|
||||
|
||||
out_unlock:
|
||||
xfs_iunlock_map_shared(ip, lock);
|
||||
@ -1377,8 +1376,7 @@ xfs_seek_hole(
|
||||
* situation in particular.
|
||||
*/
|
||||
offset = min_t(loff_t, offset, isize);
|
||||
if (offset != file->f_pos)
|
||||
file->f_pos = offset;
|
||||
offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
|
||||
|
||||
out_unlock:
|
||||
xfs_iunlock_map_shared(ip, lock);
|
||||
|
@ -146,10 +146,8 @@ enum dentry_d_lock_class
|
||||
struct dentry_operations {
|
||||
int (*d_revalidate)(struct dentry *, unsigned int);
|
||||
int (*d_weak_revalidate)(struct dentry *, unsigned int);
|
||||
int (*d_hash)(const struct dentry *, const struct inode *,
|
||||
struct qstr *);
|
||||
int (*d_compare)(const struct dentry *, const struct inode *,
|
||||
const struct dentry *, const struct inode *,
|
||||
int (*d_hash)(const struct dentry *, struct qstr *);
|
||||
int (*d_compare)(const struct dentry *, const struct dentry *,
|
||||
unsigned int, const char *, const struct qstr *);
|
||||
int (*d_delete)(const struct dentry *);
|
||||
void (*d_release)(struct dentry *);
|
||||
@ -246,6 +244,8 @@ extern struct dentry * d_make_root(struct inode *);
|
||||
/* <clickety>-<click> the ramfs-type tree */
|
||||
extern void d_genocide(struct dentry *);
|
||||
|
||||
extern void d_tmpfile(struct dentry *, struct inode *);
|
||||
|
||||
extern struct dentry *d_find_alias(struct inode *);
|
||||
extern void d_prune_aliases(struct inode *);
|
||||
|
||||
@ -300,8 +300,7 @@ extern struct dentry *d_lookup(const struct dentry *, const struct qstr *);
|
||||
extern struct dentry *d_hash_and_lookup(struct dentry *, struct qstr *);
|
||||
extern struct dentry *__d_lookup(const struct dentry *, const struct qstr *);
|
||||
extern struct dentry *__d_lookup_rcu(const struct dentry *parent,
|
||||
const struct qstr *name,
|
||||
unsigned *seq, struct inode *inode);
|
||||
const struct qstr *name, unsigned *seq);
|
||||
|
||||
/**
|
||||
* __d_rcu_to_refcount - take a refcount on dentry if sequence check is ok
|
||||
|
@ -908,6 +908,7 @@ struct file_lock_operations {
|
||||
|
||||
struct lock_manager_operations {
|
||||
int (*lm_compare_owner)(struct file_lock *, struct file_lock *);
|
||||
unsigned long (*lm_owner_key)(struct file_lock *);
|
||||
void (*lm_notify)(struct file_lock *); /* unblock callback */
|
||||
int (*lm_grant)(struct file_lock *, struct file_lock *, int);
|
||||
void (*lm_break)(struct file_lock *);
|
||||
@ -926,9 +927,27 @@ int locks_in_grace(struct net *);
|
||||
/* that will die - we need it for nfs_lock_info */
|
||||
#include <linux/nfs_fs_i.h>
|
||||
|
||||
/*
|
||||
* struct file_lock represents a generic "file lock". It's used to represent
|
||||
* POSIX byte range locks, BSD (flock) locks, and leases. It's important to
|
||||
* note that the same struct is used to represent both a request for a lock and
|
||||
* the lock itself, but the same object is never used for both.
|
||||
*
|
||||
* FIXME: should we create a separate "struct lock_request" to help distinguish
|
||||
* these two uses?
|
||||
*
|
||||
* The i_flock list is ordered by:
|
||||
*
|
||||
* 1) lock type -- FL_LEASEs first, then FL_FLOCK, and finally FL_POSIX
|
||||
* 2) lock owner
|
||||
* 3) lock range start
|
||||
* 4) lock range end
|
||||
*
|
||||
* Obviously, the last two criteria only matter for POSIX locks.
|
||||
*/
|
||||
struct file_lock {
|
||||
struct file_lock *fl_next; /* singly linked list for this inode */
|
||||
struct list_head fl_link; /* doubly linked list of all locks */
|
||||
struct hlist_node fl_link; /* node in global lists */
|
||||
struct list_head fl_block; /* circular list of blocked processes */
|
||||
fl_owner_t fl_owner;
|
||||
unsigned int fl_flags;
|
||||
@ -994,7 +1013,7 @@ extern void locks_release_private(struct file_lock *);
|
||||
extern void posix_test_lock(struct file *, struct file_lock *);
|
||||
extern int posix_lock_file(struct file *, struct file_lock *, struct file_lock *);
|
||||
extern int posix_lock_file_wait(struct file *, struct file_lock *);
|
||||
extern int posix_unblock_lock(struct file *, struct file_lock *);
|
||||
extern int posix_unblock_lock(struct file_lock *);
|
||||
extern int vfs_test_lock(struct file *, struct file_lock *);
|
||||
extern int vfs_lock_file(struct file *, unsigned int, struct file_lock *, struct file_lock *);
|
||||
extern int vfs_cancel_lock(struct file *filp, struct file_lock *fl);
|
||||
@ -1006,9 +1025,6 @@ extern int vfs_setlease(struct file *, long, struct file_lock **);
|
||||
extern int lease_modify(struct file_lock **, int);
|
||||
extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
|
||||
extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
|
||||
extern void locks_delete_block(struct file_lock *waiter);
|
||||
extern void lock_flocks(void);
|
||||
extern void unlock_flocks(void);
|
||||
#else /* !CONFIG_FILE_LOCKING */
|
||||
static inline int fcntl_getlk(struct file *file, struct flock __user *user)
|
||||
{
|
||||
@ -1084,8 +1100,7 @@ static inline int posix_lock_file_wait(struct file *filp, struct file_lock *fl)
|
||||
return -ENOLCK;
|
||||
}
|
||||
|
||||
static inline int posix_unblock_lock(struct file *filp,
|
||||
struct file_lock *waiter)
|
||||
static inline int posix_unblock_lock(struct file_lock *waiter)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
@ -1150,19 +1165,6 @@ static inline int lock_may_write(struct inode *inode, loff_t start,
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void locks_delete_block(struct file_lock *waiter)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void lock_flocks(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void unlock_flocks(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_FILE_LOCKING */
|
||||
|
||||
|
||||
@ -1580,6 +1582,7 @@ struct inode_operations {
|
||||
int (*atomic_open)(struct inode *, struct dentry *,
|
||||
struct file *, unsigned open_flag,
|
||||
umode_t create_mode, int *opened);
|
||||
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
|
||||
} ____cacheline_aligned;
|
||||
|
||||
ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
|
||||
@ -1743,6 +1746,7 @@ struct super_operations {
|
||||
#define I_REFERENCED (1 << 8)
|
||||
#define __I_DIO_WAKEUP 9
|
||||
#define I_DIO_WAKEUP (1 << I_DIO_WAKEUP)
|
||||
#define I_LINKABLE (1 << 10)
|
||||
|
||||
#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
|
||||
|
||||
@ -1896,7 +1900,6 @@ extern int current_umask(void);
|
||||
extern struct kobject *fs_kobj;
|
||||
|
||||
#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)
|
||||
extern int rw_verify_area(int, struct file *, loff_t *, size_t);
|
||||
|
||||
#define FLOCK_VERIFY_READ 1
|
||||
#define FLOCK_VERIFY_WRITE 2
|
||||
@ -2309,7 +2312,6 @@ extern struct file * open_exec(const char *);
|
||||
/* fs/dcache.c -- generic fs support functions */
|
||||
extern int is_subdir(struct dentry *, struct dentry *);
|
||||
extern int path_is_under(struct path *, struct path *);
|
||||
extern ino_t find_inode_number(struct dentry *, struct qstr *);
|
||||
|
||||
#include <linux/err.h>
|
||||
|
||||
@ -2424,9 +2426,12 @@ extern void
|
||||
file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping);
|
||||
extern loff_t noop_llseek(struct file *file, loff_t offset, int whence);
|
||||
extern loff_t no_llseek(struct file *file, loff_t offset, int whence);
|
||||
extern loff_t vfs_setpos(struct file *file, loff_t offset, loff_t maxsize);
|
||||
extern loff_t generic_file_llseek(struct file *file, loff_t offset, int whence);
|
||||
extern loff_t generic_file_llseek_size(struct file *file, loff_t offset,
|
||||
int whence, loff_t maxsize, loff_t eof);
|
||||
extern loff_t fixed_size_llseek(struct file *file, loff_t offset,
|
||||
int whence, loff_t size);
|
||||
extern int generic_file_open(struct inode * inode, struct file * filp);
|
||||
extern int nonseekable_open(struct inode * inode, struct file * filp);
|
||||
|
||||
|
@ -38,7 +38,7 @@ static inline int fsnotify_parent(struct path *path, struct dentry *dentry, __u3
|
||||
static inline int fsnotify_perm(struct file *file, int mask)
|
||||
{
|
||||
struct path *path = &file->f_path;
|
||||
struct inode *inode = path->dentry->d_inode;
|
||||
struct inode *inode = file_inode(file);
|
||||
__u32 fsnotify_mask = 0;
|
||||
int ret;
|
||||
|
||||
@ -192,7 +192,7 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry)
|
||||
static inline void fsnotify_access(struct file *file)
|
||||
{
|
||||
struct path *path = &file->f_path;
|
||||
struct inode *inode = path->dentry->d_inode;
|
||||
struct inode *inode = file_inode(file);
|
||||
__u32 mask = FS_ACCESS;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
@ -210,7 +210,7 @@ static inline void fsnotify_access(struct file *file)
|
||||
static inline void fsnotify_modify(struct file *file)
|
||||
{
|
||||
struct path *path = &file->f_path;
|
||||
struct inode *inode = path->dentry->d_inode;
|
||||
struct inode *inode = file_inode(file);
|
||||
__u32 mask = FS_MODIFY;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
@ -228,7 +228,7 @@ static inline void fsnotify_modify(struct file *file)
|
||||
static inline void fsnotify_open(struct file *file)
|
||||
{
|
||||
struct path *path = &file->f_path;
|
||||
struct inode *inode = path->dentry->d_inode;
|
||||
struct inode *inode = file_inode(file);
|
||||
__u32 mask = FS_OPEN;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
|
@ -84,6 +84,10 @@
|
||||
#define O_PATH 010000000
|
||||
#endif
|
||||
|
||||
#ifndef O_TMPFILE
|
||||
#define O_TMPFILE 020000000
|
||||
#endif
|
||||
|
||||
#ifndef O_NDELAY
|
||||
#define O_NDELAY O_NONBLOCK
|
||||
#endif
|
||||
|
37
mm/shmem.c
37
mm/shmem.c
@ -1798,10 +1798,7 @@ static loff_t shmem_file_llseek(struct file *file, loff_t offset, int whence)
|
||||
}
|
||||
}
|
||||
|
||||
if (offset >= 0 && offset != file->f_pos) {
|
||||
file->f_pos = offset;
|
||||
file->f_version = 0;
|
||||
}
|
||||
offset = vfs_setpos(file, offset, MAX_LFS_FILESIZE);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return offset;
|
||||
}
|
||||
@ -1965,6 +1962,37 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
shmem_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
struct inode *inode;
|
||||
int error = -ENOSPC;
|
||||
|
||||
inode = shmem_get_inode(dir->i_sb, dir, mode, 0, VM_NORESERVE);
|
||||
if (inode) {
|
||||
error = security_inode_init_security(inode, dir,
|
||||
NULL,
|
||||
shmem_initxattrs, NULL);
|
||||
if (error) {
|
||||
if (error != -EOPNOTSUPP) {
|
||||
iput(inode);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_TMPFS_POSIX_ACL
|
||||
error = generic_acl_init(inode, dir);
|
||||
if (error) {
|
||||
iput(inode);
|
||||
return error;
|
||||
}
|
||||
#else
|
||||
error = 0;
|
||||
#endif
|
||||
d_tmpfile(dentry, inode);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static int shmem_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
int error;
|
||||
@ -2723,6 +2751,7 @@ static const struct inode_operations shmem_dir_inode_operations = {
|
||||
.rmdir = shmem_rmdir,
|
||||
.mknod = shmem_mknod,
|
||||
.rename = shmem_rename,
|
||||
.tmpfile = shmem_tmpfile,
|
||||
#endif
|
||||
#ifdef CONFIG_TMPFS_XATTR
|
||||
.setxattr = shmem_setxattr,
|
||||
|
@ -1330,7 +1330,7 @@ static int wait_for_gss_proxy(struct net *net, struct file *file)
|
||||
static ssize_t write_gssp(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct net *net = PDE_DATA(file->f_path.dentry->d_inode);
|
||||
struct net *net = PDE_DATA(file_inode(file));
|
||||
char tbuf[20];
|
||||
unsigned long i;
|
||||
int res;
|
||||
@ -1358,7 +1358,7 @@ static ssize_t write_gssp(struct file *file, const char __user *buf,
|
||||
static ssize_t read_gssp(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct net *net = PDE_DATA(file->f_path.dentry->d_inode);
|
||||
struct net *net = PDE_DATA(file_inode(file));
|
||||
unsigned long p = *ppos;
|
||||
char tbuf[10];
|
||||
size_t len;
|
||||
|
@ -57,7 +57,7 @@ __setup("ima_hash=", hash_setup);
|
||||
static void ima_rdwr_violation_check(struct file *file)
|
||||
{
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct inode *inode = file_inode(file);
|
||||
fmode_t mode = file->f_mode;
|
||||
int must_measure;
|
||||
bool send_tomtou = false, send_writers = false;
|
||||
|
@ -1547,6 +1547,18 @@ static inline int path_has_perm(const struct cred *cred,
|
||||
return inode_has_perm(cred, inode, av, &ad, 0);
|
||||
}
|
||||
|
||||
/* Same as path_has_perm, but uses the inode from the file struct. */
|
||||
static inline int file_path_has_perm(const struct cred *cred,
|
||||
struct file *file,
|
||||
u32 av)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_PATH;
|
||||
ad.u.path = file->f_path;
|
||||
return inode_has_perm(cred, file_inode(file), av, &ad, 0);
|
||||
}
|
||||
|
||||
/* Check whether a task can use an open file descriptor to
|
||||
access an inode in a given way. Check access to the
|
||||
descriptor itself, and then use dentry_has_perm to
|
||||
@ -2141,14 +2153,14 @@ static inline void flush_unauthorized_files(const struct cred *cred,
|
||||
struct tty_file_private *file_priv;
|
||||
|
||||
/* Revalidate access to controlling tty.
|
||||
Use path_has_perm on the tty path directly rather
|
||||
than using file_has_perm, as this particular open
|
||||
file may belong to another process and we are only
|
||||
interested in the inode-based check here. */
|
||||
Use file_path_has_perm on the tty path directly
|
||||
rather than using file_has_perm, as this particular
|
||||
open file may belong to another process and we are
|
||||
only interested in the inode-based check here. */
|
||||
file_priv = list_first_entry(&tty->tty_files,
|
||||
struct tty_file_private, list);
|
||||
file = file_priv->file;
|
||||
if (path_has_perm(cred, &file->f_path, FILE__READ | FILE__WRITE))
|
||||
if (file_path_has_perm(cred, file, FILE__READ | FILE__WRITE))
|
||||
drop_tty = 1;
|
||||
}
|
||||
spin_unlock(&tty_files_lock);
|
||||
@ -3259,7 +3271,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred)
|
||||
* new inode label or new policy.
|
||||
* This check is not redundant - do not remove.
|
||||
*/
|
||||
return path_has_perm(cred, &file->f_path, open_file_to_av(file));
|
||||
return file_path_has_perm(cred, file, open_file_to_av(file));
|
||||
}
|
||||
|
||||
/* task security operations */
|
||||
|
@ -1589,29 +1589,16 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
|
||||
}
|
||||
|
||||
|
||||
/* WARNING: Don't forget to fput back the file */
|
||||
static struct file *snd_pcm_file_fd(int fd, int *fput_needed)
|
||||
static bool is_pcm_file(struct file *file)
|
||||
{
|
||||
struct file *file;
|
||||
struct inode *inode;
|
||||
struct inode *inode = file_inode(file);
|
||||
unsigned int minor;
|
||||
|
||||
file = fget_light(fd, fput_needed);
|
||||
if (!file)
|
||||
return NULL;
|
||||
inode = file_inode(file);
|
||||
if (!S_ISCHR(inode->i_mode) ||
|
||||
imajor(inode) != snd_major) {
|
||||
fput_light(file, *fput_needed);
|
||||
return NULL;
|
||||
}
|
||||
if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major)
|
||||
return false;
|
||||
minor = iminor(inode);
|
||||
if (!snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) &&
|
||||
!snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE)) {
|
||||
fput_light(file, *fput_needed);
|
||||
return NULL;
|
||||
}
|
||||
return file;
|
||||
return snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) ||
|
||||
snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1620,16 +1607,18 @@ static struct file *snd_pcm_file_fd(int fd, int *fput_needed)
|
||||
static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
|
||||
{
|
||||
int res = 0;
|
||||
struct file *file;
|
||||
struct snd_pcm_file *pcm_file;
|
||||
struct snd_pcm_substream *substream1;
|
||||
struct snd_pcm_group *group;
|
||||
int fput_needed;
|
||||
struct fd f = fdget(fd);
|
||||
|
||||
file = snd_pcm_file_fd(fd, &fput_needed);
|
||||
if (!file)
|
||||
if (!f.file)
|
||||
return -EBADFD;
|
||||
pcm_file = file->private_data;
|
||||
if (!is_pcm_file(f.file)) {
|
||||
res = -EBADFD;
|
||||
goto _badf;
|
||||
}
|
||||
pcm_file = f.file->private_data;
|
||||
substream1 = pcm_file->substream;
|
||||
group = kmalloc(sizeof(*group), GFP_KERNEL);
|
||||
if (!group) {
|
||||
@ -1663,8 +1652,9 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
|
||||
up_write(&snd_pcm_link_rwsem);
|
||||
_nolock:
|
||||
snd_card_unref(substream1->pcm->card);
|
||||
fput_light(file, fput_needed);
|
||||
kfree(group);
|
||||
_badf:
|
||||
fdput(f);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user