]>
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 | * 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 | 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 = vfsub_getattr(&xf->f_path, &st); | |
66 | if (!err) { | |
67 | if (do_fcnt) | |
68 | p->n = snprintf | |
69 | (p->a, sizeof(p->a), "%ld, %llux%u %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%u %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 | 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 hlist_bl_head *hbl; | |
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, hbl = sbinfo->si_plink; i < AuPlink_NHASH; | |
138 | i++, hbl++) { | |
139 | n = au_hbl_count(hbl); | |
140 | sum += n; | |
141 | ||
142 | n = snprintf(p->a + p->n, limit, "%lu ", n); | |
143 | p->n += n; | |
144 | limit -= n; | |
145 | if (unlikely(limit <= 0)) | |
146 | goto out_free; | |
147 | } | |
148 | p->a[p->n - 1] = '\n'; | |
149 | ||
150 | /* the sum of plinks */ | |
151 | n = snprintf(p->a + p->n, limit, "%lu\n", sum); | |
152 | p->n += n; | |
153 | limit -= n; | |
154 | if (unlikely(limit <= 0)) | |
155 | goto out_free; | |
156 | } else { | |
157 | #define str "1\n0\n0\n" | |
158 | p->n = sizeof(str) - 1; | |
159 | strcpy(p->a, str); | |
160 | #undef str | |
161 | } | |
162 | si_read_unlock(sb); | |
163 | ||
164 | err = 0; | |
165 | file->private_data = p; | |
166 | goto out; /* success */ | |
167 | ||
168 | out_free: | |
169 | free_page((unsigned long)p); | |
170 | out: | |
171 | return err; | |
172 | } | |
173 | ||
174 | static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf, | |
175 | size_t count, loff_t *ppos) | |
176 | { | |
177 | struct dbgaufs_plink_arg *p; | |
178 | ||
179 | p = file->private_data; | |
180 | return simple_read_from_buffer(buf, count, ppos, p->a, p->n); | |
181 | } | |
182 | ||
183 | static const struct file_operations dbgaufs_plink_fop = { | |
184 | .owner = THIS_MODULE, | |
185 | .open = dbgaufs_plink_open, | |
186 | .release = dbgaufs_plink_release, | |
187 | .read = dbgaufs_plink_read | |
188 | }; | |
189 | ||
190 | /* ---------------------------------------------------------------------- */ | |
191 | ||
192 | static int dbgaufs_xib_open(struct inode *inode, struct file *file) | |
193 | { | |
194 | int err; | |
195 | struct au_sbinfo *sbinfo; | |
196 | struct super_block *sb; | |
197 | ||
198 | sbinfo = inode->i_private; | |
199 | sb = sbinfo->si_sb; | |
200 | si_noflush_read_lock(sb); | |
201 | err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0); | |
202 | si_read_unlock(sb); | |
203 | return err; | |
204 | } | |
205 | ||
206 | static const struct file_operations dbgaufs_xib_fop = { | |
207 | .owner = THIS_MODULE, | |
208 | .open = dbgaufs_xib_open, | |
209 | .release = dbgaufs_xi_release, | |
210 | .read = dbgaufs_xi_read | |
211 | }; | |
212 | ||
213 | /* ---------------------------------------------------------------------- */ | |
214 | ||
215 | #define DbgaufsXi_PREFIX "xi" | |
216 | ||
217 | static int dbgaufs_xino_open(struct inode *inode, struct file *file) | |
218 | { | |
219 | int err; | |
220 | long l; | |
221 | struct au_sbinfo *sbinfo; | |
222 | struct super_block *sb; | |
223 | struct file *xf; | |
224 | struct qstr *name; | |
225 | ||
226 | err = -ENOENT; | |
227 | xf = NULL; | |
228 | name = &file->f_path.dentry->d_name; | |
229 | if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX) | |
230 | || memcmp(name->name, DbgaufsXi_PREFIX, | |
231 | sizeof(DbgaufsXi_PREFIX) - 1))) | |
232 | goto out; | |
233 | err = kstrtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); | |
234 | if (unlikely(err)) | |
235 | goto out; | |
236 | ||
237 | sbinfo = inode->i_private; | |
238 | sb = sbinfo->si_sb; | |
239 | si_noflush_read_lock(sb); | |
240 | if (l <= au_sbbot(sb)) { | |
241 | xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file; | |
242 | err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1); | |
243 | } else | |
244 | err = -ENOENT; | |
245 | si_read_unlock(sb); | |
246 | ||
247 | out: | |
248 | return err; | |
249 | } | |
250 | ||
251 | static const struct file_operations dbgaufs_xino_fop = { | |
252 | .owner = THIS_MODULE, | |
253 | .open = dbgaufs_xino_open, | |
254 | .release = dbgaufs_xi_release, | |
255 | .read = dbgaufs_xi_read | |
256 | }; | |
257 | ||
258 | void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) | |
259 | { | |
260 | aufs_bindex_t bbot; | |
261 | struct au_branch *br; | |
262 | struct au_xino_file *xi; | |
263 | ||
264 | if (!au_sbi(sb)->si_dbgaufs) | |
265 | return; | |
266 | ||
267 | bbot = au_sbbot(sb); | |
268 | for (; bindex <= bbot; bindex++) { | |
269 | br = au_sbr(sb, bindex); | |
270 | xi = &br->br_xino; | |
271 | /* debugfs acquires the parent i_mutex */ | |
272 | lockdep_off(); | |
273 | debugfs_remove(xi->xi_dbgaufs); | |
274 | lockdep_on(); | |
275 | xi->xi_dbgaufs = NULL; | |
276 | } | |
277 | } | |
278 | ||
279 | void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) | |
280 | { | |
281 | struct au_sbinfo *sbinfo; | |
282 | struct dentry *parent; | |
283 | struct au_branch *br; | |
284 | struct au_xino_file *xi; | |
285 | aufs_bindex_t bbot; | |
286 | char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */ | |
287 | ||
288 | sbinfo = au_sbi(sb); | |
289 | parent = sbinfo->si_dbgaufs; | |
290 | if (!parent) | |
291 | return; | |
292 | ||
293 | bbot = au_sbbot(sb); | |
294 | for (; bindex <= bbot; bindex++) { | |
295 | snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); | |
296 | br = au_sbr(sb, bindex); | |
297 | xi = &br->br_xino; | |
298 | AuDebugOn(xi->xi_dbgaufs); | |
299 | /* debugfs acquires the parent i_mutex */ | |
300 | lockdep_off(); | |
301 | xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, | |
302 | sbinfo, &dbgaufs_xino_fop); | |
303 | lockdep_on(); | |
304 | /* ignore an error */ | |
305 | if (unlikely(!xi->xi_dbgaufs)) | |
306 | AuWarn1("failed %s under debugfs\n", name); | |
307 | } | |
308 | } | |
309 | ||
310 | /* ---------------------------------------------------------------------- */ | |
311 | ||
312 | #ifdef CONFIG_AUFS_EXPORT | |
313 | static int dbgaufs_xigen_open(struct inode *inode, struct file *file) | |
314 | { | |
315 | int err; | |
316 | struct au_sbinfo *sbinfo; | |
317 | struct super_block *sb; | |
318 | ||
319 | sbinfo = inode->i_private; | |
320 | sb = sbinfo->si_sb; | |
321 | si_noflush_read_lock(sb); | |
322 | err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0); | |
323 | si_read_unlock(sb); | |
324 | return err; | |
325 | } | |
326 | ||
327 | static const struct file_operations dbgaufs_xigen_fop = { | |
328 | .owner = THIS_MODULE, | |
329 | .open = dbgaufs_xigen_open, | |
330 | .release = dbgaufs_xi_release, | |
331 | .read = dbgaufs_xi_read | |
332 | }; | |
333 | ||
334 | static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) | |
335 | { | |
336 | int err; | |
337 | ||
338 | /* | |
339 | * This function is a dynamic '__init' function actually, | |
340 | * so the tiny check for si_rwsem is unnecessary. | |
341 | */ | |
342 | /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ | |
343 | ||
344 | err = -EIO; | |
345 | sbinfo->si_dbgaufs_xigen = debugfs_create_file | |
346 | ("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, | |
347 | &dbgaufs_xigen_fop); | |
348 | if (sbinfo->si_dbgaufs_xigen) | |
349 | err = 0; | |
350 | ||
351 | return err; | |
352 | } | |
353 | #else | |
354 | static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) | |
355 | { | |
356 | return 0; | |
357 | } | |
358 | #endif /* CONFIG_AUFS_EXPORT */ | |
359 | ||
360 | /* ---------------------------------------------------------------------- */ | |
361 | ||
362 | void dbgaufs_si_fin(struct au_sbinfo *sbinfo) | |
363 | { | |
364 | /* | |
365 | * This function is a dynamic '__fin' function actually, | |
366 | * so the tiny check for si_rwsem is unnecessary. | |
367 | */ | |
368 | /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ | |
369 | ||
370 | debugfs_remove_recursive(sbinfo->si_dbgaufs); | |
371 | sbinfo->si_dbgaufs = NULL; | |
372 | kobject_put(&sbinfo->si_kobj); | |
373 | } | |
374 | ||
375 | int dbgaufs_si_init(struct au_sbinfo *sbinfo) | |
376 | { | |
377 | int err; | |
378 | char name[SysaufsSiNameLen]; | |
379 | ||
380 | /* | |
381 | * This function is a dynamic '__init' function actually, | |
382 | * so the tiny check for si_rwsem is unnecessary. | |
383 | */ | |
384 | /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ | |
385 | ||
386 | err = -ENOENT; | |
387 | if (!dbgaufs) { | |
388 | AuErr1("/debug/aufs is uninitialized\n"); | |
389 | goto out; | |
390 | } | |
391 | ||
392 | err = -EIO; | |
393 | sysaufs_name(sbinfo, name); | |
394 | sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs); | |
395 | if (unlikely(!sbinfo->si_dbgaufs)) | |
396 | goto out; | |
397 | kobject_get(&sbinfo->si_kobj); | |
398 | ||
399 | sbinfo->si_dbgaufs_xib = debugfs_create_file | |
400 | ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, | |
401 | &dbgaufs_xib_fop); | |
402 | if (unlikely(!sbinfo->si_dbgaufs_xib)) | |
403 | goto out_dir; | |
404 | ||
405 | sbinfo->si_dbgaufs_plink = debugfs_create_file | |
406 | ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, | |
407 | &dbgaufs_plink_fop); | |
408 | if (unlikely(!sbinfo->si_dbgaufs_plink)) | |
409 | goto out_dir; | |
410 | ||
411 | err = dbgaufs_xigen_init(sbinfo); | |
412 | if (!err) | |
413 | goto out; /* success */ | |
414 | ||
415 | out_dir: | |
416 | dbgaufs_si_fin(sbinfo); | |
417 | out: | |
418 | return err; | |
419 | } | |
420 | ||
421 | /* ---------------------------------------------------------------------- */ | |
422 | ||
423 | void dbgaufs_fin(void) | |
424 | { | |
425 | debugfs_remove(dbgaufs); | |
426 | } | |
427 | ||
428 | int __init dbgaufs_init(void) | |
429 | { | |
430 | int err; | |
431 | ||
432 | err = -EIO; | |
433 | dbgaufs = debugfs_create_dir(AUFS_NAME, NULL); | |
434 | if (dbgaufs) | |
435 | err = 0; | |
436 | return err; | |
437 | } |