Commit | Line | Data |
---|---|---|
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 | ||
15 | static 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 | ||
37 | static 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 | ||
73 | static 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 |
95 | static 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 | ||
117 | static int ovl_release(struct inode *inode, struct file *file) | |
118 | { | |
119 | fput(file->private_data); | |
120 | ||
121 | return 0; | |
122 | } | |
123 | ||
124 | static 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 |
133 | static 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 | ||
155 | static 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 | ||
172 | static 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 |
198 | const 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 | }; |