Commit | Line | Data |
---|---|---|
ee184c81 DC |
1 | /* |
2 | * fs/sdcardfs/file.c | |
3 | * | |
4 | * Copyright (c) 2013 Samsung Electronics Co. Ltd | |
5 | * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, | |
6 | * Sunghwan Yun, Sungjong Seo | |
7 | * | |
8 | * This program has been developed as a stackable file system based on | |
9 | * the WrapFS which written by | |
10 | * | |
11 | * Copyright (c) 1998-2011 Erez Zadok | |
12 | * Copyright (c) 2009 Shrikar Archak | |
13 | * Copyright (c) 2003-2011 Stony Brook University | |
14 | * Copyright (c) 2003-2011 The Research Foundation of SUNY | |
15 | * | |
16 | * This file is dual licensed. It may be redistributed and/or modified | |
17 | * under the terms of the Apache 2.0 License OR version 2 of the GNU | |
18 | * General Public License. | |
19 | */ | |
20 | ||
21 | #include "sdcardfs.h" | |
22 | #ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE | |
23 | #include <linux/backing-dev.h> | |
24 | #endif | |
25 | ||
26 | static ssize_t sdcardfs_read(struct file *file, char __user *buf, | |
27 | size_t count, loff_t *ppos) | |
28 | { | |
29 | int err; | |
30 | struct file *lower_file; | |
31 | struct dentry *dentry = file->f_path.dentry; | |
32 | #ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE | |
33 | struct backing_dev_info *bdi; | |
34 | #endif | |
35 | ||
36 | lower_file = sdcardfs_lower_file(file); | |
37 | ||
38 | #ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE | |
39 | if (file->f_mode & FMODE_NOACTIVE) { | |
40 | if (!(lower_file->f_mode & FMODE_NOACTIVE)) { | |
41 | bdi = lower_file->f_mapping->backing_dev_info; | |
42 | lower_file->f_ra.ra_pages = bdi->ra_pages * 2; | |
43 | spin_lock(&lower_file->f_lock); | |
44 | lower_file->f_mode |= FMODE_NOACTIVE; | |
45 | spin_unlock(&lower_file->f_lock); | |
46 | } | |
47 | } | |
48 | #endif | |
49 | ||
50 | err = vfs_read(lower_file, buf, count, ppos); | |
51 | /* update our inode atime upon a successful lower read */ | |
52 | if (err >= 0) | |
53 | fsstack_copy_attr_atime(dentry->d_inode, | |
54 | lower_file->f_path.dentry->d_inode); | |
55 | ||
56 | return err; | |
57 | } | |
58 | ||
59 | static ssize_t sdcardfs_write(struct file *file, const char __user *buf, | |
60 | size_t count, loff_t *ppos) | |
61 | { | |
62 | int err = 0; | |
63 | struct file *lower_file; | |
64 | struct dentry *dentry = file->f_path.dentry; | |
65 | ||
66 | /* check disk space */ | |
67 | if (!check_min_free_space(dentry, count, 0)) { | |
68 | printk(KERN_INFO "No minimum free space.\n"); | |
69 | return -ENOSPC; | |
70 | } | |
71 | ||
72 | lower_file = sdcardfs_lower_file(file); | |
73 | err = vfs_write(lower_file, buf, count, ppos); | |
74 | /* update our inode times+sizes upon a successful lower write */ | |
75 | if (err >= 0) { | |
76 | fsstack_copy_inode_size(dentry->d_inode, | |
77 | lower_file->f_path.dentry->d_inode); | |
78 | fsstack_copy_attr_times(dentry->d_inode, | |
79 | lower_file->f_path.dentry->d_inode); | |
80 | } | |
81 | ||
82 | return err; | |
83 | } | |
84 | ||
542a676e | 85 | static int sdcardfs_readdir(struct file *file, struct dir_context *ctx) |
ee184c81 DC |
86 | { |
87 | int err = 0; | |
88 | struct file *lower_file = NULL; | |
89 | struct dentry *dentry = file->f_path.dentry; | |
90 | ||
91 | lower_file = sdcardfs_lower_file(file); | |
92 | ||
93 | lower_file->f_pos = file->f_pos; | |
542a676e | 94 | err = iterate_dir(lower_file, ctx); |
ee184c81 DC |
95 | file->f_pos = lower_file->f_pos; |
96 | if (err >= 0) /* copy the atime */ | |
97 | fsstack_copy_attr_atime(dentry->d_inode, | |
98 | lower_file->f_path.dentry->d_inode); | |
99 | return err; | |
100 | } | |
101 | ||
102 | static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, | |
103 | unsigned long arg) | |
104 | { | |
105 | long err = -ENOTTY; | |
106 | struct file *lower_file; | |
107 | ||
108 | lower_file = sdcardfs_lower_file(file); | |
109 | ||
110 | /* XXX: use vfs_ioctl if/when VFS exports it */ | |
111 | if (!lower_file || !lower_file->f_op) | |
112 | goto out; | |
113 | if (lower_file->f_op->unlocked_ioctl) | |
114 | err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); | |
115 | ||
116 | out: | |
117 | return err; | |
118 | } | |
119 | ||
120 | #ifdef CONFIG_COMPAT | |
121 | static long sdcardfs_compat_ioctl(struct file *file, unsigned int cmd, | |
122 | unsigned long arg) | |
123 | { | |
124 | long err = -ENOTTY; | |
125 | struct file *lower_file; | |
126 | ||
127 | lower_file = sdcardfs_lower_file(file); | |
128 | ||
129 | /* XXX: use vfs_ioctl if/when VFS exports it */ | |
130 | if (!lower_file || !lower_file->f_op) | |
131 | goto out; | |
132 | if (lower_file->f_op->compat_ioctl) | |
133 | err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg); | |
134 | ||
135 | out: | |
136 | return err; | |
137 | } | |
138 | #endif | |
139 | ||
140 | static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) | |
141 | { | |
142 | int err = 0; | |
143 | bool willwrite; | |
144 | struct file *lower_file; | |
145 | const struct vm_operations_struct *saved_vm_ops = NULL; | |
146 | ||
147 | /* this might be deferred to mmap's writepage */ | |
148 | willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags); | |
149 | ||
150 | /* | |
151 | * File systems which do not implement ->writepage may use | |
152 | * generic_file_readonly_mmap as their ->mmap op. If you call | |
153 | * generic_file_readonly_mmap with VM_WRITE, you'd get an -EINVAL. | |
154 | * But we cannot call the lower ->mmap op, so we can't tell that | |
155 | * writeable mappings won't work. Therefore, our only choice is to | |
156 | * check if the lower file system supports the ->writepage, and if | |
157 | * not, return EINVAL (the same error that | |
158 | * generic_file_readonly_mmap returns in that case). | |
159 | */ | |
160 | lower_file = sdcardfs_lower_file(file); | |
161 | if (willwrite && !lower_file->f_mapping->a_ops->writepage) { | |
162 | err = -EINVAL; | |
163 | printk(KERN_ERR "sdcardfs: lower file system does not " | |
164 | "support writeable mmap\n"); | |
165 | goto out; | |
166 | } | |
167 | ||
168 | /* | |
169 | * find and save lower vm_ops. | |
170 | * | |
171 | * XXX: the VFS should have a cleaner way of finding the lower vm_ops | |
172 | */ | |
173 | if (!SDCARDFS_F(file)->lower_vm_ops) { | |
174 | err = lower_file->f_op->mmap(lower_file, vma); | |
175 | if (err) { | |
176 | printk(KERN_ERR "sdcardfs: lower mmap failed %d\n", err); | |
177 | goto out; | |
178 | } | |
179 | saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */ | |
180 | err = do_munmap(current->mm, vma->vm_start, | |
181 | vma->vm_end - vma->vm_start); | |
182 | if (err) { | |
183 | printk(KERN_ERR "sdcardfs: do_munmap failed %d\n", err); | |
184 | goto out; | |
185 | } | |
186 | } | |
187 | ||
188 | /* | |
189 | * Next 3 lines are all I need from generic_file_mmap. I definitely | |
190 | * don't want its test for ->readpage which returns -ENOEXEC. | |
191 | */ | |
192 | file_accessed(file); | |
193 | vma->vm_ops = &sdcardfs_vm_ops; | |
ee184c81 DC |
194 | |
195 | file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */ | |
196 | if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */ | |
197 | SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops; | |
198 | ||
199 | out: | |
200 | return err; | |
201 | } | |
202 | ||
203 | static int sdcardfs_open(struct inode *inode, struct file *file) | |
204 | { | |
205 | int err = 0; | |
206 | struct file *lower_file = NULL; | |
207 | struct path lower_path; | |
208 | struct dentry *dentry = file->f_path.dentry; | |
209 | struct dentry *parent = dget_parent(dentry); | |
210 | struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); | |
211 | const struct cred *saved_cred = NULL; | |
ee184c81 DC |
212 | |
213 | /* don't open unhashed/deleted files */ | |
214 | if (d_unhashed(dentry)) { | |
215 | err = -ENOENT; | |
216 | goto out_err; | |
217 | } | |
218 | ||
0bd58959 | 219 | if(!check_caller_access_to_name(parent->d_inode, &dentry->d_name)) { |
ee184c81 DC |
220 | printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" |
221 | " dentry: %s, task:%s\n", | |
222 | __func__, dentry->d_name.name, current->comm); | |
223 | err = -EACCES; | |
224 | goto out_err; | |
225 | } | |
226 | ||
227 | /* save current_cred and override it */ | |
f4d33b9a | 228 | OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode)); |
ee184c81 | 229 | |
8e67babd | 230 | file->f_mode |= FMODE_NONMAPPABLE; |
ee184c81 DC |
231 | file->private_data = |
232 | kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL); | |
233 | if (!SDCARDFS_F(file)) { | |
234 | err = -ENOMEM; | |
235 | goto out_revert_cred; | |
236 | } | |
237 | ||
238 | /* open lower object and link sdcardfs's file struct to lower's */ | |
239 | sdcardfs_get_lower_path(file->f_path.dentry, &lower_path); | |
542a676e DC |
240 | lower_file = dentry_open(&lower_path, file->f_flags, current_cred()); |
241 | path_put(&lower_path); | |
ee184c81 DC |
242 | if (IS_ERR(lower_file)) { |
243 | err = PTR_ERR(lower_file); | |
244 | lower_file = sdcardfs_lower_file(file); | |
245 | if (lower_file) { | |
246 | sdcardfs_set_lower_file(file, NULL); | |
247 | fput(lower_file); /* fput calls dput for lower_dentry */ | |
248 | } | |
249 | } else { | |
250 | sdcardfs_set_lower_file(file, lower_file); | |
251 | } | |
252 | ||
253 | if (err) | |
254 | kfree(SDCARDFS_F(file)); | |
255 | else { | |
3ebb8b99 | 256 | sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode)); |
ee184c81 DC |
257 | } |
258 | ||
259 | out_revert_cred: | |
260 | REVERT_CRED(saved_cred); | |
261 | out_err: | |
262 | dput(parent); | |
263 | return err; | |
264 | } | |
265 | ||
266 | static int sdcardfs_flush(struct file *file, fl_owner_t id) | |
267 | { | |
268 | int err = 0; | |
269 | struct file *lower_file = NULL; | |
270 | ||
271 | lower_file = sdcardfs_lower_file(file); | |
272 | if (lower_file && lower_file->f_op && lower_file->f_op->flush) | |
273 | err = lower_file->f_op->flush(lower_file, id); | |
274 | ||
275 | return err; | |
276 | } | |
277 | ||
278 | /* release all lower object references & free the file info structure */ | |
279 | static int sdcardfs_file_release(struct inode *inode, struct file *file) | |
280 | { | |
281 | struct file *lower_file; | |
282 | ||
283 | lower_file = sdcardfs_lower_file(file); | |
284 | if (lower_file) { | |
285 | sdcardfs_set_lower_file(file, NULL); | |
286 | fput(lower_file); | |
287 | } | |
288 | ||
289 | kfree(SDCARDFS_F(file)); | |
290 | return 0; | |
291 | } | |
292 | ||
542a676e DC |
293 | static int sdcardfs_fsync(struct file *file, loff_t start, loff_t end, |
294 | int datasync) | |
ee184c81 DC |
295 | { |
296 | int err; | |
297 | struct file *lower_file; | |
298 | struct path lower_path; | |
299 | struct dentry *dentry = file->f_path.dentry; | |
300 | ||
542a676e DC |
301 | err = generic_file_fsync(file, start, end, datasync); |
302 | if (err) | |
303 | goto out; | |
304 | ||
ee184c81 DC |
305 | lower_file = sdcardfs_lower_file(file); |
306 | sdcardfs_get_lower_path(dentry, &lower_path); | |
542a676e | 307 | err = vfs_fsync_range(lower_file, start, end, datasync); |
ee184c81 | 308 | sdcardfs_put_lower_path(dentry, &lower_path); |
542a676e | 309 | out: |
ee184c81 DC |
310 | return err; |
311 | } | |
312 | ||
313 | static int sdcardfs_fasync(int fd, struct file *file, int flag) | |
314 | { | |
315 | int err = 0; | |
316 | struct file *lower_file = NULL; | |
317 | ||
318 | lower_file = sdcardfs_lower_file(file); | |
319 | if (lower_file->f_op && lower_file->f_op->fasync) | |
320 | err = lower_file->f_op->fasync(fd, lower_file, flag); | |
321 | ||
322 | return err; | |
323 | } | |
324 | ||
8e67babd | 325 | static struct file *sdcardfs_get_lower_file(struct file *f) |
326 | { | |
327 | return sdcardfs_lower_file(f); | |
328 | } | |
329 | ||
ee184c81 DC |
330 | const struct file_operations sdcardfs_main_fops = { |
331 | .llseek = generic_file_llseek, | |
332 | .read = sdcardfs_read, | |
333 | .write = sdcardfs_write, | |
334 | .unlocked_ioctl = sdcardfs_unlocked_ioctl, | |
335 | #ifdef CONFIG_COMPAT | |
336 | .compat_ioctl = sdcardfs_compat_ioctl, | |
337 | #endif | |
338 | .mmap = sdcardfs_mmap, | |
339 | .open = sdcardfs_open, | |
340 | .flush = sdcardfs_flush, | |
341 | .release = sdcardfs_file_release, | |
342 | .fsync = sdcardfs_fsync, | |
343 | .fasync = sdcardfs_fasync, | |
8e67babd | 344 | .get_lower_file = sdcardfs_get_lower_file, |
ee184c81 DC |
345 | }; |
346 | ||
347 | /* trimmed directory options */ | |
348 | const struct file_operations sdcardfs_dir_fops = { | |
349 | .llseek = generic_file_llseek, | |
350 | .read = generic_read_dir, | |
542a676e | 351 | .iterate = sdcardfs_readdir, |
ee184c81 DC |
352 | .unlocked_ioctl = sdcardfs_unlocked_ioctl, |
353 | #ifdef CONFIG_COMPAT | |
354 | .compat_ioctl = sdcardfs_compat_ioctl, | |
355 | #endif | |
356 | .open = sdcardfs_open, | |
357 | .release = sdcardfs_file_release, | |
358 | .flush = sdcardfs_flush, | |
359 | .fsync = sdcardfs_fsync, | |
360 | .fasync = sdcardfs_fasync, | |
8e67babd | 361 | .get_lower_file = sdcardfs_get_lower_file, |
ee184c81 | 362 | }; |