X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Ffat%2Ffile.c;h=815f219064c502f69ca71d80839d6759176649c0;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=3b48219aefbef5e53cf157b0494e2e256964d4c0;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/fs/fat/file.c b/fs/fat/file.c index 3b48219ae..815f21906 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -6,13 +6,26 @@ * 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); + 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, @@ -25,63 +38,117 @@ struct file_operations fat_file_operations = { .sendfile = generic_file_sendfile, }; -struct inode_operations fat_file_inode_operations = { - .truncate = fat_truncate, - .setattr = fat_notify_change, -}; - -int fat_get_block(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create) +int fat_notify_change(struct dentry *dentry, struct iattr *attr) { - struct super_block *sb = inode->i_sb; - sector_t phys; - int err; - - err = fat_bmap(inode, iblock, &phys); - if (err) - return err; - if (phys) { - map_bh(bh_result, sb, phys); - return 0; + 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; + } } - if (!create) - return 0; - if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) { - fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)", - MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private); - return -EIO; + + error = inode_change_ok(inode, attr); + if (error) { + if (sbi->options.quiet) + error = 0; + goto out; } - if (!((unsigned long)iblock & (MSDOS_SB(sb)->sec_per_clus - 1))) { - int error; + 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; - error = fat_add_cluster(inode); - if (error < 0) - return error; + if (error) { + if (sbi->options.quiet) + error = 0; + goto out; } - MSDOS_I(inode)->mmu_private += sb->s_blocksize; - err = fat_bmap(inode, iblock, &phys); - if (err) - return err; - if (!phys) - BUG(); - set_buffer_new(bh_result); - map_bh(bh_result, sb, phys); - return 0; + 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; } -static ssize_t fat_file_write(struct file *filp, const char __user *buf, - size_t count, loff_t *ppos) +EXPORT_SYMBOL(fat_notify_change); + +/* Free all clusters after the skip'th cluster. */ +static int fat_free(struct inode *inode, int skip) { - struct inode *inode = filp->f_dentry->d_inode; - int retval; + struct super_block *sb = inode->i_sb; + int nr, ret, fclus, dclus; - retval = generic_file_write(filp, buf, count, ppos); - if (retval > 0) { - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - MSDOS_I(inode)->i_attrs |= ATTR_ARCH; + 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); } - return retval; + + 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) @@ -90,7 +157,7 @@ void fat_truncate(struct inode *inode) 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. */ @@ -103,6 +170,11 @@ void fat_truncate(struct inode *inode) fat_free(inode, nr_clusters); MSDOS_I(inode)->i_attrs |= ATTR_ARCH; unlock_kernel(); - inode->i_ctime = inode->i_mtime = CURRENT_TIME; + 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, +};