diff options
Diffstat (limited to 'fs/file.c')
| -rw-r--r-- | fs/file.c | 106 | 
1 files changed, 106 insertions, 0 deletions
diff --git a/fs/file.c b/fs/file.c index 0d1bf051511..6eef55ce30c 100644 --- a/fs/file.c +++ b/fs/file.c @@ -625,3 +625,109 @@ void fd_install(unsigned int fd, struct file *file)  }  EXPORT_SYMBOL(fd_install); + +struct file *fget(unsigned int fd) +{ +	struct file *file; +	struct files_struct *files = current->files; + +	rcu_read_lock(); +	file = fcheck_files(files, fd); +	if (file) { +		/* File object ref couldn't be taken */ +		if (file->f_mode & FMODE_PATH || +		    !atomic_long_inc_not_zero(&file->f_count)) +			file = NULL; +	} +	rcu_read_unlock(); + +	return file; +} + +EXPORT_SYMBOL(fget); + +struct file *fget_raw(unsigned int fd) +{ +	struct file *file; +	struct files_struct *files = current->files; + +	rcu_read_lock(); +	file = fcheck_files(files, fd); +	if (file) { +		/* File object ref couldn't be taken */ +		if (!atomic_long_inc_not_zero(&file->f_count)) +			file = NULL; +	} +	rcu_read_unlock(); + +	return file; +} + +EXPORT_SYMBOL(fget_raw); + +/* + * Lightweight file lookup - no refcnt increment if fd table isn't shared. + * + * You can use this instead of fget if you satisfy all of the following + * conditions: + * 1) You must call fput_light before exiting the syscall and returning control + *    to userspace (i.e. you cannot remember the returned struct file * after + *    returning to userspace). + * 2) You must not call filp_close on the returned struct file * in between + *    calls to fget_light and fput_light. + * 3) You must not clone the current task in between the calls to fget_light + *    and fput_light. + * + * The fput_needed flag returned by fget_light should be passed to the + * corresponding fput_light. + */ +struct file *fget_light(unsigned int fd, int *fput_needed) +{ +	struct file *file; +	struct files_struct *files = current->files; + +	*fput_needed = 0; +	if (atomic_read(&files->count) == 1) { +		file = fcheck_files(files, fd); +		if (file && (file->f_mode & FMODE_PATH)) +			file = NULL; +	} else { +		rcu_read_lock(); +		file = fcheck_files(files, fd); +		if (file) { +			if (!(file->f_mode & FMODE_PATH) && +			    atomic_long_inc_not_zero(&file->f_count)) +				*fput_needed = 1; +			else +				/* Didn't get the reference, someone's freed */ +				file = NULL; +		} +		rcu_read_unlock(); +	} + +	return file; +} + +struct file *fget_raw_light(unsigned int fd, int *fput_needed) +{ +	struct file *file; +	struct files_struct *files = current->files; + +	*fput_needed = 0; +	if (atomic_read(&files->count) == 1) { +		file = fcheck_files(files, fd); +	} else { +		rcu_read_lock(); +		file = fcheck_files(files, fd); +		if (file) { +			if (atomic_long_inc_not_zero(&file->f_count)) +				*fput_needed = 1; +			else +				/* Didn't get the reference, someone's freed */ +				file = NULL; +		} +		rcu_read_unlock(); +	} + +	return file; +}  |