Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156// SPDX-License-Identifier: GPL-2.0-only#include <linux/export.h>#include <linux/sched/signal.h>#include <linux/sched/task.h>#include <linux/fs.h>#include <linux/path.h>#include <linux/slab.h>#include <linux/fs_struct.h>#include <linux/init_task.h>#include "internal.h"
/* * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values. * It can block. */void set_fs_root(struct fs_struct *fs, const struct path *path){ struct path old_root;
path_get(path); write_seqlock(&fs->seq); old_root = fs->root; fs->root = *path; write_sequnlock(&fs->seq); if (old_root.dentry) path_put(&old_root);}
/* * Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values. * It can block. */void set_fs_pwd(struct fs_struct *fs, const struct path *path){ struct path old_pwd;
path_get(path); write_seqlock(&fs->seq); old_pwd = fs->pwd; fs->pwd = *path; write_sequnlock(&fs->seq);
if (old_pwd.dentry) path_put(&old_pwd);}
static inline int replace_path(struct path *p, const struct path *old, const struct path *new){ if (likely(p->dentry != old->dentry || p->mnt != old->mnt)) return 0; *p = *new; return 1;}
void chroot_fs_refs(const struct path *old_root, const struct path *new_root){ struct task_struct *g, *p; struct fs_struct *fs; int count = 0;
read_lock(&tasklist_lock); for_each_process_thread(g, p) { task_lock(p); fs = p->fs; if (fs) { int hits = 0; write_seqlock(&fs->seq); hits += replace_path(&fs->root, old_root, new_root); hits += replace_path(&fs->pwd, old_root, new_root); while (hits--) { count++; path_get(new_root); } write_sequnlock(&fs->seq); } task_unlock(p); } read_unlock(&tasklist_lock); while (count--) path_put(old_root);}
void free_fs_struct(struct fs_struct *fs){ path_put(&fs->root); path_put(&fs->pwd); kmem_cache_free(fs_cachep, fs);}
void exit_fs(struct task_struct *tsk){ struct fs_struct *fs = tsk->fs;
if (fs) { int kill; task_lock(tsk); read_seqlock_excl(&fs->seq); tsk->fs = NULL; kill = !--fs->users; read_sequnlock_excl(&fs->seq); task_unlock(tsk); if (kill) free_fs_struct(fs); }}
struct fs_struct *copy_fs_struct(struct fs_struct *old){ struct fs_struct *fs = kmem_cache_alloc(fs_cachep, GFP_KERNEL); /* We don't need to lock fs - think why ;-) */ if (fs) { fs->users = 1; fs->in_exec = 0; seqlock_init(&fs->seq); fs->umask = old->umask;
read_seqlock_excl(&old->seq); fs->root = old->root; path_get(&fs->root); fs->pwd = old->pwd; path_get(&fs->pwd); read_sequnlock_excl(&old->seq); } return fs;}
int unshare_fs_struct(void){ struct fs_struct *fs = current->fs; struct fs_struct *new_fs = copy_fs_struct(fs); int kill;
if (!new_fs) return -ENOMEM;
task_lock(current); read_seqlock_excl(&fs->seq); kill = !--fs->users; current->fs = new_fs; read_sequnlock_excl(&fs->seq); task_unlock(current);
if (kill) free_fs_struct(fs);
return 0;}EXPORT_SYMBOL_GPL(unshare_fs_struct);
/* to be mentioned only in INIT_TASK */struct fs_struct init_fs = { .users = 1, .seq = __SEQLOCK_UNLOCKED(init_fs.seq), .umask = 0022,};