blob: 7590e43369f6be324f58c6395e0d192a0bcb90c7 [file] [log] [blame]
Miklos Szeredid1d04ef2018-07-18 15:44:41 +02001/*
2 * Copyright (C) 2017 Red Hat, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published by
6 * the Free Software Foundation.
7 */
8
9#include <linux/cred.h>
10#include <linux/file.h>
Miklos Szeredidab5ca82018-07-18 15:44:42 +020011#include <linux/mount.h>
Miklos Szeredid1d04ef2018-07-18 15:44:41 +020012#include <linux/xattr.h>
Miklos Szeredi16914e62018-07-18 15:44:41 +020013#include <linux/uio.h>
Miklos Szeredid1d04ef2018-07-18 15:44:41 +020014#include "overlayfs.h"
15
16static struct file *ovl_open_realfile(const struct file *file)
17{
18 struct inode *inode = file_inode(file);
19 struct inode *upperinode = ovl_inode_upper(inode);
20 struct inode *realinode = upperinode ?: ovl_inode_lower(inode);
21 struct file *realfile;
22 const struct cred *old_cred;
23
24 old_cred = ovl_override_creds(inode->i_sb);
25 realfile = open_with_fake_path(&file->f_path, file->f_flags | O_NOATIME,
26 realinode, current_cred());
27 revert_creds(old_cred);
28
29 pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
30 file, file, upperinode ? 'u' : 'l', file->f_flags,
31 realfile, IS_ERR(realfile) ? 0 : realfile->f_flags);
32
33 return realfile;
34}
35
Miklos Szeredi2ef66b82018-07-18 15:44:41 +020036#define OVL_SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT)
37
38static int ovl_change_flags(struct file *file, unsigned int flags)
39{
40 struct inode *inode = file_inode(file);
41 int err;
42
43 /* No atime modificaton on underlying */
44 flags |= O_NOATIME;
45
46 /* If some flag changed that cannot be changed then something's amiss */
47 if (WARN_ON((file->f_flags ^ flags) & ~OVL_SETFL_MASK))
48 return -EIO;
49
50 flags &= OVL_SETFL_MASK;
51
52 if (((flags ^ file->f_flags) & O_APPEND) && IS_APPEND(inode))
53 return -EPERM;
54
55 if (flags & O_DIRECT) {
56 if (!file->f_mapping->a_ops ||
57 !file->f_mapping->a_ops->direct_IO)
58 return -EINVAL;
59 }
60
61 if (file->f_op->check_flags) {
62 err = file->f_op->check_flags(flags);
63 if (err)
64 return err;
65 }
66
67 spin_lock(&file->f_lock);
68 file->f_flags = (file->f_flags & ~OVL_SETFL_MASK) | flags;
69 spin_unlock(&file->f_lock);
70
71 return 0;
72}
73
74static int ovl_real_fdget(const struct file *file, struct fd *real)
75{
76 struct inode *inode = file_inode(file);
77
78 real->flags = 0;
79 real->file = file->private_data;
80
81 /* Has it been copied up since we'd opened it? */
82 if (unlikely(file_inode(real->file) != ovl_inode_real(inode))) {
83 real->flags = FDPUT_FPUT;
84 real->file = ovl_open_realfile(file);
85
86 return PTR_ERR_OR_ZERO(real->file);
87 }
88
89 /* Did the flags change since open? */
90 if (unlikely((file->f_flags ^ real->file->f_flags) & ~O_NOATIME))
91 return ovl_change_flags(real->file, file->f_flags);
92
93 return 0;
94}
95
Miklos Szeredid1d04ef2018-07-18 15:44:41 +020096static int ovl_open(struct inode *inode, struct file *file)
97{
98 struct dentry *dentry = file_dentry(file);
99 struct file *realfile;
100 int err;
101
102 err = ovl_open_maybe_copy_up(dentry, file->f_flags);
103 if (err)
104 return err;
105
106 /* No longer need these flags, so don't pass them on to underlying fs */
107 file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
108
109 realfile = ovl_open_realfile(file);
110 if (IS_ERR(realfile))
111 return PTR_ERR(realfile);
112
113 file->private_data = realfile;
114
115 return 0;
116}
117
118static int ovl_release(struct inode *inode, struct file *file)
119{
120 fput(file->private_data);
121
122 return 0;
123}
124
125static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
126{
127 struct inode *realinode = ovl_inode_real(file_inode(file));
128
129 return generic_file_llseek_size(file, offset, whence,
130 realinode->i_sb->s_maxbytes,
131 i_size_read(realinode));
132}
133
Miklos Szeredi16914e62018-07-18 15:44:41 +0200134static void ovl_file_accessed(struct file *file)
135{
136 struct inode *inode, *upperinode;
137
138 if (file->f_flags & O_NOATIME)
139 return;
140
141 inode = file_inode(file);
142 upperinode = ovl_inode_upper(inode);
143
144 if (!upperinode)
145 return;
146
147 if ((!timespec64_equal(&inode->i_mtime, &upperinode->i_mtime) ||
148 !timespec64_equal(&inode->i_ctime, &upperinode->i_ctime))) {
149 inode->i_mtime = upperinode->i_mtime;
150 inode->i_ctime = upperinode->i_ctime;
151 }
152
153 touch_atime(&file->f_path);
154}
155
156static rwf_t ovl_iocb_to_rwf(struct kiocb *iocb)
157{
158 int ifl = iocb->ki_flags;
159 rwf_t flags = 0;
160
161 if (ifl & IOCB_NOWAIT)
162 flags |= RWF_NOWAIT;
163 if (ifl & IOCB_HIPRI)
164 flags |= RWF_HIPRI;
165 if (ifl & IOCB_DSYNC)
166 flags |= RWF_DSYNC;
167 if (ifl & IOCB_SYNC)
168 flags |= RWF_SYNC;
169
170 return flags;
171}
172
173static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
174{
175 struct file *file = iocb->ki_filp;
176 struct fd real;
177 const struct cred *old_cred;
178 ssize_t ret;
179
180 if (!iov_iter_count(iter))
181 return 0;
182
183 ret = ovl_real_fdget(file, &real);
184 if (ret)
185 return ret;
186
187 old_cred = ovl_override_creds(file_inode(file)->i_sb);
188 ret = vfs_iter_read(real.file, iter, &iocb->ki_pos,
189 ovl_iocb_to_rwf(iocb));
190 revert_creds(old_cred);
191
192 ovl_file_accessed(file);
193
194 fdput(real);
195
196 return ret;
197}
198
Miklos Szeredi2a92e072018-07-18 15:44:41 +0200199static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
200{
201 struct file *file = iocb->ki_filp;
202 struct inode *inode = file_inode(file);
203 struct fd real;
204 const struct cred *old_cred;
205 ssize_t ret;
206
207 if (!iov_iter_count(iter))
208 return 0;
209
210 inode_lock(inode);
211 /* Update mode */
212 ovl_copyattr(ovl_inode_real(inode), inode);
213 ret = file_remove_privs(file);
214 if (ret)
215 goto out_unlock;
216
217 ret = ovl_real_fdget(file, &real);
218 if (ret)
219 goto out_unlock;
220
221 old_cred = ovl_override_creds(file_inode(file)->i_sb);
222 ret = vfs_iter_write(real.file, iter, &iocb->ki_pos,
223 ovl_iocb_to_rwf(iocb));
224 revert_creds(old_cred);
225
226 /* Update size */
227 ovl_copyattr(ovl_inode_real(inode), inode);
228
229 fdput(real);
230
231out_unlock:
232 inode_unlock(inode);
233
234 return ret;
235}
236
Miklos Szeredide30dfd2018-07-18 15:44:42 +0200237static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
238{
239 struct fd real;
240 const struct cred *old_cred;
241 int ret;
242
243 ret = ovl_real_fdget(file, &real);
244 if (ret)
245 return ret;
246
247 /* Don't sync lower file for fear of receiving EROFS error */
248 if (file_inode(real.file) == ovl_inode_upper(file_inode(file))) {
249 old_cred = ovl_override_creds(file_inode(file)->i_sb);
250 ret = vfs_fsync_range(real.file, start, end, datasync);
251 revert_creds(old_cred);
252 }
253
254 fdput(real);
255
256 return ret;
257}
258
Miklos Szeredi2f502832018-07-18 15:44:42 +0200259static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
260{
261 struct file *realfile = file->private_data;
262 const struct cred *old_cred;
263 int ret;
264
265 if (!realfile->f_op->mmap)
266 return -ENODEV;
267
268 if (WARN_ON(file != vma->vm_file))
269 return -EIO;
270
271 vma->vm_file = get_file(realfile);
272
273 old_cred = ovl_override_creds(file_inode(file)->i_sb);
274 ret = call_mmap(vma->vm_file, vma);
275 revert_creds(old_cred);
276
277 if (ret) {
278 /* Drop reference count from new vm_file value */
279 fput(realfile);
280 } else {
281 /* Drop reference count from previous vm_file value */
282 fput(file);
283 }
284
285 ovl_file_accessed(file);
286
287 return ret;
288}
289
Miklos Szerediaab88482018-07-18 15:44:42 +0200290static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
291{
292 struct inode *inode = file_inode(file);
293 struct fd real;
294 const struct cred *old_cred;
295 int ret;
296
297 ret = ovl_real_fdget(file, &real);
298 if (ret)
299 return ret;
300
301 old_cred = ovl_override_creds(file_inode(file)->i_sb);
302 ret = vfs_fallocate(real.file, mode, offset, len);
303 revert_creds(old_cred);
304
305 /* Update size */
306 ovl_copyattr(ovl_inode_real(inode), inode);
307
308 fdput(real);
309
310 return ret;
311}
312
Miklos Szeredidab5ca82018-07-18 15:44:42 +0200313static long ovl_real_ioctl(struct file *file, unsigned int cmd,
314 unsigned long arg)
315{
316 struct fd real;
317 const struct cred *old_cred;
318 long ret;
319
320 ret = ovl_real_fdget(file, &real);
321 if (ret)
322 return ret;
323
324 old_cred = ovl_override_creds(file_inode(file)->i_sb);
325 ret = vfs_ioctl(real.file, cmd, arg);
326 revert_creds(old_cred);
327
328 fdput(real);
329
330 return ret;
331}
332
333static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
334{
335 long ret;
336 struct inode *inode = file_inode(file);
337
338 switch (cmd) {
339 case FS_IOC_GETFLAGS:
340 ret = ovl_real_ioctl(file, cmd, arg);
341 break;
342
343 case FS_IOC_SETFLAGS:
344 if (!inode_owner_or_capable(inode))
345 return -EACCES;
346
347 ret = mnt_want_write_file(file);
348 if (ret)
349 return ret;
350
351 ret = ovl_copy_up(file_dentry(file));
352 if (!ret) {
353 ret = ovl_real_ioctl(file, cmd, arg);
354
355 inode_lock(inode);
356 ovl_copyflags(ovl_inode_real(inode), inode);
357 inode_unlock(inode);
358 }
359
360 mnt_drop_write_file(file);
361 break;
362
363 default:
364 ret = -ENOTTY;
365 }
366
367 return ret;
368}
369
370static long ovl_compat_ioctl(struct file *file, unsigned int cmd,
371 unsigned long arg)
372{
373 switch (cmd) {
374 case FS_IOC32_GETFLAGS:
375 cmd = FS_IOC_GETFLAGS;
376 break;
377
378 case FS_IOC32_SETFLAGS:
379 cmd = FS_IOC_SETFLAGS;
380 break;
381
382 default:
383 return -ENOIOCTLCMD;
384 }
385
386 return ovl_ioctl(file, cmd, arg);
387}
388
Miklos Szeredid1d04ef2018-07-18 15:44:41 +0200389const struct file_operations ovl_file_operations = {
390 .open = ovl_open,
391 .release = ovl_release,
392 .llseek = ovl_llseek,
Miklos Szeredi16914e62018-07-18 15:44:41 +0200393 .read_iter = ovl_read_iter,
Miklos Szeredi2a92e072018-07-18 15:44:41 +0200394 .write_iter = ovl_write_iter,
Miklos Szeredide30dfd2018-07-18 15:44:42 +0200395 .fsync = ovl_fsync,
Miklos Szeredi2f502832018-07-18 15:44:42 +0200396 .mmap = ovl_mmap,
Miklos Szerediaab88482018-07-18 15:44:42 +0200397 .fallocate = ovl_fallocate,
Miklos Szeredidab5ca82018-07-18 15:44:42 +0200398 .unlocked_ioctl = ovl_ioctl,
399 .compat_ioctl = ovl_compat_ioctl,
Miklos Szeredid1d04ef2018-07-18 15:44:41 +0200400};