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  */
cap_build_meta(struct hfs_cap_info * meta,struct hfs_cat_entry * entry)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 
cap_info_llseek(struct file * file,loff_t offset,int origin)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  */
cap_info_read(struct file * filp,char * buf,hfs_rwarg_t count,loff_t * ppos)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  */
cap_info_write(struct file * filp,const char * buf,hfs_rwarg_t count,loff_t * ppos)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, last;
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 	last = pos + count;
211 	if (last > HFS_FORK_MAX) {
212 		last = HFS_FORK_MAX;
213 		count = HFS_FORK_MAX - pos;
214 	}
215 
216 	if (last > inode->i_size)
217 	        inode->i_size = last;
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 	*ppos = last;
276 	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
277 	mark_inode_dirty(inode);
278 	return count;
279 }
280