1 /* 2 * linux/fs/fat/file.c 3 * 4 * Written 1992,1993 by Werner Almesberger 5 * 6 * regular file handling primitives for fat-based filesystems 7 */ 8 9 #include <linux/capability.h> 10 #include <linux/module.h> 11 #include <linux/time.h> 12 #include <linux/msdos_fs.h> 13 #include <linux/smp_lock.h> 14 #include <linux/buffer_head.h> 15 #include <linux/writeback.h> 16 #include <linux/backing-dev.h> 17 #include <linux/blkdev.h> 18 19 int fat_generic_ioctl(struct inode *inode, struct file *filp, 20 unsigned int cmd, unsigned long arg) 21 { 22 struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); 23 u32 __user *user_attr = (u32 __user *)arg; 24 25 switch (cmd) { 26 case FAT_IOCTL_GET_ATTRIBUTES: 27 { 28 u32 attr; 29 30 if (inode->i_ino == MSDOS_ROOT_INO) 31 attr = ATTR_DIR; 32 else 33 attr = fat_attr(inode); 34 35 return put_user(attr, user_attr); 36 } 37 case FAT_IOCTL_SET_ATTRIBUTES: 38 { 39 u32 attr, oldattr; 40 int err, is_dir = S_ISDIR(inode->i_mode); 41 struct iattr ia; 42 43 err = get_user(attr, user_attr); 44 if (err) 45 return err; 46 47 mutex_lock(&inode->i_mutex); 48 49 if (IS_RDONLY(inode)) { 50 err = -EROFS; 51 goto up; 52 } 53 54 /* 55 * ATTR_VOLUME and ATTR_DIR cannot be changed; this also 56 * prevents the user from turning us into a VFAT 57 * longname entry. Also, we obviously can't set 58 * any of the NTFS attributes in the high 24 bits. 59 */ 60 attr &= 0xff & ~(ATTR_VOLUME | ATTR_DIR); 61 /* Merge in ATTR_VOLUME and ATTR_DIR */ 62 attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) | 63 (is_dir ? ATTR_DIR : 0); 64 oldattr = fat_attr(inode); 65 66 /* Equivalent to a chmod() */ 67 ia.ia_valid = ATTR_MODE | ATTR_CTIME; 68 if (is_dir) { 69 ia.ia_mode = MSDOS_MKMODE(attr, 70 S_IRWXUGO & ~sbi->options.fs_dmask) 71 | S_IFDIR; 72 } else { 73 ia.ia_mode = MSDOS_MKMODE(attr, 74 (S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO)) 75 & ~sbi->options.fs_fmask) 76 | S_IFREG; 77 } 78 79 /* The root directory has no attributes */ 80 if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) { 81 err = -EINVAL; 82 goto up; 83 } 84 85 if (sbi->options.sys_immutable) { 86 if ((attr | oldattr) & ATTR_SYS) { 87 if (!capable(CAP_LINUX_IMMUTABLE)) { 88 err = -EPERM; 89 goto up; 90 } 91 } 92 } 93 94 /* This MUST be done before doing anything irreversible... */ 95 err = notify_change(filp->f_path.dentry, &ia); 96 if (err) 97 goto up; 98 99 if (sbi->options.sys_immutable) { 100 if (attr & ATTR_SYS) 101 inode->i_flags |= S_IMMUTABLE; 102 else 103 inode->i_flags &= S_IMMUTABLE; 104 } 105 106 MSDOS_I(inode)->i_attrs = attr & ATTR_UNUSED; 107 mark_inode_dirty(inode); 108 up: 109 mutex_unlock(&inode->i_mutex); 110 return err; 111 } 112 default: 113 return -ENOTTY; /* Inappropriate ioctl for device */ 114 } 115 } 116 117 static int fat_file_release(struct inode *inode, struct file *filp) 118 { 119 if ((filp->f_mode & FMODE_WRITE) && 120 MSDOS_SB(inode->i_sb)->options.flush) { 121 fat_flush_inodes(inode->i_sb, inode, NULL); 122 congestion_wait(WRITE, HZ/10); 123 } 124 return 0; 125 } 126 127 const struct file_operations fat_file_operations = { 128 .llseek = generic_file_llseek, 129 .read = do_sync_read, 130 .write = do_sync_write, 131 .aio_read = generic_file_aio_read, 132 .aio_write = generic_file_aio_write, 133 .mmap = generic_file_mmap, 134 .release = fat_file_release, 135 .ioctl = fat_generic_ioctl, 136 .fsync = file_fsync, 137 .splice_read = generic_file_splice_read, 138 }; 139 140 static int fat_cont_expand(struct inode *inode, loff_t size) 141 { 142 struct address_space *mapping = inode->i_mapping; 143 loff_t start = inode->i_size, count = size - inode->i_size; 144 int err; 145 146 err = generic_cont_expand_simple(inode, size); 147 if (err) 148 goto out; 149 150 inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; 151 mark_inode_dirty(inode); 152 if (IS_SYNC(inode)) 153 err = sync_page_range_nolock(inode, mapping, start, count); 154 out: 155 return err; 156 } 157 158 int fat_notify_change(struct dentry *dentry, struct iattr *attr) 159 { 160 struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); 161 struct inode *inode = dentry->d_inode; 162 int mask, error = 0; 163 164 lock_kernel(); 165 166 /* 167 * Expand the file. Since inode_setattr() updates ->i_size 168 * before calling the ->truncate(), but FAT needs to fill the 169 * hole before it. 170 */ 171 if (attr->ia_valid & ATTR_SIZE) { 172 if (attr->ia_size > inode->i_size) { 173 error = fat_cont_expand(inode, attr->ia_size); 174 if (error || attr->ia_valid == ATTR_SIZE) 175 goto out; 176 attr->ia_valid &= ~ATTR_SIZE; 177 } 178 } 179 180 error = inode_change_ok(inode, attr); 181 if (error) { 182 if (sbi->options.quiet) 183 error = 0; 184 goto out; 185 } 186 if (((attr->ia_valid & ATTR_UID) && 187 (attr->ia_uid != sbi->options.fs_uid)) || 188 ((attr->ia_valid & ATTR_GID) && 189 (attr->ia_gid != sbi->options.fs_gid)) || 190 ((attr->ia_valid & ATTR_MODE) && 191 (attr->ia_mode & ~MSDOS_VALID_MODE))) 192 error = -EPERM; 193 194 if (error) { 195 if (sbi->options.quiet) 196 error = 0; 197 goto out; 198 } 199 error = inode_setattr(inode, attr); 200 if (error) 201 goto out; 202 203 if (S_ISDIR(inode->i_mode)) 204 mask = sbi->options.fs_dmask; 205 else 206 mask = sbi->options.fs_fmask; 207 inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask); 208 out: 209 unlock_kernel(); 210 return error; 211 } 212 213 EXPORT_SYMBOL_GPL(fat_notify_change); 214 215 /* Free all clusters after the skip'th cluster. */ 216 static int fat_free(struct inode *inode, int skip) 217 { 218 struct super_block *sb = inode->i_sb; 219 int err, wait, free_start, i_start, i_logstart; 220 221 if (MSDOS_I(inode)->i_start == 0) 222 return 0; 223 224 fat_cache_inval_inode(inode); 225 226 wait = IS_DIRSYNC(inode); 227 i_start = free_start = MSDOS_I(inode)->i_start; 228 i_logstart = MSDOS_I(inode)->i_logstart; 229 230 /* First, we write the new file size. */ 231 if (!skip) { 232 MSDOS_I(inode)->i_start = 0; 233 MSDOS_I(inode)->i_logstart = 0; 234 } 235 MSDOS_I(inode)->i_attrs |= ATTR_ARCH; 236 inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; 237 if (wait) { 238 err = fat_sync_inode(inode); 239 if (err) { 240 MSDOS_I(inode)->i_start = i_start; 241 MSDOS_I(inode)->i_logstart = i_logstart; 242 return err; 243 } 244 } else 245 mark_inode_dirty(inode); 246 247 /* Write a new EOF, and get the remaining cluster chain for freeing. */ 248 if (skip) { 249 struct fat_entry fatent; 250 int ret, fclus, dclus; 251 252 ret = fat_get_cluster(inode, skip - 1, &fclus, &dclus); 253 if (ret < 0) 254 return ret; 255 else if (ret == FAT_ENT_EOF) 256 return 0; 257 258 fatent_init(&fatent); 259 ret = fat_ent_read(inode, &fatent, dclus); 260 if (ret == FAT_ENT_EOF) { 261 fatent_brelse(&fatent); 262 return 0; 263 } else if (ret == FAT_ENT_FREE) { 264 fat_fs_panic(sb, 265 "%s: invalid cluster chain (i_pos %lld)", 266 __FUNCTION__, MSDOS_I(inode)->i_pos); 267 ret = -EIO; 268 } else if (ret > 0) { 269 err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, wait); 270 if (err) 271 ret = err; 272 } 273 fatent_brelse(&fatent); 274 if (ret < 0) 275 return ret; 276 277 free_start = ret; 278 } 279 inode->i_blocks = skip << (MSDOS_SB(sb)->cluster_bits - 9); 280 281 /* Freeing the remained cluster chain */ 282 return fat_free_clusters(inode, free_start); 283 } 284 285 void fat_truncate(struct inode *inode) 286 { 287 struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); 288 const unsigned int cluster_size = sbi->cluster_size; 289 int nr_clusters; 290 291 /* 292 * This protects against truncating a file bigger than it was then 293 * trying to write into the hole. 294 */ 295 if (MSDOS_I(inode)->mmu_private > inode->i_size) 296 MSDOS_I(inode)->mmu_private = inode->i_size; 297 298 nr_clusters = (inode->i_size + (cluster_size - 1)) >> sbi->cluster_bits; 299 300 lock_kernel(); 301 fat_free(inode, nr_clusters); 302 unlock_kernel(); 303 fat_flush_inodes(inode->i_sb, inode, NULL); 304 } 305 306 int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) 307 { 308 struct inode *inode = dentry->d_inode; 309 generic_fillattr(inode, stat); 310 stat->blksize = MSDOS_SB(inode->i_sb)->cluster_size; 311 return 0; 312 } 313 EXPORT_SYMBOL_GPL(fat_getattr); 314 315 const struct inode_operations fat_file_inode_operations = { 316 .truncate = fat_truncate, 317 .setattr = fat_notify_change, 318 .getattr = fat_getattr, 319 }; 320