162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/fat/file.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Written 1992,1993 by Werner Almesberger 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * regular file handling primitives for fat-based filesystems 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/capability.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/compat.h> 1362306a36Sopenharmony_ci#include <linux/mount.h> 1462306a36Sopenharmony_ci#include <linux/blkdev.h> 1562306a36Sopenharmony_ci#include <linux/backing-dev.h> 1662306a36Sopenharmony_ci#include <linux/fsnotify.h> 1762306a36Sopenharmony_ci#include <linux/security.h> 1862306a36Sopenharmony_ci#include <linux/falloc.h> 1962306a36Sopenharmony_ci#include "fat.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic long fat_fallocate(struct file *file, int mode, 2262306a36Sopenharmony_ci loff_t offset, loff_t len); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int fat_ioctl_get_attributes(struct inode *inode, u32 __user *user_attr) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci u32 attr; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci inode_lock_shared(inode); 2962306a36Sopenharmony_ci attr = fat_make_attrs(inode); 3062306a36Sopenharmony_ci inode_unlock_shared(inode); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci return put_user(attr, user_attr); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct inode *inode = file_inode(file); 3862306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); 3962306a36Sopenharmony_ci int is_dir = S_ISDIR(inode->i_mode); 4062306a36Sopenharmony_ci u32 attr, oldattr; 4162306a36Sopenharmony_ci struct iattr ia; 4262306a36Sopenharmony_ci int err; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci err = get_user(attr, user_attr); 4562306a36Sopenharmony_ci if (err) 4662306a36Sopenharmony_ci goto out; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci err = mnt_want_write_file(file); 4962306a36Sopenharmony_ci if (err) 5062306a36Sopenharmony_ci goto out; 5162306a36Sopenharmony_ci inode_lock(inode); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* 5462306a36Sopenharmony_ci * ATTR_VOLUME and ATTR_DIR cannot be changed; this also 5562306a36Sopenharmony_ci * prevents the user from turning us into a VFAT 5662306a36Sopenharmony_ci * longname entry. Also, we obviously can't set 5762306a36Sopenharmony_ci * any of the NTFS attributes in the high 24 bits. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ci attr &= 0xff & ~(ATTR_VOLUME | ATTR_DIR); 6062306a36Sopenharmony_ci /* Merge in ATTR_VOLUME and ATTR_DIR */ 6162306a36Sopenharmony_ci attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) | 6262306a36Sopenharmony_ci (is_dir ? ATTR_DIR : 0); 6362306a36Sopenharmony_ci oldattr = fat_make_attrs(inode); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* Equivalent to a chmod() */ 6662306a36Sopenharmony_ci ia.ia_valid = ATTR_MODE | ATTR_CTIME; 6762306a36Sopenharmony_ci ia.ia_ctime = current_time(inode); 6862306a36Sopenharmony_ci if (is_dir) 6962306a36Sopenharmony_ci ia.ia_mode = fat_make_mode(sbi, attr, S_IRWXUGO); 7062306a36Sopenharmony_ci else { 7162306a36Sopenharmony_ci ia.ia_mode = fat_make_mode(sbi, attr, 7262306a36Sopenharmony_ci S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO)); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* The root directory has no attributes */ 7662306a36Sopenharmony_ci if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) { 7762306a36Sopenharmony_ci err = -EINVAL; 7862306a36Sopenharmony_ci goto out_unlock_inode; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (sbi->options.sys_immutable && 8262306a36Sopenharmony_ci ((attr | oldattr) & ATTR_SYS) && 8362306a36Sopenharmony_ci !capable(CAP_LINUX_IMMUTABLE)) { 8462306a36Sopenharmony_ci err = -EPERM; 8562306a36Sopenharmony_ci goto out_unlock_inode; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* 8962306a36Sopenharmony_ci * The security check is questionable... We single 9062306a36Sopenharmony_ci * out the RO attribute for checking by the security 9162306a36Sopenharmony_ci * module, just because it maps to a file mode. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci err = security_inode_setattr(file_mnt_idmap(file), 9462306a36Sopenharmony_ci file->f_path.dentry, &ia); 9562306a36Sopenharmony_ci if (err) 9662306a36Sopenharmony_ci goto out_unlock_inode; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* This MUST be done before doing anything irreversible... */ 9962306a36Sopenharmony_ci err = fat_setattr(file_mnt_idmap(file), file->f_path.dentry, &ia); 10062306a36Sopenharmony_ci if (err) 10162306a36Sopenharmony_ci goto out_unlock_inode; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci fsnotify_change(file->f_path.dentry, ia.ia_valid); 10462306a36Sopenharmony_ci if (sbi->options.sys_immutable) { 10562306a36Sopenharmony_ci if (attr & ATTR_SYS) 10662306a36Sopenharmony_ci inode->i_flags |= S_IMMUTABLE; 10762306a36Sopenharmony_ci else 10862306a36Sopenharmony_ci inode->i_flags &= ~S_IMMUTABLE; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci fat_save_attrs(inode, attr); 11262306a36Sopenharmony_ci mark_inode_dirty(inode); 11362306a36Sopenharmony_ciout_unlock_inode: 11462306a36Sopenharmony_ci inode_unlock(inode); 11562306a36Sopenharmony_ci mnt_drop_write_file(file); 11662306a36Sopenharmony_ciout: 11762306a36Sopenharmony_ci return err; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int fat_ioctl_get_volume_id(struct inode *inode, u32 __user *user_attr) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); 12362306a36Sopenharmony_ci return put_user(sbi->vol_id, user_attr); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int fat_ioctl_fitrim(struct inode *inode, unsigned long arg) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 12962306a36Sopenharmony_ci struct fstrim_range __user *user_range; 13062306a36Sopenharmony_ci struct fstrim_range range; 13162306a36Sopenharmony_ci int err; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 13462306a36Sopenharmony_ci return -EPERM; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (!bdev_max_discard_sectors(sb->s_bdev)) 13762306a36Sopenharmony_ci return -EOPNOTSUPP; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci user_range = (struct fstrim_range __user *)arg; 14062306a36Sopenharmony_ci if (copy_from_user(&range, user_range, sizeof(range))) 14162306a36Sopenharmony_ci return -EFAULT; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci range.minlen = max_t(unsigned int, range.minlen, 14462306a36Sopenharmony_ci bdev_discard_granularity(sb->s_bdev)); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci err = fat_trim_fs(inode, &range); 14762306a36Sopenharmony_ci if (err < 0) 14862306a36Sopenharmony_ci return err; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (copy_to_user(user_range, &range, sizeof(range))) 15162306a36Sopenharmony_ci return -EFAULT; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cilong fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 15962306a36Sopenharmony_ci u32 __user *user_attr = (u32 __user *)arg; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci switch (cmd) { 16262306a36Sopenharmony_ci case FAT_IOCTL_GET_ATTRIBUTES: 16362306a36Sopenharmony_ci return fat_ioctl_get_attributes(inode, user_attr); 16462306a36Sopenharmony_ci case FAT_IOCTL_SET_ATTRIBUTES: 16562306a36Sopenharmony_ci return fat_ioctl_set_attributes(filp, user_attr); 16662306a36Sopenharmony_ci case FAT_IOCTL_GET_VOLUME_ID: 16762306a36Sopenharmony_ci return fat_ioctl_get_volume_id(inode, user_attr); 16862306a36Sopenharmony_ci case FITRIM: 16962306a36Sopenharmony_ci return fat_ioctl_fitrim(inode, arg); 17062306a36Sopenharmony_ci default: 17162306a36Sopenharmony_ci return -ENOTTY; /* Inappropriate ioctl for device */ 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int fat_file_release(struct inode *inode, struct file *filp) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci if ((filp->f_mode & FMODE_WRITE) && 17862306a36Sopenharmony_ci MSDOS_SB(inode->i_sb)->options.flush) { 17962306a36Sopenharmony_ci fat_flush_inodes(inode->i_sb, inode, NULL); 18062306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 18162306a36Sopenharmony_ci io_schedule_timeout(HZ/10); 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ciint fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct inode *inode = filp->f_mapping->host; 18962306a36Sopenharmony_ci int err; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci err = __generic_file_fsync(filp, start, end, datasync); 19262306a36Sopenharmony_ci if (err) 19362306a36Sopenharmony_ci return err; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci err = sync_mapping_buffers(MSDOS_SB(inode->i_sb)->fat_inode->i_mapping); 19662306a36Sopenharmony_ci if (err) 19762306a36Sopenharmony_ci return err; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return blkdev_issue_flush(inode->i_sb->s_bdev); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ciconst struct file_operations fat_file_operations = { 20462306a36Sopenharmony_ci .llseek = generic_file_llseek, 20562306a36Sopenharmony_ci .read_iter = generic_file_read_iter, 20662306a36Sopenharmony_ci .write_iter = generic_file_write_iter, 20762306a36Sopenharmony_ci .mmap = generic_file_mmap, 20862306a36Sopenharmony_ci .release = fat_file_release, 20962306a36Sopenharmony_ci .unlocked_ioctl = fat_generic_ioctl, 21062306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 21162306a36Sopenharmony_ci .fsync = fat_file_fsync, 21262306a36Sopenharmony_ci .splice_read = filemap_splice_read, 21362306a36Sopenharmony_ci .splice_write = iter_file_splice_write, 21462306a36Sopenharmony_ci .fallocate = fat_fallocate, 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int fat_cont_expand(struct inode *inode, loff_t size) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct address_space *mapping = inode->i_mapping; 22062306a36Sopenharmony_ci loff_t start = inode->i_size, count = size - inode->i_size; 22162306a36Sopenharmony_ci int err; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci err = generic_cont_expand_simple(inode, size); 22462306a36Sopenharmony_ci if (err) 22562306a36Sopenharmony_ci goto out; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci fat_truncate_time(inode, NULL, S_CTIME|S_MTIME); 22862306a36Sopenharmony_ci mark_inode_dirty(inode); 22962306a36Sopenharmony_ci if (IS_SYNC(inode)) { 23062306a36Sopenharmony_ci int err2; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* 23362306a36Sopenharmony_ci * Opencode syncing since we don't have a file open to use 23462306a36Sopenharmony_ci * standard fsync path. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci err = filemap_fdatawrite_range(mapping, start, 23762306a36Sopenharmony_ci start + count - 1); 23862306a36Sopenharmony_ci err2 = sync_mapping_buffers(mapping); 23962306a36Sopenharmony_ci if (!err) 24062306a36Sopenharmony_ci err = err2; 24162306a36Sopenharmony_ci err2 = write_inode_now(inode, 1); 24262306a36Sopenharmony_ci if (!err) 24362306a36Sopenharmony_ci err = err2; 24462306a36Sopenharmony_ci if (!err) { 24562306a36Sopenharmony_ci err = filemap_fdatawait_range(mapping, start, 24662306a36Sopenharmony_ci start + count - 1); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ciout: 25062306a36Sopenharmony_ci return err; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* 25462306a36Sopenharmony_ci * Preallocate space for a file. This implements fat's fallocate file 25562306a36Sopenharmony_ci * operation, which gets called from sys_fallocate system call. User 25662306a36Sopenharmony_ci * space requests len bytes at offset. If FALLOC_FL_KEEP_SIZE is set 25762306a36Sopenharmony_ci * we just allocate clusters without zeroing them out. Otherwise we 25862306a36Sopenharmony_ci * allocate and zero out clusters via an expanding truncate. 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_cistatic long fat_fallocate(struct file *file, int mode, 26162306a36Sopenharmony_ci loff_t offset, loff_t len) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci int nr_cluster; /* Number of clusters to be allocated */ 26462306a36Sopenharmony_ci loff_t mm_bytes; /* Number of bytes to be allocated for file */ 26562306a36Sopenharmony_ci loff_t ondisksize; /* block aligned on-disk size in bytes*/ 26662306a36Sopenharmony_ci struct inode *inode = file->f_mapping->host; 26762306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 26862306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 26962306a36Sopenharmony_ci int err = 0; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* No support for hole punch or other fallocate flags. */ 27262306a36Sopenharmony_ci if (mode & ~FALLOC_FL_KEEP_SIZE) 27362306a36Sopenharmony_ci return -EOPNOTSUPP; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* No support for dir */ 27662306a36Sopenharmony_ci if (!S_ISREG(inode->i_mode)) 27762306a36Sopenharmony_ci return -EOPNOTSUPP; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci inode_lock(inode); 28062306a36Sopenharmony_ci if (mode & FALLOC_FL_KEEP_SIZE) { 28162306a36Sopenharmony_ci ondisksize = inode->i_blocks << 9; 28262306a36Sopenharmony_ci if ((offset + len) <= ondisksize) 28362306a36Sopenharmony_ci goto error; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* First compute the number of clusters to be allocated */ 28662306a36Sopenharmony_ci mm_bytes = offset + len - ondisksize; 28762306a36Sopenharmony_ci nr_cluster = (mm_bytes + (sbi->cluster_size - 1)) >> 28862306a36Sopenharmony_ci sbi->cluster_bits; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Start the allocation.We are not zeroing out the clusters */ 29162306a36Sopenharmony_ci while (nr_cluster-- > 0) { 29262306a36Sopenharmony_ci err = fat_add_cluster(inode); 29362306a36Sopenharmony_ci if (err) 29462306a36Sopenharmony_ci goto error; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci } else { 29762306a36Sopenharmony_ci if ((offset + len) <= i_size_read(inode)) 29862306a36Sopenharmony_ci goto error; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* This is just an expanding truncate */ 30162306a36Sopenharmony_ci err = fat_cont_expand(inode, (offset + len)); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cierror: 30562306a36Sopenharmony_ci inode_unlock(inode); 30662306a36Sopenharmony_ci return err; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci/* Free all clusters after the skip'th cluster. */ 31062306a36Sopenharmony_cistatic int fat_free(struct inode *inode, int skip) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 31362306a36Sopenharmony_ci int err, wait, free_start, i_start, i_logstart; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (MSDOS_I(inode)->i_start == 0) 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci fat_cache_inval_inode(inode); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci wait = IS_DIRSYNC(inode); 32162306a36Sopenharmony_ci i_start = free_start = MSDOS_I(inode)->i_start; 32262306a36Sopenharmony_ci i_logstart = MSDOS_I(inode)->i_logstart; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* First, we write the new file size. */ 32562306a36Sopenharmony_ci if (!skip) { 32662306a36Sopenharmony_ci MSDOS_I(inode)->i_start = 0; 32762306a36Sopenharmony_ci MSDOS_I(inode)->i_logstart = 0; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci MSDOS_I(inode)->i_attrs |= ATTR_ARCH; 33062306a36Sopenharmony_ci fat_truncate_time(inode, NULL, S_CTIME|S_MTIME); 33162306a36Sopenharmony_ci if (wait) { 33262306a36Sopenharmony_ci err = fat_sync_inode(inode); 33362306a36Sopenharmony_ci if (err) { 33462306a36Sopenharmony_ci MSDOS_I(inode)->i_start = i_start; 33562306a36Sopenharmony_ci MSDOS_I(inode)->i_logstart = i_logstart; 33662306a36Sopenharmony_ci return err; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci } else 33962306a36Sopenharmony_ci mark_inode_dirty(inode); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* Write a new EOF, and get the remaining cluster chain for freeing. */ 34262306a36Sopenharmony_ci if (skip) { 34362306a36Sopenharmony_ci struct fat_entry fatent; 34462306a36Sopenharmony_ci int ret, fclus, dclus; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ret = fat_get_cluster(inode, skip - 1, &fclus, &dclus); 34762306a36Sopenharmony_ci if (ret < 0) 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci else if (ret == FAT_ENT_EOF) 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci fatent_init(&fatent); 35362306a36Sopenharmony_ci ret = fat_ent_read(inode, &fatent, dclus); 35462306a36Sopenharmony_ci if (ret == FAT_ENT_EOF) { 35562306a36Sopenharmony_ci fatent_brelse(&fatent); 35662306a36Sopenharmony_ci return 0; 35762306a36Sopenharmony_ci } else if (ret == FAT_ENT_FREE) { 35862306a36Sopenharmony_ci fat_fs_error(sb, 35962306a36Sopenharmony_ci "%s: invalid cluster chain (i_pos %lld)", 36062306a36Sopenharmony_ci __func__, MSDOS_I(inode)->i_pos); 36162306a36Sopenharmony_ci ret = -EIO; 36262306a36Sopenharmony_ci } else if (ret > 0) { 36362306a36Sopenharmony_ci err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, wait); 36462306a36Sopenharmony_ci if (err) 36562306a36Sopenharmony_ci ret = err; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci fatent_brelse(&fatent); 36862306a36Sopenharmony_ci if (ret < 0) 36962306a36Sopenharmony_ci return ret; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci free_start = ret; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci inode->i_blocks = skip << (MSDOS_SB(sb)->cluster_bits - 9); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Freeing the remained cluster chain */ 37662306a36Sopenharmony_ci return fat_free_clusters(inode, free_start); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_civoid fat_truncate_blocks(struct inode *inode, loff_t offset) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); 38262306a36Sopenharmony_ci const unsigned int cluster_size = sbi->cluster_size; 38362306a36Sopenharmony_ci int nr_clusters; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * This protects against truncating a file bigger than it was then 38762306a36Sopenharmony_ci * trying to write into the hole. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci if (MSDOS_I(inode)->mmu_private > offset) 39062306a36Sopenharmony_ci MSDOS_I(inode)->mmu_private = offset; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci nr_clusters = (offset + (cluster_size - 1)) >> sbi->cluster_bits; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci fat_free(inode, nr_clusters); 39562306a36Sopenharmony_ci fat_flush_inodes(inode->i_sb, inode, NULL); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ciint fat_getattr(struct mnt_idmap *idmap, const struct path *path, 39962306a36Sopenharmony_ci struct kstat *stat, u32 request_mask, unsigned int flags) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct inode *inode = d_inode(path->dentry); 40262306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci generic_fillattr(idmap, request_mask, inode, stat); 40562306a36Sopenharmony_ci stat->blksize = sbi->cluster_size; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (sbi->options.nfs == FAT_NFS_NOSTALE_RO) { 40862306a36Sopenharmony_ci /* Use i_pos for ino. This is used as fileid of nfs. */ 40962306a36Sopenharmony_ci stat->ino = fat_i_pos_read(sbi, inode); 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (sbi->options.isvfat && request_mask & STATX_BTIME) { 41362306a36Sopenharmony_ci stat->result_mask |= STATX_BTIME; 41462306a36Sopenharmony_ci stat->btime = MSDOS_I(inode)->i_crtime; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return 0; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_getattr); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int fat_sanitize_mode(const struct msdos_sb_info *sbi, 42262306a36Sopenharmony_ci struct inode *inode, umode_t *mode_ptr) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci umode_t mask, perm; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* 42762306a36Sopenharmony_ci * Note, the basic check is already done by a caller of 42862306a36Sopenharmony_ci * (attr->ia_mode & ~FAT_VALID_MODE) 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (S_ISREG(inode->i_mode)) 43262306a36Sopenharmony_ci mask = sbi->options.fs_fmask; 43362306a36Sopenharmony_ci else 43462306a36Sopenharmony_ci mask = sbi->options.fs_dmask; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci perm = *mode_ptr & ~(S_IFMT | mask); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* 43962306a36Sopenharmony_ci * Of the r and x bits, all (subject to umask) must be present. Of the 44062306a36Sopenharmony_ci * w bits, either all (subject to umask) or none must be present. 44162306a36Sopenharmony_ci * 44262306a36Sopenharmony_ci * If fat_mode_can_hold_ro(inode) is false, can't change w bits. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ci if ((perm & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO))) 44562306a36Sopenharmony_ci return -EPERM; 44662306a36Sopenharmony_ci if (fat_mode_can_hold_ro(inode)) { 44762306a36Sopenharmony_ci if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask))) 44862306a36Sopenharmony_ci return -EPERM; 44962306a36Sopenharmony_ci } else { 45062306a36Sopenharmony_ci if ((perm & S_IWUGO) != (S_IWUGO & ~mask)) 45162306a36Sopenharmony_ci return -EPERM; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci *mode_ptr &= S_IFMT | perm; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci return 0; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int fat_allow_set_time(struct mnt_idmap *idmap, 46062306a36Sopenharmony_ci struct msdos_sb_info *sbi, struct inode *inode) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci umode_t allow_utime = sbi->options.allow_utime; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (!vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), 46562306a36Sopenharmony_ci current_fsuid())) { 46662306a36Sopenharmony_ci if (vfsgid_in_group_p(i_gid_into_vfsgid(idmap, inode))) 46762306a36Sopenharmony_ci allow_utime >>= 3; 46862306a36Sopenharmony_ci if (allow_utime & MAY_WRITE) 46962306a36Sopenharmony_ci return 1; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* use a default check */ 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci#define TIMES_SET_FLAGS (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET) 47762306a36Sopenharmony_ci/* valid file mode bits */ 47862306a36Sopenharmony_ci#define FAT_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXUGO) 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ciint fat_setattr(struct mnt_idmap *idmap, struct dentry *dentry, 48162306a36Sopenharmony_ci struct iattr *attr) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); 48462306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 48562306a36Sopenharmony_ci unsigned int ia_valid; 48662306a36Sopenharmony_ci int error; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* Check for setting the inode time. */ 48962306a36Sopenharmony_ci ia_valid = attr->ia_valid; 49062306a36Sopenharmony_ci if (ia_valid & TIMES_SET_FLAGS) { 49162306a36Sopenharmony_ci if (fat_allow_set_time(idmap, sbi, inode)) 49262306a36Sopenharmony_ci attr->ia_valid &= ~TIMES_SET_FLAGS; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci error = setattr_prepare(idmap, dentry, attr); 49662306a36Sopenharmony_ci attr->ia_valid = ia_valid; 49762306a36Sopenharmony_ci if (error) { 49862306a36Sopenharmony_ci if (sbi->options.quiet) 49962306a36Sopenharmony_ci error = 0; 50062306a36Sopenharmony_ci goto out; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* 50462306a36Sopenharmony_ci * Expand the file. Since inode_setattr() updates ->i_size 50562306a36Sopenharmony_ci * before calling the ->truncate(), but FAT needs to fill the 50662306a36Sopenharmony_ci * hole before it. XXX: this is no longer true with new truncate 50762306a36Sopenharmony_ci * sequence. 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_ci if (attr->ia_valid & ATTR_SIZE) { 51062306a36Sopenharmony_ci inode_dio_wait(inode); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (attr->ia_size > inode->i_size) { 51362306a36Sopenharmony_ci error = fat_cont_expand(inode, attr->ia_size); 51462306a36Sopenharmony_ci if (error || attr->ia_valid == ATTR_SIZE) 51562306a36Sopenharmony_ci goto out; 51662306a36Sopenharmony_ci attr->ia_valid &= ~ATTR_SIZE; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (((attr->ia_valid & ATTR_UID) && 52162306a36Sopenharmony_ci (!uid_eq(from_vfsuid(idmap, i_user_ns(inode), attr->ia_vfsuid), 52262306a36Sopenharmony_ci sbi->options.fs_uid))) || 52362306a36Sopenharmony_ci ((attr->ia_valid & ATTR_GID) && 52462306a36Sopenharmony_ci (!gid_eq(from_vfsgid(idmap, i_user_ns(inode), attr->ia_vfsgid), 52562306a36Sopenharmony_ci sbi->options.fs_gid))) || 52662306a36Sopenharmony_ci ((attr->ia_valid & ATTR_MODE) && 52762306a36Sopenharmony_ci (attr->ia_mode & ~FAT_VALID_MODE))) 52862306a36Sopenharmony_ci error = -EPERM; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (error) { 53162306a36Sopenharmony_ci if (sbi->options.quiet) 53262306a36Sopenharmony_ci error = 0; 53362306a36Sopenharmony_ci goto out; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* 53762306a36Sopenharmony_ci * We don't return -EPERM here. Yes, strange, but this is too 53862306a36Sopenharmony_ci * old behavior. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ci if (attr->ia_valid & ATTR_MODE) { 54162306a36Sopenharmony_ci if (fat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0) 54262306a36Sopenharmony_ci attr->ia_valid &= ~ATTR_MODE; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (attr->ia_valid & ATTR_SIZE) { 54662306a36Sopenharmony_ci error = fat_block_truncate_page(inode, attr->ia_size); 54762306a36Sopenharmony_ci if (error) 54862306a36Sopenharmony_ci goto out; 54962306a36Sopenharmony_ci down_write(&MSDOS_I(inode)->truncate_lock); 55062306a36Sopenharmony_ci truncate_setsize(inode, attr->ia_size); 55162306a36Sopenharmony_ci fat_truncate_blocks(inode, attr->ia_size); 55262306a36Sopenharmony_ci up_write(&MSDOS_I(inode)->truncate_lock); 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* 55662306a36Sopenharmony_ci * setattr_copy can't truncate these appropriately, so we'll 55762306a36Sopenharmony_ci * copy them ourselves 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ci if (attr->ia_valid & ATTR_ATIME) 56062306a36Sopenharmony_ci fat_truncate_time(inode, &attr->ia_atime, S_ATIME); 56162306a36Sopenharmony_ci if (attr->ia_valid & ATTR_CTIME) 56262306a36Sopenharmony_ci fat_truncate_time(inode, &attr->ia_ctime, S_CTIME); 56362306a36Sopenharmony_ci if (attr->ia_valid & ATTR_MTIME) 56462306a36Sopenharmony_ci fat_truncate_time(inode, &attr->ia_mtime, S_MTIME); 56562306a36Sopenharmony_ci attr->ia_valid &= ~(ATTR_ATIME|ATTR_CTIME|ATTR_MTIME); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci setattr_copy(idmap, inode, attr); 56862306a36Sopenharmony_ci mark_inode_dirty(inode); 56962306a36Sopenharmony_ciout: 57062306a36Sopenharmony_ci return error; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_setattr); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ciconst struct inode_operations fat_file_inode_operations = { 57562306a36Sopenharmony_ci .setattr = fat_setattr, 57662306a36Sopenharmony_ci .getattr = fat_getattr, 57762306a36Sopenharmony_ci .update_time = fat_update_time, 57862306a36Sopenharmony_ci}; 579