v2.4.8 -> v2.4.8.1
[opensuse:kernel.git] / fs / hfs / file_cap.c
1 /*
2  * linux/fs/hfs/file_cap.c
3  *
4  * Copyright (C) 1995-1997  Paul H. Hargrove
5  * This file may be distributed under the terms of the GNU General Public License.
6  *
7  * This file contains the file_ops and inode_ops for the metadata
8  * files under the CAP representation.
9  *
10  * The source code distribution of the Columbia AppleTalk Package for
11  * UNIX, version 6.0, (CAP) was used as a specification of the
12  * location and format of files used by CAP's Aufs.  No code from CAP
13  * appears in hfs_fs.  hfs_fs is not a work ``derived'' from CAP in
14  * the sense of intellectual property law.
15  *
16  * "XXX" in a comment is a note to myself to consider changing something.
17  *
18  * In function preconditions the term "valid" applied to a pointer to
19  * a structure means that the pointer is non-NULL and the structure it
20  * points to has all fields initialized to consistent values.
21  */
22
23 #include "hfs.h"
24 #include <linux/hfs_fs_sb.h>
25 #include <linux/hfs_fs_i.h>
26 #include <linux/hfs_fs.h>
27
28 /*================ Forward declarations ================*/
29 static loff_t      cap_info_llseek(struct file *, loff_t,
30                                    int);
31 static hfs_rwret_t cap_info_read(struct file *, char *,
32                                  hfs_rwarg_t, loff_t *);
33 static hfs_rwret_t cap_info_write(struct file *, const char *,
34                                   hfs_rwarg_t, loff_t *);
35 /*================ Function-like macros ================*/
36
37 /*
38  * OVERLAPS()
39  *
40  * Determines if a given range overlaps the specified structure member
41  */
42 #define OVERLAPS(START, END, TYPE, MEMB) \
43         ((END > offsetof(TYPE, MEMB)) && \
44          (START < offsetof(TYPE, MEMB) + sizeof(((TYPE *)0)->MEMB)))
45
46 /*================ Global variables ================*/
47
48 struct file_operations hfs_cap_info_operations = {
49         llseek:         cap_info_llseek,
50         read:           cap_info_read,
51         write:          cap_info_write,
52         fsync:          file_fsync,
53 };
54
55 struct inode_operations hfs_cap_info_inode_operations = {
56         setattr:        hfs_notify_change_cap,
57 };
58
59 /*================ File-local functions ================*/
60
61 /*
62  * cap_build_meta()
63  *
64  * Build the metadata structure.
65  */
66 static void cap_build_meta(struct hfs_cap_info *meta,
67                            struct hfs_cat_entry *entry)
68 {
69         memset(meta, 0, sizeof(*meta));
70         memcpy(meta->fi_fndr, &entry->info, 32);
71         if ((entry->type == HFS_CDR_FIL) &&
72             (entry->u.file.flags & HFS_FIL_LOCK)) {
73                 /* Couple the locked bit of the file to the
74                    AFP {write,rename,delete} inhibit bits. */
75                 hfs_put_hs(HFS_AFP_RDONLY, meta->fi_attr);
76         }
77         meta->fi_magic1 = HFS_CAP_MAGIC1;
78         meta->fi_version = HFS_CAP_VERSION;
79         meta->fi_magic = HFS_CAP_MAGIC;
80         meta->fi_bitmap = HFS_CAP_LONGNAME;
81         memcpy(meta->fi_macfilename, entry->key.CName.Name,
82                entry->key.CName.Len);
83         meta->fi_datemagic = HFS_CAP_DMAGIC;
84         meta->fi_datevalid = HFS_CAP_MDATE | HFS_CAP_CDATE;
85         hfs_put_nl(hfs_m_to_htime(entry->create_date), meta->fi_ctime);
86         hfs_put_nl(hfs_m_to_htime(entry->modify_date), meta->fi_mtime);
87         hfs_put_nl(CURRENT_TIME,                       meta->fi_utime);
88 }
89
90 static loff_t cap_info_llseek(struct file *file, loff_t offset, int origin)
91 {
92         long long retval;
93
94         switch (origin) {
95                 case 2:
96                         offset += file->f_dentry->d_inode->i_size;
97                         break;
98                 case 1:
99                         offset += file->f_pos;
100         }
101         retval = -EINVAL;
102         if (offset>=0 && offset<=HFS_FORK_MAX) {
103                 if (offset != file->f_pos) {
104                         file->f_pos = offset;
105                         file->f_reada = 0;
106                         file->f_version = ++event;
107                 }
108                 retval = offset;
109         }
110         return retval;
111 }
112
113 /*
114  * cap_info_read()
115  *
116  * This is the read() entry in the file_operations structure for CAP
117  * metadata files.  The purpose is to transfer up to 'count' bytes
118  * from the file corresponding to 'inode' beginning at offset
119  * 'file->f_pos' to user-space at the address 'buf'.  The return value
120  * is the number of bytes actually transferred.
121  */
122 static hfs_rwret_t cap_info_read(struct file *filp, char *buf,
123                                  hfs_rwarg_t count, loff_t *ppos)
124 {
125         struct inode *inode = filp->f_dentry->d_inode;
126         struct hfs_cat_entry *entry = HFS_I(inode)->entry;
127         hfs_s32 left, size, read = 0;
128         hfs_u32 pos;
129
130         if (!S_ISREG(inode->i_mode)) {
131                 hfs_warn("hfs_cap_info_read: mode = %07o\n", inode->i_mode);
132                 return -EINVAL;
133         }
134
135         pos = *ppos;
136         if (pos > HFS_FORK_MAX) {
137                 return 0;
138         }
139         size = inode->i_size;
140         if (pos > size) {
141                 left = 0;
142         } else {
143                 left = size - pos;
144         }
145         if (left > count) {
146                 left = count;
147         }
148         if (left <= 0) {
149                 return 0;
150         }
151
152         if (pos < sizeof(struct hfs_cap_info)) {
153                 int memcount = sizeof(struct hfs_cap_info) - pos;
154                 struct hfs_cap_info meta;
155
156                 if (memcount > left) {
157                         memcount = left;
158                 }
159                 cap_build_meta(&meta, entry);
160                 memcount -= copy_to_user(buf, ((char *)&meta) + pos, memcount);
161                 left -= memcount;
162                 read += memcount;
163                 pos += memcount;
164                 buf += memcount;
165         }
166
167         if (left > 0) {
168                 clear_user(buf, left);
169                 pos += left;
170         }
171
172         if (read) {
173                 inode->i_atime = CURRENT_TIME;
174                 *ppos = pos;
175                 mark_inode_dirty(inode);
176         }
177
178         return read;
179 }
180
181 /*
182  * cap_info_write()
183  *
184  * This is the write() entry in the file_operations structure for CAP
185  * metadata files.  The purpose is to transfer up to 'count' bytes
186  * to the file corresponding to 'inode' beginning at offset
187  * '*ppos' from user-space at the address 'buf'.
188  * The return value is the number of bytes actually transferred.
189  */
190 static hfs_rwret_t cap_info_write(struct file *filp, const char *buf, 
191                                   hfs_rwarg_t count, loff_t *ppos)
192 {
193         struct inode *inode = filp->f_dentry->d_inode;
194         hfs_u32 pos;
195
196         if (!S_ISREG(inode->i_mode)) {
197                 hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode);
198                 return -EINVAL;
199         }
200         if (count <= 0) {
201                 return 0;
202         }
203         
204         pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos;
205
206         if (pos > HFS_FORK_MAX) {
207                 return 0;
208         }
209
210         *ppos += count;
211         if (*ppos > HFS_FORK_MAX) {
212                 *ppos = HFS_FORK_MAX;
213                 count = HFS_FORK_MAX - pos;
214         }
215
216         if (*ppos > inode->i_size)
217                 inode->i_size = *ppos;
218
219         /* Only deal with the part we store in memory */
220         if (pos < sizeof(struct hfs_cap_info)) {
221                 int end, mem_count;
222                 struct hfs_cat_entry *entry = HFS_I(inode)->entry;
223                 struct hfs_cap_info meta;
224
225                 mem_count = sizeof(struct hfs_cap_info) - pos;
226                 if (mem_count > count) {
227                         mem_count = count;
228                 }
229                 end = pos + mem_count;
230
231                 cap_build_meta(&meta, entry);
232                 mem_count -= copy_from_user(((char *)&meta) + pos, buf, mem_count);
233
234                 /* Update finder attributes if changed */
235                 if (OVERLAPS(pos, end, struct hfs_cap_info, fi_fndr)) {
236                         memcpy(&entry->info, meta.fi_fndr, 32);
237                         hfs_cat_mark_dirty(entry);
238                 }
239
240                 /* Update file flags if changed */
241                 if (OVERLAPS(pos, end, struct hfs_cap_info, fi_attr) &&
242                     (entry->type == HFS_CDR_FIL)) {
243                         int locked = hfs_get_ns(&meta.fi_attr) &
244                                                         htons(HFS_AFP_WRI);
245                         hfs_u8 new_flags;
246
247                         if (locked) {
248                                 new_flags = entry->u.file.flags | HFS_FIL_LOCK;
249                         } else {
250                                 new_flags = entry->u.file.flags & ~HFS_FIL_LOCK;
251                         }
252
253                         if (new_flags != entry->u.file.flags) {
254                                 entry->u.file.flags = new_flags;
255                                 hfs_cat_mark_dirty(entry);
256                                 hfs_file_fix_mode(entry);
257                         }
258                 }
259
260                 /* Update CrDat if changed */
261                 if (OVERLAPS(pos, end, struct hfs_cap_info, fi_ctime)) {
262                         entry->create_date =
263                                 hfs_h_to_mtime(hfs_get_nl(meta.fi_ctime));
264                         hfs_cat_mark_dirty(entry);
265                 }
266
267                 /* Update MdDat if changed */
268                 if (OVERLAPS(pos, end, struct hfs_cap_info, fi_mtime)) {
269                         entry->modify_date =
270                                 hfs_h_to_mtime(hfs_get_nl(meta.fi_mtime));
271                         hfs_cat_mark_dirty(entry);
272                 }
273         }
274
275         inode->i_mtime = inode->i_ctime = CURRENT_TIME;
276         mark_inode_dirty(inode);
277         return count;
278 }