Linux 2.3.0
[davej-history.git] / fs / hfs / file_cap.c
blobb3a58912c708d42945145797a710145a4a443d9a
1 /*
2 * linux/fs/hfs/file_cap.c
4 * Copyright (C) 1995-1997 Paul H. Hargrove
5 * This file may be distributed under the terms of the GNU Public License.
7 * This file contains the file_ops and inode_ops for the metadata
8 * files under the CAP representation.
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.
16 * "XXX" in a comment is a note to myself to consider changing something.
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.
23 #include "hfs.h"
24 #include <linux/hfs_fs_sb.h>
25 #include <linux/hfs_fs_i.h>
26 #include <linux/hfs_fs.h>
28 /*================ Forward declarations ================*/
30 static hfs_rwret_t cap_info_read(struct file *, char *,
31 hfs_rwarg_t, loff_t *);
32 static hfs_rwret_t cap_info_write(struct file *, const char *,
33 hfs_rwarg_t, loff_t *);
34 static void cap_info_truncate(struct inode *);
36 /*================ Function-like macros ================*/
39 * OVERLAPS()
41 * Determines if a given range overlaps the specified structure member
43 #define OVERLAPS(START, END, TYPE, MEMB) \
44 ((END > offsetof(TYPE, MEMB)) && \
45 (START < offsetof(TYPE, MEMB) + sizeof(((TYPE *)0)->MEMB)))
47 /*================ Global variables ================*/
49 static struct file_operations hfs_cap_info_operations = {
50 NULL, /* lseek - default */
51 cap_info_read, /* read */
52 cap_info_write, /* write */
53 NULL, /* readdir - bad */
54 NULL, /* select - default */
55 NULL, /* ioctl - default */
56 NULL, /* mmap - not yet */
57 NULL, /* no special open code */
58 NULL, /* flush */
59 NULL, /* no special release code */
60 file_fsync, /* fsync - default */
61 NULL, /* fasync - default */
62 NULL, /* check_media_change - none */
63 NULL, /* revalidate - none */
64 NULL /* lock - none */
67 struct inode_operations hfs_cap_info_inode_operations = {
68 &hfs_cap_info_operations, /* default file operations */
69 NULL, /* create */
70 NULL, /* lookup */
71 NULL, /* link */
72 NULL, /* unlink */
73 NULL, /* symlink */
74 NULL, /* mkdir */
75 NULL, /* rmdir */
76 NULL, /* mknod */
77 NULL, /* rename */
78 NULL, /* readlink */
79 NULL, /* follow_link */
80 NULL, /* readpage */
81 NULL, /* writepage */
82 NULL, /* bmap - none */
83 cap_info_truncate, /* truncate */
84 NULL, /* permission */
85 NULL, /* smap */
86 NULL, /* updatepage */
87 NULL /* revalidata */
90 /*================ File-local functions ================*/
93 * cap_build_meta()
95 * Build the metadata structure.
97 static void cap_build_meta(struct hfs_cap_info *meta,
98 struct hfs_cat_entry *entry)
100 memset(meta, 0, sizeof(*meta));
101 memcpy(meta->fi_fndr, &entry->info, 32);
102 if ((entry->type == HFS_CDR_FIL) &&
103 (entry->u.file.flags & HFS_FIL_LOCK)) {
104 /* Couple the locked bit of the file to the
105 AFP {write,rename,delete} inhibit bits. */
106 hfs_put_hs(HFS_AFP_RDONLY, meta->fi_attr);
108 meta->fi_magic1 = HFS_CAP_MAGIC1;
109 meta->fi_version = HFS_CAP_VERSION;
110 meta->fi_magic = HFS_CAP_MAGIC;
111 meta->fi_bitmap = HFS_CAP_LONGNAME;
112 memcpy(meta->fi_macfilename, entry->key.CName.Name,
113 entry->key.CName.Len);
114 meta->fi_datemagic = HFS_CAP_DMAGIC;
115 meta->fi_datevalid = HFS_CAP_MDATE | HFS_CAP_CDATE;
116 hfs_put_nl(hfs_m_to_htime(entry->create_date), meta->fi_ctime);
117 hfs_put_nl(hfs_m_to_htime(entry->modify_date), meta->fi_mtime);
118 hfs_put_nl(CURRENT_TIME, meta->fi_utime);
122 * cap_info_read()
124 * This is the read() entry in the file_operations structure for CAP
125 * metadata files. The purpose is to transfer up to 'count' bytes
126 * from the file corresponding to 'inode' beginning at offset
127 * 'file->f_pos' to user-space at the address 'buf'. The return value
128 * is the number of bytes actually transferred.
130 static hfs_rwret_t cap_info_read(struct file *filp, char *buf,
131 hfs_rwarg_t count, loff_t *ppos)
133 struct inode *inode = filp->f_dentry->d_inode;
134 struct hfs_cat_entry *entry = HFS_I(inode)->entry;
135 hfs_s32 left, size, read = 0;
136 hfs_u32 pos;
138 if (!S_ISREG(inode->i_mode)) {
139 hfs_warn("hfs_cap_info_read: mode = %07o\n", inode->i_mode);
140 return -EINVAL;
143 pos = *ppos;
144 if (pos > HFS_FORK_MAX) {
145 return 0;
147 size = inode->i_size;
148 if (pos > size) {
149 left = 0;
150 } else {
151 left = size - pos;
153 if (left > count) {
154 left = count;
156 if (left <= 0) {
157 return 0;
160 if (pos < sizeof(struct hfs_cap_info)) {
161 int memcount = sizeof(struct hfs_cap_info) - pos;
162 struct hfs_cap_info meta;
164 if (memcount > left) {
165 memcount = left;
167 cap_build_meta(&meta, entry);
168 memcount -= copy_to_user(buf, ((char *)&meta) + pos, memcount);
169 left -= memcount;
170 read += memcount;
171 pos += memcount;
172 buf += memcount;
175 if (left > 0) {
176 clear_user(buf, left);
177 pos += left;
180 if (read) {
181 inode->i_atime = CURRENT_TIME;
182 *ppos = pos;
183 mark_inode_dirty(inode);
186 return read;
190 * cap_info_write()
192 * This is the write() entry in the file_operations structure for CAP
193 * metadata files. The purpose is to transfer up to 'count' bytes
194 * to the file corresponding to 'inode' beginning at offset
195 * '*ppos' from user-space at the address 'buf'.
196 * The return value is the number of bytes actually transferred.
198 static hfs_rwret_t cap_info_write(struct file *filp, const char *buf,
199 hfs_rwarg_t count, loff_t *ppos)
201 struct inode *inode = filp->f_dentry->d_inode;
202 hfs_u32 pos;
204 if (!S_ISREG(inode->i_mode)) {
205 hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode);
206 return -EINVAL;
208 if (count <= 0) {
209 return 0;
212 pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos;
214 if (pos > HFS_FORK_MAX) {
215 return 0;
218 *ppos += count;
219 if (*ppos > HFS_FORK_MAX) {
220 *ppos = HFS_FORK_MAX;
221 count = HFS_FORK_MAX - pos;
224 if (*ppos > inode->i_size)
225 inode->i_size = *ppos;
227 /* Only deal with the part we store in memory */
228 if (pos < sizeof(struct hfs_cap_info)) {
229 int end, mem_count;
230 struct hfs_cat_entry *entry = HFS_I(inode)->entry;
231 struct hfs_cap_info meta;
233 mem_count = sizeof(struct hfs_cap_info) - pos;
234 if (mem_count > count) {
235 mem_count = count;
237 end = pos + mem_count;
239 cap_build_meta(&meta, entry);
240 mem_count -= copy_from_user(((char *)&meta) + pos, buf, mem_count);
242 /* Update finder attributes if changed */
243 if (OVERLAPS(pos, end, struct hfs_cap_info, fi_fndr)) {
244 memcpy(&entry->info, meta.fi_fndr, 32);
245 hfs_cat_mark_dirty(entry);
248 /* Update file flags if changed */
249 if (OVERLAPS(pos, end, struct hfs_cap_info, fi_attr) &&
250 (entry->type == HFS_CDR_FIL)) {
251 int locked = hfs_get_ns(&meta.fi_attr) &
252 htons(HFS_AFP_WRI);
253 hfs_u8 new_flags;
255 if (locked) {
256 new_flags = entry->u.file.flags | HFS_FIL_LOCK;
257 } else {
258 new_flags = entry->u.file.flags & ~HFS_FIL_LOCK;
261 if (new_flags != entry->u.file.flags) {
262 entry->u.file.flags = new_flags;
263 hfs_cat_mark_dirty(entry);
264 hfs_file_fix_mode(entry);
268 /* Update CrDat if changed */
269 if (OVERLAPS(pos, end, struct hfs_cap_info, fi_ctime)) {
270 entry->create_date =
271 hfs_h_to_mtime(hfs_get_nl(meta.fi_ctime));
272 hfs_cat_mark_dirty(entry);
275 /* Update MdDat if changed */
276 if (OVERLAPS(pos, end, struct hfs_cap_info, fi_mtime)) {
277 entry->modify_date =
278 hfs_h_to_mtime(hfs_get_nl(meta.fi_mtime));
279 hfs_cat_mark_dirty(entry);
283 inode->i_mtime = inode->i_ctime = CURRENT_TIME;
284 mark_inode_dirty(inode);
285 return count;
289 * cap_info_truncate()
291 * This is the truncate field in the inode_operations structure for
292 * CAP metadata files.
294 static void cap_info_truncate(struct inode *inode)
296 if (inode->i_size > HFS_FORK_MAX) {
297 inode->i_size = HFS_FORK_MAX;
298 mark_inode_dirty(inode);