vserver 1.9.5.x5
[linux-2.6.git] / fs / fat / file.c
index 3b48219..815f219 100644 (file)
@@ -6,13 +6,26 @@
  *  regular file handling primitives for fat-based filesystems
  */
 
+#include <linux/module.h>
 #include <linux/time.h>
 #include <linux/msdos_fs.h>
 #include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
 
 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,
+};