History

simple_recursive_removal(): kernel-side rm -rf for ramfs-style filesystems

two requirements: no file creations in IS_DEADDIR and no cross-directory renames whatsoever.

Al Viro committed 6 years ago

Commit a3d1e7e1 parent e42617b9 files · +104 −218Browse files

Unified · Split

9 files
  1. fs/debugfs/inode.c +14 −107
  2. fs/libfs.c +70 −0
  3. fs/tracefs/inode.c +11 −103
  4. include/linux/debugfs.h +1 −1
  5. include/linux/fs.h +2 −0
  6. include/linux/tracefs.h +0 −1
  7. kernel/trace/trace.c +2 −2
  8. kernel/trace/trace_events.c +3 −3
  9. kernel/trace/trace_hwlat.c +1 −1
fs/debugfs/inode.c+14−107
@@ -332,7 +332,10 @@ static struct dentry *start_creating(const char *name, struct dentry *parent) · 331 unmodified lines
332332 parent = debugfs_mount->mnt_root;
333333
334334 inode_lock(d_inode(parent));
335− dentry = lookup_one_len(name, parent, strlen(name));
335+ if (unlikely(IS_DEADDIR(d_inode(parent))))
336+ dentry = ERR_PTR(-ENOENT);
337+ else
338+ dentry = lookup_one_len(name, parent, strlen(name));
336339 if (!IS_ERR(dentry) && d_really_is_positive(dentry)) {
337340 if (d_is_dir(dentry))
338341 pr_err("Directory '%s' with parent '%s' already present!\n",
@@ -681,62 +684,15 @@ static void __debugfs_file_removed(struct dentry *dentry) · 342 unmodified lines
681684 wait_for_completion(&fsd->active_users_drained);
682685 }
683686
684−static int __debugfs_remove(struct dentry *dentry, struct dentry *parent)
685−{
686− int ret = 0;
687−
688− if (simple_positive(dentry)) {
689− dget(dentry);
690− if (d_is_dir(dentry)) {
691− ret = simple_rmdir(d_inode(parent), dentry);
692− if (!ret)
693− fsnotify_rmdir(d_inode(parent), dentry);
694− } else {
695− simple_unlink(d_inode(parent), dentry);
696− fsnotify_unlink(d_inode(parent), dentry);
697− }
698− if (!ret)
699− d_delete(dentry);
700− if (d_is_reg(dentry))
701− __debugfs_file_removed(dentry);
702− dput(dentry);
703− }
704− return ret;
705−}
706−
707−/**
708− * debugfs_remove - removes a file or directory from the debugfs filesystem
709− * @dentry: a pointer to a the dentry of the file or directory to be
710− * removed. If this parameter is NULL or an error value, nothing
711− * will be done.
712− *
713− * This function removes a file or directory in debugfs that was previously
714− * created with a call to another debugfs function (like
715− * debugfs_create_file() or variants thereof.)
716− *
717− * This function is required to be called in order for the file to be
718− * removed, no automatic cleanup of files will happen when a module is
719− * removed, you are responsible here.
720− */
721−void debugfs_remove(struct dentry *dentry)
687+static void remove_one(struct dentry *victim)
722688 {
723− struct dentry *parent;
724− int ret;
725−
726− if (IS_ERR_OR_NULL(dentry))
727− return;
728−
729− parent = dentry->d_parent;
730− inode_lock(d_inode(parent));
731− ret = __debugfs_remove(dentry, parent);
732− inode_unlock(d_inode(parent));
733− if (!ret)
734− simple_release_fs(&debugfs_mount, &debugfs_mount_count);
689+ if (d_is_reg(victim))
690+ __debugfs_file_removed(victim);
691+ simple_release_fs(&debugfs_mount, &debugfs_mount_count);
735692 }
736−EXPORT_SYMBOL_GPL(debugfs_remove);
737693
738694 /**
739− * debugfs_remove_recursive - recursively removes a directory
695+ * debugfs_remove - recursively removes a directory
740696 * @dentry: a pointer to a the dentry of the directory to be removed. If this
741697 * parameter is NULL or an error value, nothing will be done.
742698 *
@@ -748,65 +704,16 @@ EXPORT_SYMBOL_GPL(debugfs_remove); · 5 unmodified lines
748704 * removed, no automatic cleanup of files will happen when a module is
749705 * removed, you are responsible here.
750706 */
751−void debugfs_remove_recursive(struct dentry *dentry)
707+void debugfs_remove(struct dentry *dentry)
752708 {
753− struct dentry *child, *parent;
754−
755709 if (IS_ERR_OR_NULL(dentry))
756710 return;
757711
758− parent = dentry;
759− down:
760− inode_lock(d_inode(parent));
761− loop:
762− /*
763− * The parent->d_subdirs is protected by the d_lock. Outside that
764− * lock, the child can be unlinked and set to be freed which can
765− * use the d_u.d_child as the rcu head and corrupt this list.
766− */
767− spin_lock(&parent->d_lock);
768− list_for_each_entry(child, &parent->d_subdirs, d_child) {
769− if (!simple_positive(child))
770− continue;
771−
772− /* perhaps simple_empty(child) makes more sense */
773− if (!list_empty(&child->d_subdirs)) {
774− spin_unlock(&parent->d_lock);
775− inode_unlock(d_inode(parent));
776− parent = child;
777− goto down;
778− }
779−
780− spin_unlock(&parent->d_lock);
781−
782− if (!__debugfs_remove(child, parent))
783− simple_release_fs(&debugfs_mount, &debugfs_mount_count);
784−
785− /*
786− * The parent->d_lock protects agaist child from unlinking
787− * from d_subdirs. When releasing the parent->d_lock we can
788− * no longer trust that the next pointer is valid.
789− * Restart the loop. We'll skip this one with the
790− * simple_positive() check.
791− */
792− goto loop;
793− }
794− spin_unlock(&parent->d_lock);
795−
796− inode_unlock(d_inode(parent));
797− child = parent;
798− parent = parent->d_parent;
799− inode_lock(d_inode(parent));
800−
801− if (child != dentry)
802− /* go up */
803− goto loop;
804−
805− if (!__debugfs_remove(child, parent))
806− simple_release_fs(&debugfs_mount, &debugfs_mount_count);
807− inode_unlock(d_inode(parent));
712+ simple_pin_fs(&debug_fs_type, &debugfs_mount, &debugfs_mount_count);
713+ simple_recursive_removal(dentry, remove_one);
714+ simple_release_fs(&debugfs_mount, &debugfs_mount_count);
808715 }
809−EXPORT_SYMBOL_GPL(debugfs_remove_recursive);
716+EXPORT_SYMBOL_GPL(debugfs_remove);
810717
811718 /**
812719 * debugfs_rename - rename a file/directory in the debugfs filesystem
fs/libfs.c+70−0
@@ -19,6 +19,7 @@ · 18 unmodified lines
1919 #include <linux/buffer_head.h> /* sync_mapping_buffers */
2020 #include <linux/fs_context.h>
2121 #include <linux/pseudo_fs.h>
22+#include <linux/fsnotify.h>
2223
2324 #include <linux/uaccess.h>
2425
@@ -239,6 +240,75 @@ const struct inode_operations simple_dir_inode_operations = { · 214 unmodified lines
239240 };
240241 EXPORT_SYMBOL(simple_dir_inode_operations);
241242
243+static struct dentry *find_next_child(struct dentry *parent, struct dentry *prev)
244+{
245+ struct dentry *child = NULL;
246+ struct list_head *p = prev ? &prev->d_child : &parent->d_subdirs;
247+
248+ spin_lock(&parent->d_lock);
249+ while ((p = p->next) != &parent->d_subdirs) {
250+ struct dentry *d = container_of(p, struct dentry, d_child);
251+ if (simple_positive(d)) {
252+ spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);
253+ if (simple_positive(d))
254+ child = dget_dlock(d);
255+ spin_unlock(&d->d_lock);
256+ if (likely(child))
257+ break;
258+ }
259+ }
260+ spin_unlock(&parent->d_lock);
261+ dput(prev);
262+ return child;
263+}
264+
265+void simple_recursive_removal(struct dentry *dentry,
266+ void (*callback)(struct dentry *))
267+{
268+ struct dentry *this = dget(dentry);
269+ while (true) {
270+ struct dentry *victim = NULL, *child;
271+ struct inode *inode = this->d_inode;
272+
273+ inode_lock(inode);
274+ if (d_is_dir(this))
275+ inode->i_flags |= S_DEAD;
276+ while ((child = find_next_child(this, victim)) == NULL) {
277+ // kill and ascend
278+ // update metadata while it's still locked
279+ inode->i_ctime = current_time(inode);
280+ clear_nlink(inode);
281+ inode_unlock(inode);
282+ victim = this;
283+ this = this->d_parent;
284+ inode = this->d_inode;
285+ inode_lock(inode);
286+ if (simple_positive(victim)) {
287+ d_invalidate(victim); // avoid lost mounts
288+ if (d_is_dir(victim))
289+ fsnotify_rmdir(inode, victim);
290+ else
291+ fsnotify_unlink(inode, victim);
292+ if (callback)
293+ callback(victim);
294+ dput(victim); // unpin it
295+ }
296+ if (victim == dentry) {
297+ inode->i_ctime = inode->i_mtime =
298+ current_time(inode);
299+ if (d_is_dir(dentry))
300+ drop_nlink(inode);
301+ inode_unlock(inode);
302+ dput(dentry);
303+ return;
304+ }
305+ }
306+ inode_unlock(inode);
307+ this = child;
308+ }
309+}
310+EXPORT_SYMBOL(simple_recursive_removal);
311+
242312 static const struct super_operations simple_super_operations = {
243313 .statfs = simple_statfs,
244314 };
fs/tracefs/inode.c+11−103
@@ -330,7 +330,10 @@ static struct dentry *start_creating(const char *name, struct dentry *parent) · 329 unmodified lines
330330 parent = tracefs_mount->mnt_root;
331331
332332 inode_lock(parent->d_inode);
333− dentry = lookup_one_len(name, parent, strlen(name));
333+ if (unlikely(IS_DEADDIR(parent->d_inode)))
334+ dentry = ERR_PTR(-ENOENT);
335+ else
336+ dentry = lookup_one_len(name, parent, strlen(name));
334337 if (!IS_ERR(dentry) && dentry->d_inode) {
335338 dput(dentry);
336339 dentry = ERR_PTR(-EEXIST);
@@ -499,122 +502,27 @@ __init struct dentry *tracefs_create_instance_dir(const char *name, · 162 unmodified lines
499502 return dentry;
500503 }
501504
502−static int __tracefs_remove(struct dentry *dentry, struct dentry *parent)
505+static void remove_one(struct dentry *victim)
503506 {
504− int ret = 0;
505−
506− if (simple_positive(dentry)) {
507− if (dentry->d_inode) {
508− dget(dentry);
509− switch (dentry->d_inode->i_mode & S_IFMT) {
510− case S_IFDIR:
511− ret = simple_rmdir(parent->d_inode, dentry);
512− if (!ret)
513− fsnotify_rmdir(parent->d_inode, dentry);
514− break;
515− default:
516− simple_unlink(parent->d_inode, dentry);
517− fsnotify_unlink(parent->d_inode, dentry);
518− break;
519− }
520− if (!ret)
521− d_delete(dentry);
522− dput(dentry);
523− }
524− }
525− return ret;
526−}
527−
528−/**
529− * tracefs_remove - removes a file or directory from the tracefs filesystem
530− * @dentry: a pointer to a the dentry of the file or directory to be
531− * removed.
532− *
533− * This function removes a file or directory in tracefs that was previously
534− * created with a call to another tracefs function (like
535− * tracefs_create_file() or variants thereof.)
536− */
537−void tracefs_remove(struct dentry *dentry)
538−{
539− struct dentry *parent;
540− int ret;
541−
542− if (IS_ERR_OR_NULL(dentry))
543− return;
544−
545− parent = dentry->d_parent;
546− inode_lock(parent->d_inode);
547− ret = __tracefs_remove(dentry, parent);
548− inode_unlock(parent->d_inode);
549− if (!ret)
550− simple_release_fs(&tracefs_mount, &tracefs_mount_count);
507+ simple_release_fs(&tracefs_mount, &tracefs_mount_count);
551508 }
552509
553510 /**
554− * tracefs_remove_recursive - recursively removes a directory
511+ * tracefs_remove - recursively removes a directory
555512 * @dentry: a pointer to a the dentry of the directory to be removed.
556513 *
557514 * This function recursively removes a directory tree in tracefs that
558515 * was previously created with a call to another tracefs function
559516 * (like tracefs_create_file() or variants thereof.)
560517 */
561−void tracefs_remove_recursive(struct dentry *dentry)
518+void tracefs_remove(struct dentry *dentry)
562519 {
563− struct dentry *child, *parent;
564−
565520 if (IS_ERR_OR_NULL(dentry))
566521 return;
567522
568− parent = dentry;
569− down:
570− inode_lock(parent->d_inode);
571− loop:
572− /*
573− * The parent->d_subdirs is protected by the d_lock. Outside that
574− * lock, the child can be unlinked and set to be freed which can
575− * use the d_u.d_child as the rcu head and corrupt this list.
576− */
577− spin_lock(&parent->d_lock);
578− list_for_each_entry(child, &parent->d_subdirs, d_child) {
579− if (!simple_positive(child))
580− continue;
581−
582− /* perhaps simple_empty(child) makes more sense */
583− if (!list_empty(&child->d_subdirs)) {
584− spin_unlock(&parent->d_lock);
585− inode_unlock(parent->d_inode);
586− parent = child;
587− goto down;
588− }
589−
590− spin_unlock(&parent->d_lock);
591−
592− if (!__tracefs_remove(child, parent))
593− simple_release_fs(&tracefs_mount, &tracefs_mount_count);
594−
595− /*
596− * The parent->d_lock protects agaist child from unlinking
597− * from d_subdirs. When releasing the parent->d_lock we can
598− * no longer trust that the next pointer is valid.
599− * Restart the loop. We'll skip this one with the
600− * simple_positive() check.
601− */
602− goto loop;
603− }
604− spin_unlock(&parent->d_lock);
605−
606− inode_unlock(parent->d_inode);
607− child = parent;
608− parent = parent->d_parent;
609− inode_lock(parent->d_inode);
610−
611− if (child != dentry)
612− /* go up */
613− goto loop;
614−
615− if (!__tracefs_remove(child, parent))
616− simple_release_fs(&tracefs_mount, &tracefs_mount_count);
617− inode_unlock(parent->d_inode);
523+ simple_pin_fs(&trace_fs_type, &tracefs_mount, &tracefs_mount_count);
524+ simple_recursive_removal(dentry, remove_one);
525+ simple_release_fs(&tracefs_mount, &tracefs_mount_count);
618526 }
619527
620528 /**
include/linux/debugfs.h+1−1
@@ -83,7 +83,7 @@ struct dentry *debugfs_create_automount(const char *name, · 82 unmodified lines
8383 void *data);
8484
8585 void debugfs_remove(struct dentry *dentry);
86−void debugfs_remove_recursive(struct dentry *dentry);
86+#define debugfs_remove_recursive debugfs_remove
8787
8888 const struct file_operations *debugfs_real_fops(const struct file *filp);
8989
include/linux/fs.h+2−0
@@ -3303,6 +3303,8 @@ extern int simple_unlink(struct inode *, struct dentry *); · 3302 unmodified lines
33033303 extern int simple_rmdir(struct inode *, struct dentry *);
33043304 extern int simple_rename(struct inode *, struct dentry *,
33053305 struct inode *, struct dentry *, unsigned int);
3306+extern void simple_recursive_removal(struct dentry *,
3307+ void (*callback)(struct dentry *));
33063308 extern int noop_fsync(struct file *, loff_t, loff_t, int);
33073309 extern int noop_set_page_dirty(struct page *page);
33083310 extern void noop_invalidatepage(struct page *page, unsigned int offset,
include/linux/tracefs.h+0−1
@@ -28,7 +28,6 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode, · 27 unmodified lines
2828 struct dentry *tracefs_create_dir(const char *name, struct dentry *parent);
2929
3030 void tracefs_remove(struct dentry *dentry);
31−void tracefs_remove_recursive(struct dentry *dentry);
3231
3332 struct dentry *tracefs_create_instance_dir(const char *name, struct dentry *parent,
3433 int (*mkdir)(const char *name),
kernel/trace/trace.c+2−2
@@ -8496,7 +8496,7 @@ static struct trace_array *trace_array_create(const char *name) · 8495 unmodified lines
84968496
84978497 ret = event_trace_add_tracer(tr->dir, tr);
84988498 if (ret) {
8499− tracefs_remove_recursive(tr->dir);
8499+ tracefs_remove(tr->dir);
85008500 goto out_free_tr;
85018501 }
85028502
@@ -8605,7 +8605,7 @@ static int __remove_instance(struct trace_array *tr) · 102 unmodified lines
86058605 event_trace_del_tracer(tr);
86068606 ftrace_clear_pids(tr);
86078607 ftrace_destroy_function_files(tr);
8608− tracefs_remove_recursive(tr->dir);
8608+ tracefs_remove(tr->dir);
86098609 free_trace_buffers(tr);
86108610
86118611 for (i = 0; i < tr->nr_topts; i++) {
kernel/trace/trace_events.c+3−3
@@ -697,7 +697,7 @@ static void remove_subsystem(struct trace_subsystem_dir *dir) · 696 unmodified lines
697697 return;
698698
699699 if (!--dir->nr_events) {
700− tracefs_remove_recursive(dir->entry);
700+ tracefs_remove(dir->entry);
701701 list_del(&dir->list);
702702 __put_system_dir(dir);
703703 }
@@ -716,7 +716,7 @@ static void remove_event_file_dir(struct trace_event_file *file) · 12 unmodified lines
716716 }
717717 spin_unlock(&dir->d_lock);
718718
719− tracefs_remove_recursive(dir);
719+ tracefs_remove(dir);
720720 }
721721
722722 list_del(&file->list);
@@ -3064,7 +3064,7 @@ int event_trace_del_tracer(struct trace_array *tr) · 2341 unmodified lines
30643064
30653065 down_write(&trace_event_sem);
30663066 __trace_remove_event_dirs(tr);
3067− tracefs_remove_recursive(tr->event_dir);
3067+ tracefs_remove(tr->event_dir);
30683068 up_write(&trace_event_sem);
30693069
30703070 tr->event_dir = NULL;
kernel/trace/trace_hwlat.c+1−1
@@ -556,7 +556,7 @@ static int init_tracefs(void) · 555 unmodified lines
556556 return 0;
557557
558558 err:
559− tracefs_remove_recursive(top_dir);
559+ tracefs_remove(top_dir);
560560 return -ENOMEM;
561561 }
562562