[TOMOYO #7 09/30] Access control part of tamper-proof device filesystem.

From: Tetsuo Handa
Date: Fri Apr 04 2008 - 08:51:27 EST


This file uses a bit tricky approach for fetching address of blkdev_open()
and chrdev_open() so that this filesystem can be compiled as a module.
If I'm permitted to add EXPORT_SYMBOL for blkdev_open() and chrdev_open(),
I will directly call them.

Signed-off-by: Kentaro Takeda <takedakn@xxxxxxxxxxxxx>
Signed-off-by: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx>
Signed-off-by: Toshiharu Harada <haradats@xxxxxxxxxxxxx>
---
include/linux/syaoran.h | 1023 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1023 insertions(+)

--- /dev/null
+++ linux-2.6.25-rc8-mm1/include/linux/syaoran.h
@@ -0,0 +1,1023 @@
+/*
+ * include/linux/syaoran.h
+ *
+ * Implementation of the Tamper-Proof Device Filesystem.
+ *
+ * Copyright (C) 2005-2008 NTT DATA CORPORATION
+ *
+ * Version: 1.6.0 2008/04/01
+ *
+ */
+/*
+ * A brief description about SYAORAN:
+ *
+ * SYAORAN stands for "Simple Yet All-important Object Realizing Abiding
+ * Nexus". SYAORAN is a filesystem for /dev with Mandatory Access Control.
+ *
+ * /dev cannot be mounted for read-only mode, but this means that files on
+ * /dev might be tampered with. In other words, a device file might have
+ * inappropriate attributes (e.g. /dev/null has char-1-5 attributes).
+ * SYAORAN can restrict combinations of (pathname, attribute) that
+ * the system can create so that all files on this filesystem have appropriate
+ * attributes (e.g. /dev/null has char-1-3 attributes).
+ *
+ * The attribute is one of directory, regular file, FIFO, UNIX domain socket,
+ * symbolic link, character or block device file with major/minor device
+ * numbers.
+ *
+ * You can use SYAORAN alone, but I recommend you to use SYAORAN
+ * with SAKURA and TOMOYO.
+ */
+
+#ifndef _LINUX_SYAORAN_H
+#define _LINUX_SYAORAN_H
+
+#define false 0
+#define true 1
+
+/**
+ * list_for_each_cookie - iterate over a list with cookie.
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @cookie: the &struct list_head to use as a cookie.
+ * @head: the head for your list.
+ *
+ * Same with list_for_each except that this primitive uses cookie
+ * so that we can continue iteration.
+ */
+#define list_for_each_cookie(pos, cookie, head) \
+ for (({ if (!cookie) \
+ cookie = head; }), pos = (cookie)->next; \
+ prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
+ (cookie) = pos, pos = pos->next)
+
+/* The following constants are used to restrict operations.*/
+#define MAY_CREATE 1 /* This file is allowed to mknod() */
+#define MAY_DELETE 2 /* This file is allowed to unlink() */
+#define MAY_CHMOD 4 /* This file is allowed to chmod() */
+#define MAY_CHOWN 8 /* This file is allowed to chown() */
+#define DEVICE_USED 16 /* This block or character device file is used. */
+#define NO_CREATE_AT_MOUNT 32 /* Don't create this file at mount(). */
+
+/* some random number */
+#define SYAORAN_MAGIC 0x2F646576 /* = '/dev' */
+
+static void syaoran_put_super(struct super_block *sb);
+static int syaoran_initialize(struct super_block *sb, void *data);
+static void syaoran_make_initial_nodes(struct super_block *sb);
+static int syaoran_may_create_node(struct dentry *dentry, int mode, int dev);
+static int syaoran_may_modify_node(struct dentry *dentry, unsigned int flags);
+static int syaoran_create_tracelog(struct super_block *sb,
+ const char *filename);
+
+/* Wraps blkdev_open() to trace open operation for block devices. */
+static int (*org_blkdev_open) (struct inode *inode, struct file *filp);
+static struct file_operations wrapped_def_blk_fops;
+
+static int wrapped_blkdev_open(struct inode *inode, struct file *filp)
+{
+ int error = org_blkdev_open(inode, filp);
+ if (error != -ENXIO)
+ syaoran_may_modify_node(filp->f_dentry, DEVICE_USED);
+ return error;
+}
+
+/* Wraps chrdev_open() to trace open operation for character devices. */
+static int (*org_chrdev_open) (struct inode *inode, struct file *filp);
+static struct file_operations wrapped_def_chr_fops;
+
+static int wrapped_chrdev_open(struct inode *inode, struct file *filp)
+{
+ int error = org_chrdev_open(inode, filp);
+ if (error != -ENXIO)
+ syaoran_may_modify_node(filp->f_dentry, DEVICE_USED);
+ return error;
+}
+
+/* lookup_create() without nameidata. Called only while initialization. */
+static struct dentry *lookup_create2(const char *name, struct dentry *base,
+ const bool is_dir)
+{
+ struct dentry *dentry;
+ const int len = name ? strlen(name) : 0;
+ mutex_lock(&base->d_inode->i_mutex);
+ dentry = lookup_one_len(name, base, len);
+ if (IS_ERR(dentry))
+ goto fail;
+ if (!is_dir && name[len] && !dentry->d_inode)
+ goto enoent;
+ return dentry;
+ enoent:
+ dput(dentry);
+ dentry = ERR_PTR(-ENOENT);
+ fail:
+ return dentry;
+}
+
+/* mkdir(). Called only while initialization. */
+static int fs_mkdir(const char *pathname, struct dentry *base, int mode,
+ uid_t user, gid_t group)
+{
+ struct dentry *dentry = lookup_create2(pathname, base, 1);
+ int error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ error = vfs_mkdir(base->d_inode, dentry, mode);
+ if (!error) {
+ lock_kernel();
+ dentry->d_inode->i_uid = user;
+ dentry->d_inode->i_gid = group;
+ unlock_kernel();
+ }
+ dput(dentry);
+ }
+ mutex_unlock(&base->d_inode->i_mutex);
+ return error;
+}
+
+/* mknod(). Called only while initialization. */
+static int fs_mknod(const char *filename, struct dentry *base, int mode,
+ dev_t dev, uid_t user, gid_t group)
+{
+ struct dentry *dentry;
+ int error;
+ switch (mode & S_IFMT) {
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFIFO:
+ case S_IFSOCK:
+ case S_IFREG:
+ break;
+ default:
+ return -EPERM;
+ }
+ dentry = lookup_create2(filename, base, 0);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ error = vfs_mknod(base->d_inode, dentry, mode, dev);
+ if (!error) {
+ lock_kernel();
+ dentry->d_inode->i_uid = user;
+ dentry->d_inode->i_gid = group;
+ unlock_kernel();
+ }
+ dput(dentry);
+ }
+ mutex_unlock(&base->d_inode->i_mutex);
+ return error;
+}
+
+/* symlink(). Called only while initialization. */
+static int fs_symlink(const char *pathname, struct dentry *base, char *oldname,
+ int mode, uid_t user, gid_t group)
+{
+ struct dentry *dentry = lookup_create2(pathname, base, 0);
+ int error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ error = vfs_symlink(base->d_inode, dentry, oldname, S_IALLUGO);
+ if (!error) {
+ lock_kernel();
+ dentry->d_inode->i_mode = mode;
+ dentry->d_inode->i_uid = user;
+ dentry->d_inode->i_gid = group;
+ unlock_kernel();
+ }
+ dput(dentry);
+ }
+ mutex_unlock(&base->d_inode->i_mutex);
+ return error;
+}
+
+/*
+ * Format string.
+ * Leading and trailing whitespaces are removed.
+ * Multiple whitespaces are packed into single space.
+ */
+static void normalize_line(unsigned char *buffer)
+{
+ unsigned char *sp = buffer;
+ unsigned char *dp = buffer;
+ bool first = true;
+ while (*sp && (*sp <= ' ' || *sp >= 127))
+ sp++;
+ while (*sp) {
+ if (!first)
+ *dp++ = ' ';
+ first = false;
+ while (*sp > ' ' && *sp < 127)
+ *dp++ = *sp++;
+ while (*sp && (*sp <= ' ' || *sp >= 127))
+ sp++;
+ }
+ *dp = '\0';
+}
+
+/* Convert text form of filename into binary form. */
+static void unescape(char *filename)
+{
+ char *cp = filename;
+ char c;
+ char d;
+ char e;
+ if (!cp)
+ return;
+ while ((c = *filename++) != '\0') {
+ if (c != '\\') {
+ *cp++ = c;
+ continue;
+ }
+ c = *filename++;
+ if (c == '\\') {
+ *cp++ = c;
+ continue;
+ }
+ if (c < '0' || c > '3')
+ break;
+ d = *filename++;
+ if (d < '0' || d > '7')
+ break;
+ e = *filename++;
+ if (e < '0' || e > '7')
+ break;
+ *(unsigned char *) cp++ = (unsigned char)
+ (((unsigned char) (c - '0') << 6)
+ + ((unsigned char) (d - '0') << 3)
+ + (unsigned char) (e - '0'));
+ }
+ *cp = '\0';
+}
+
+static char *strdup(const char *str)
+{
+ const int len = str ? strlen(str) + 1 : 0;
+ char *cp = kzalloc(len, GFP_KERNEL);
+ if (cp)
+ memmove(cp, str, len);
+ return cp;
+}
+
+/* -1: Not specified, 0: Enforce by default, 1: Accept by default. */
+static int syaoran_default_mode = -1;
+
+#if !defined(MODULE)
+static int __init syaoran_setup(char *str)
+{
+ if (!strcmp(str, "accept"))
+ syaoran_default_mode = 1;
+ else if (!strcmp(str, "enforce"))
+ syaoran_default_mode = 0;
+ return 0;
+}
+
+__setup("SYAORAN=", syaoran_setup);
+#endif
+
+/* The structure for possible device list. */
+struct dev_entry {
+ struct list_head list;
+ /* Binary form of pathname under mount point. Never NULL. */
+ char *name;
+ /*
+ * Mode and permissions.
+ * setuid/setgid/sticky bits are not supported.
+ */
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ dev_t kdev;
+ /*
+ * Binary form of initial contents for the symlink. NULL if not symlink.
+ */
+ char *symlink_data;
+ /* File access control flags. */
+ unsigned int flags;
+ /* Text form of pathname under mount point. Never NULL. */
+ const char *printable_name;
+ /*
+ * Text form of initial contents for the symlink. NULL if not symlink.
+ */
+ const char *printable_symlink_data;
+};
+
+struct syaoran_sb_info {
+ struct list_head list;
+ bool initialize_done; /* False if initialization is in progress. */
+ bool is_permissive_mode; /* True if permissive mode. */
+};
+
+static int syaoran_register_node_info(char *buffer, struct super_block *sb)
+{
+ enum {
+ ARG_FILENAME = 0,
+ ARG_PERMISSION = 1,
+ ARG_UID = 2,
+ ARG_GID = 3,
+ ARG_FLAGS = 4,
+ ARG_DEV_TYPE = 5,
+ ARG_SYMLINK_DATA = 6,
+ ARG_DEV_MAJOR = 6,
+ ARG_DEV_MINOR = 7,
+ MAX_ARG = 8
+ };
+ char *args[MAX_ARG];
+ int i;
+ int error = -EINVAL;
+ unsigned int perm;
+ unsigned int uid;
+ unsigned int gid;
+ unsigned int flags;
+ unsigned int major = 0;
+ unsigned int minor = 0;
+ struct syaoran_sb_info *info =
+ (struct syaoran_sb_info *) sb->s_fs_info;
+ struct dev_entry *entry;
+ if (!info)
+ return -EINVAL;
+ memset(args, 0, sizeof(args));
+ args[0] = buffer;
+ for (i = 1; i < MAX_ARG; i++) {
+ args[i] = strchr(args[i - 1] + 1, ' ');
+ if (!args[i])
+ break;
+ *args[i]++ = '\0';
+ }
+ /*
+ printk(KERN_DEBUG "<%s> <%s> <%s> <%s> <%s> <%s> <%s> <%s>\n",
+ args[0], args[1], args[2], args[3], args[4], args[5], args[6],
+ args[7]);
+ */
+ if (!args[ARG_FILENAME] || !args[ARG_PERMISSION] || !args[ARG_UID] ||
+ !args[ARG_GID] || !args[ARG_DEV_TYPE] || !args[ARG_FLAGS])
+ goto out;
+ if (sscanf(args[ARG_PERMISSION], "%o", &perm) != 1 ||
+ !(perm <= 0777) || sscanf(args[ARG_UID], "%u", &uid) != 1 ||
+ sscanf(args[ARG_GID], "%u", &gid) != 1 ||
+ sscanf(args[ARG_FLAGS], "%u", &flags) != 1 ||
+ *(args[ARG_DEV_TYPE] + 1))
+ goto out;
+ switch (*args[ARG_DEV_TYPE]) {
+ case 'c':
+ perm |= S_IFCHR;
+ if (!args[ARG_DEV_MAJOR] ||
+ sscanf(args[ARG_DEV_MAJOR], "%u", &major) != 1 ||
+ !args[ARG_DEV_MINOR] ||
+ sscanf(args[ARG_DEV_MINOR], "%u", &minor) != 1)
+ goto out;
+ break;
+ case 'b':
+ perm |= S_IFBLK;
+ if (!args[ARG_DEV_MAJOR] ||
+ sscanf(args[ARG_DEV_MAJOR], "%u", &major) != 1 ||
+ !args[ARG_DEV_MINOR] ||
+ sscanf(args[ARG_DEV_MINOR], "%u", &minor) != 1)
+ goto out;
+ break;
+ case 'l':
+ perm |= S_IFLNK;
+ if (!args[ARG_SYMLINK_DATA])
+ goto out;
+ break;
+ case 'd':
+ perm |= S_IFDIR;
+ break;
+ case 's':
+ perm |= S_IFSOCK;
+ break;
+ case 'p':
+ perm |= S_IFIFO;
+ break;
+ case 'f':
+ perm |= S_IFREG;
+ break;
+ default:
+ goto out;
+ }
+ error = -ENOMEM;
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ goto out;
+ if (S_ISLNK(perm)) {
+ entry->printable_symlink_data = strdup(args[ARG_SYMLINK_DATA]);
+ if (!entry->printable_symlink_data)
+ goto out_freemem;
+ }
+ entry->printable_name = strdup(args[ARG_FILENAME]);
+ if (!entry->printable_name)
+ goto out_freemem;
+ if (S_ISLNK(perm)) {
+ entry->symlink_data = strdup(entry->printable_symlink_data);
+ if (!entry->symlink_data)
+ goto out_freemem;
+ unescape(entry->symlink_data);
+ }
+ entry->name = strdup(entry->printable_name);
+ if (!entry->name)
+ goto out_freemem;
+ unescape(entry->name);
+ {
+ /*
+ * Drop trailing '/', for get_local_absolute_path() doesn't
+ * append trailing '/'.
+ */
+ const int len = strlen(entry->name);
+ if (len && entry->name[len - 1] == '/')
+ entry->name[len - 1] = '\0';
+ }
+ entry->mode = perm;
+ entry->uid = uid;
+ entry->gid = gid;
+ entry->kdev = S_ISCHR(perm) || S_ISBLK(perm) ? MKDEV(major, minor) : 0;
+ entry->flags = flags;
+ list_add_tail(&entry->list, &info->list);
+ /* printk(KERN_DEBUG "Entry added.\n"); */
+ error = 0;
+ out:
+ return error;
+ out_freemem:
+ kfree(entry->printable_symlink_data);
+ kfree(entry->printable_name);
+ kfree(entry->symlink_data);
+ kfree(entry);
+ goto out;
+}
+
+static void syaoran_put_super(struct super_block *sb)
+{
+ struct syaoran_sb_info *info;
+ struct dev_entry *entry;
+ struct dev_entry *tmp;
+ if (!sb)
+ return;
+ info = (struct syaoran_sb_info *) sb->s_fs_info;
+ if (!info)
+ return;
+ sb->s_fs_info = NULL;
+ list_for_each_entry_safe(entry, tmp, &info->list, list) {
+ kfree(entry->name);
+ kfree(entry->symlink_data);
+ kfree(entry->printable_name);
+ kfree(entry->printable_symlink_data);
+ list_del(&entry->list);
+ /* printk(KERN_DEBUG "Entry removed.\n"); */
+ kfree(entry);
+ }
+ kfree(info);
+ printk(KERN_INFO "%s: Unused memory freed.\n", __func__);
+}
+
+static int syaoran_read_config_file(struct file *file, struct super_block *sb)
+{
+ char *buffer;
+ int len;
+ char *cp;
+ unsigned long offset = 0;
+ int error = -ENOMEM;
+ if (!file)
+ return -EINVAL;
+ buffer = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buffer)
+ goto out;
+ while ((len = kernel_read(file, offset, buffer, PAGE_SIZE)) > 0 &&
+ (cp = memchr(buffer, '\n', len)) != NULL) {
+ *cp = '\0';
+ offset += cp - buffer + 1;
+ normalize_line(buffer);
+ if (syaoran_register_node_info(buffer, sb) == -ENOMEM)
+ goto out;
+ }
+ error = 0;
+ out:
+ kfree(buffer);
+ return error;
+}
+
+static void syaoran_make_node(struct dev_entry *entry, struct dentry *root)
+{
+ struct dentry *base = dget(root);
+ char *filename = entry->name;
+ char *name = filename;
+ unsigned int c;
+ const mode_t perm = entry->mode;
+ const uid_t uid = entry->uid;
+ const gid_t gid = entry->gid;
+ goto start;
+ while ((c = *(unsigned char *) filename) != '\0') {
+ if (c == '/') {
+ struct dentry *new_base;
+ const int len = filename - name;
+ *filename = '\0';
+ mutex_lock(&base->d_inode->i_mutex);
+ new_base = lookup_one_len(name, base, len);
+ mutex_unlock(&base->d_inode->i_mutex);
+ dput(base);
+ *filename++ = '/';
+ if (IS_ERR(new_base)) {
+ /*
+ printk(KERN_DEBUG "'%s' = %ld\n", entry->name,
+ PTR_ERR(new_base));
+ */
+ return;
+ } else if (!new_base->d_inode ||
+ !S_ISDIR(new_base->d_inode->i_mode)) {
+ /*
+ printk(KERN_DEBUG
+ "Directory '%s' does not exist.\n",
+ entry->name);
+ */
+ dput(new_base);
+ return;
+ }
+ /*
+ printk(KERN_DEBUG "Directory '%s' exists.\n",
+ entry->name);
+ */
+ base = new_base;
+ start:
+ name = filename;
+ } else {
+ filename++;
+ }
+ }
+ filename = (char *) name;
+ if (S_ISLNK(perm))
+ fs_symlink(filename, base, entry->symlink_data, perm, uid, gid);
+ else if (S_ISDIR(perm))
+ fs_mkdir(filename, base, perm ^ S_IFDIR, uid, gid);
+ else if (S_ISSOCK(perm) || S_ISFIFO(perm) || S_ISREG(perm))
+ fs_mknod(filename, base, perm, 0, uid, gid);
+ else if (S_ISCHR(perm) || S_ISBLK(perm))
+ fs_mknod(filename, base, perm, entry->kdev, uid, gid);
+ dput(base);
+}
+
+/* Create files according to the policy file. */
+static void syaoran_make_initial_nodes(struct super_block *sb)
+{
+ struct syaoran_sb_info *info;
+ struct dev_entry *entry;
+ if (!sb)
+ return;
+ info = (struct syaoran_sb_info *) sb->s_fs_info;
+ if (!info)
+ return;
+ if (info->is_permissive_mode) {
+ syaoran_create_tracelog(sb, ".syaoran");
+ syaoran_create_tracelog(sb, ".syaoran_all");
+ }
+ list_for_each_entry(entry, &info->list, list) {
+ if ((entry->flags & NO_CREATE_AT_MOUNT) == 0)
+ syaoran_make_node(entry, sb->s_root);
+ }
+ info->initialize_done = true;
+}
+
+/* Read policy file. */
+static int syaoran_initialize(struct super_block *sb, void *data)
+{
+ int error = -EINVAL;
+ struct file *f;
+ char *filename = (char *) data;
+ bool is_permissive_mode = syaoran_default_mode;
+ static bool first = true;
+ if (first) {
+ first = false;
+ printk(KERN_INFO "SYAORAN: 1.6.0 2008/04/01\n");
+ }
+ {
+ struct inode *inode = new_inode(sb);
+ if (!inode)
+ return -EINVAL;
+ /* Create /dev/ram0 to get the value of blkdev_open(). */
+ init_special_inode(inode, S_IFBLK | 0666, MKDEV(1, 0));
+ wrapped_def_blk_fops = *inode->i_fop;
+ iput(inode);
+ org_blkdev_open = wrapped_def_blk_fops.open;
+ wrapped_def_blk_fops.open = wrapped_blkdev_open;
+ }
+ {
+ struct inode *inode = new_inode(sb);
+ if (!inode)
+ return -EINVAL;
+ /* Create /dev/null to get the value of chrdev_open(). */
+ init_special_inode(inode, S_IFCHR | 0666, MKDEV(1, 3));
+ wrapped_def_chr_fops = *inode->i_fop;
+ iput(inode);
+ org_chrdev_open = wrapped_def_chr_fops.open;
+ wrapped_def_chr_fops.open = wrapped_chrdev_open;
+ }
+ if (!filename) {
+ printk(KERN_WARNING "SYAORAN: Missing config-file path.\n");
+ return -EINVAL;
+ }
+ /* If mode is given with mount operation, use it. */
+ if (!strncmp(filename, "accept=", 7)) {
+ filename += 7;
+ is_permissive_mode = true;
+ } else if (!strncmp(filename, "enforce=", 8)) {
+ filename += 8;
+ is_permissive_mode = false;
+ } else if (syaoran_default_mode == -1) {
+ /*
+ * If mode is not given with command line,
+ * abort mount.
+ */
+ printk(KERN_WARNING
+ "SYAORAN: Missing 'accept=' or 'enforce='.\n");
+ return -EINVAL;
+ }
+ f = filp_open(filename, O_RDONLY, 0600);
+ if (IS_ERR(f)) {
+ printk(KERN_WARNING "SYAORAN: Can't open '%s'\n", filename);
+ return -EINVAL;
+ }
+ if (!S_ISREG(f->f_dentry->d_inode->i_mode))
+ goto out;
+ sb->s_fs_info = kzalloc(sizeof(struct syaoran_sb_info), GFP_KERNEL);
+ if (!sb->s_fs_info)
+ goto out;
+ ((struct syaoran_sb_info *) sb->s_fs_info)->is_permissive_mode
+ = is_permissive_mode;
+ INIT_LIST_HEAD(&((struct syaoran_sb_info *) sb->s_fs_info)->list);
+ printk(KERN_INFO "SYAORAN: Reading '%s'\n", filename);
+ error = syaoran_read_config_file(f, sb);
+ out:
+ if (error)
+ printk(KERN_WARNING "SYAORAN: Can't read '%s'\n", filename);
+ filp_close(f, NULL);
+ return error;
+}
+
+/* Get absolute pathname from mount point. */
+static int get_local_absolute_path(struct dentry *dentry, char *buffer,
+ int buflen)
+{
+ /***** CRITICAL SECTION START *****/
+ char *start = buffer;
+ char *end = buffer + buflen;
+ int namelen;
+
+ if (buflen < 256)
+ goto out;
+
+ *--end = '\0';
+ buflen--;
+ for (;;) {
+ struct dentry *parent;
+ if (IS_ROOT(dentry))
+ break;
+ parent = dentry->d_parent;
+ namelen = dentry->d_name.len;
+ buflen -= namelen + 1;
+ if (buflen < 0)
+ goto out;
+ end -= namelen;
+ memcpy(end, dentry->d_name.name, namelen);
+ *--end = '/';
+ dentry = parent;
+ }
+ if (*end == '/') {
+ buflen++;
+ end++;
+ }
+ namelen = dentry->d_name.len;
+ buflen -= namelen;
+ if (buflen < 0)
+ goto out;
+ end -= namelen;
+ memcpy(end, dentry->d_name.name, namelen);
+ memmove(start, end, strlen(end) + 1);
+ return 0;
+ out:
+ return -ENOMEM;
+ /***** CRITICAL SECTION END *****/
+}
+
+/* Get absolute pathname of the given dentry from mount point. */
+static int local_ccs_realpath_from_dentry(struct dentry *dentry, char *newname,
+ int newname_len)
+{
+ /***** CRITICAL SECTION START *****/
+ int error;
+ struct dentry *d_dentry;
+ if (!dentry || !newname || newname_len <= 0)
+ return -EINVAL;
+ d_dentry = dget(dentry);
+ spin_lock(&dcache_lock);
+ error = get_local_absolute_path(d_dentry, newname, newname_len);
+ spin_unlock(&dcache_lock);
+ dput(d_dentry);
+ return error;
+ /***** CRITICAL SECTION END *****/
+}
+
+static int syaoran_check_flags(struct syaoran_sb_info *info,
+ struct dentry *dentry,
+ int mode, int dev, unsigned int flags)
+{
+ int error;
+ /*
+ * I use static buffer, for local_ccs_realpath_from_dentry() needs
+ * dcache_lock.
+ */
+ static char filename[PAGE_SIZE];
+ static DEFINE_SPINLOCK(lock);
+ /***** CRITICAL SECTION START *****/
+ spin_lock(&lock);
+ memset(filename, 0, sizeof(filename));
+ error = local_ccs_realpath_from_dentry(dentry, filename,
+ sizeof(filename) - 1);
+ if (!error) {
+ struct dev_entry *entry;
+ error = -EPERM;
+ list_for_each_entry(entry, &info->list, list) {
+ if ((mode & S_IFMT) != (entry->mode & S_IFMT))
+ continue;
+ if ((S_ISBLK(mode) || S_ISCHR(mode)) &&
+ dev != entry->kdev)
+ continue;
+ if (strcmp(entry->name, filename + 1))
+ continue;
+ if (info->is_permissive_mode) {
+ entry->flags |= flags;
+ error = 0;
+ } else if ((entry->flags & flags) == flags)
+ error = 0;
+ break;
+ }
+ }
+ if (!error) {
+ const char *name;
+ struct task_struct *task = current;
+ const uid_t uid = task->fsuid;
+ const gid_t gid = task->fsgid;
+ const mode_t perm = mode & 0777;
+ flags &= ~DEVICE_USED;
+ {
+ char *end = filename + sizeof(filename) - 1;
+ const char *cp = strchr(filename, '\0') - 1;
+ while (cp > filename && end > cp &&
+ end > filename + 16) {
+ const unsigned char c = *cp--;
+ if (c == '\\') {
+ *--end = '\\';
+ *--end = '\\';
+ } else if (c > ' ' && c < 127) {
+ *--end = c;
+ } else {
+ *--end = (c & 7) + '0';
+ *--end = ((c >> 3) & 7) + '0';
+ *--end = (c >> 6) + '0';
+ *--end = '\\';
+ }
+ }
+ name = end;
+ }
+ switch (mode & S_IFMT) {
+ case S_IFCHR:
+ printk(KERN_DEBUG
+ "SYAORAN-ERROR: %s %3o %3u %3u %2u %c %3u %3u\n",
+ name, perm, uid, gid, flags, 'c',
+ MAJOR(dev), MINOR(dev));
+ break;
+ case S_IFBLK:
+ printk(KERN_DEBUG
+ "SYAORAN-ERROR: %s %3o %3u %3u %2u %c %3u %3u\n",
+ name, perm, uid, gid, flags, 'b',
+ MAJOR(dev), MINOR(dev));
+ break;
+ case S_IFIFO:
+ printk(KERN_DEBUG
+ "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name,
+ perm, uid, gid, flags, 'p');
+ break;
+ case S_IFSOCK:
+ printk(KERN_DEBUG
+ "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name,
+ perm, uid, gid, flags, 's');
+ break;
+ case S_IFDIR:
+ printk(KERN_DEBUG
+ "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name,
+ perm, uid, gid, flags, 'd');
+ break;
+ case S_IFLNK:
+ printk(KERN_DEBUG
+ "SYAORAN-ERROR: %s %3o %3u %3u %2u %c %s\n",
+ name, perm, uid, gid, flags, 'l', "unknown");
+ break;
+ case S_IFREG:
+ printk(KERN_DEBUG
+ "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name,
+ perm, uid, gid, flags, 'f');
+ break;
+ }
+ }
+ spin_unlock(&lock);
+ /***** CRITICAL SECTION END *****/
+ return error;
+}
+
+/* Check whether the given dentry is allowed to mknod. */
+static int syaoran_may_create_node(struct dentry *dentry, int mode, int dev)
+{
+ struct syaoran_sb_info *info
+ = (struct syaoran_sb_info *) dentry->d_sb->s_fs_info;
+ if (!info) {
+ printk(KERN_WARNING "%s: dentry->d_sb->s_fs_info == NULL\n",
+ __func__);
+ return -EPERM;
+ }
+ if (!info->initialize_done)
+ return 0;
+ return syaoran_check_flags(info, dentry, mode, dev, MAY_CREATE);
+}
+
+/* Check whether the given dentry is allowed to chmod/chown/unlink. */
+static int syaoran_may_modify_node(struct dentry *dentry, unsigned int flags)
+{
+ struct syaoran_sb_info *info
+ = (struct syaoran_sb_info *) dentry->d_sb->s_fs_info;
+ if (!info) {
+ printk(KERN_WARNING "%s: dentry->d_sb->s_fs_info == NULL\n",
+ __func__);
+ return -EPERM;
+ }
+ if (flags == DEVICE_USED && !info->is_permissive_mode)
+ return 0;
+ if (!dentry->d_inode)
+ return -ENOENT;
+ return syaoran_check_flags(info, dentry, dentry->d_inode->i_mode,
+ dentry->d_inode->i_rdev, flags);
+}
+
+/*
+ * The following structure and codes are used for transferring data
+ * to interfaces files.
+ */
+
+struct syaoran_read_struct {
+ char *buf; /* Buffer for reading. */
+ int avail; /* Bytes available for reading. */
+ struct super_block *sb; /* The super_block of this partition. */
+ struct dev_entry *entry; /* The entry currently reading from. */
+ bool read_all; /* Print all entries? */
+ struct list_head *pos; /* Current position. */
+};
+
+static void syaoran_read_table(struct syaoran_read_struct *head, char *buf,
+ int count)
+{
+ struct super_block *sb = head->sb;
+ struct syaoran_sb_info *info = (struct syaoran_sb_info *) sb->s_fs_info;
+ struct list_head *pos;
+ const bool read_all = head->read_all;
+ if (!info)
+ return;
+ if (!head->pos)
+ return;
+ list_for_each_cookie(pos, head->pos, &info->list) {
+ struct dev_entry *entry
+ = list_entry(pos, struct dev_entry, list);
+ const unsigned int flags
+ = read_all ? entry->flags : entry->flags & ~DEVICE_USED;
+ const char *name = entry->printable_name;
+ const uid_t uid = entry->uid;
+ const gid_t gid = entry->gid;
+ const mode_t perm = entry->mode & 0777;
+ int len = 0;
+ switch (entry->mode & S_IFMT) {
+ case S_IFCHR:
+ if (!head->read_all && !(entry->flags & DEVICE_USED))
+ break;
+ len = snprintf(buf, count,
+ "%-20s %3o %3u %3u %2u %c %3u %3u\n",
+ name, perm, uid, gid, flags, 'c',
+ MAJOR(entry->kdev), MINOR(entry->kdev));
+ break;
+ case S_IFBLK:
+ if (!head->read_all && !(entry->flags & DEVICE_USED))
+ break;
+ len = snprintf(buf, count,
+ "%-20s %3o %3u %3u %2u %c %3u %3u\n",
+ name, perm, uid, gid, flags, 'b',
+ MAJOR(entry->kdev), MINOR(entry->kdev));
+ break;
+ case S_IFIFO:
+ len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n",
+ name, perm, uid, gid, flags, 'p');
+ break;
+ case S_IFSOCK:
+ len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n",
+ name, perm, uid, gid, flags, 's');
+ break;
+ case S_IFDIR:
+ len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n",
+ name, perm, uid, gid, flags, 'd');
+ break;
+ case S_IFLNK:
+ len = snprintf(buf, count,
+ "%-20s %3o %3u %3u %2u %c %s\n", name,
+ perm, uid, gid, flags, 'l',
+ entry->printable_symlink_data);
+ break;
+ case S_IFREG:
+ len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n",
+ name, perm, uid, gid, flags, 'f');
+ break;
+ }
+ if (len < 0 || count <= len)
+ break;
+ count -= len;
+ buf += len;
+ head->avail += len;
+ }
+}
+
+static int syaoran_trace_open(struct inode *inode, struct file *file)
+{
+ struct syaoran_read_struct *head = kzalloc(sizeof(*head), GFP_KERNEL);
+ if (!head)
+ return -ENOMEM;
+ head->sb = inode->i_sb;
+ head->read_all
+ = (strcmp(file->f_dentry->d_name.name, ".syaoran_all") == 0);
+ head->pos = &((struct syaoran_sb_info *) head->sb->s_fs_info)->list;
+ /* Don't allow open() after unmount() */
+ if (head->sb->s_fs_info)
+ head->buf = kzalloc(PAGE_SIZE * 2, GFP_KERNEL);
+ if (!head->buf) {
+ kfree(head);
+ return -ENOMEM;
+ }
+ file->private_data = head;
+ return 0;
+}
+
+static int syaoran_trace_release(struct inode *inode, struct file *file)
+{
+ struct syaoran_read_struct *head = file->private_data;
+ kfree(head->buf);
+ kfree(head);
+ file->private_data = NULL;
+ return 0;
+}
+
+static ssize_t syaoran_trace_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct syaoran_read_struct *head
+ = (struct syaoran_read_struct *) file->private_data;
+ int len = head->avail;
+ char *cp = head->buf;
+ if (!access_ok(VERIFY_WRITE, buf, count))
+ return -EFAULT;
+ syaoran_read_table(head, cp + len, PAGE_SIZE * 2 - len);
+ len = head->avail;
+ if (len > count)
+ len = count;
+ if (len > 0) {
+ if (copy_to_user(buf, cp, len))
+ return -EFAULT;
+ head->avail -= len;
+ memmove(cp, cp + len, head->avail);
+ }
+ return len;
+}
+
+static struct file_operations syaoran_trace_operations = {
+ .open = syaoran_trace_open,
+ .release = syaoran_trace_release,
+ .read = syaoran_trace_read,
+};
+
+/* Create interface files for reading status. */
+static int syaoran_create_tracelog(struct super_block *sb, const char *filename)
+{
+ struct dentry *base = dget(sb->s_root);
+ struct dentry *dentry = lookup_create2(filename, base, 0);
+ int error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ struct inode *inode = new_inode(sb);
+ if (inode) {
+ inode->i_mode = S_IFREG | 0400;
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+ inode->i_blocks = 0;
+ inode->i_mapping->a_ops = &syaoran_aops;
+ inode->i_mapping->backing_dev_info
+ = &syaoran_backing_dev_info;
+ inode->i_op = &syaoran_file_inode_operations;
+ inode->i_ctime = CURRENT_TIME;
+ inode->i_mtime = inode->i_ctime;
+ inode->i_atime = inode->i_mtime;
+ inode->i_fop = &syaoran_trace_operations;
+ d_instantiate(dentry, inode);
+ dget(dentry); /* Extra count - pin the dentry in core */
+ error = 0;
+ }
+ dput(dentry);
+ }
+ mutex_unlock(&base->d_inode->i_mutex);
+ dput(base);
+ return error;
+}
+
+#endif

--
--
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/