[RFC v2 PATCH 2/3] initramfs: use kern_sys_* wrappers instead of syscall
From: Namhyung Kim
Date: Sun Aug 29 2010 - 13:29:10 EST
replace direct call to syscall routines to its wrapper functions
defined in init/sys-wrapper.h
Signed-off-by: Namhyung Kim <namhyung@xxxxxxxxx>
---
init/sys-wrapper.c | 589 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 589 insertions(+), 0 deletions(-)
create mode 100644 init/sys-wrapper.c
diff --git a/init/sys-wrapper.c b/init/sys-wrapper.c
new file mode 100644
index 0000000..fa5949f
--- /dev/null
+++ b/init/sys-wrapper.c
@@ -0,0 +1,589 @@
+/*
+ * init/sys-wrapper.c
+ *
+ * Copyright (C) 2010 Namhyung Kim <namhyung@xxxxxxxxx>
+ *
+ * Wrappers for various syscalls for use in the init code.
+ * Most of these functions are copied from their syscall implementation
+ * verbatim except that path lookup codes are changed to use kernel
+ * functions and security checks are removed.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/file.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/fcntl.h>
+#include <linux/dirent.h>
+#include <linux/syscalls.h>
+#include <linux/highuid.h>
+#include "sys-wrapper.h"
+
+int __init kern_sys_link(const char *oldname, const char *newname)
+{
+ struct path old_path;
+ struct dentry *new_dentry;
+ struct nameidata nd;
+ int error;
+
+ error = kern_path(oldname, 0, &old_path);
+ if (error)
+ goto out;
+
+ error = path_lookup(newname, LOOKUP_PARENT, &nd);
+ if (error)
+ goto out_path;
+
+ error = -EXDEV;
+ if (old_path.mnt != nd.path.mnt)
+ goto out_nd;
+
+ new_dentry = lookup_create(&nd, 0);
+ if (IS_ERR(new_dentry)) {
+ error = PTR_ERR(new_dentry);
+ goto out_unlock;
+ }
+
+ error = mnt_want_write(nd.path.mnt);
+ if (error)
+ goto out_dput;
+
+ error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
+
+ mnt_drop_write(nd.path.mnt);
+out_dput:
+ dput(new_dentry);
+out_unlock:
+ mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+out_nd:
+ path_put(&nd.path);
+out_path:
+ path_put(&old_path);
+out:
+ return error;
+}
+
+static struct dentry *lookup_hash(struct nameidata *nd)
+{
+ int err;
+ struct dentry *base;
+ struct qstr *name;
+ struct inode *inode;
+ struct dentry *dentry;
+
+ base = nd->path.dentry;
+ name = &nd->last;
+ inode = base->d_inode;
+
+ if (inode->i_op->permission) {
+ err = inode->i_op->permission(inode, MAY_EXEC);
+ if (err)
+ return ERR_PTR(err);
+ }
+
+ /*
+ * See if the low-level filesystem might want
+ * to use its own hash..
+ */
+ if (base->d_op && base->d_op->d_hash) {
+ err = base->d_op->d_hash(base, name);
+ if (err < 0)
+ return ERR_PTR(err);
+ }
+
+ /*
+ * Don't bother with __d_lookup: callers are for creat as
+ * well as unlink, so a lot of the time it would cost
+ * a double lookup.
+ */
+ dentry = d_lookup(nd->path.dentry, &nd->last);
+
+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
+ int status = dentry->d_op->d_revalidate(dentry, nd);
+ if (unlikely(status <= 0)) {
+ /*
+ * The dentry failed validation.
+ * If d_revalidate returned 0 attempt to invalidate
+ * the dentry otherwise d_revalidate is asking us
+ * to return a fail status.
+ */
+ if (!status) {
+ if (!d_invalidate(dentry)) {
+ dput(dentry);
+ dentry = NULL;
+ }
+ } else {
+ dput(dentry);
+ return ERR_PTR(status);
+ }
+ }
+ }
+
+ if (!dentry) {
+ struct dentry *old;
+ /* Don't create child dentry for a dead directory. */
+ if (unlikely(IS_DEADDIR(inode)))
+ return ERR_PTR(-ENOENT);
+
+ dentry = d_alloc(base, name);
+ if (unlikely(!dentry))
+ return ERR_PTR(-ENOMEM);
+
+ old = inode->i_op->lookup(inode, dentry, nd);
+ if (unlikely(old)) {
+ dput(dentry);
+ dentry = old;
+ }
+ }
+ return dentry;
+}
+
+int __init kern_sys_unlink(const char *pathname)
+{
+ int error;
+ struct dentry *dentry;
+ struct nameidata nd;
+ struct inode *inode = NULL;
+
+ error = path_lookup(pathname, LOOKUP_PARENT, &nd);
+ if (error)
+ return error;
+
+ error = -EISDIR;
+ if (nd.last_type != LAST_NORM)
+ goto out_path;
+
+ nd.flags &= ~LOOKUP_PARENT;
+
+ mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+ dentry = lookup_hash(&nd);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ /* Why not before? Because we want correct error value */
+ if (nd.last.name[nd.last.len])
+ goto slashes;
+ inode = dentry->d_inode;
+ if (inode)
+ atomic_inc(&inode->i_count);
+ error = mnt_want_write(nd.path.mnt);
+ if (error)
+ goto out_dput;
+ error = vfs_unlink(nd.path.dentry->d_inode, dentry);
+
+ mnt_drop_write(nd.path.mnt);
+ out_dput:
+ dput(dentry);
+ }
+ mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+ if (inode)
+ iput(inode); /* truncate the inode here */
+out_path:
+ path_put(&nd.path);
+ return error;
+
+slashes:
+ error = !dentry->d_inode ? -ENOENT :
+ S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
+ goto out_dput;
+}
+
+/* This functions is taken from fs/stat.c */
+static int __init cp_new_stat(struct kstat *stat, struct stat *statbuf)
+{
+ struct stat tmp;
+
+#if BITS_PER_LONG == 32
+ if (!old_valid_dev(stat->dev) || !old_valid_dev(stat->rdev))
+ return -EOVERFLOW;
+#else
+ if (!new_valid_dev(stat->dev) || !new_valid_dev(stat->rdev))
+ return -EOVERFLOW;
+#endif
+
+ memset(&tmp, 0, sizeof(tmp));
+#if BITS_PER_LONG == 32
+ tmp.st_dev = old_encode_dev(stat->dev);
+#else
+ tmp.st_dev = new_encode_dev(stat->dev);
+#endif
+ tmp.st_ino = stat->ino;
+ if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
+ return -EOVERFLOW;
+ tmp.st_mode = stat->mode;
+ tmp.st_nlink = stat->nlink;
+ if (tmp.st_nlink != stat->nlink)
+ return -EOVERFLOW;
+ SET_UID(tmp.st_uid, stat->uid);
+ SET_GID(tmp.st_gid, stat->gid);
+#if BITS_PER_LONG == 32
+ tmp.st_rdev = old_encode_dev(stat->rdev);
+#else
+ tmp.st_rdev = new_encode_dev(stat->rdev);
+#endif
+#if BITS_PER_LONG == 32
+ if (stat->size > MAX_NON_LFS)
+ return -EOVERFLOW;
+#endif
+ tmp.st_size = stat->size;
+ tmp.st_atime = stat->atime.tv_sec;
+ tmp.st_mtime = stat->mtime.tv_sec;
+ tmp.st_ctime = stat->ctime.tv_sec;
+#ifdef STAT_HAVE_NSEC
+ tmp.st_atime_nsec = stat->atime.tv_nsec;
+ tmp.st_mtime_nsec = stat->mtime.tv_nsec;
+ tmp.st_ctime_nsec = stat->ctime.tv_nsec;
+#endif
+ tmp.st_blocks = stat->blocks;
+ tmp.st_blksize = stat->blksize;
+
+ memcpy(statbuf, &tmp, sizeof(tmp));
+ return 0;
+}
+
+int __init kern_sys_newlstat(const char *filename, struct stat *statbuf)
+{
+ int error;
+ struct path path;
+ struct kstat kstat;
+
+ error = kern_path(filename, 0, &path);
+ if (error)
+ return error;
+
+ error = vfs_getattr(path.mnt, path.dentry, &kstat);
+ if (error)
+ goto out;
+
+ cp_new_stat(&kstat, statbuf);
+out:
+ path_put(&path);
+ return error;
+}
+
+int __init kern_sys_mkdir(const char *pathname, int mode)
+{
+ int error;
+ struct dentry *dentry;
+ struct nameidata nd;
+
+ error = path_lookup(pathname, LOOKUP_PARENT, &nd);
+ if (error)
+ goto out_err;
+
+ dentry = lookup_create(&nd, 1);
+ if (IS_ERR(dentry)) {
+ error = PTR_ERR(dentry);
+ goto out_unlock;
+ }
+
+ if (!IS_POSIXACL(nd.path.dentry->d_inode))
+ mode &= ~current_umask();
+
+ error = mnt_want_write(nd.path.mnt);
+ if (error)
+ goto out_dput;
+
+ error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
+
+ mnt_drop_write(nd.path.mnt);
+out_dput:
+ dput(dentry);
+out_unlock:
+ mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+ path_put(&nd.path);
+out_err:
+ return error;
+}
+
+int __init kern_sys_rmdir(const char *pathname)
+{
+ int error;
+ struct dentry *dentry;
+ struct nameidata nd;
+
+ error = path_lookup(pathname, LOOKUP_PARENT, &nd);
+ if (error)
+ return error;
+
+ switch(nd.last_type) {
+ case LAST_DOTDOT:
+ error = -ENOTEMPTY;
+ goto exit1;
+ case LAST_DOT:
+ error = -EINVAL;
+ goto exit1;
+ case LAST_ROOT:
+ error = -EBUSY;
+ goto exit1;
+ }
+
+ nd.flags &= ~LOOKUP_PARENT;
+
+ mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+
+ dentry = lookup_hash(&nd);
+ if (IS_ERR(dentry)) {
+ error = PTR_ERR(dentry);
+ goto exit2;
+ }
+
+ error = mnt_want_write(nd.path.mnt);
+ if (error)
+ goto exit3;
+
+ error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
+
+ mnt_drop_write(nd.path.mnt);
+exit3:
+ dput(dentry);
+exit2:
+ mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+exit1:
+ path_put(&nd.path);
+ return error;
+}
+
+int __init kern_sys_mknod(const char *filename, int mode, unsigned dev)
+{
+ int error;
+ struct dentry *dentry;
+ struct nameidata nd;
+
+ if (S_ISDIR(mode))
+ return -EPERM;
+
+ error = path_lookup(filename, LOOKUP_PARENT, &nd);
+ if (error)
+ return error;
+
+ dentry = lookup_create(&nd, 0);
+ if (IS_ERR(dentry)) {
+ error = PTR_ERR(dentry);
+ goto out_unlock;
+ }
+
+ if (!IS_POSIXACL(nd.path.dentry->d_inode))
+ mode &= ~current_umask();
+
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFIFO:
+ case S_IFSOCK:
+ case 0: /* zero mode translates to S_IFREG */
+ break;
+ case S_IFDIR:
+ error = -EPERM;
+ goto out_dput;
+ default:
+ error = -EINVAL;
+ goto out_dput;
+ }
+
+ error = mnt_want_write(nd.path.mnt);
+ if (error)
+ goto out_dput;
+
+ switch (mode & S_IFMT) {
+ case 0:
+ case S_IFREG:
+ error = vfs_create(nd.path.dentry->d_inode,dentry, mode, &nd);
+ break;
+
+ case S_IFCHR:
+ case S_IFBLK:
+ error = vfs_mknod(nd.path.dentry->d_inode,dentry, mode,
+ new_decode_dev(dev));
+ break;
+
+ case S_IFIFO:
+ case S_IFSOCK:
+ error = vfs_mknod(nd.path.dentry->d_inode,dentry, mode, 0);
+ break;
+ }
+
+ mnt_drop_write(nd.path.mnt);
+out_dput:
+ dput(dentry);
+out_unlock:
+ mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+ path_put(&nd.path);
+
+ return error;
+}
+
+static int __init chown_common(struct path *path, uid_t user, gid_t group)
+{
+ int error;
+ struct iattr newattrs;
+ struct inode *inode = path->dentry->d_inode;
+
+ newattrs.ia_valid = ATTR_CTIME;
+ if (user != (uid_t) -1) {
+ newattrs.ia_valid |= ATTR_UID;
+ newattrs.ia_uid = user;
+ }
+ if (group != (gid_t) -1) {
+ newattrs.ia_valid |= ATTR_GID;
+ newattrs.ia_gid = group;
+ }
+ if (!S_ISDIR(inode->i_mode))
+ newattrs.ia_valid |=
+ ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
+
+ mutex_lock(&inode->i_mutex);
+ error = notify_change(path->dentry, &newattrs);
+ mutex_unlock(&inode->i_mutex);
+
+ return error;
+}
+
+int __init kern_sys_chown(const char *filename, uid_t user, gid_t group)
+{
+ int error;
+ struct path path;
+
+ error = kern_path(filename, LOOKUP_FOLLOW, &path);
+ if (error)
+ goto out;
+
+ error = mnt_want_write(path.mnt);
+ if (error)
+ goto out_release;
+
+ error = chown_common(&path, user, group);
+
+ mnt_drop_write(path.mnt);
+out_release:
+ path_put(&path);
+out:
+ return error;
+}
+
+int __init kern_sys_chmod(const char *filename, mode_t mode)
+{
+ int error;
+ struct path path;
+ struct inode *inode;
+ struct iattr newattrs;
+
+ error = kern_path(filename, LOOKUP_FOLLOW, &path);
+ if (error)
+ goto out;
+
+ inode = path.dentry->d_inode;
+
+ error = mnt_want_write(path.mnt);
+ if (error)
+ goto dput_and_out;
+
+ mutex_lock(&inode->i_mutex);
+
+ if (mode == (mode_t) -1)
+ mode = inode->i_mode;
+
+ newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+ newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+
+ error = notify_change(path.dentry, &newattrs);
+
+ mutex_unlock(&inode->i_mutex);
+ mnt_drop_write(path.mnt);
+dput_and_out:
+ path_put(&path);
+out:
+ return error;
+}
+
+int __init kern_sys_open(const char *filename, int flags, int mode)
+{
+ int fd;
+
+ if (force_o_largefile())
+ flags |= O_LARGEFILE;
+
+ fd = get_unused_fd_flags(flags);
+ if (fd >= 0) {
+ struct file *f = do_filp_open(AT_FDCWD, filename, flags,
+ mode, 0);
+ if (IS_ERR(f)) {
+ put_unused_fd(fd);
+ fd = PTR_ERR(f);
+ } else {
+ fd_install(fd, f);
+ }
+ }
+ return fd;
+}
+
+int __init kern_sys_symlink(const char *oldname, const char *newname)
+{
+ int error;
+ struct dentry *dentry;
+ struct nameidata nd;
+
+ error = path_lookup(newname, LOOKUP_PARENT, &nd);
+ if (error)
+ goto out_putname;
+
+ dentry = lookup_create(&nd, 0);
+ if (IS_ERR(dentry)) {
+ error = PTR_ERR(dentry);
+ goto out_unlock;
+ }
+
+ error = mnt_want_write(nd.path.mnt);
+ if (error)
+ goto out_dput;
+
+ error = vfs_symlink(nd.path.dentry->d_inode, dentry, oldname);
+
+ mnt_drop_write(nd.path.mnt);
+out_dput:
+ dput(dentry);
+out_unlock:
+ mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+ path_put(&nd.path);
+out_putname:
+ return error;
+}
+
+int __init kern_sys_lchown(const char *filename, uid_t user, gid_t group)
+{
+ int error;
+ struct path path;
+
+ error = kern_path(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
+ if (error)
+ goto out;
+
+ error = mnt_want_write(path.mnt);
+ if (error)
+ goto out_release;
+
+ error = chown_common(&path, user, group);
+
+ mnt_drop_write(path.mnt);
+out_release:
+ path_put(&path);
+out:
+ return error;
+}
--
1.7.2.2
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/