hc
2024-02-19 1c055e55a242a33e574e48be530e06770a210dcd
kernel/fs/tracefs/inode.c
....@@ -1,3 +1,4 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * inode.c - part of tracefs, a pseudo file system for activating tracing
34 *
....@@ -5,12 +6,7 @@
56 *
67 * Copyright (C) 2014 Red Hat Inc, author: Steven Rostedt <srostedt@redhat.com>
78 *
8
- * This program is free software; you can redistribute it and/or
9
- * modify it under the terms of the GNU General Public License version
10
- * 2 as published by the Free Software Foundation.
11
- *
129 * tracefs is the file system that is used by the tracing infrastructure.
13
- *
1410 */
1511
1612 #include <linux/module.h>
....@@ -20,6 +16,7 @@
2016 #include <linux/namei.h>
2117 #include <linux/tracefs.h>
2218 #include <linux/fsnotify.h>
19
+#include <linux/security.h>
2320 #include <linux/seq_file.h>
2421 #include <linux/parser.h>
2522 #include <linux/magic.h>
....@@ -142,6 +139,8 @@
142139 kuid_t uid;
143140 kgid_t gid;
144141 umode_t mode;
142
+ /* Opt_* bitfield. */
143
+ unsigned int opts;
145144 };
146145
147146 enum {
....@@ -199,7 +198,7 @@
199198
200199 if (!list_empty(&dentry->d_subdirs)) {
201200 spin_unlock(&this_parent->d_lock);
202
- spin_release(&dentry->d_lock.dep_map, 1, _RET_IP_);
201
+ spin_release(&dentry->d_lock.dep_map, _RET_IP_);
203202 this_parent = dentry;
204203 spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_);
205204 goto repeat;
....@@ -242,6 +241,7 @@
242241 kgid_t gid;
243242 char *p;
244243
244
+ opts->opts = 0;
245245 opts->mode = TRACEFS_DEFAULT_MODE;
246246
247247 while ((p = strsep(&data, ",")) != NULL) {
....@@ -276,24 +276,36 @@
276276 * but traditionally tracefs has ignored all mount options
277277 */
278278 }
279
+
280
+ opts->opts |= BIT(token);
279281 }
280282
281283 return 0;
282284 }
283285
284
-static int tracefs_apply_options(struct super_block *sb)
286
+static int tracefs_apply_options(struct super_block *sb, bool remount)
285287 {
286288 struct tracefs_fs_info *fsi = sb->s_fs_info;
287289 struct inode *inode = sb->s_root->d_inode;
288290 struct tracefs_mount_opts *opts = &fsi->mount_opts;
289291
290
- inode->i_mode &= ~S_IALLUGO;
291
- inode->i_mode |= opts->mode;
292
+ /*
293
+ * On remount, only reset mode/uid/gid if they were provided as mount
294
+ * options.
295
+ */
292296
293
- inode->i_uid = opts->uid;
297
+ if (!remount || opts->opts & BIT(Opt_mode)) {
298
+ inode->i_mode &= ~S_IALLUGO;
299
+ inode->i_mode |= opts->mode;
300
+ }
294301
295
- /* Set all the group ids to the mount option */
296
- set_gid(sb->s_root, opts->gid);
302
+ if (!remount || opts->opts & BIT(Opt_uid))
303
+ inode->i_uid = opts->uid;
304
+
305
+ if (!remount || opts->opts & BIT(Opt_gid)) {
306
+ /* Set all the group ids to the mount option */
307
+ set_gid(sb->s_root, opts->gid);
308
+ }
297309
298310 return 0;
299311 }
....@@ -308,7 +320,7 @@
308320 if (err)
309321 goto fail;
310322
311
- tracefs_apply_options(sb);
323
+ tracefs_apply_options(sb, true);
312324
313325 fail:
314326 return err;
....@@ -360,7 +372,7 @@
360372
361373 sb->s_op = &tracefs_super_operations;
362374
363
- tracefs_apply_options(sb);
375
+ tracefs_apply_options(sb, false);
364376
365377 return 0;
366378
....@@ -406,7 +418,10 @@
406418 parent = tracefs_mount->mnt_root;
407419
408420 inode_lock(parent->d_inode);
409
- dentry = lookup_one_len(name, parent, strlen(name));
421
+ if (unlikely(IS_DEADDIR(parent->d_inode)))
422
+ dentry = ERR_PTR(-ENOENT);
423
+ else
424
+ dentry = lookup_one_len(name, parent, strlen(name));
410425 if (!IS_ERR(dentry) && dentry->d_inode) {
411426 dput(dentry);
412427 dentry = ERR_PTR(-EEXIST);
....@@ -466,6 +481,9 @@
466481 {
467482 struct dentry *dentry;
468483 struct inode *inode;
484
+
485
+ if (security_locked_down(LOCKDOWN_TRACEFS))
486
+ return NULL;
469487
470488 if (!(mode & S_IFMT))
471489 mode |= S_IFREG;
....@@ -535,6 +553,9 @@
535553 */
536554 struct dentry *tracefs_create_dir(const char *name, struct dentry *parent)
537555 {
556
+ if (security_locked_down(LOCKDOWN_TRACEFS))
557
+ return NULL;
558
+
538559 return __create_dir(name, parent, &simple_dir_inode_operations);
539560 }
540561
....@@ -576,119 +597,27 @@
576597 return dentry;
577598 }
578599
579
-static int __tracefs_remove(struct dentry *dentry, struct dentry *parent)
600
+static void remove_one(struct dentry *victim)
580601 {
581
- int ret = 0;
582
-
583
- if (simple_positive(dentry)) {
584
- if (dentry->d_inode) {
585
- dget(dentry);
586
- switch (dentry->d_inode->i_mode & S_IFMT) {
587
- case S_IFDIR:
588
- ret = simple_rmdir(parent->d_inode, dentry);
589
- break;
590
- default:
591
- simple_unlink(parent->d_inode, dentry);
592
- break;
593
- }
594
- if (!ret)
595
- d_delete(dentry);
596
- dput(dentry);
597
- }
598
- }
599
- return ret;
602
+ simple_release_fs(&tracefs_mount, &tracefs_mount_count);
600603 }
601604
602605 /**
603
- * tracefs_remove - removes a file or directory from the tracefs filesystem
604
- * @dentry: a pointer to a the dentry of the file or directory to be
605
- * removed.
606
- *
607
- * This function removes a file or directory in tracefs that was previously
608
- * created with a call to another tracefs function (like
609
- * tracefs_create_file() or variants thereof.)
610
- */
611
-void tracefs_remove(struct dentry *dentry)
612
-{
613
- struct dentry *parent;
614
- int ret;
615
-
616
- if (IS_ERR_OR_NULL(dentry))
617
- return;
618
-
619
- parent = dentry->d_parent;
620
- inode_lock(parent->d_inode);
621
- ret = __tracefs_remove(dentry, parent);
622
- inode_unlock(parent->d_inode);
623
- if (!ret)
624
- simple_release_fs(&tracefs_mount, &tracefs_mount_count);
625
-}
626
-
627
-/**
628
- * tracefs_remove_recursive - recursively removes a directory
606
+ * tracefs_remove - recursively removes a directory
629607 * @dentry: a pointer to a the dentry of the directory to be removed.
630608 *
631609 * This function recursively removes a directory tree in tracefs that
632610 * was previously created with a call to another tracefs function
633611 * (like tracefs_create_file() or variants thereof.)
634612 */
635
-void tracefs_remove_recursive(struct dentry *dentry)
613
+void tracefs_remove(struct dentry *dentry)
636614 {
637
- struct dentry *child, *parent;
638
-
639615 if (IS_ERR_OR_NULL(dentry))
640616 return;
641617
642
- parent = dentry;
643
- down:
644
- inode_lock(parent->d_inode);
645
- loop:
646
- /*
647
- * The parent->d_subdirs is protected by the d_lock. Outside that
648
- * lock, the child can be unlinked and set to be freed which can
649
- * use the d_u.d_child as the rcu head and corrupt this list.
650
- */
651
- spin_lock(&parent->d_lock);
652
- list_for_each_entry(child, &parent->d_subdirs, d_child) {
653
- if (!simple_positive(child))
654
- continue;
655
-
656
- /* perhaps simple_empty(child) makes more sense */
657
- if (!list_empty(&child->d_subdirs)) {
658
- spin_unlock(&parent->d_lock);
659
- inode_unlock(parent->d_inode);
660
- parent = child;
661
- goto down;
662
- }
663
-
664
- spin_unlock(&parent->d_lock);
665
-
666
- if (!__tracefs_remove(child, parent))
667
- simple_release_fs(&tracefs_mount, &tracefs_mount_count);
668
-
669
- /*
670
- * The parent->d_lock protects agaist child from unlinking
671
- * from d_subdirs. When releasing the parent->d_lock we can
672
- * no longer trust that the next pointer is valid.
673
- * Restart the loop. We'll skip this one with the
674
- * simple_positive() check.
675
- */
676
- goto loop;
677
- }
678
- spin_unlock(&parent->d_lock);
679
-
680
- inode_unlock(parent->d_inode);
681
- child = parent;
682
- parent = parent->d_parent;
683
- inode_lock(parent->d_inode);
684
-
685
- if (child != dentry)
686
- /* go up */
687
- goto loop;
688
-
689
- if (!__tracefs_remove(child, parent))
690
- simple_release_fs(&tracefs_mount, &tracefs_mount_count);
691
- inode_unlock(parent->d_inode);
618
+ simple_pin_fs(&trace_fs_type, &tracefs_mount, &tracefs_mount_count);
619
+ simple_recursive_removal(dentry, remove_one);
620
+ simple_release_fs(&tracefs_mount, &tracefs_mount_count);
692621 }
693622
694623 /**