ovl: add ovl_read_iter()
[linux-block.git] / fs / overlayfs / file.c
CommitLineData
d1d04ef8
MS
1/*
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>
11#include <linux/xattr.h>
16914e6f 12#include <linux/uio.h>
d1d04ef8
MS
13#include "overlayfs.h"
14
15static struct file *ovl_open_realfile(const struct file *file)
16{
17 struct inode *inode = file_inode(file);
18 struct inode *upperinode = ovl_inode_upper(inode);
19 struct inode *realinode = upperinode ?: ovl_inode_lower(inode);
20 struct file *realfile;
21 const struct cred *old_cred;
22
23 old_cred = ovl_override_creds(inode->i_sb);
24 realfile = open_with_fake_path(&file->f_path, file->f_flags | O_NOATIME,
25 realinode, current_cred());
26 revert_creds(old_cred);
27
28 pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
29 file, file, upperinode ? 'u' : 'l', file->f_flags,
30 realfile, IS_ERR(realfile) ? 0 : realfile->f_flags);
31
32 return realfile;
33}
34
2ef66b8a
MS
35#define OVL_SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT)
36
37static int ovl_change_flags(struct file *file, unsigned int flags)
38{
39 struct inode *inode = file_inode(file);
40 int err;
41
42 /* No atime modificaton on underlying */
43 flags |= O_NOATIME;
44
45 /* If some flag changed that cannot be changed then something's amiss */
46 if (WARN_ON((file->f_flags ^ flags) & ~OVL_SETFL_MASK))
47 return -EIO;
48
49 flags &= OVL_SETFL_MASK;
50
51 if (((flags ^ file->f_flags) & O_APPEND) && IS_APPEND(inode))
52 return -EPERM;
53
54 if (flags & O_DIRECT) {
55 if (!file->f_mapping->a_ops ||
56 !file->f_mapping->a_ops->direct_IO)
57 return -EINVAL;
58 }
59
60 if (file->f_op->check_flags) {
61 err = file->f_op->check_flags(flags);
62 if (err)
63 return err;
64 }
65
66 spin_lock(&file->f_lock);
67 file->f_flags = (file->f_flags & ~OVL_SETFL_MASK) | flags;
68 spin_unlock(&file->f_lock);
69
70 return 0;
71}
72
73static int ovl_real_fdget(const struct file *file, struct fd *real)
74{
75 struct inode *inode = file_inode(file);
76
77 real->flags = 0;
78 real->file = file->private_data;
79
80 /* Has it been copied up since we'd opened it? */
81 if (unlikely(file_inode(real->file) != ovl_inode_real(inode))) {
82 real->flags = FDPUT_FPUT;
83 real->file = ovl_open_realfile(file);
84
85 return PTR_ERR_OR_ZERO(real->file);
86 }
87
88 /* Did the flags change since open? */
89 if (unlikely((file->f_flags ^ real->file->f_flags) & ~O_NOATIME))
90 return ovl_change_flags(real->file, file->f_flags);
91
92 return 0;
93}
94
d1d04ef8
MS
95static int ovl_open(struct inode *inode, struct file *file)
96{
97 struct dentry *dentry = file_dentry(file);
98 struct file *realfile;
99 int err;
100
101 err = ovl_open_maybe_copy_up(dentry, file->f_flags);
102 if (err)
103 return err;
104
105 /* No longer need these flags, so don't pass them on to underlying fs */
106 file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
107
108 realfile = ovl_open_realfile(file);
109 if (IS_ERR(realfile))
110 return PTR_ERR(realfile);
111
112 file->private_data = realfile;
113
114 return 0;
115}
116
117static int ovl_release(struct inode *inode, struct file *file)
118{
119 fput(file->private_data);
120
121 return 0;
122}
123
124static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
125{
126 struct inode *realinode = ovl_inode_real(file_inode(file));
127
128 return generic_file_llseek_size(file, offset, whence,
129 realinode->i_sb->s_maxbytes,
130 i_size_read(realinode));
131}
132
16914e6f
MS
133static void ovl_file_accessed(struct file *file)
134{
135 struct inode *inode, *upperinode;
136
137 if (file->f_flags & O_NOATIME)
138 return;
139
140 inode = file_inode(file);
141 upperinode = ovl_inode_upper(inode);
142
143 if (!upperinode)
144 return;
145
146 if ((!timespec64_equal(&inode->i_mtime, &upperinode->i_mtime) ||
147 !timespec64_equal(&inode->i_ctime, &upperinode->i_ctime))) {
148 inode->i_mtime = upperinode->i_mtime;
149 inode->i_ctime = upperinode->i_ctime;
150 }
151
152 touch_atime(&file->f_path);
153}
154
155static rwf_t ovl_iocb_to_rwf(struct kiocb *iocb)
156{
157 int ifl = iocb->ki_flags;
158 rwf_t flags = 0;
159
160 if (ifl & IOCB_NOWAIT)
161 flags |= RWF_NOWAIT;
162 if (ifl & IOCB_HIPRI)
163 flags |= RWF_HIPRI;
164 if (ifl & IOCB_DSYNC)
165 flags |= RWF_DSYNC;
166 if (ifl & IOCB_SYNC)
167 flags |= RWF_SYNC;
168
169 return flags;
170}
171
172static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
173{
174 struct file *file = iocb->ki_filp;
175 struct fd real;
176 const struct cred *old_cred;
177 ssize_t ret;
178
179 if (!iov_iter_count(iter))
180 return 0;
181
182 ret = ovl_real_fdget(file, &real);
183 if (ret)
184 return ret;
185
186 old_cred = ovl_override_creds(file_inode(file)->i_sb);
187 ret = vfs_iter_read(real.file, iter, &iocb->ki_pos,
188 ovl_iocb_to_rwf(iocb));
189 revert_creds(old_cred);
190
191 ovl_file_accessed(file);
192
193 fdput(real);
194
195 return ret;
196}
197
d1d04ef8
MS
198const struct file_operations ovl_file_operations = {
199 .open = ovl_open,
200 .release = ovl_release,
201 .llseek = ovl_llseek,
16914e6f 202 .read_iter = ovl_read_iter,
d1d04ef8 203};