/* * linux/fs/fat/file.c * * Written 1992,1993 by Werner Almesberger * * regular file handling primitives for fat-based filesystems */ #include #include #include #include #include static ssize_t fat_file_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; int retval; retval = generic_file_write(filp, buf, count, ppos); if (retval > 0) { inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; MSDOS_I(inode)->i_attrs |= ATTR_ARCH; mark_inode_dirty(inode); } return retval; } struct file_operations fat_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .write = fat_file_write, .mmap = generic_file_mmap, .fsync = file_fsync, .readv = generic_file_readv, .writev = generic_file_writev, .sendfile = generic_file_sendfile, }; int fat_notify_change(struct dentry *dentry, struct iattr *attr) { struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); struct inode *inode = dentry->d_inode; int mask, error = 0; lock_kernel(); /* FAT cannot truncate to a longer file */ if (attr->ia_valid & ATTR_SIZE) { if (attr->ia_size > inode->i_size) { error = -EPERM; goto out; } } error = inode_change_ok(inode, attr); if (error) { if (sbi->options.quiet) error = 0; goto out; } if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != sbi->options.fs_uid)) || ((attr->ia_valid & ATTR_GID) && (attr->ia_gid != sbi->options.fs_gid)) || ((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~MSDOS_VALID_MODE))) error = -EPERM; if (error) { if (sbi->options.quiet) error = 0; goto out; } error = inode_setattr(inode, attr); if (error) goto out; if (S_ISDIR(inode->i_mode)) mask = sbi->options.fs_dmask; else mask = sbi->options.fs_fmask; inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask); out: unlock_kernel(); return error; } EXPORT_SYMBOL(fat_notify_change); /* Free all clusters after the skip'th cluster. */ static int fat_free(struct inode *inode, int skip) { struct super_block *sb = inode->i_sb; int nr, ret, fclus, dclus; if (MSDOS_I(inode)->i_start == 0) return 0; if (skip) { ret = fat_get_cluster(inode, skip - 1, &fclus, &dclus); if (ret < 0) return ret; else if (ret == FAT_ENT_EOF) return 0; nr = fat_access(sb, dclus, -1); if (nr == FAT_ENT_EOF) return 0; else if (nr > 0) { /* * write a new EOF, and get the remaining cluster * chain for freeing. */ nr = fat_access(sb, dclus, FAT_ENT_EOF); } if (nr < 0) return nr; fat_cache_inval_inode(inode); } else { fat_cache_inval_inode(inode); nr = MSDOS_I(inode)->i_start; MSDOS_I(inode)->i_start = 0; MSDOS_I(inode)->i_logstart = 0; mark_inode_dirty(inode); } lock_fat(sb); do { nr = fat_access(sb, nr, FAT_ENT_FREE); if (nr < 0) goto error; else if (nr == FAT_ENT_FREE) { fat_fs_panic(sb, "%s: deleting beyond EOF (i_pos %lld)", __FUNCTION__, MSDOS_I(inode)->i_pos); nr = -EIO; goto error; } if (MSDOS_SB(sb)->free_clusters != -1) MSDOS_SB(sb)->free_clusters++; inode->i_blocks -= MSDOS_SB(sb)->cluster_size >> 9; } while (nr != FAT_ENT_EOF); fat_clusters_flush(sb); nr = 0; error: unlock_fat(sb); return nr; } void fat_truncate(struct inode *inode) { struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); const unsigned int cluster_size = sbi->cluster_size; int nr_clusters; /* * This protects against truncating a file bigger than it was then * trying to write into the hole. */ if (MSDOS_I(inode)->mmu_private > inode->i_size) MSDOS_I(inode)->mmu_private = inode->i_size; nr_clusters = (inode->i_size + (cluster_size - 1)) >> sbi->cluster_bits; lock_kernel(); fat_free(inode, nr_clusters); MSDOS_I(inode)->i_attrs |= ATTR_ARCH; unlock_kernel(); inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; mark_inode_dirty(inode); } struct inode_operations fat_file_inode_operations = { .truncate = fat_truncate, .setattr = fat_notify_change, };