diff options
Diffstat (limited to 'fs/f2fs/file.c')
| -rw-r--r-- | fs/f2fs/file.c | 282 | 
1 files changed, 239 insertions, 43 deletions
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 3e58a6f697dd..29bc0a542759 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -20,6 +20,7 @@  #include <linux/uio.h>  #include <linux/uuid.h>  #include <linux/file.h> +#include <linux/nls.h>  #include "f2fs.h"  #include "node.h" @@ -57,6 +58,11 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)  		goto err;  	} +	if (!f2fs_is_checkpoint_ready(sbi)) { +		err = -ENOSPC; +		goto err; +	} +  	sb_start_pagefault(inode->i_sb);  	f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); @@ -496,6 +502,10 @@ static int f2fs_file_open(struct inode *inode, struct file *filp)  	if (err)  		return err; +	err = fsverity_file_open(inode, filp); +	if (err) +		return err; +  	filp->f_mode |= FMODE_NOWAIT;  	return dquot_file_open(inode, filp); @@ -741,15 +751,18 @@ static void __setattr_copy(struct inode *inode, const struct iattr *attr)  		inode->i_uid = attr->ia_uid;  	if (ia_valid & ATTR_GID)  		inode->i_gid = attr->ia_gid; -	if (ia_valid & ATTR_ATIME) -		inode->i_atime = timespec64_trunc(attr->ia_atime, -						  inode->i_sb->s_time_gran); -	if (ia_valid & ATTR_MTIME) -		inode->i_mtime = timespec64_trunc(attr->ia_mtime, -						  inode->i_sb->s_time_gran); -	if (ia_valid & ATTR_CTIME) -		inode->i_ctime = timespec64_trunc(attr->ia_ctime, -						  inode->i_sb->s_time_gran); +	if (ia_valid & ATTR_ATIME) { +		inode->i_atime = timestamp_truncate(attr->ia_atime, +						  inode); +	} +	if (ia_valid & ATTR_MTIME) { +		inode->i_mtime = timestamp_truncate(attr->ia_mtime, +						  inode); +	} +	if (ia_valid & ATTR_CTIME) { +		inode->i_ctime = timestamp_truncate(attr->ia_ctime, +						  inode); +	}  	if (ia_valid & ATTR_MODE) {  		umode_t mode = attr->ia_mode; @@ -778,6 +791,10 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)  	if (err)  		return err; +	err = fsverity_prepare_setattr(dentry, attr); +	if (err) +		return err; +  	if (is_quota_modification(inode, attr)) {  		err = dquot_initialize(inode);  		if (err) @@ -808,14 +825,24 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)  	}  	if (attr->ia_valid & ATTR_SIZE) { -		bool to_smaller = (attr->ia_size <= i_size_read(inode)); +		loff_t old_size = i_size_read(inode); + +		if (attr->ia_size > MAX_INLINE_DATA(inode)) { +			/* +			 * should convert inline inode before i_size_write to +			 * keep smaller than inline_data size with inline flag. +			 */ +			err = f2fs_convert_inline_inode(inode); +			if (err) +				return err; +		}  		down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);  		down_write(&F2FS_I(inode)->i_mmap_sem);  		truncate_setsize(inode, attr->ia_size); -		if (to_smaller) +		if (attr->ia_size <= old_size)  			err = f2fs_truncate(inode);  		/*  		 * do not trim all blocks after i_size if target size is @@ -823,21 +850,11 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)  		 */  		up_write(&F2FS_I(inode)->i_mmap_sem);  		up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); -  		if (err)  			return err; -		if (!to_smaller) { -			/* should convert inline inode here */ -			if (!f2fs_may_inline_data(inode)) { -				err = f2fs_convert_inline_inode(inode); -				if (err) -					return err; -			} -			inode->i_mtime = inode->i_ctime = current_time(inode); -		} -  		down_write(&F2FS_I(inode)->i_sem); +		inode->i_mtime = inode->i_ctime = current_time(inode);  		F2FS_I(inode)->last_disk_size = i_size_read(inode);  		up_write(&F2FS_I(inode)->i_sem);  	} @@ -1030,7 +1047,7 @@ next_dnode:  			if (test_opt(sbi, LFS)) {  				f2fs_put_dnode(&dn); -				return -ENOTSUPP; +				return -EOPNOTSUPP;  			}  			/* do not invalidate this block address */ @@ -1567,6 +1584,8 @@ static long f2fs_fallocate(struct file *file, int mode,  	if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))  		return -EIO; +	if (!f2fs_is_checkpoint_ready(F2FS_I_SB(inode))) +		return -ENOSPC;  	/* f2fs only support ->fallocate for regular file */  	if (!S_ISREG(inode->i_mode)) @@ -1658,6 +1677,13 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)  	if (IS_NOQUOTA(inode))  		return -EPERM; +	if ((iflags ^ fi->i_flags) & F2FS_CASEFOLD_FL) { +		if (!f2fs_sb_has_casefold(F2FS_I_SB(inode))) +			return -EOPNOTSUPP; +		if (!f2fs_empty_dir(inode)) +			return -ENOTEMPTY; +	} +  	fi->i_flags = iflags | (fi->i_flags & ~mask);  	if (fi->i_flags & F2FS_PROJINHERIT_FL) @@ -1692,6 +1718,7 @@ static const struct {  	{ F2FS_INDEX_FL,	FS_INDEX_FL },  	{ F2FS_DIRSYNC_FL,	FS_DIRSYNC_FL },  	{ F2FS_PROJINHERIT_FL,	FS_PROJINHERIT_FL }, +	{ F2FS_CASEFOLD_FL,	FS_CASEFOLD_FL },  };  #define F2FS_GETTABLE_FS_FL (		\ @@ -1705,7 +1732,9 @@ static const struct {  		FS_PROJINHERIT_FL |	\  		FS_ENCRYPT_FL |		\  		FS_INLINE_DATA_FL |	\ -		FS_NOCOW_FL) +		FS_NOCOW_FL |		\ +		FS_VERITY_FL |		\ +		FS_CASEFOLD_FL)  #define F2FS_SETTABLE_FS_FL (		\  		FS_SYNC_FL |		\ @@ -1714,7 +1743,8 @@ static const struct {  		FS_NODUMP_FL |		\  		FS_NOATIME_FL |		\  		FS_DIRSYNC_FL |		\ -		FS_PROJINHERIT_FL) +		FS_PROJINHERIT_FL |	\ +		FS_CASEFOLD_FL)  /* Convert f2fs on-disk i_flags to FS_IOC_{GET,SET}FLAGS flags */  static inline u32 f2fs_iflags_to_fsflags(u32 iflags) @@ -1750,6 +1780,8 @@ static int f2fs_ioc_getflags(struct file *filp, unsigned long arg)  	if (IS_ENCRYPTED(inode))  		fsflags |= FS_ENCRYPT_FL; +	if (IS_VERITY(inode)) +		fsflags |= FS_VERITY_FL;  	if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode))  		fsflags |= FS_INLINE_DATA_FL;  	if (is_inode_flag_set(inode, FI_PIN_FILE)) @@ -1811,6 +1843,8 @@ static int f2fs_ioc_getversion(struct file *filp, unsigned long arg)  static int f2fs_ioc_start_atomic_write(struct file *filp)  {  	struct inode *inode = file_inode(filp); +	struct f2fs_inode_info *fi = F2FS_I(inode); +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);  	int ret;  	if (!inode_owner_or_capable(inode)) @@ -1819,6 +1853,9 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)  	if (!S_ISREG(inode->i_mode))  		return -EINVAL; +	if (filp->f_flags & O_DIRECT) +		return -EINVAL; +  	ret = mnt_want_write_file(filp);  	if (ret)  		return ret; @@ -1850,6 +1887,12 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)  		goto out;  	} +	spin_lock(&sbi->inode_lock[ATOMIC_FILE]); +	if (list_empty(&fi->inmem_ilist)) +		list_add_tail(&fi->inmem_ilist, &sbi->inode_list[ATOMIC_FILE]); +	spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); + +	/* add inode in inmem_list first and set atomic_file */  	set_inode_flag(inode, FI_ATOMIC_FILE);  	clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);  	up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); @@ -1891,11 +1934,8 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp)  			goto err_out;  		ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); -		if (!ret) { -			clear_inode_flag(inode, FI_ATOMIC_FILE); -			F2FS_I(inode)->i_gc_failures[GC_FAILURE_ATOMIC] = 0; -			stat_dec_atomic_write(inode); -		} +		if (!ret) +			f2fs_drop_inmem_pages(inode);  	} else {  		ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 1, false);  	} @@ -2184,6 +2224,49 @@ out_err:  	return err;  } +static int f2fs_ioc_get_encryption_policy_ex(struct file *filp, +					     unsigned long arg) +{ +	if (!f2fs_sb_has_encrypt(F2FS_I_SB(file_inode(filp)))) +		return -EOPNOTSUPP; + +	return fscrypt_ioctl_get_policy_ex(filp, (void __user *)arg); +} + +static int f2fs_ioc_add_encryption_key(struct file *filp, unsigned long arg) +{ +	if (!f2fs_sb_has_encrypt(F2FS_I_SB(file_inode(filp)))) +		return -EOPNOTSUPP; + +	return fscrypt_ioctl_add_key(filp, (void __user *)arg); +} + +static int f2fs_ioc_remove_encryption_key(struct file *filp, unsigned long arg) +{ +	if (!f2fs_sb_has_encrypt(F2FS_I_SB(file_inode(filp)))) +		return -EOPNOTSUPP; + +	return fscrypt_ioctl_remove_key(filp, (void __user *)arg); +} + +static int f2fs_ioc_remove_encryption_key_all_users(struct file *filp, +						    unsigned long arg) +{ +	if (!f2fs_sb_has_encrypt(F2FS_I_SB(file_inode(filp)))) +		return -EOPNOTSUPP; + +	return fscrypt_ioctl_remove_key_all_users(filp, (void __user *)arg); +} + +static int f2fs_ioc_get_encryption_key_status(struct file *filp, +					      unsigned long arg) +{ +	if (!f2fs_sb_has_encrypt(F2FS_I_SB(file_inode(filp)))) +		return -EOPNOTSUPP; + +	return fscrypt_ioctl_get_key_status(filp, (void __user *)arg); +} +  static int f2fs_ioc_gc(struct file *filp, unsigned long arg)  {  	struct inode *inode = file_inode(filp); @@ -2238,9 +2321,9 @@ static int f2fs_ioc_gc_range(struct file *filp, unsigned long arg)  		return -EROFS;  	end = range.start + range.len; -	if (range.start < MAIN_BLKADDR(sbi) || end >= MAX_BLKADDR(sbi)) { +	if (end < range.start || range.start < MAIN_BLKADDR(sbi) || +					end >= MAX_BLKADDR(sbi))  		return -EINVAL; -	}  	ret = mnt_want_write_file(filp);  	if (ret) @@ -2364,8 +2447,10 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,  		map.m_lblk += map.m_len;  	} -	if (!fragmented) +	if (!fragmented) { +		total = 0;  		goto out; +	}  	sec_num = DIV_ROUND_UP(total, BLKS_PER_SEC(sbi)); @@ -2395,7 +2480,7 @@ do_map:  		if (!(map.m_flags & F2FS_MAP_FLAGS)) {  			map.m_lblk = next_pgofs; -			continue; +			goto check;  		}  		set_inode_flag(inode, FI_DO_DEFRAG); @@ -2419,8 +2504,8 @@ do_map:  		}  		map.m_lblk = idx; - -		if (idx < pg_end && cnt < blk_per_seg) +check: +		if (map.m_lblk < pg_end && cnt < blk_per_seg)  			goto do_map;  		clear_inode_flag(inode, FI_DO_DEFRAG); @@ -3060,10 +3145,98 @@ static int f2fs_ioc_resize_fs(struct file *filp, unsigned long arg)  	return ret;  } +static int f2fs_ioc_enable_verity(struct file *filp, unsigned long arg) +{ +	struct inode *inode = file_inode(filp); + +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); + +	if (!f2fs_sb_has_verity(F2FS_I_SB(inode))) { +		f2fs_warn(F2FS_I_SB(inode), +			  "Can't enable fs-verity on inode %lu: the verity feature is not enabled on this filesystem.\n", +			  inode->i_ino); +		return -EOPNOTSUPP; +	} + +	return fsverity_ioctl_enable(filp, (const void __user *)arg); +} + +static int f2fs_ioc_measure_verity(struct file *filp, unsigned long arg) +{ +	if (!f2fs_sb_has_verity(F2FS_I_SB(file_inode(filp)))) +		return -EOPNOTSUPP; + +	return fsverity_ioctl_measure(filp, (void __user *)arg); +} + +static int f2fs_get_volume_name(struct file *filp, unsigned long arg) +{ +	struct inode *inode = file_inode(filp); +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode); +	char *vbuf; +	int count; +	int err = 0; + +	vbuf = f2fs_kzalloc(sbi, MAX_VOLUME_NAME, GFP_KERNEL); +	if (!vbuf) +		return -ENOMEM; + +	down_read(&sbi->sb_lock); +	count = utf16s_to_utf8s(sbi->raw_super->volume_name, +			ARRAY_SIZE(sbi->raw_super->volume_name), +			UTF16_LITTLE_ENDIAN, vbuf, MAX_VOLUME_NAME); +	up_read(&sbi->sb_lock); + +	if (copy_to_user((char __user *)arg, vbuf, +				min(FSLABEL_MAX, count))) +		err = -EFAULT; + +	kvfree(vbuf); +	return err; +} + +static int f2fs_set_volume_name(struct file *filp, unsigned long arg) +{ +	struct inode *inode = file_inode(filp); +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode); +	char *vbuf; +	int err = 0; + +	if (!capable(CAP_SYS_ADMIN)) +		return -EPERM; + +	vbuf = strndup_user((const char __user *)arg, FSLABEL_MAX); +	if (IS_ERR(vbuf)) +		return PTR_ERR(vbuf); + +	err = mnt_want_write_file(filp); +	if (err) +		goto out; + +	down_write(&sbi->sb_lock); + +	memset(sbi->raw_super->volume_name, 0, +			sizeof(sbi->raw_super->volume_name)); +	utf8s_to_utf16s(vbuf, strlen(vbuf), UTF16_LITTLE_ENDIAN, +			sbi->raw_super->volume_name, +			ARRAY_SIZE(sbi->raw_super->volume_name)); + +	err = f2fs_commit_super(sbi, false); + +	up_write(&sbi->sb_lock); + +	mnt_drop_write_file(filp); +out: +	kfree(vbuf); +	return err; +} +  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  {  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))  		return -EIO; +	if (!f2fs_is_checkpoint_ready(F2FS_I_SB(file_inode(filp)))) +		return -ENOSPC;  	switch (cmd) {  	case F2FS_IOC_GETFLAGS: @@ -3092,6 +3265,16 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  		return f2fs_ioc_get_encryption_policy(filp, arg);  	case F2FS_IOC_GET_ENCRYPTION_PWSALT:  		return f2fs_ioc_get_encryption_pwsalt(filp, arg); +	case FS_IOC_GET_ENCRYPTION_POLICY_EX: +		return f2fs_ioc_get_encryption_policy_ex(filp, arg); +	case FS_IOC_ADD_ENCRYPTION_KEY: +		return f2fs_ioc_add_encryption_key(filp, arg); +	case FS_IOC_REMOVE_ENCRYPTION_KEY: +		return f2fs_ioc_remove_encryption_key(filp, arg); +	case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS: +		return f2fs_ioc_remove_encryption_key_all_users(filp, arg); +	case FS_IOC_GET_ENCRYPTION_KEY_STATUS: +		return f2fs_ioc_get_encryption_key_status(filp, arg);  	case F2FS_IOC_GARBAGE_COLLECT:  		return f2fs_ioc_gc(filp, arg);  	case F2FS_IOC_GARBAGE_COLLECT_RANGE: @@ -3118,6 +3301,14 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  		return f2fs_ioc_precache_extents(filp, arg);  	case F2FS_IOC_RESIZE_FS:  		return f2fs_ioc_resize_fs(filp, arg); +	case FS_IOC_ENABLE_VERITY: +		return f2fs_ioc_enable_verity(filp, arg); +	case FS_IOC_MEASURE_VERITY: +		return f2fs_ioc_measure_verity(filp, arg); +	case F2FS_IOC_GET_VOLUME_NAME: +		return f2fs_get_volume_name(filp, arg); +	case F2FS_IOC_SET_VOLUME_NAME: +		return f2fs_set_volume_name(filp, arg);  	default:  		return -ENOTTY;  	} @@ -3134,16 +3325,12 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)  		goto out;  	} -	if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT)) { -		ret = -EINVAL; -		goto out; -	} - -	if (!inode_trylock(inode)) { -		if (iocb->ki_flags & IOCB_NOWAIT) { +	if (iocb->ki_flags & IOCB_NOWAIT) { +		if (!inode_trylock(inode)) {  			ret = -EAGAIN;  			goto out;  		} +	} else {  		inode_lock(inode);  	} @@ -3219,6 +3406,11 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  	case F2FS_IOC_SET_ENCRYPTION_POLICY:  	case F2FS_IOC_GET_ENCRYPTION_PWSALT:  	case F2FS_IOC_GET_ENCRYPTION_POLICY: +	case FS_IOC_GET_ENCRYPTION_POLICY_EX: +	case FS_IOC_ADD_ENCRYPTION_KEY: +	case FS_IOC_REMOVE_ENCRYPTION_KEY: +	case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS: +	case FS_IOC_GET_ENCRYPTION_KEY_STATUS:  	case F2FS_IOC_GARBAGE_COLLECT:  	case F2FS_IOC_GARBAGE_COLLECT_RANGE:  	case F2FS_IOC_WRITE_CHECKPOINT: @@ -3232,6 +3424,10 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  	case F2FS_IOC_SET_PIN_FILE:  	case F2FS_IOC_PRECACHE_EXTENTS:  	case F2FS_IOC_RESIZE_FS: +	case FS_IOC_ENABLE_VERITY: +	case FS_IOC_MEASURE_VERITY: +	case F2FS_IOC_GET_VOLUME_NAME: +	case F2FS_IOC_SET_VOLUME_NAME:  		break;  	default:  		return -ENOIOCTLCMD;  |