]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - fs/aufs/hfsnotify.c
UBUNTU: SAUCE: AUFS
[mirror_ubuntu-jammy-kernel.git] / fs / aufs / hfsnotify.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2005-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, see <http://www.gnu.org/licenses/>.
17 */
18
19 /*
20 * fsnotify for the lower directories
21 */
22
23 #include "aufs.h"
24
25 /* FS_IN_IGNORED is unnecessary */
26 static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE
27 | FS_CREATE | FS_EVENT_ON_CHILD);
28 static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq);
29 static __cacheline_aligned_in_smp atomic64_t au_hfsn_ifree = ATOMIC64_INIT(0);
30
31 static void au_hfsn_free_mark(struct fsnotify_mark *mark)
32 {
33 struct au_hnotify *hn = container_of(mark, struct au_hnotify,
34 hn_mark);
35 /* AuDbg("here\n"); */
36 au_cache_free_hnotify(hn);
37 smp_mb__before_atomic(); /* for atomic64_dec */
38 if (atomic64_dec_and_test(&au_hfsn_ifree))
39 wake_up(&au_hfsn_wq);
40 }
41
42 static int au_hfsn_alloc(struct au_hinode *hinode)
43 {
44 int err;
45 struct au_hnotify *hn;
46 struct super_block *sb;
47 struct au_branch *br;
48 struct fsnotify_mark *mark;
49 aufs_bindex_t bindex;
50
51 hn = hinode->hi_notify;
52 sb = hn->hn_aufs_inode->i_sb;
53 bindex = au_br_index(sb, hinode->hi_id);
54 br = au_sbr(sb, bindex);
55 AuDebugOn(!br->br_hfsn);
56
57 mark = &hn->hn_mark;
58 fsnotify_init_mark(mark, br->br_hfsn->hfsn_group);
59 mark->mask = AuHfsnMask;
60 /*
61 * by udba rename or rmdir, aufs assign a new inode to the known
62 * h_inode, so specify 1 to allow dups.
63 */
64 lockdep_off();
65 err = fsnotify_add_inode_mark(mark, hinode->hi_inode, /*allow_dups*/1);
66 lockdep_on();
67
68 return err;
69 }
70
71 static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn)
72 {
73 struct fsnotify_mark *mark;
74 unsigned long long ull;
75 struct fsnotify_group *group;
76
77 ull = atomic64_inc_return(&au_hfsn_ifree);
78 BUG_ON(!ull);
79
80 mark = &hn->hn_mark;
81 spin_lock(&mark->lock);
82 group = mark->group;
83 fsnotify_get_group(group);
84 spin_unlock(&mark->lock);
85 lockdep_off();
86 fsnotify_destroy_mark(mark, group);
87 fsnotify_put_mark(mark);
88 fsnotify_put_group(group);
89 lockdep_on();
90
91 /* free hn by myself */
92 return 0;
93 }
94
95 /* ---------------------------------------------------------------------- */
96
97 static void au_hfsn_ctl(struct au_hinode *hinode, int do_set)
98 {
99 struct fsnotify_mark *mark;
100
101 mark = &hinode->hi_notify->hn_mark;
102 spin_lock(&mark->lock);
103 if (do_set) {
104 AuDebugOn(mark->mask & AuHfsnMask);
105 mark->mask |= AuHfsnMask;
106 } else {
107 AuDebugOn(!(mark->mask & AuHfsnMask));
108 mark->mask &= ~AuHfsnMask;
109 }
110 spin_unlock(&mark->lock);
111 /* fsnotify_recalc_inode_mask(hinode->hi_inode); */
112 }
113
114 /* ---------------------------------------------------------------------- */
115
116 /* #define AuDbgHnotify */
117 #ifdef AuDbgHnotify
118 static char *au_hfsn_name(u32 mask)
119 {
120 #ifdef CONFIG_AUFS_DEBUG
121 #define test_ret(flag) \
122 do { \
123 if (mask & flag) \
124 return #flag; \
125 } while (0)
126 test_ret(FS_ACCESS);
127 test_ret(FS_MODIFY);
128 test_ret(FS_ATTRIB);
129 test_ret(FS_CLOSE_WRITE);
130 test_ret(FS_CLOSE_NOWRITE);
131 test_ret(FS_OPEN);
132 test_ret(FS_MOVED_FROM);
133 test_ret(FS_MOVED_TO);
134 test_ret(FS_CREATE);
135 test_ret(FS_DELETE);
136 test_ret(FS_DELETE_SELF);
137 test_ret(FS_MOVE_SELF);
138 test_ret(FS_UNMOUNT);
139 test_ret(FS_Q_OVERFLOW);
140 test_ret(FS_IN_IGNORED);
141 test_ret(FS_ISDIR);
142 test_ret(FS_IN_ONESHOT);
143 test_ret(FS_EVENT_ON_CHILD);
144 return "";
145 #undef test_ret
146 #else
147 return "??";
148 #endif
149 }
150 #endif
151
152 /* ---------------------------------------------------------------------- */
153
154 static void au_hfsn_free_group(struct fsnotify_group *group)
155 {
156 struct au_br_hfsnotify *hfsn = group->private;
157
158 /* AuDbg("here\n"); */
159 au_kfree_try_rcu(hfsn);
160 }
161
162 static int au_hfsn_handle_event(struct fsnotify_group *group,
163 u32 mask, const void *data, int data_type,
164 struct inode *dir,
165 const struct qstr *file_name, u32 cookie,
166 struct fsnotify_iter_info *iter_info)
167 {
168 int err;
169 struct au_hnotify *hnotify;
170 struct inode *h_dir, *h_inode;
171 struct fsnotify_mark *inode_mark;
172
173 AuDebugOn(data_type != FSNOTIFY_EVENT_INODE);
174
175 err = 0;
176 /* if FS_UNMOUNT happens, there must be another bug */
177 AuDebugOn(mask & FS_UNMOUNT);
178 if (mask & (FS_IN_IGNORED | FS_UNMOUNT))
179 goto out;
180
181 h_dir = dir;
182 h_inode = NULL;
183 #ifdef AuDbgHnotify
184 au_debug_on();
185 if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1
186 || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) {
187 AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n",
188 h_dir->i_ino, mask, au_hfsn_name(mask),
189 AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0);
190 /* WARN_ON(1); */
191 }
192 au_debug_off();
193 #endif
194
195 inode_mark = fsnotify_iter_inode_mark(iter_info);
196 AuDebugOn(!inode_mark);
197 hnotify = container_of(inode_mark, struct au_hnotify, hn_mark);
198 err = au_hnotify(h_dir, hnotify, mask, file_name, h_inode);
199
200 out:
201 return err;
202 }
203
204 static struct fsnotify_ops au_hfsn_ops = {
205 .handle_event = au_hfsn_handle_event,
206 .free_group_priv = au_hfsn_free_group,
207 .free_mark = au_hfsn_free_mark
208 };
209
210 /* ---------------------------------------------------------------------- */
211
212 static void au_hfsn_fin_br(struct au_branch *br)
213 {
214 struct au_br_hfsnotify *hfsn;
215
216 hfsn = br->br_hfsn;
217 if (hfsn) {
218 lockdep_off();
219 fsnotify_put_group(hfsn->hfsn_group);
220 lockdep_on();
221 }
222 }
223
224 static int au_hfsn_init_br(struct au_branch *br, int perm)
225 {
226 int err;
227 struct fsnotify_group *group;
228 struct au_br_hfsnotify *hfsn;
229
230 err = 0;
231 br->br_hfsn = NULL;
232 if (!au_br_hnotifyable(perm))
233 goto out;
234
235 err = -ENOMEM;
236 hfsn = kmalloc(sizeof(*hfsn), GFP_NOFS);
237 if (unlikely(!hfsn))
238 goto out;
239
240 err = 0;
241 group = fsnotify_alloc_group(&au_hfsn_ops);
242 if (IS_ERR(group)) {
243 err = PTR_ERR(group);
244 pr_err("fsnotify_alloc_group() failed, %d\n", err);
245 goto out_hfsn;
246 }
247
248 group->private = hfsn;
249 hfsn->hfsn_group = group;
250 br->br_hfsn = hfsn;
251 goto out; /* success */
252
253 out_hfsn:
254 au_kfree_try_rcu(hfsn);
255 out:
256 return err;
257 }
258
259 static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm)
260 {
261 int err;
262
263 err = 0;
264 if (!br->br_hfsn)
265 err = au_hfsn_init_br(br, perm);
266
267 return err;
268 }
269
270 /* ---------------------------------------------------------------------- */
271
272 static void au_hfsn_fin(void)
273 {
274 AuDbg("au_hfsn_ifree %lld\n", (long long)atomic64_read(&au_hfsn_ifree));
275 wait_event(au_hfsn_wq, !atomic64_read(&au_hfsn_ifree));
276 }
277
278 const struct au_hnotify_op au_hnotify_op = {
279 .ctl = au_hfsn_ctl,
280 .alloc = au_hfsn_alloc,
281 .free = au_hfsn_free,
282
283 .fin = au_hfsn_fin,
284
285 .reset_br = au_hfsn_reset_br,
286 .fin_br = au_hfsn_fin_br,
287 .init_br = au_hfsn_init_br
288 };