]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - fs/aufs/dcsub.c
UBUNTU: SAUCE: AUFS
[mirror_ubuntu-jammy-kernel.git] / fs / aufs / dcsub.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 * sub-routines for dentry cache
21 */
22
23 #include "aufs.h"
24
25 static void au_dpage_free(struct au_dpage *dpage)
26 {
27 int i;
28 struct dentry **p;
29
30 p = dpage->dentries;
31 for (i = 0; i < dpage->ndentry; i++)
32 dput(*p++);
33 free_page((unsigned long)dpage->dentries);
34 }
35
36 int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp)
37 {
38 int err;
39 void *p;
40
41 err = -ENOMEM;
42 dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp);
43 if (unlikely(!dpages->dpages))
44 goto out;
45
46 p = (void *)__get_free_page(gfp);
47 if (unlikely(!p))
48 goto out_dpages;
49
50 dpages->dpages[0].ndentry = 0;
51 dpages->dpages[0].dentries = p;
52 dpages->ndpage = 1;
53 return 0; /* success */
54
55 out_dpages:
56 au_kfree_try_rcu(dpages->dpages);
57 out:
58 return err;
59 }
60
61 void au_dpages_free(struct au_dcsub_pages *dpages)
62 {
63 int i;
64 struct au_dpage *p;
65
66 p = dpages->dpages;
67 for (i = 0; i < dpages->ndpage; i++)
68 au_dpage_free(p++);
69 au_kfree_try_rcu(dpages->dpages);
70 }
71
72 static int au_dpages_append(struct au_dcsub_pages *dpages,
73 struct dentry *dentry, gfp_t gfp)
74 {
75 int err, sz;
76 struct au_dpage *dpage;
77 void *p;
78
79 dpage = dpages->dpages + dpages->ndpage - 1;
80 sz = PAGE_SIZE / sizeof(dentry);
81 if (unlikely(dpage->ndentry >= sz)) {
82 AuLabel(new dpage);
83 err = -ENOMEM;
84 sz = dpages->ndpage * sizeof(*dpages->dpages);
85 p = au_kzrealloc(dpages->dpages, sz,
86 sz + sizeof(*dpages->dpages), gfp,
87 /*may_shrink*/0);
88 if (unlikely(!p))
89 goto out;
90
91 dpages->dpages = p;
92 dpage = dpages->dpages + dpages->ndpage;
93 p = (void *)__get_free_page(gfp);
94 if (unlikely(!p))
95 goto out;
96
97 dpage->ndentry = 0;
98 dpage->dentries = p;
99 dpages->ndpage++;
100 }
101
102 AuDebugOn(au_dcount(dentry) <= 0);
103 dpage->dentries[dpage->ndentry++] = dget_dlock(dentry);
104 return 0; /* success */
105
106 out:
107 return err;
108 }
109
110 /* todo: BAD approach */
111 /* copied from linux/fs/dcache.c */
112 enum d_walk_ret {
113 D_WALK_CONTINUE,
114 D_WALK_QUIT,
115 D_WALK_NORETRY,
116 D_WALK_SKIP,
117 };
118
119 extern void d_walk(struct dentry *parent, void *data,
120 enum d_walk_ret (*enter)(void *, struct dentry *));
121
122 struct ac_dpages_arg {
123 int err;
124 struct au_dcsub_pages *dpages;
125 struct super_block *sb;
126 au_dpages_test test;
127 void *arg;
128 };
129
130 static enum d_walk_ret au_call_dpages_append(void *_arg, struct dentry *dentry)
131 {
132 enum d_walk_ret ret;
133 struct ac_dpages_arg *arg = _arg;
134
135 ret = D_WALK_CONTINUE;
136 if (dentry->d_sb == arg->sb
137 && !IS_ROOT(dentry)
138 && au_dcount(dentry) > 0
139 && au_di(dentry)
140 && (!arg->test || arg->test(dentry, arg->arg))) {
141 arg->err = au_dpages_append(arg->dpages, dentry, GFP_ATOMIC);
142 if (unlikely(arg->err))
143 ret = D_WALK_QUIT;
144 }
145
146 return ret;
147 }
148
149 int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,
150 au_dpages_test test, void *arg)
151 {
152 struct ac_dpages_arg args = {
153 .err = 0,
154 .dpages = dpages,
155 .sb = root->d_sb,
156 .test = test,
157 .arg = arg
158 };
159
160 d_walk(root, &args, au_call_dpages_append);
161
162 return args.err;
163 }
164
165 int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry,
166 int do_include, au_dpages_test test, void *arg)
167 {
168 int err;
169
170 err = 0;
171 write_seqlock(&rename_lock);
172 spin_lock(&dentry->d_lock);
173 if (do_include
174 && au_dcount(dentry) > 0
175 && (!test || test(dentry, arg)))
176 err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
177 spin_unlock(&dentry->d_lock);
178 if (unlikely(err))
179 goto out;
180
181 /*
182 * RCU for vfsmount is unnecessary since this is a traverse in a single
183 * mount
184 */
185 while (!IS_ROOT(dentry)) {
186 dentry = dentry->d_parent; /* rename_lock is locked */
187 spin_lock(&dentry->d_lock);
188 if (au_dcount(dentry) > 0
189 && (!test || test(dentry, arg)))
190 err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
191 spin_unlock(&dentry->d_lock);
192 if (unlikely(err))
193 break;
194 }
195
196 out:
197 write_sequnlock(&rename_lock);
198 return err;
199 }
200
201 static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg)
202 {
203 return au_di(dentry) && dentry->d_sb == arg;
204 }
205
206 int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages,
207 struct dentry *dentry, int do_include)
208 {
209 return au_dcsub_pages_rev(dpages, dentry, do_include,
210 au_dcsub_dpages_aufs, dentry->d_sb);
211 }
212
213 int au_test_subdir(struct dentry *d1, struct dentry *d2)
214 {
215 struct path path[2] = {
216 {
217 .dentry = d1
218 },
219 {
220 .dentry = d2
221 }
222 };
223
224 return path_is_under(path + 0, path + 1);
225 }