In preparation for making the cpio functions generally available rather than just at init make sure we're using versions of the filesystem related functions that aren't in the __init section. Remove functions only used by us from fs/init.c while folding into the cpio code directly. Signed-off-by: Jonathan McDowell <noodles@xxxxxx> --- v2: - Fold in directory EEXIST checking from patch 4 - Add EEXIST checking for device nodes (kernel test reboot boot test) --- fs/init.c | 101 --------------------- fs/internal.h | 4 - include/linux/fs.h | 4 + include/linux/init_syscalls.h | 6 -- lib/cpio.c | 162 +++++++++++++++++++++++++++++----- 5 files changed, 145 insertions(+), 132 deletions(-) diff --git a/fs/init.c b/fs/init.c index 5c36adaa9b44..a946ad672dee 100644 --- a/fs/init.c +++ b/fs/init.c @@ -79,37 +79,6 @@ int __init init_chroot(const char *filename) return error; } -int __init init_chown(const char *filename, uid_t user, gid_t group, int flags) -{ - int lookup_flags = (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; - struct path path; - int error; - - error = kern_path(filename, lookup_flags, &path); - if (error) - return error; - error = mnt_want_write(path.mnt); - if (!error) { - error = chown_common(&path, user, group); - mnt_drop_write(path.mnt); - } - path_put(&path); - return error; -} - -int __init init_chmod(const char *filename, umode_t mode) -{ - struct path path; - int error; - - error = kern_path(filename, LOOKUP_FOLLOW, &path); - if (error) - return error; - error = chmod_common(&path, mode); - path_put(&path); - return error; -} - int __init init_eaccess(const char *filename) { struct path path; @@ -163,58 +132,6 @@ int __init init_mknod(const char *filename, umode_t mode, unsigned int dev) return error; } -int __init init_link(const char *oldname, const char *newname) -{ - struct dentry *new_dentry; - struct path old_path, new_path; - struct user_namespace *mnt_userns; - int error; - - error = kern_path(oldname, 0, &old_path); - if (error) - return error; - - new_dentry = kern_path_create(AT_FDCWD, newname, &new_path, 0); - error = PTR_ERR(new_dentry); - if (IS_ERR(new_dentry)) - goto out; - - error = -EXDEV; - if (old_path.mnt != new_path.mnt) - goto out_dput; - mnt_userns = mnt_user_ns(new_path.mnt); - error = may_linkat(mnt_userns, &old_path); - if (unlikely(error)) - goto out_dput; - error = security_path_link(old_path.dentry, &new_path, new_dentry); - if (error) - goto out_dput; - error = vfs_link(old_path.dentry, mnt_userns, new_path.dentry->d_inode, - new_dentry, NULL); -out_dput: - done_path_create(&new_path, new_dentry); -out: - path_put(&old_path); - return error; -} - -int __init init_symlink(const char *oldname, const char *newname) -{ - struct dentry *dentry; - struct path path; - int error; - - dentry = kern_path_create(AT_FDCWD, newname, &path, 0); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - error = security_path_symlink(&path, dentry, oldname); - if (!error) - error = vfs_symlink(mnt_user_ns(path.mnt), path.dentry->d_inode, - dentry, oldname); - done_path_create(&path, dentry); - return error; -} - int __init init_unlink(const char *pathname) { return do_unlinkat(AT_FDCWD, getname_kernel(pathname)); @@ -239,24 +156,6 @@ int __init init_mkdir(const char *pathname, umode_t mode) return error; } -int __init init_rmdir(const char *pathname) -{ - return do_rmdir(AT_FDCWD, getname_kernel(pathname)); -} - -int __init init_utimes(char *filename, struct timespec64 *ts) -{ - struct path path; - int error; - - error = kern_path(filename, 0, &path); - if (error) - return error; - error = vfs_utimes(&path, ts); - path_put(&path); - return error; -} - int __init init_dup(struct file *file) { int fd; diff --git a/fs/internal.h b/fs/internal.h index 87e96b9024ce..c57d5f0aa731 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -60,9 +60,6 @@ extern int filename_lookup(int dfd, struct filename *name, unsigned flags, struct path *path, struct path *root); extern int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, unsigned int, struct path *); -int do_rmdir(int dfd, struct filename *name); -int do_unlinkat(int dfd, struct filename *name); -int may_linkat(struct user_namespace *mnt_userns, struct path *link); int do_renameat2(int olddfd, struct filename *oldname, int newdfd, struct filename *newname, unsigned int flags); int do_mkdirat(int dfd, struct filename *name, umode_t mode); @@ -132,7 +129,6 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small); int chmod_common(const struct path *path, umode_t mode); int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag); -int chown_common(const struct path *path, uid_t user, gid_t group); extern int vfs_open(const struct path *, struct file *); /* diff --git a/include/linux/fs.h b/include/linux/fs.h index 9ad5e3520fae..1cb51a54799b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2636,11 +2636,15 @@ static inline struct file *file_clone_open(struct file *file) return dentry_open(&file->f_path, file->f_flags, file->f_cred); } extern int filp_close(struct file *, fl_owner_t id); +extern int chown_common(const struct path *path, uid_t user, gid_t group); +extern int do_rmdir(int dfd, struct filename *name); +extern int do_unlinkat(int dfd, struct filename *name); extern struct filename *getname_flags(const char __user *, int, int *); extern struct filename *getname_uflags(const char __user *, int); extern struct filename *getname(const char __user *); extern struct filename *getname_kernel(const char *); +extern int may_linkat(struct user_namespace *mnt_userns, struct path *link); extern void putname(struct filename *name); extern int finish_open(struct file *file, struct dentry *dentry, diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index 92045d18cbfc..196030cd958d 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -5,15 +5,9 @@ int __init init_mount(const char *dev_name, const char *dir_name, int __init init_umount(const char *name, int flags); int __init init_chdir(const char *filename); int __init init_chroot(const char *filename); -int __init init_chown(const char *filename, uid_t user, gid_t group, int flags); -int __init init_chmod(const char *filename, umode_t mode); int __init init_eaccess(const char *filename); int __init init_stat(const char *filename, struct kstat *stat, int flags); int __init init_mknod(const char *filename, umode_t mode, unsigned int dev); -int __init init_link(const char *oldname, const char *newname); -int __init init_symlink(const char *oldname, const char *newname); int __init init_unlink(const char *pathname); int __init init_mkdir(const char *pathname, umode_t mode); -int __init init_rmdir(const char *pathname); -int __init init_utimes(char *filename, struct timespec64 *ts); int __init init_dup(struct file *file); diff --git a/lib/cpio.c b/lib/cpio.c index 5d150939704f..9a0120c638db 100644 --- a/lib/cpio.c +++ b/lib/cpio.c @@ -3,8 +3,9 @@ #include <linux/file.h> #include <linux/fs.h> #include <linux/init.h> -#include <linux/init_syscalls.h> #include <linux/list.h> +#include <linux/namei.h> +#include <linux/security.h> #include <linux/slab.h> static ssize_t __init xwrite(struct cpio_context *ctx, struct file *file, @@ -92,18 +93,25 @@ static void __init free_hash(struct cpio_context *ctx) } #ifdef CONFIG_INITRAMFS_PRESERVE_MTIME -static void __init do_utime(char *filename, time64_t mtime) +static void __init do_utime_path(const struct path *path, time64_t mtime) { struct timespec64 t[2] = { { .tv_sec = mtime }, { .tv_sec = mtime } }; - init_utimes(filename, t); + vfs_utimes(path, t); } -static void __init do_utime_path(const struct path *path, time64_t mtime) +static int __init do_utime(char *filename, time64_t mtime) { - struct timespec64 t[2] = { { .tv_sec = mtime }, { .tv_sec = mtime } }; + struct path path; + int error; - vfs_utimes(path, t); + error = kern_path(filename, 0, &path); + if (error) + return error; + do_utime_path(&path, mtime); + path_put(&path); + + return error; } static int __init dir_add(struct cpio_context *ctx, const char *name, time64_t mtime) @@ -133,12 +141,31 @@ static void __init dir_utime(struct cpio_context *ctx) } } #else -static void __init do_utime(char *filename, time64_t mtime) {} +static int __init do_utime(char *filename, time64_t mtime) { return 0; } static void __init do_utime_path(const struct path *path, time64_t mtime) {} static int __init dir_add(struct cpio_context *ctx, const char *name, time64_t mtime) { return 0; } static void __init dir_utime(struct cpio_context *ctx) {} #endif +static int __init cpio_chown(const char *filename, uid_t user, gid_t group, + int flags) +{ + int lookup_flags = (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; + struct path path; + int error; + + error = kern_path(filename, lookup_flags, &path); + if (error) + return error; + error = mnt_want_write(path.mnt); + if (!error) { + error = chown_common(&path, user, group); + mnt_drop_write(path.mnt); + } + path_put(&path); + return error; +} + /* cpio header parsing */ static void __init parse_header(struct cpio_context *ctx, char *s) @@ -269,27 +296,67 @@ static int __init do_reset(struct cpio_context *ctx) return 1; } -static void __init clean_path(char *path, umode_t fmode) +static void __init clean_path(char *pathname, umode_t fmode) { + struct path path; struct kstat st; + int error; - if (!init_stat(path, &st, AT_SYMLINK_NOFOLLOW) && - (st.mode ^ fmode) & S_IFMT) { + error = kern_path(pathname, 0, &path); + if (error) + return; + error = vfs_getattr(&path, &st, STATX_BASIC_STATS, AT_NO_AUTOMOUNT); + path_put(&path); + if (error) + return; + + if ((st.mode ^ fmode) & S_IFMT) { if (S_ISDIR(st.mode)) - init_rmdir(path); + do_rmdir(AT_FDCWD, getname_kernel(pathname)); else - init_unlink(path); + do_unlinkat(AT_FDCWD, getname_kernel(pathname)); } } static int __init maybe_link(struct cpio_context *ctx) { + struct dentry *new_dentry; + struct path old_path, new_path; + struct user_namespace *mnt_userns; + int error; + if (ctx->nlink >= 2) { char *old = find_link(ctx, ctx->major, ctx->minor, ctx->ino, ctx->mode, ctx->collected); if (old) { clean_path(ctx->collected, 0); - return (init_link(old, ctx->collected) < 0) ? -1 : 1; + + error = kern_path(old, 0, &old_path); + if (error) + return error; + + new_dentry = kern_path_create(AT_FDCWD, ctx->collected, &new_path, 0); + error = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto out; + + error = -EXDEV; + if (old_path.mnt != new_path.mnt) + goto out_dput; + mnt_userns = mnt_user_ns(new_path.mnt); + error = may_linkat(mnt_userns, &old_path); + if (unlikely(error)) + goto out_dput; + error = security_path_link(old_path.dentry, &new_path, new_dentry); + if (error) + goto out_dput; + error = vfs_link(old_path.dentry, mnt_userns, new_path.dentry->d_inode, + new_dentry, NULL); +out_dput: + done_path_create(&new_path, new_dentry); +out: + path_put(&old_path); + return (error < 0) ? error : 1; } } return 0; @@ -297,6 +364,10 @@ static int __init maybe_link(struct cpio_context *ctx) static int __init do_name(struct cpio_context *ctx) { + struct dentry *dentry; + struct path path; + int error; + ctx->state = CPIO_SKIPIT; ctx->next_state = CPIO_RESET; if (strcmp(ctx->collected, "TRAILER!!!") == 0) { @@ -325,16 +396,48 @@ static int __init do_name(struct cpio_context *ctx) ctx->state = CPIO_COPYFILE; } } else if (S_ISDIR(ctx->mode)) { - init_mkdir(ctx->collected, ctx->mode); - init_chown(ctx->collected, ctx->uid, ctx->gid, 0); - init_chmod(ctx->collected, ctx->mode); + dentry = kern_path_create(AT_FDCWD, ctx->collected, &path, LOOKUP_DIRECTORY); + + if (!IS_ERR(dentry)) { + error = security_path_mkdir(&path, dentry, ctx->mode); + if (!error) + error = vfs_mkdir(mnt_user_ns(path.mnt), + path.dentry->d_inode, + dentry, ctx->mode); + done_path_create(&path, dentry); + } else { + error = PTR_ERR(dentry); + } + + if (error && error != -EEXIST) + return error; + + cpio_chown(ctx->collected, ctx->uid, ctx->gid, 0); dir_add(ctx, ctx->collected, ctx->mtime); } else if (S_ISBLK(ctx->mode) || S_ISCHR(ctx->mode) || S_ISFIFO(ctx->mode) || S_ISSOCK(ctx->mode)) { if (maybe_link(ctx) == 0) { - init_mknod(ctx->collected, ctx->mode, ctx->rdev); - init_chown(ctx->collected, ctx->uid, ctx->gid, 0); - init_chmod(ctx->collected, ctx->mode); + if (S_ISFIFO(ctx->mode) || S_ISSOCK(ctx->mode)) + ctx->rdev = 0; + + dentry = kern_path_create(AT_FDCWD, ctx->collected, &path, 0); + if (!IS_ERR(dentry)) { + error = security_path_mknod(&path, dentry, ctx->mode, + ctx->rdev); + if (!error) + error = vfs_mknod(mnt_user_ns(path.mnt), + path.dentry->d_inode, + dentry, ctx->mode, + new_decode_dev(ctx->rdev)); + done_path_create(&path, dentry); + } else { + error = PTR_ERR(dentry); + } + + if (error && error != -EEXIST) + return error; + + cpio_chown(ctx->collected, ctx->uid, ctx->gid, 0); do_utime(ctx->collected, ctx->mtime); } } @@ -373,10 +476,27 @@ static int __init do_copy(struct cpio_context *ctx) static int __init do_symlink(struct cpio_context *ctx) { + struct dentry *dentry; + struct path path; + int error; + ctx->collected[N_ALIGN(ctx->name_len) + ctx->body_len] = '\0'; clean_path(ctx->collected, 0); - init_symlink(ctx->collected + N_ALIGN(ctx->name_len), ctx->collected); - init_chown(ctx->collected, ctx->uid, ctx->gid, AT_SYMLINK_NOFOLLOW); + + dentry = kern_path_create(AT_FDCWD, ctx->collected, &path, 0); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + error = security_path_symlink(&path, dentry, + ctx->collected + N_ALIGN(ctx->name_len)); + if (!error) + error = vfs_symlink(mnt_user_ns(path.mnt), path.dentry->d_inode, + dentry, + ctx->collected + N_ALIGN(ctx->name_len)); + done_path_create(&path, dentry); + if (error) + return error; + + cpio_chown(ctx->collected, ctx->uid, ctx->gid, AT_SYMLINK_NOFOLLOW); do_utime(ctx->collected, ctx->mtime); ctx->state = CPIO_SKIPIT; ctx->next_state = CPIO_RESET; -- 2.30.2