vserver 1.9.5.x5
[linux-2.6.git] / fs / fat / file.c
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 static ssize_t fat_file_write(struct file *filp, const char __user *buf,
16                               size_t count, loff_t *ppos)
17 {
18         struct inode *inode = filp->f_dentry->d_inode;
19         int retval;
20
21         retval = generic_file_write(filp, buf, count, ppos);
22         if (retval > 0) {
23                 inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
24                 MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
25                 mark_inode_dirty(inode);
26         }
27         return retval;
28 }
29
30 struct file_operations fat_file_operations = {
31         .llseek         = generic_file_llseek,
32         .read           = generic_file_read,
33         .write          = fat_file_write,
34         .mmap           = generic_file_mmap,
35         .fsync          = file_fsync,
36         .readv          = generic_file_readv,
37         .writev         = generic_file_writev,
38         .sendfile       = generic_file_sendfile,
39 };
40
41 int fat_notify_change(struct dentry *dentry, struct iattr *attr)
42 {
43         struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
44         struct inode *inode = dentry->d_inode;
45         int mask, error = 0;
46
47         lock_kernel();
48
49         /* FAT cannot truncate to a longer file */
50         if (attr->ia_valid & ATTR_SIZE) {
51                 if (attr->ia_size > inode->i_size) {
52                         error = -EPERM;
53                         goto out;
54                 }
55         }
56
57         error = inode_change_ok(inode, attr);
58         if (error) {
59                 if (sbi->options.quiet)
60                         error = 0;
61                 goto out;
62         }
63         if (((attr->ia_valid & ATTR_UID) &&
64              (attr->ia_uid != sbi->options.fs_uid)) ||
65             ((attr->ia_valid & ATTR_GID) &&
66              (attr->ia_gid != sbi->options.fs_gid)) ||
67             ((attr->ia_valid & ATTR_MODE) &&
68              (attr->ia_mode & ~MSDOS_VALID_MODE)))
69                 error = -EPERM;
70
71         if (error) {
72                 if (sbi->options.quiet)
73                         error = 0;
74                 goto out;
75         }
76         error = inode_setattr(inode, attr);
77         if (error)
78                 goto out;
79
80         if (S_ISDIR(inode->i_mode))
81                 mask = sbi->options.fs_dmask;
82         else
83                 mask = sbi->options.fs_fmask;
84         inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask);
85 out:
86         unlock_kernel();
87         return error;
88 }
89
90 EXPORT_SYMBOL(fat_notify_change);
91
92 /* Free all clusters after the skip'th cluster. */
93 static int fat_free(struct inode *inode, int skip)
94 {
95         struct super_block *sb = inode->i_sb;
96         int nr, ret, fclus, dclus;
97
98         if (MSDOS_I(inode)->i_start == 0)
99                 return 0;
100
101         if (skip) {
102                 ret = fat_get_cluster(inode, skip - 1, &fclus, &dclus);
103                 if (ret < 0)
104                         return ret;
105                 else if (ret == FAT_ENT_EOF)
106                         return 0;
107
108                 nr = fat_access(sb, dclus, -1);
109                 if (nr == FAT_ENT_EOF)
110                         return 0;
111                 else if (nr > 0) {
112                         /*
113                          * write a new EOF, and get the remaining cluster
114                          * chain for freeing.
115                          */
116                         nr = fat_access(sb, dclus, FAT_ENT_EOF);
117                 }
118                 if (nr < 0)
119                         return nr;
120
121                 fat_cache_inval_inode(inode);
122         } else {
123                 fat_cache_inval_inode(inode);
124
125                 nr = MSDOS_I(inode)->i_start;
126                 MSDOS_I(inode)->i_start = 0;
127                 MSDOS_I(inode)->i_logstart = 0;
128                 mark_inode_dirty(inode);
129         }
130
131         lock_fat(sb);
132         do {
133                 nr = fat_access(sb, nr, FAT_ENT_FREE);
134                 if (nr < 0)
135                         goto error;
136                 else if (nr == FAT_ENT_FREE) {
137                         fat_fs_panic(sb, "%s: deleting beyond EOF (i_pos %lld)",
138                                      __FUNCTION__, MSDOS_I(inode)->i_pos);
139                         nr = -EIO;
140                         goto error;
141                 }
142                 if (MSDOS_SB(sb)->free_clusters != -1)
143                         MSDOS_SB(sb)->free_clusters++;
144                 inode->i_blocks -= MSDOS_SB(sb)->cluster_size >> 9;
145         } while (nr != FAT_ENT_EOF);
146         fat_clusters_flush(sb);
147         nr = 0;
148 error:
149         unlock_fat(sb);
150
151         return nr;
152 }
153
154 void fat_truncate(struct inode *inode)
155 {
156         struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
157         const unsigned int cluster_size = sbi->cluster_size;
158         int nr_clusters;
159
160         /*
161          * This protects against truncating a file bigger than it was then
162          * trying to write into the hole.
163          */
164         if (MSDOS_I(inode)->mmu_private > inode->i_size)
165                 MSDOS_I(inode)->mmu_private = inode->i_size;
166
167         nr_clusters = (inode->i_size + (cluster_size - 1)) >> sbi->cluster_bits;
168
169         lock_kernel();
170         fat_free(inode, nr_clusters);
171         MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
172         unlock_kernel();
173         inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
174         mark_inode_dirty(inode);
175 }
176
177 struct inode_operations fat_file_inode_operations = {
178         .truncate       = fat_truncate,
179         .setattr        = fat_notify_change,
180 };