]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - fs/aufs/fhsm.c
UBUNTU: SAUCE: AUFS
[mirror_ubuntu-jammy-kernel.git] / fs / aufs / fhsm.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2011-2021 Junjiro R. Okajima
4 *
5 * This program, aufs is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 /*
21 * File-based Hierarchy Storage Management
22 */
23
24 #include <linux/anon_inodes.h>
25 #include <linux/poll.h>
26 #include <linux/seq_file.h>
27 #include <linux/statfs.h>
28 #include "aufs.h"
29
30 static aufs_bindex_t au_fhsm_bottom(struct super_block *sb)
31 {
32 struct au_sbinfo *sbinfo;
33 struct au_fhsm *fhsm;
34
35 SiMustAnyLock(sb);
36
37 sbinfo = au_sbi(sb);
38 fhsm = &sbinfo->si_fhsm;
39 AuDebugOn(!fhsm);
40 return fhsm->fhsm_bottom;
41 }
42
43 void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex)
44 {
45 struct au_sbinfo *sbinfo;
46 struct au_fhsm *fhsm;
47
48 SiMustWriteLock(sb);
49
50 sbinfo = au_sbi(sb);
51 fhsm = &sbinfo->si_fhsm;
52 AuDebugOn(!fhsm);
53 fhsm->fhsm_bottom = bindex;
54 }
55
56 /* ---------------------------------------------------------------------- */
57
58 static int au_fhsm_test_jiffy(struct au_sbinfo *sbinfo, struct au_branch *br)
59 {
60 struct au_br_fhsm *bf;
61
62 bf = br->br_fhsm;
63 MtxMustLock(&bf->bf_lock);
64
65 return !bf->bf_readable
66 || time_after(jiffies,
67 bf->bf_jiffy + sbinfo->si_fhsm.fhsm_expire);
68 }
69
70 /* ---------------------------------------------------------------------- */
71
72 static void au_fhsm_notify(struct super_block *sb, int val)
73 {
74 struct au_sbinfo *sbinfo;
75 struct au_fhsm *fhsm;
76
77 SiMustAnyLock(sb);
78
79 sbinfo = au_sbi(sb);
80 fhsm = &sbinfo->si_fhsm;
81 if (au_fhsm_pid(fhsm)
82 && atomic_read(&fhsm->fhsm_readable) != -1) {
83 atomic_set(&fhsm->fhsm_readable, val);
84 if (val)
85 wake_up(&fhsm->fhsm_wqh);
86 }
87 }
88
89 static int au_fhsm_stfs(struct super_block *sb, aufs_bindex_t bindex,
90 struct aufs_stfs *rstfs, int do_lock, int do_notify)
91 {
92 int err;
93 struct au_branch *br;
94 struct au_br_fhsm *bf;
95
96 br = au_sbr(sb, bindex);
97 AuDebugOn(au_br_rdonly(br));
98 bf = br->br_fhsm;
99 AuDebugOn(!bf);
100
101 if (do_lock)
102 mutex_lock(&bf->bf_lock);
103 else
104 MtxMustLock(&bf->bf_lock);
105
106 /* sb->s_root for NFS is unreliable */
107 err = au_br_stfs(br, &bf->bf_stfs);
108 if (unlikely(err)) {
109 AuErr1("FHSM failed (%d), b%d, ignored.\n", bindex, err);
110 goto out;
111 }
112
113 bf->bf_jiffy = jiffies;
114 bf->bf_readable = 1;
115 if (do_notify)
116 au_fhsm_notify(sb, /*val*/1);
117 if (rstfs)
118 *rstfs = bf->bf_stfs;
119
120 out:
121 if (do_lock)
122 mutex_unlock(&bf->bf_lock);
123 au_fhsm_notify(sb, /*val*/1);
124
125 return err;
126 }
127
128 void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force)
129 {
130 int err;
131 struct au_sbinfo *sbinfo;
132 struct au_fhsm *fhsm;
133 struct au_branch *br;
134 struct au_br_fhsm *bf;
135
136 AuDbg("b%d, force %d\n", bindex, force);
137 SiMustAnyLock(sb);
138
139 sbinfo = au_sbi(sb);
140 fhsm = &sbinfo->si_fhsm;
141 if (!au_ftest_si(sbinfo, FHSM)
142 || fhsm->fhsm_bottom == bindex)
143 return;
144
145 br = au_sbr(sb, bindex);
146 bf = br->br_fhsm;
147 AuDebugOn(!bf);
148 mutex_lock(&bf->bf_lock);
149 if (force
150 || au_fhsm_pid(fhsm)
151 || au_fhsm_test_jiffy(sbinfo, br))
152 err = au_fhsm_stfs(sb, bindex, /*rstfs*/NULL, /*do_lock*/0,
153 /*do_notify*/1);
154 mutex_unlock(&bf->bf_lock);
155 }
156
157 void au_fhsm_wrote_all(struct super_block *sb, int force)
158 {
159 aufs_bindex_t bindex, bbot;
160 struct au_branch *br;
161
162 /* exclude the bottom */
163 bbot = au_fhsm_bottom(sb);
164 for (bindex = 0; bindex < bbot; bindex++) {
165 br = au_sbr(sb, bindex);
166 if (au_br_fhsm(br->br_perm))
167 au_fhsm_wrote(sb, bindex, force);
168 }
169 }
170
171 /* ---------------------------------------------------------------------- */
172
173 static __poll_t au_fhsm_poll(struct file *file, struct poll_table_struct *wait)
174 {
175 __poll_t mask;
176 struct au_sbinfo *sbinfo;
177 struct au_fhsm *fhsm;
178
179 mask = 0;
180 sbinfo = file->private_data;
181 fhsm = &sbinfo->si_fhsm;
182 poll_wait(file, &fhsm->fhsm_wqh, wait);
183 if (atomic_read(&fhsm->fhsm_readable))
184 mask = EPOLLIN /* | EPOLLRDNORM */;
185
186 if (!mask)
187 AuDbg("mask 0x%x\n", mask);
188 return mask;
189 }
190
191 static int au_fhsm_do_read_one(struct aufs_stbr __user *stbr,
192 struct aufs_stfs *stfs, __s16 brid)
193 {
194 int err;
195
196 err = copy_to_user(&stbr->stfs, stfs, sizeof(*stfs));
197 if (!err)
198 err = __put_user(brid, &stbr->brid);
199 if (unlikely(err))
200 err = -EFAULT;
201
202 return err;
203 }
204
205 static ssize_t au_fhsm_do_read(struct super_block *sb,
206 struct aufs_stbr __user *stbr, size_t count)
207 {
208 ssize_t err;
209 int nstbr;
210 aufs_bindex_t bindex, bbot;
211 struct au_branch *br;
212 struct au_br_fhsm *bf;
213
214 /* except the bottom branch */
215 err = 0;
216 nstbr = 0;
217 bbot = au_fhsm_bottom(sb);
218 for (bindex = 0; !err && bindex < bbot; bindex++) {
219 br = au_sbr(sb, bindex);
220 if (!au_br_fhsm(br->br_perm))
221 continue;
222
223 bf = br->br_fhsm;
224 mutex_lock(&bf->bf_lock);
225 if (bf->bf_readable) {
226 err = -EFAULT;
227 if (count >= sizeof(*stbr))
228 err = au_fhsm_do_read_one(stbr++, &bf->bf_stfs,
229 br->br_id);
230 if (!err) {
231 bf->bf_readable = 0;
232 count -= sizeof(*stbr);
233 nstbr++;
234 }
235 }
236 mutex_unlock(&bf->bf_lock);
237 }
238 if (!err)
239 err = sizeof(*stbr) * nstbr;
240
241 return err;
242 }
243
244 static ssize_t au_fhsm_read(struct file *file, char __user *buf, size_t count,
245 loff_t *pos)
246 {
247 ssize_t err;
248 int readable;
249 aufs_bindex_t nfhsm, bindex, bbot;
250 struct au_sbinfo *sbinfo;
251 struct au_fhsm *fhsm;
252 struct au_branch *br;
253 struct super_block *sb;
254
255 err = 0;
256 sbinfo = file->private_data;
257 fhsm = &sbinfo->si_fhsm;
258 need_data:
259 spin_lock_irq(&fhsm->fhsm_wqh.lock);
260 if (!atomic_read(&fhsm->fhsm_readable)) {
261 if (vfsub_file_flags(file) & O_NONBLOCK)
262 err = -EAGAIN;
263 else
264 err = wait_event_interruptible_locked_irq
265 (fhsm->fhsm_wqh,
266 atomic_read(&fhsm->fhsm_readable));
267 }
268 spin_unlock_irq(&fhsm->fhsm_wqh.lock);
269 if (unlikely(err))
270 goto out;
271
272 /* sb may already be dead */
273 au_rw_read_lock(&sbinfo->si_rwsem);
274 readable = atomic_read(&fhsm->fhsm_readable);
275 if (readable > 0) {
276 sb = sbinfo->si_sb;
277 AuDebugOn(!sb);
278 /* exclude the bottom branch */
279 nfhsm = 0;
280 bbot = au_fhsm_bottom(sb);
281 for (bindex = 0; bindex < bbot; bindex++) {
282 br = au_sbr(sb, bindex);
283 if (au_br_fhsm(br->br_perm))
284 nfhsm++;
285 }
286 err = -EMSGSIZE;
287 if (nfhsm * sizeof(struct aufs_stbr) <= count) {
288 atomic_set(&fhsm->fhsm_readable, 0);
289 err = au_fhsm_do_read(sbinfo->si_sb, (void __user *)buf,
290 count);
291 }
292 }
293 au_rw_read_unlock(&sbinfo->si_rwsem);
294 if (!readable)
295 goto need_data;
296
297 out:
298 return err;
299 }
300
301 static int au_fhsm_release(struct inode *inode, struct file *file)
302 {
303 struct au_sbinfo *sbinfo;
304 struct au_fhsm *fhsm;
305
306 /* sb may already be dead */
307 sbinfo = file->private_data;
308 fhsm = &sbinfo->si_fhsm;
309 spin_lock(&fhsm->fhsm_spin);
310 fhsm->fhsm_pid = 0;
311 spin_unlock(&fhsm->fhsm_spin);
312 kobject_put(&sbinfo->si_kobj);
313
314 return 0;
315 }
316
317 static const struct file_operations au_fhsm_fops = {
318 .owner = THIS_MODULE,
319 .llseek = noop_llseek,
320 .read = au_fhsm_read,
321 .poll = au_fhsm_poll,
322 .release = au_fhsm_release
323 };
324
325 int au_fhsm_fd(struct super_block *sb, int oflags)
326 {
327 int err, fd;
328 struct au_sbinfo *sbinfo;
329 struct au_fhsm *fhsm;
330
331 err = -EPERM;
332 if (unlikely(!capable(CAP_SYS_ADMIN)))
333 goto out;
334
335 err = -EINVAL;
336 if (unlikely(oflags & ~(O_CLOEXEC | O_NONBLOCK)))
337 goto out;
338
339 err = 0;
340 sbinfo = au_sbi(sb);
341 fhsm = &sbinfo->si_fhsm;
342 spin_lock(&fhsm->fhsm_spin);
343 if (!fhsm->fhsm_pid)
344 fhsm->fhsm_pid = current->pid;
345 else
346 err = -EBUSY;
347 spin_unlock(&fhsm->fhsm_spin);
348 if (unlikely(err))
349 goto out;
350
351 oflags |= O_RDONLY;
352 /* oflags |= FMODE_NONOTIFY; */
353 fd = anon_inode_getfd("[aufs_fhsm]", &au_fhsm_fops, sbinfo, oflags);
354 err = fd;
355 if (unlikely(fd < 0))
356 goto out_pid;
357
358 /* succeed regardless 'fhsm' status */
359 kobject_get(&sbinfo->si_kobj);
360 si_noflush_read_lock(sb);
361 if (au_ftest_si(sbinfo, FHSM))
362 au_fhsm_wrote_all(sb, /*force*/0);
363 si_read_unlock(sb);
364 goto out; /* success */
365
366 out_pid:
367 spin_lock(&fhsm->fhsm_spin);
368 fhsm->fhsm_pid = 0;
369 spin_unlock(&fhsm->fhsm_spin);
370 out:
371 AuTraceErr(err);
372 return err;
373 }
374
375 /* ---------------------------------------------------------------------- */
376
377 int au_fhsm_br_alloc(struct au_branch *br)
378 {
379 int err;
380
381 err = 0;
382 br->br_fhsm = kmalloc(sizeof(*br->br_fhsm), GFP_NOFS);
383 if (br->br_fhsm)
384 au_br_fhsm_init(br->br_fhsm);
385 else
386 err = -ENOMEM;
387
388 return err;
389 }
390
391 /* ---------------------------------------------------------------------- */
392
393 void au_fhsm_fin(struct super_block *sb)
394 {
395 au_fhsm_notify(sb, /*val*/-1);
396 }
397
398 void au_fhsm_init(struct au_sbinfo *sbinfo)
399 {
400 struct au_fhsm *fhsm;
401
402 fhsm = &sbinfo->si_fhsm;
403 spin_lock_init(&fhsm->fhsm_spin);
404 init_waitqueue_head(&fhsm->fhsm_wqh);
405 atomic_set(&fhsm->fhsm_readable, 0);
406 fhsm->fhsm_expire
407 = msecs_to_jiffies(AUFS_FHSM_CACHE_DEF_SEC * MSEC_PER_SEC);
408 fhsm->fhsm_bottom = -1;
409 }
410
411 void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec)
412 {
413 sbinfo->si_fhsm.fhsm_expire
414 = msecs_to_jiffies(sec * MSEC_PER_SEC);
415 }
416
417 void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo)
418 {
419 unsigned int u;
420
421 if (!au_ftest_si(sbinfo, FHSM))
422 return;
423
424 u = jiffies_to_msecs(sbinfo->si_fhsm.fhsm_expire) / MSEC_PER_SEC;
425 if (u != AUFS_FHSM_CACHE_DEF_SEC)
426 seq_printf(seq, ",fhsm_sec=%u", u);
427 }