]>
Commit | Line | Data |
---|---|---|
e14748e8 SF |
1 | /* |
2 | * Copyright (C) 2005-2016 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 | * debugfs interface | |
20 | */ | |
21 | ||
22 | #include <linux/debugfs.h> | |
23 | #include "aufs.h" | |
24 | ||
25 | #ifndef CONFIG_SYSFS | |
26 | #error DEBUG_FS depends upon SYSFS | |
27 | #endif | |
28 | ||
29 | static struct dentry *dbgaufs; | |
30 | static const mode_t dbgaufs_mode = S_IRUSR | S_IRGRP | S_IROTH; | |
31 | ||
32 | /* 20 is max digits length of ulong 64 */ | |
33 | struct dbgaufs_arg { | |
34 | int n; | |
35 | char a[20 * 4]; | |
36 | }; | |
37 | ||
38 | /* | |
39 | * common function for all XINO files | |
40 | */ | |
41 | static int dbgaufs_xi_release(struct inode *inode __maybe_unused, | |
42 | struct file *file) | |
43 | { | |
44 | au_delayed_kfree(file->private_data); | |
45 | return 0; | |
46 | } | |
47 | ||
48 | static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt) | |
49 | { | |
50 | int err; | |
51 | struct kstat st; | |
52 | struct dbgaufs_arg *p; | |
53 | ||
54 | err = -ENOMEM; | |
55 | p = kmalloc(sizeof(*p), GFP_NOFS); | |
56 | if (unlikely(!p)) | |
57 | goto out; | |
58 | ||
59 | err = 0; | |
60 | p->n = 0; | |
61 | file->private_data = p; | |
62 | if (!xf) | |
63 | goto out; | |
64 | ||
65 | err = vfs_getattr(&xf->f_path, &st); | |
66 | if (!err) { | |
67 | if (do_fcnt) | |
68 | p->n = snprintf | |
69 | (p->a, sizeof(p->a), "%ld, %llux%lu %lld\n", | |
70 | (long)file_count(xf), st.blocks, st.blksize, | |
71 | (long long)st.size); | |
72 | else | |
73 | p->n = snprintf(p->a, sizeof(p->a), "%llux%lu %lld\n", | |
74 | st.blocks, st.blksize, | |
75 | (long long)st.size); | |
76 | AuDebugOn(p->n >= sizeof(p->a)); | |
77 | } else { | |
78 | p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err); | |
79 | err = 0; | |
80 | } | |
81 | ||
82 | out: | |
83 | return err; | |
84 | ||
85 | } | |
86 | ||
87 | static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf, | |
88 | size_t count, loff_t *ppos) | |
89 | { | |
90 | struct dbgaufs_arg *p; | |
91 | ||
92 | p = file->private_data; | |
93 | return simple_read_from_buffer(buf, count, ppos, p->a, p->n); | |
94 | } | |
95 | ||
96 | /* ---------------------------------------------------------------------- */ | |
97 | ||
98 | struct dbgaufs_plink_arg { | |
99 | int n; | |
100 | char a[]; | |
101 | }; | |
102 | ||
103 | static int dbgaufs_plink_release(struct inode *inode __maybe_unused, | |
104 | struct file *file) | |
105 | { | |
106 | au_delayed_free_page((unsigned long)file->private_data); | |
107 | return 0; | |
108 | } | |
109 | ||
110 | static int dbgaufs_plink_open(struct inode *inode, struct file *file) | |
111 | { | |
112 | int err, i, limit; | |
113 | unsigned long n, sum; | |
114 | struct dbgaufs_plink_arg *p; | |
115 | struct au_sbinfo *sbinfo; | |
116 | struct super_block *sb; | |
117 | struct au_sphlhead *sphl; | |
118 | ||
119 | err = -ENOMEM; | |
120 | p = (void *)get_zeroed_page(GFP_NOFS); | |
121 | if (unlikely(!p)) | |
122 | goto out; | |
123 | ||
124 | err = -EFBIG; | |
125 | sbinfo = inode->i_private; | |
126 | sb = sbinfo->si_sb; | |
127 | si_noflush_read_lock(sb); | |
128 | if (au_opt_test(au_mntflags(sb), PLINK)) { | |
129 | limit = PAGE_SIZE - sizeof(p->n); | |
130 | ||
131 | /* the number of buckets */ | |
132 | n = snprintf(p->a + p->n, limit, "%d\n", AuPlink_NHASH); | |
133 | p->n += n; | |
134 | limit -= n; | |
135 | ||
136 | sum = 0; | |
137 | for (i = 0, sphl = sbinfo->si_plink; | |
138 | i < AuPlink_NHASH; | |
139 | i++, sphl++) { | |
140 | n = au_sphl_count(sphl); | |
141 | sum += n; | |
142 | ||
143 | n = snprintf(p->a + p->n, limit, "%lu ", n); | |
144 | p->n += n; | |
145 | limit -= n; | |
146 | if (unlikely(limit <= 0)) | |
147 | goto out_free; | |
148 | } | |
149 | p->a[p->n - 1] = '\n'; | |
150 | ||
151 | /* the sum of plinks */ | |
152 | n = snprintf(p->a + p->n, limit, "%lu\n", sum); | |
153 | p->n += n; | |
154 | limit -= n; | |
155 | if (unlikely(limit <= 0)) | |
156 | goto out_free; | |
157 | } else { | |
158 | #define str "1\n0\n0\n" | |
159 | p->n = sizeof(str) - 1; | |
160 | strcpy(p->a, str); | |
161 | #undef str | |
162 | } | |
163 | si_read_unlock(sb); | |
164 | ||
165 | err = 0; | |
166 | file->private_data = p; | |
167 | goto out; /* success */ | |
168 | ||
169 | out_free: | |
170 | au_delayed_free_page((unsigned long)p); | |
171 | out: | |
172 | return err; | |
173 | } | |
174 | ||
175 | static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf, | |
176 | size_t count, loff_t *ppos) | |
177 | { | |
178 | struct dbgaufs_plink_arg *p; | |
179 | ||
180 | p = file->private_data; | |
181 | return simple_read_from_buffer(buf, count, ppos, p->a, p->n); | |
182 | } | |
183 | ||
184 | static const struct file_operations dbgaufs_plink_fop = { | |
185 | .owner = THIS_MODULE, | |
186 | .open = dbgaufs_plink_open, | |
187 | .release = dbgaufs_plink_release, | |
188 | .read = dbgaufs_plink_read | |
189 | }; | |
190 | ||
191 | /* ---------------------------------------------------------------------- */ | |
192 | ||
193 | static int dbgaufs_xib_open(struct inode *inode, struct file *file) | |
194 | { | |
195 | int err; | |
196 | struct au_sbinfo *sbinfo; | |
197 | struct super_block *sb; | |
198 | ||
199 | sbinfo = inode->i_private; | |
200 | sb = sbinfo->si_sb; | |
201 | si_noflush_read_lock(sb); | |
202 | err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0); | |
203 | si_read_unlock(sb); | |
204 | return err; | |
205 | } | |
206 | ||
207 | static const struct file_operations dbgaufs_xib_fop = { | |
208 | .owner = THIS_MODULE, | |
209 | .open = dbgaufs_xib_open, | |
210 | .release = dbgaufs_xi_release, | |
211 | .read = dbgaufs_xi_read | |
212 | }; | |
213 | ||
214 | /* ---------------------------------------------------------------------- */ | |
215 | ||
216 | #define DbgaufsXi_PREFIX "xi" | |
217 | ||
218 | static int dbgaufs_xino_open(struct inode *inode, struct file *file) | |
219 | { | |
220 | int err; | |
221 | long l; | |
222 | struct au_sbinfo *sbinfo; | |
223 | struct super_block *sb; | |
224 | struct file *xf; | |
225 | struct qstr *name; | |
226 | ||
227 | err = -ENOENT; | |
228 | xf = NULL; | |
229 | name = &file->f_path.dentry->d_name; | |
230 | if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX) | |
231 | || memcmp(name->name, DbgaufsXi_PREFIX, | |
232 | sizeof(DbgaufsXi_PREFIX) - 1))) | |
233 | goto out; | |
234 | err = kstrtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); | |
235 | if (unlikely(err)) | |
236 | goto out; | |
237 | ||
238 | sbinfo = inode->i_private; | |
239 | sb = sbinfo->si_sb; | |
240 | si_noflush_read_lock(sb); | |
241 | if (l <= au_sbbot(sb)) { | |
242 | xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file; | |
243 | err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1); | |
244 | } else | |
245 | err = -ENOENT; | |
246 | si_read_unlock(sb); | |
247 | ||
248 | out: | |
249 | return err; | |
250 | } | |
251 | ||
252 | static const struct file_operations dbgaufs_xino_fop = { | |
253 | .owner = THIS_MODULE, | |
254 | .open = dbgaufs_xino_open, | |
255 | .release = dbgaufs_xi_release, | |
256 | .read = dbgaufs_xi_read | |
257 | }; | |
258 | ||
259 | void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) | |
260 | { | |
261 | aufs_bindex_t bbot; | |
262 | struct au_branch *br; | |
263 | struct au_xino_file *xi; | |
264 | ||
265 | if (!au_sbi(sb)->si_dbgaufs) | |
266 | return; | |
267 | ||
268 | bbot = au_sbbot(sb); | |
269 | for (; bindex <= bbot; bindex++) { | |
270 | br = au_sbr(sb, bindex); | |
271 | xi = &br->br_xino; | |
272 | /* debugfs acquires the parent i_mutex */ | |
273 | lockdep_off(); | |
274 | debugfs_remove(xi->xi_dbgaufs); | |
275 | lockdep_on(); | |
276 | xi->xi_dbgaufs = NULL; | |
277 | } | |
278 | } | |
279 | ||
280 | void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) | |
281 | { | |
282 | struct au_sbinfo *sbinfo; | |
283 | struct dentry *parent; | |
284 | struct au_branch *br; | |
285 | struct au_xino_file *xi; | |
286 | aufs_bindex_t bbot; | |
287 | char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */ | |
288 | ||
289 | sbinfo = au_sbi(sb); | |
290 | parent = sbinfo->si_dbgaufs; | |
291 | if (!parent) | |
292 | return; | |
293 | ||
294 | bbot = au_sbbot(sb); | |
295 | for (; bindex <= bbot; bindex++) { | |
296 | snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); | |
297 | br = au_sbr(sb, bindex); | |
298 | xi = &br->br_xino; | |
299 | AuDebugOn(xi->xi_dbgaufs); | |
300 | /* debugfs acquires the parent i_mutex */ | |
301 | lockdep_off(); | |
302 | xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, | |
303 | sbinfo, &dbgaufs_xino_fop); | |
304 | lockdep_on(); | |
305 | /* ignore an error */ | |
306 | if (unlikely(!xi->xi_dbgaufs)) | |
307 | AuWarn1("failed %s under debugfs\n", name); | |
308 | } | |
309 | } | |
310 | ||
311 | /* ---------------------------------------------------------------------- */ | |
312 | ||
313 | #ifdef CONFIG_AUFS_EXPORT | |
314 | static int dbgaufs_xigen_open(struct inode *inode, struct file *file) | |
315 | { | |
316 | int err; | |
317 | struct au_sbinfo *sbinfo; | |
318 | struct super_block *sb; | |
319 | ||
320 | sbinfo = inode->i_private; | |
321 | sb = sbinfo->si_sb; | |
322 | si_noflush_read_lock(sb); | |
323 | err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0); | |
324 | si_read_unlock(sb); | |
325 | return err; | |
326 | } | |
327 | ||
328 | static const struct file_operations dbgaufs_xigen_fop = { | |
329 | .owner = THIS_MODULE, | |
330 | .open = dbgaufs_xigen_open, | |
331 | .release = dbgaufs_xi_release, | |
332 | .read = dbgaufs_xi_read | |
333 | }; | |
334 | ||
335 | static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) | |
336 | { | |
337 | int err; | |
338 | ||
339 | /* | |
340 | * This function is a dynamic '__init' function actually, | |
341 | * so the tiny check for si_rwsem is unnecessary. | |
342 | */ | |
343 | /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ | |
344 | ||
345 | err = -EIO; | |
346 | sbinfo->si_dbgaufs_xigen = debugfs_create_file | |
347 | ("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, | |
348 | &dbgaufs_xigen_fop); | |
349 | if (sbinfo->si_dbgaufs_xigen) | |
350 | err = 0; | |
351 | ||
352 | return err; | |
353 | } | |
354 | #else | |
355 | static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) | |
356 | { | |
357 | return 0; | |
358 | } | |
359 | #endif /* CONFIG_AUFS_EXPORT */ | |
360 | ||
361 | /* ---------------------------------------------------------------------- */ | |
362 | ||
363 | void dbgaufs_si_fin(struct au_sbinfo *sbinfo) | |
364 | { | |
365 | /* | |
366 | * This function is a dynamic '__fin' function actually, | |
367 | * so the tiny check for si_rwsem is unnecessary. | |
368 | */ | |
369 | /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ | |
370 | ||
371 | debugfs_remove_recursive(sbinfo->si_dbgaufs); | |
372 | sbinfo->si_dbgaufs = NULL; | |
373 | kobject_put(&sbinfo->si_kobj); | |
374 | } | |
375 | ||
376 | int dbgaufs_si_init(struct au_sbinfo *sbinfo) | |
377 | { | |
378 | int err; | |
379 | char name[SysaufsSiNameLen]; | |
380 | ||
381 | /* | |
382 | * This function is a dynamic '__init' function actually, | |
383 | * so the tiny check for si_rwsem is unnecessary. | |
384 | */ | |
385 | /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ | |
386 | ||
387 | err = -ENOENT; | |
388 | if (!dbgaufs) { | |
389 | AuErr1("/debug/aufs is uninitialized\n"); | |
390 | goto out; | |
391 | } | |
392 | ||
393 | err = -EIO; | |
394 | sysaufs_name(sbinfo, name); | |
395 | sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs); | |
396 | if (unlikely(!sbinfo->si_dbgaufs)) | |
397 | goto out; | |
398 | kobject_get(&sbinfo->si_kobj); | |
399 | ||
400 | sbinfo->si_dbgaufs_xib = debugfs_create_file | |
401 | ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, | |
402 | &dbgaufs_xib_fop); | |
403 | if (unlikely(!sbinfo->si_dbgaufs_xib)) | |
404 | goto out_dir; | |
405 | ||
406 | sbinfo->si_dbgaufs_plink = debugfs_create_file | |
407 | ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, | |
408 | &dbgaufs_plink_fop); | |
409 | if (unlikely(!sbinfo->si_dbgaufs_plink)) | |
410 | goto out_dir; | |
411 | ||
412 | err = dbgaufs_xigen_init(sbinfo); | |
413 | if (!err) | |
414 | goto out; /* success */ | |
415 | ||
416 | out_dir: | |
417 | dbgaufs_si_fin(sbinfo); | |
418 | out: | |
419 | return err; | |
420 | } | |
421 | ||
422 | /* ---------------------------------------------------------------------- */ | |
423 | ||
424 | void dbgaufs_fin(void) | |
425 | { | |
426 | debugfs_remove(dbgaufs); | |
427 | } | |
428 | ||
429 | int __init dbgaufs_init(void) | |
430 | { | |
431 | int err; | |
432 | ||
433 | err = -EIO; | |
434 | dbgaufs = debugfs_create_dir(AUFS_NAME, NULL); | |
435 | if (dbgaufs) | |
436 | err = 0; | |
437 | return err; | |
438 | } |