]>
Commit | Line | Data |
---|---|---|
c088e31d SF |
1 | /* |
2 | * Copyright (C) 2005-2017 Junjiro R. Okajima | |
3 | * | |
4 | * This program, aufs is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | /* | |
19 | * sysfs interface | |
20 | */ | |
21 | ||
22 | #include <linux/compat.h> | |
23 | #include <linux/seq_file.h> | |
24 | #include "aufs.h" | |
25 | ||
26 | #ifdef CONFIG_AUFS_FS_MODULE | |
27 | /* this entry violates the "one line per file" policy of sysfs */ | |
28 | static ssize_t config_show(struct kobject *kobj, struct kobj_attribute *attr, | |
29 | char *buf) | |
30 | { | |
31 | ssize_t err; | |
32 | static char *conf = | |
33 | /* this file is generated at compiling */ | |
34 | #include "conf.str" | |
35 | ; | |
36 | ||
37 | err = snprintf(buf, PAGE_SIZE, conf); | |
38 | if (unlikely(err >= PAGE_SIZE)) | |
39 | err = -EFBIG; | |
40 | return err; | |
41 | } | |
42 | ||
43 | static struct kobj_attribute au_config_attr = __ATTR_RO(config); | |
44 | #endif | |
45 | ||
46 | static struct attribute *au_attr[] = { | |
47 | #ifdef CONFIG_AUFS_FS_MODULE | |
48 | &au_config_attr.attr, | |
49 | #endif | |
50 | NULL, /* need to NULL terminate the list of attributes */ | |
51 | }; | |
52 | ||
53 | static struct attribute_group sysaufs_attr_group_body = { | |
54 | .attrs = au_attr | |
55 | }; | |
56 | ||
57 | struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body; | |
58 | ||
59 | /* ---------------------------------------------------------------------- */ | |
60 | ||
61 | int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) | |
62 | { | |
63 | int err; | |
64 | ||
65 | SiMustAnyLock(sb); | |
66 | ||
67 | err = 0; | |
68 | if (au_opt_test(au_mntflags(sb), XINO)) { | |
69 | err = au_xino_path(seq, au_sbi(sb)->si_xib); | |
70 | seq_putc(seq, '\n'); | |
71 | } | |
72 | return err; | |
73 | } | |
74 | ||
75 | /* | |
76 | * the lifetime of branch is independent from the entry under sysfs. | |
77 | * sysfs handles the lifetime of the entry, and never call ->show() after it is | |
78 | * unlinked. | |
79 | */ | |
80 | static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, | |
81 | aufs_bindex_t bindex, int idx) | |
82 | { | |
83 | int err; | |
84 | struct path path; | |
85 | struct dentry *root; | |
86 | struct au_branch *br; | |
87 | au_br_perm_str_t perm; | |
88 | ||
89 | AuDbg("b%d\n", bindex); | |
90 | ||
91 | err = 0; | |
92 | root = sb->s_root; | |
93 | di_read_lock_parent(root, !AuLock_IR); | |
94 | br = au_sbr(sb, bindex); | |
95 | ||
96 | switch (idx) { | |
97 | case AuBrSysfs_BR: | |
98 | path.mnt = au_br_mnt(br); | |
99 | path.dentry = au_h_dptr(root, bindex); | |
100 | err = au_seq_path(seq, &path); | |
101 | if (!err) { | |
102 | au_optstr_br_perm(&perm, br->br_perm); | |
103 | seq_printf(seq, "=%s\n", perm.a); | |
104 | } | |
105 | break; | |
106 | case AuBrSysfs_BRID: | |
107 | seq_printf(seq, "%d\n", br->br_id); | |
108 | break; | |
109 | } | |
110 | di_read_unlock(root, !AuLock_IR); | |
111 | if (unlikely(err || seq_has_overflowed(seq))) | |
112 | err = -E2BIG; | |
113 | ||
114 | return err; | |
115 | } | |
116 | ||
117 | /* ---------------------------------------------------------------------- */ | |
118 | ||
119 | static struct seq_file *au_seq(char *p, ssize_t len) | |
120 | { | |
121 | struct seq_file *seq; | |
122 | ||
123 | seq = kzalloc(sizeof(*seq), GFP_NOFS); | |
124 | if (seq) { | |
125 | /* mutex_init(&seq.lock); */ | |
126 | seq->buf = p; | |
127 | seq->size = len; | |
128 | return seq; /* success */ | |
129 | } | |
130 | ||
131 | seq = ERR_PTR(-ENOMEM); | |
132 | return seq; | |
133 | } | |
134 | ||
135 | #define SysaufsBr_PREFIX "br" | |
136 | #define SysaufsBrid_PREFIX "brid" | |
137 | ||
138 | /* todo: file size may exceed PAGE_SIZE */ | |
139 | ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, | |
140 | char *buf) | |
141 | { | |
142 | ssize_t err; | |
143 | int idx; | |
144 | long l; | |
145 | aufs_bindex_t bbot; | |
146 | struct au_sbinfo *sbinfo; | |
147 | struct super_block *sb; | |
148 | struct seq_file *seq; | |
149 | char *name; | |
150 | struct attribute **cattr; | |
151 | ||
152 | sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); | |
153 | sb = sbinfo->si_sb; | |
154 | ||
155 | /* | |
156 | * prevent a race condition between sysfs and aufs. | |
157 | * for instance, sysfs_file_read() calls sysfs_get_active_two() which | |
158 | * prohibits maintaining the sysfs entries. | |
159 | * hew we acquire read lock after sysfs_get_active_two(). | |
160 | * on the other hand, the remount process may maintain the sysfs/aufs | |
161 | * entries after acquiring write lock. | |
162 | * it can cause a deadlock. | |
163 | * simply we gave up processing read here. | |
164 | */ | |
165 | err = -EBUSY; | |
166 | if (unlikely(!si_noflush_read_trylock(sb))) | |
167 | goto out; | |
168 | ||
169 | seq = au_seq(buf, PAGE_SIZE); | |
170 | err = PTR_ERR(seq); | |
171 | if (IS_ERR(seq)) | |
172 | goto out_unlock; | |
173 | ||
174 | name = (void *)attr->name; | |
175 | cattr = sysaufs_si_attrs; | |
176 | while (*cattr) { | |
177 | if (!strcmp(name, (*cattr)->name)) { | |
178 | err = container_of(*cattr, struct sysaufs_si_attr, attr) | |
179 | ->show(seq, sb); | |
180 | goto out_seq; | |
181 | } | |
182 | cattr++; | |
183 | } | |
184 | ||
185 | if (!strncmp(name, SysaufsBrid_PREFIX, | |
186 | sizeof(SysaufsBrid_PREFIX) - 1)) { | |
187 | idx = AuBrSysfs_BRID; | |
188 | name += sizeof(SysaufsBrid_PREFIX) - 1; | |
189 | } else if (!strncmp(name, SysaufsBr_PREFIX, | |
190 | sizeof(SysaufsBr_PREFIX) - 1)) { | |
191 | idx = AuBrSysfs_BR; | |
192 | name += sizeof(SysaufsBr_PREFIX) - 1; | |
193 | } else | |
194 | BUG(); | |
195 | ||
196 | err = kstrtol(name, 10, &l); | |
197 | if (!err) { | |
198 | bbot = au_sbbot(sb); | |
199 | if (l <= bbot) | |
200 | err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx); | |
201 | else | |
202 | err = -ENOENT; | |
203 | } | |
204 | ||
205 | out_seq: | |
206 | if (!err) { | |
207 | err = seq->count; | |
208 | /* sysfs limit */ | |
209 | if (unlikely(err == PAGE_SIZE)) | |
210 | err = -EFBIG; | |
211 | } | |
212 | kfree(seq); | |
213 | out_unlock: | |
214 | si_read_unlock(sb); | |
215 | out: | |
216 | return err; | |
217 | } | |
218 | ||
219 | /* ---------------------------------------------------------------------- */ | |
220 | ||
221 | static int au_brinfo(struct super_block *sb, union aufs_brinfo __user *arg) | |
222 | { | |
223 | int err; | |
224 | int16_t brid; | |
225 | aufs_bindex_t bindex, bbot; | |
226 | size_t sz; | |
227 | char *buf; | |
228 | struct seq_file *seq; | |
229 | struct au_branch *br; | |
230 | ||
231 | si_read_lock(sb, AuLock_FLUSH); | |
232 | bbot = au_sbbot(sb); | |
233 | err = bbot + 1; | |
234 | if (!arg) | |
235 | goto out; | |
236 | ||
237 | err = -ENOMEM; | |
238 | buf = (void *)__get_free_page(GFP_NOFS); | |
239 | if (unlikely(!buf)) | |
240 | goto out; | |
241 | ||
242 | seq = au_seq(buf, PAGE_SIZE); | |
243 | err = PTR_ERR(seq); | |
244 | if (IS_ERR(seq)) | |
245 | goto out_buf; | |
246 | ||
247 | sz = sizeof(*arg) - offsetof(union aufs_brinfo, path); | |
248 | for (bindex = 0; bindex <= bbot; bindex++, arg++) { | |
249 | err = !access_ok(VERIFY_WRITE, arg, sizeof(*arg)); | |
250 | if (unlikely(err)) | |
251 | break; | |
252 | ||
253 | br = au_sbr(sb, bindex); | |
254 | brid = br->br_id; | |
255 | BUILD_BUG_ON(sizeof(brid) != sizeof(arg->id)); | |
256 | err = __put_user(brid, &arg->id); | |
257 | if (unlikely(err)) | |
258 | break; | |
259 | ||
260 | BUILD_BUG_ON(sizeof(br->br_perm) != sizeof(arg->perm)); | |
261 | err = __put_user(br->br_perm, &arg->perm); | |
262 | if (unlikely(err)) | |
263 | break; | |
264 | ||
265 | err = au_seq_path(seq, &br->br_path); | |
266 | if (unlikely(err)) | |
267 | break; | |
268 | seq_putc(seq, '\0'); | |
269 | if (!seq_has_overflowed(seq)) { | |
270 | err = copy_to_user(arg->path, seq->buf, seq->count); | |
271 | seq->count = 0; | |
272 | if (unlikely(err)) | |
273 | break; | |
274 | } else { | |
275 | err = -E2BIG; | |
276 | goto out_seq; | |
277 | } | |
278 | } | |
279 | if (unlikely(err)) | |
280 | err = -EFAULT; | |
281 | ||
282 | out_seq: | |
283 | kfree(seq); | |
284 | out_buf: | |
285 | free_page((unsigned long)buf); | |
286 | out: | |
287 | si_read_unlock(sb); | |
288 | return err; | |
289 | } | |
290 | ||
291 | long au_brinfo_ioctl(struct file *file, unsigned long arg) | |
292 | { | |
293 | return au_brinfo(file->f_path.dentry->d_sb, (void __user *)arg); | |
294 | } | |
295 | ||
296 | #ifdef CONFIG_COMPAT | |
297 | long au_brinfo_compat_ioctl(struct file *file, unsigned long arg) | |
298 | { | |
299 | return au_brinfo(file->f_path.dentry->d_sb, compat_ptr(arg)); | |
300 | } | |
301 | #endif | |
302 | ||
303 | /* ---------------------------------------------------------------------- */ | |
304 | ||
305 | void sysaufs_br_init(struct au_branch *br) | |
306 | { | |
307 | int i; | |
308 | struct au_brsysfs *br_sysfs; | |
309 | struct attribute *attr; | |
310 | ||
311 | br_sysfs = br->br_sysfs; | |
312 | for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { | |
313 | attr = &br_sysfs->attr; | |
314 | sysfs_attr_init(attr); | |
315 | attr->name = br_sysfs->name; | |
316 | attr->mode = S_IRUGO; | |
317 | br_sysfs++; | |
318 | } | |
319 | } | |
320 | ||
321 | void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) | |
322 | { | |
323 | struct au_branch *br; | |
324 | struct kobject *kobj; | |
325 | struct au_brsysfs *br_sysfs; | |
326 | int i; | |
327 | aufs_bindex_t bbot; | |
328 | ||
329 | dbgaufs_brs_del(sb, bindex); | |
330 | ||
331 | if (!sysaufs_brs) | |
332 | return; | |
333 | ||
334 | kobj = &au_sbi(sb)->si_kobj; | |
335 | bbot = au_sbbot(sb); | |
336 | for (; bindex <= bbot; bindex++) { | |
337 | br = au_sbr(sb, bindex); | |
338 | br_sysfs = br->br_sysfs; | |
339 | for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { | |
340 | sysfs_remove_file(kobj, &br_sysfs->attr); | |
341 | br_sysfs++; | |
342 | } | |
343 | } | |
344 | } | |
345 | ||
346 | void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) | |
347 | { | |
348 | int err, i; | |
349 | aufs_bindex_t bbot; | |
350 | struct kobject *kobj; | |
351 | struct au_branch *br; | |
352 | struct au_brsysfs *br_sysfs; | |
353 | ||
354 | dbgaufs_brs_add(sb, bindex); | |
355 | ||
356 | if (!sysaufs_brs) | |
357 | return; | |
358 | ||
359 | kobj = &au_sbi(sb)->si_kobj; | |
360 | bbot = au_sbbot(sb); | |
361 | for (; bindex <= bbot; bindex++) { | |
362 | br = au_sbr(sb, bindex); | |
363 | br_sysfs = br->br_sysfs; | |
364 | snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name), | |
365 | SysaufsBr_PREFIX "%d", bindex); | |
366 | snprintf(br_sysfs[AuBrSysfs_BRID].name, sizeof(br_sysfs->name), | |
367 | SysaufsBrid_PREFIX "%d", bindex); | |
368 | for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { | |
369 | err = sysfs_create_file(kobj, &br_sysfs->attr); | |
370 | if (unlikely(err)) | |
371 | pr_warn("failed %s under sysfs(%d)\n", | |
372 | br_sysfs->name, err); | |
373 | br_sysfs++; | |
374 | } | |
375 | } | |
376 | } |