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