diff options
Diffstat (limited to 'fs/open.c')
| -rw-r--r-- | fs/open.c | 135 | 
1 files changed, 72 insertions, 63 deletions
diff --git a/fs/open.c b/fs/open.c index d420331ca32..d6fd3acde13 100644 --- a/fs/open.c +++ b/fs/open.c @@ -57,7 +57,8 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,  		newattrs.ia_valid |= ret | ATTR_FORCE;  	mutex_lock(&dentry->d_inode->i_mutex); -	ret = notify_change(dentry, &newattrs); +	/* Note any delegations or leases have already been broken: */ +	ret = notify_change(dentry, &newattrs, NULL);  	mutex_unlock(&dentry->d_inode->i_mutex);  	return ret;  } @@ -230,7 +231,13 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)  		return -EINVAL;  	/* Return error if mode is not supported */ -	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) +	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | +		     FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE)) +		return -EOPNOTSUPP; + +	/* Punch hole and zero range are mutually exclusive */ +	if ((mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE)) == +	    (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))  		return -EOPNOTSUPP;  	/* Punch hole must have keep size set */ @@ -238,17 +245,30 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)  	    !(mode & FALLOC_FL_KEEP_SIZE))  		return -EOPNOTSUPP; +	/* Collapse range should only be used exclusively. */ +	if ((mode & FALLOC_FL_COLLAPSE_RANGE) && +	    (mode & ~FALLOC_FL_COLLAPSE_RANGE)) +		return -EINVAL; +  	if (!(file->f_mode & FMODE_WRITE))  		return -EBADF; -	/* It's not possible punch hole on append only file */ -	if (mode & FALLOC_FL_PUNCH_HOLE && IS_APPEND(inode)) +	/* +	 * We can only allow pure fallocate on append only files +	 */ +	if ((mode & ~FALLOC_FL_KEEP_SIZE) && IS_APPEND(inode))  		return -EPERM;  	if (IS_IMMUTABLE(inode))  		return -EPERM;  	/* +	 * We cannot allow any fallocate operation on an active swapfile +	 */ +	if (IS_SWAPFILE(inode)) +		return -ETXTBSY; + +	/*  	 * Revalidate the write permissions, in case security policy has  	 * changed since the files were opened.  	 */ @@ -464,21 +484,28 @@ out:  static int chmod_common(struct path *path, umode_t mode)  {  	struct inode *inode = path->dentry->d_inode; +	struct inode *delegated_inode = NULL;  	struct iattr newattrs;  	int error;  	error = mnt_want_write(path->mnt);  	if (error)  		return error; +retry_deleg:  	mutex_lock(&inode->i_mutex);  	error = security_path_chmod(path, mode);  	if (error)  		goto out_unlock;  	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);  	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; -	error = notify_change(path->dentry, &newattrs); +	error = notify_change(path->dentry, &newattrs, &delegated_inode);  out_unlock:  	mutex_unlock(&inode->i_mutex); +	if (delegated_inode) { +		error = break_deleg_wait(&delegated_inode); +		if (!error) +			goto retry_deleg; +	}  	mnt_drop_write(path->mnt);  	return error;  } @@ -522,6 +549,7 @@ SYSCALL_DEFINE2(chmod, const char __user *, filename, umode_t, mode)  static int chown_common(struct path *path, uid_t user, gid_t group)  {  	struct inode *inode = path->dentry->d_inode; +	struct inode *delegated_inode = NULL;  	int error;  	struct iattr newattrs;  	kuid_t uid; @@ -546,12 +574,17 @@ static int chown_common(struct path *path, uid_t user, gid_t group)  	if (!S_ISDIR(inode->i_mode))  		newattrs.ia_valid |=  			ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; +retry_deleg:  	mutex_lock(&inode->i_mutex);  	error = security_path_chown(path, uid, gid);  	if (!error) -		error = notify_change(path->dentry, &newattrs); +		error = notify_change(path->dentry, &newattrs, &delegated_inode);  	mutex_unlock(&inode->i_mutex); - +	if (delegated_inode) { +		error = break_deleg_wait(&delegated_inode); +		if (!error) +			goto retry_deleg; +	}  	return error;  } @@ -618,35 +651,6 @@ out:  	return error;  } -/* - * You have to be very careful that these write - * counts get cleaned up in error cases and - * upon __fput().  This should probably never - * be called outside of __dentry_open(). - */ -static inline int __get_file_write_access(struct inode *inode, -					  struct vfsmount *mnt) -{ -	int error; -	error = get_write_access(inode); -	if (error) -		return error; -	/* -	 * Do not take mount writer counts on -	 * special files since no writes to -	 * the mount itself will occur. -	 */ -	if (!special_file(inode->i_mode)) { -		/* -		 * Balanced in __fput() -		 */ -		error = __mnt_want_write(mnt); -		if (error) -			put_write_access(inode); -	} -	return error; -} -  int open_check_o_direct(struct file *f)  {  	/* NB: we're sure to have correct a_ops only after f_op->open */ @@ -671,28 +675,37 @@ static int do_dentry_open(struct file *f,  	f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK |  				FMODE_PREAD | FMODE_PWRITE; -	if (unlikely(f->f_flags & O_PATH)) -		f->f_mode = FMODE_PATH; -  	path_get(&f->f_path);  	inode = f->f_inode = f->f_path.dentry->d_inode; -	if (f->f_mode & FMODE_WRITE) { -		error = __get_file_write_access(inode, f->f_path.mnt); -		if (error) -			goto cleanup_file; -		if (!special_file(inode->i_mode)) -			file_take_write(f); -	} -  	f->f_mapping = inode->i_mapping; -	file_sb_list_add(f, inode->i_sb); -	if (unlikely(f->f_mode & FMODE_PATH)) { +	if (unlikely(f->f_flags & O_PATH)) { +		f->f_mode = FMODE_PATH;  		f->f_op = &empty_fops;  		return 0;  	} +	if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) { +		error = get_write_access(inode); +		if (unlikely(error)) +			goto cleanup_file; +		error = __mnt_want_write(f->f_path.mnt); +		if (unlikely(error)) { +			put_write_access(inode); +			goto cleanup_file; +		} +		f->f_mode |= FMODE_WRITER; +	} + +	/* POSIX.1-2008/SUSv4 Section XSI 2.9.7 */ +	if (S_ISREG(inode->i_mode)) +		f->f_mode |= FMODE_ATOMIC_POS; +  	f->f_op = fops_get(inode->i_fop); +	if (unlikely(WARN_ON(!f->f_op))) { +		error = -ENODEV; +		goto cleanup_all; +	}  	error = security_file_open(f, cred);  	if (error) @@ -702,7 +715,7 @@ static int do_dentry_open(struct file *f,  	if (error)  		goto cleanup_all; -	if (!open && f->f_op) +	if (!open)  		open = f->f_op->open;  	if (open) {  		error = open(inode, f); @@ -711,6 +724,12 @@ static int do_dentry_open(struct file *f,  	}  	if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)  		i_readcount_inc(inode); +	if ((f->f_mode & FMODE_READ) && +	     likely(f->f_op->read || f->f_op->aio_read || f->f_op->read_iter)) +		f->f_mode |= FMODE_CAN_READ; +	if ((f->f_mode & FMODE_WRITE) && +	     likely(f->f_op->write || f->f_op->aio_write || f->f_op->write_iter)) +		f->f_mode |= FMODE_CAN_WRITE;  	f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); @@ -720,19 +739,9 @@ static int do_dentry_open(struct file *f,  cleanup_all:  	fops_put(f->f_op); -	file_sb_list_del(f); -	if (f->f_mode & FMODE_WRITE) { +	if (f->f_mode & FMODE_WRITER) {  		put_write_access(inode); -		if (!special_file(inode->i_mode)) { -			/* -			 * We don't consider this a real -			 * mnt_want/drop_write() pair -			 * because it all happenend right -			 * here, so just reset the state. -			 */ -			file_reset_write(f); -			__mnt_drop_write(f->f_path.mnt); -		} +		__mnt_drop_write(f->f_path.mnt);  	}  cleanup_file:  	path_put(&f->f_path); @@ -1023,7 +1032,7 @@ int filp_close(struct file *filp, fl_owner_t id)  		return 0;  	} -	if (filp->f_op && filp->f_op->flush) +	if (filp->f_op->flush)  		retval = filp->f_op->flush(filp, id);  	if (likely(!(filp->f_mode & FMODE_PATH))) {  | 
