Messages in this thread | | | Subject | [RFC][2.4 PATCH] A restricted /dev filesystem. | From | Tetsuo Handa <> | Date | Mon, 1 Nov 2004 21:15:47 +0900 |
| |
Purpose:
Ensure that device files in /dev directory are never faked. For example, the cracker who got root privileges can't do like 'rm -f /dev/null; mknod /dev/null b 8 0'.
Making root-fs read-only will improve security. But since some device files in /dev needs to be chown()/chmod()-able, some special technique is required to make root-fs read-only.
Devfs is a nice choice, but devfs can't ensure the bindings between filenames and their device numbers (i.e. /dev/null may be 'block-8-0', which should be 'char-1-3'.)
So I want a filesystem that can ensure the bindings. And here is my work, but since I'm a newbie developing a new filesystem, I'm afraid there are some bugs, especially locking related bugs. So, could someone please check the code? I made this code based on /usr/src/linux-2.4.27/fs/ramfs/inode.c .
Code Name:
SYAORAN (Simple Yet All-important Object Realizing Abiding Nexus)
Specifications:
No regular files allowed. (since I think /dev needn't to keep regular files.) No hardlinks allowed. (since I think hardlinks are unused for /dev .) "Files which are created at mount time" and "files which can be created/deleted after mount time" are given via a config file using "mount -o" option. Modified part starts with '/***** SYAORAN start. *****/' and ends with '/***** SYAORAN end. *****/' .
Config File Format:
There are 10 elements that describes an entry.
'filename' is a fullpath seen from the mount point, but no leading / . 'permission' is specified in an octal format, between 000 and 777. 'uid' and 'gid' are specified in a decimal format. 'may_create' is a boolean flag that controls whether the entry can be created after mount. 'may_delete' is a boolean flag that controls whether the entry can be deleted after mount. 'type' is a character that specifies the device type, one of 'd' (dir), 'f' (fifo), 's' (socket), 'c' (char-dev), 'b' (blk-dev), 'l' (symlink). 'symlink_data' is an initial content that is used when creating a symlink at mount time. 'major' and 'minor' are device's major/minor number specified in a decimal format.
Lines are separeted by '\n', and all chars that matches "(unsigned char) c <= ' '" are treated as delimiters. The permission, uid, gid are ignored for symlinks.
Usage:
gcc -Wall -O3 -D__KERNEL__ -DMODULE -I/usr/src/linux-2.4.27/include/ -c syaoran.c insmod syaoran.o mkdir -p /mnt/test/ mount -t syaoran -o /root/syaoran.conf none /mnt/test/
Regards....
--- Tetsuo Handa
---------- Example of config file ( /root/syaoran.conf ) ----------
#filename permission uid gid may_create may_delete type [ symlink_data | major minor ] pts 755 0 0 0 0 d shm 755 0 0 0 0 d fd 777 0 0 0 0 l /proc/self/fd stdin 777 0 0 0 0 l /proc/self/fd/0 stdout 777 0 0 0 0 l /proc/self/fd/1 stderr 777 0 0 0 0 l /proc/self/fd/2 null 666 0 0 0 0 c 1 3 zero 666 0 0 0 0 c 1 5 random 644 0 0 0 0 c 1 8 urandom 644 0 0 0 0 c 1 9 tty 666 0 0 0 0 c 5 0 tty0 600 0 0 0 0 c 4 0 tty1 600 0 0 0 0 c 4 1 tty2 600 0 0 0 0 c 4 2 tty3 600 0 0 0 0 c 4 3 tty4 600 0 0 0 0 c 4 4 tty5 600 0 0 0 0 c 4 5 tty6 600 0 0 0 0 c 4 6 tty7 600 0 0 0 0 c 4 7 tty8 600 0 0 0 0 c 4 8 cdrom 777 0 0 1 1 l /dev/scd0 mouse 777 0 0 1 1 l psaux console 600 0 0 1 0 c 5 1 fd0 660 0 19 0 0 b 2 0 fd1 660 0 19 0 0 b 2 1 fd2 660 0 19 0 0 b 2 2 fd3 660 0 19 0 0 b 2 3 hda 660 0 6 0 0 b 3 0 hda1 660 0 6 0 0 b 3 1 hda2 660 0 6 0 0 b 3 2 hda3 660 0 6 0 0 b 3 3 hda5 660 0 6 0 0 b 3 5 hda6 660 0 6 0 0 b 3 6 hda7 660 0 6 0 0 b 3 7 hda8 660 0 6 0 0 b 3 8 hda9 660 0 6 0 0 b 3 9 hda10 660 0 6 0 0 b 3 10 hda11 660 0 6 0 0 b 3 11 initctl 600 0 0 1 1 p log 666 0 0 1 1 s rtc 644 0 0 0 0 c 10 135 ptmx 666 0 0 0 0 c 5 2 ram 777 0 0 1 1 l /dev/ram0 ram0 660 0 6 0 0 b 1 0 ram1 660 0 6 0 0 b 1 1 sda 660 0 6 0 0 b 8 0 initrd 660 0 6 1 0 b 1 250 psaux 600 0 0 0 0 c 10 1 apm_bios 600 0 0 0 0 c 10 134 cpu 755 0 0 0 0 d cpu/0 755 0 0 0 0 d cpu/0/microcode 600 0 0 0 0 c 10 184 ttyS0 660 0 14 0 0 c 4 64 ttyS1 660 0 14 0 0 c 4 65 ttyS2 660 0 14 0 0 c 4 66 ttyS3 660 0 14 0 0 c 4 67 ptal-printd 777 0 0 1 1 l /var/run/ptal-printd gpmctl 700 0 0 1 1 s scd0 660 0 6 0 0 b 11 0
---------- Source Code ----------
/* * Restricted /dev filesystem. [EXPERIMENTAL] * * based on /usr/src/linux-2.4.27/fs/ramfs/inode.c * */
/* * Resizable simple ram filesystem for Linux. * * Copyright (C) 2000 Linus Torvalds. * 2000 Transmeta Corp. * * Usage limits added by David Gibson, Linuxcare Australia. * This file is released under the GPL. */
/* * NOTE! This filesystem is probably most useful * not as a real filesystem, but as an example of * how virtual filesystems can be written. * * It doesn't get much simpler than this. Consider * that this file implements the full semantics of * a POSIX-compliant read-write filesystem. * * Note in particular how the filesystem does not * need to implement any data structures of its own * to keep track of the virtual data: using the VFS * caches is sufficient. */
#include <linux/module.h> #include <linux/fs.h> #include <linux/pagemap.h> #include <linux/init.h> #include <linux/string.h> #include <linux/locks.h>
#include <asm/uaccess.h>
/***** SYAORAN start. *****/ static void syaoran_put_super(struct super_block *sb); static int Syaoran_Initialize(struct super_block *sb, void *data); static void MakeInitialNodes(struct super_block *sb); static int MayCreateNode(struct dentry *dentry, int mode, int dev); static int MayDeleteNode(struct dentry *dentry); /***** SYAORAN end. *****/
/* some random number */ #define SYAORAN_MAGIC 0x2F646576 /* = '/dev' */
static struct super_operations syaoran_ops; static struct address_space_operations syaoran_aops; static struct file_operations syaoran_file_operations; static struct inode_operations syaoran_dir_inode_operations;
static int syaoran_statfs(struct super_block *sb, struct statfs *buf) { buf->f_type = SYAORAN_MAGIC; buf->f_bsize = PAGE_CACHE_SIZE; buf->f_namelen = NAME_MAX; return 0; }
/* * Lookup the data. This is trivial - if the dentry didn't already * exist, we know it is negative. */ static struct dentry * syaoran_lookup(struct inode *dir, struct dentry *dentry) { if (dentry->d_name.len > NAME_MAX) return ERR_PTR(-ENAMETOOLONG); d_add(dentry, NULL); return NULL; }
/* * Read a page. Again trivial. If it didn't already exist * in the page cache, it is zero-filled. */ static int syaoran_readpage(struct file *file, struct page * page) { if (!Page_Uptodate(page)) { memset(kmap(page), 0, PAGE_CACHE_SIZE); kunmap(page); flush_dcache_page(page); SetPageUptodate(page); } UnlockPage(page); return 0; }
static int syaoran_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) { void *addr = kmap(page); if (!Page_Uptodate(page)) { memset(addr, 0, PAGE_CACHE_SIZE); flush_dcache_page(page); SetPageUptodate(page); } SetPageDirty(page); return 0; }
static int syaoran_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) { struct inode *inode = page->mapping->host; loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
kunmap(page); if (pos > inode->i_size) inode->i_size = pos; return 0; }
struct inode *syaoran_get_inode(struct super_block *sb, int mode, int dev) { struct inode * inode = new_inode(sb);
if (inode) { inode->i_mode = mode; inode->i_uid = current->fsuid; inode->i_gid = current->fsgid; inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0; inode->i_rdev = NODEV; inode->i_mapping->a_ops = &syaoran_aops; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; switch (mode & S_IFMT) { default: init_special_inode(inode, mode, dev); break; case S_IFREG: inode->i_fop = &syaoran_file_operations; break; case S_IFDIR: inode->i_op = &syaoran_dir_inode_operations; inode->i_fop = &dcache_dir_ops; break; case S_IFLNK: inode->i_op = &page_symlink_inode_operations; break; } } return inode; }
/* * File creation. Allocate an inode, and we're done.. */ static int syaoran_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev) { /***** SYAORAN start. *****/ if (MayCreateNode(dentry, mode, dev) < 0) return -EPERM; /***** SYAORAN end. *****/ struct inode * inode = syaoran_get_inode(dir->i_sb, mode, dev); int error = -ENOSPC;
if (inode) { if (dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; if (S_ISDIR(mode)) inode->i_mode |= S_ISGID; } d_instantiate(dentry, inode); dget(dentry); /* Extra count - pin the dentry in core */ error = 0; } return error; }
static int syaoran_mkdir(struct inode * dir, struct dentry * dentry, int mode) { return syaoran_mknod(dir, dentry, mode | S_IFDIR, 0); }
static int syaoran_create(struct inode *dir, struct dentry *dentry, int mode) { return syaoran_mknod(dir, dentry, mode | S_IFREG, 0); }
/* * Link a file.. */ static int syaoran_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry) { /***** SYAORAN start. *****/ return -EPERM; /* No hardlinks allowed. */ /***** SYAORAN end. *****/ struct inode *inode = old_dentry->d_inode;
if (S_ISDIR(inode->i_mode)) return -EPERM;
inode->i_nlink++; atomic_inc(&inode->i_count); /* New dentry reference */ dget(dentry); /* Extra pinning count for the created dentry */ d_instantiate(dentry, inode); return 0; }
static inline int syaoran_positive(struct dentry *dentry) { return dentry->d_inode && !d_unhashed(dentry); }
/* * Check that a directory is empty (this works * for regular files too, they'll just always be * considered empty..). * * Note that an empty directory can still have * children, they just all have to be negative.. */ static int syaoran_empty(struct dentry *dentry) { struct list_head *list;
spin_lock(&dcache_lock); list = dentry->d_subdirs.next;
while (list != &dentry->d_subdirs) { struct dentry *de = list_entry(list, struct dentry, d_child);
if (syaoran_positive(de)) { spin_unlock(&dcache_lock); return 0; } list = list->next; } spin_unlock(&dcache_lock); return 1; }
/* * This works for both directories and regular files. * (non-directories will always have empty subdirs) */ static int syaoran_unlink(struct inode * dir, struct dentry *dentry) { /***** SYAORAN start. *****/ if (MayDeleteNode(dentry) < 0) return -EPERM; /***** SYAORAN end. *****/ int retval = -ENOTEMPTY; if (syaoran_empty(dentry)) { struct inode *inode = dentry->d_inode;
inode->i_nlink--; dput(dentry); /* Undo the count from "create" - this does all the work */ retval = 0; } return retval; }
#define syaoran_rmdir syaoran_unlink
/* * The VFS layer already does all the dentry stuff for rename, * we just have to decrement the usage count for the target if * it exists so that the VFS layer correctly free's it when it * gets overwritten. */ static int syaoran_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry) { /***** SYAORAN start. *****/ struct inode *inode = old_dentry->d_inode; if (!inode || MayDeleteNode(old_dentry) < 0 || MayCreateNode(new_dentry, inode->i_mode, inode->i_rdev) < 0) return -EPERM; /***** SYAORAN end. *****/ int error = -ENOTEMPTY;
if (syaoran_empty(new_dentry)) { struct inode *inode = new_dentry->d_inode; if (inode) { inode->i_nlink--; dput(new_dentry); } error = 0; } return error; }
static int syaoran_symlink(struct inode * dir, struct dentry *dentry, const char * symname) { int error;
error = syaoran_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0); if (!error) { int l = strlen(symname)+1; struct inode *inode = dentry->d_inode; error = block_symlink(inode, symname, l); } return error; }
static int syaoran_sync_file(struct file * file, struct dentry *dentry, int datasync) { return 0; }
static struct address_space_operations syaoran_aops = { readpage: syaoran_readpage, writepage: fail_writepage, prepare_write: syaoran_prepare_write, commit_write: syaoran_commit_write };
static struct file_operations syaoran_file_operations = { read: generic_file_read, write: generic_file_write, mmap: generic_file_mmap, fsync: syaoran_sync_file, };
static struct inode_operations syaoran_dir_inode_operations = { create: syaoran_create, lookup: syaoran_lookup, link: syaoran_link, unlink: syaoran_unlink, symlink: syaoran_symlink, mkdir: syaoran_mkdir, rmdir: syaoran_rmdir, mknod: syaoran_mknod, rename: syaoran_rename, };
static struct super_operations syaoran_ops = { statfs: syaoran_statfs, put_inode: force_delete, /***** SYAORAN start. *****/ put_super: syaoran_put_super, /***** SYAORAN end. *****/ };
static struct super_block *syaoran_read_super(struct super_block * sb, void * data, int silent) { struct inode * inode; struct dentry * root;
sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = SYAORAN_MAGIC; sb->s_op = &syaoran_ops; /***** SYAORAN start. *****/ if (Syaoran_Initialize(sb, data) < 0) return NULL; /***** SYAORAN end. *****/ inode = syaoran_get_inode(sb, S_IFDIR | 0755, 0); if (!inode) return NULL;
root = d_alloc_root(inode); if (!root) { iput(inode); return NULL; } sb->s_root = root; /***** SYAORAN start. *****/ MakeInitialNodes(sb); /***** SYAORAN end. *****/ return sb; }
static DECLARE_FSTYPE(syaoran_fs_type, "syaoran", syaoran_read_super, FS_LITTER);
static int __init init_syaoran_fs(void) { return register_filesystem(&syaoran_fs_type); }
static void __exit exit_syaoran_fs(void) { unregister_filesystem(&syaoran_fs_type); }
module_init(init_syaoran_fs) module_exit(exit_syaoran_fs)
MODULE_LICENSE("GPL");
/***** SYAORAN start. *****/
#if !defined(lock_kernel) #include <asm/smplock.h> #endif extern void *kmalloc(size_t, int); extern void kfree(const void *);
/* Similar to lookup_create(). */ static struct dentry *lookup_create2(const char *name, struct dentry *base, int is_dir) { unsigned long hash; struct qstr this; unsigned int c; struct dentry *dentry = ERR_PTR(-EACCES); int len = name ? strlen(name) : 0; this.name = name; this.len = len; down(&base->d_inode->i_sem); if (!len) goto fail; hash = init_name_hash(); while (len--) { c = * (const unsigned char *) name++; if (c == '/' || c == '\0') goto fail; hash = partial_name_hash(c, hash); } this.hash = end_name_hash(hash); dentry = lookup_hash(&this, base); if (IS_ERR(dentry)) goto fail; if (!is_dir && this.name[this.len] && !dentry->d_inode) goto enoent; return dentry; enoent: dput(dentry); dentry = ERR_PTR(-ENOENT); fail: return dentry; }
/* mkdir() used at mount time. */ 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); } up(&base->d_inode->i_sem); return error; }
/* mknod() used at mount time. */ static int fs_mknod(const char *filename, struct dentry *base, int mode, dev_t dev, uid_t user, gid_t group) { switch (mode & S_IFMT) { case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: break; default: return -EPERM; } struct dentry *dentry = lookup_create2(filename, base, 0); int 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); } up(&base->d_inode->i_sem); return error; }
/* symlink() used at mount time. */ static int fs_symlink(const char *pathname, struct dentry *base, char *oldname) { 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); dput(dentry); } up(&base->d_inode->i_sem); return error; }
/* Format a line. Compress multiple delimiters. */ static void NormalizeLine(unsigned char *buffer) { unsigned char *sp = buffer, *dp = buffer; int first = 1; while (*sp && *sp <= ' ') sp++; while (*sp) { if (!first) *dp++ = ' '; first = 0; while (*sp > ' ') *dp++ = *sp++; while (*sp && *sp <= ' ') sp++; } *dp = '\0'; }
/* strdup() */ static char *strdup(const char *str) { char *cp; int len = str ? strlen(str) + 1 : 0; if ((cp = kmalloc(len, GFP_KERNEL)) != NULL) memmove(cp, str, len); return cp; }
typedef struct { char *name; /* Pathname under the mount point, excluding the beggining / . */ mode_t mode; /* Mode and permission */ uid_t uid; /* User */ gid_t gid; /* Group */ kdev_t kdev; /* Device number */ char *symlink_data; /* Symlink's content */ int may_create; /* Can this file created after mount? */ int may_delete; /* Can this file deleted after mount? */ } DEVLIST;
struct syaoran_sb_info { DEVLIST *entry; /* entry's list. */ unsigned int entry_count; /* entry[0] ... entry[entry_count - 1] */ unsigned int buffer_size; /* kmalloc()'ed size for entry[] */ int initialize_done; /* mount operation finished? */ };
/* Add file info to the entry[]. */ static int RegisterNodeInfo(char *buffer, struct super_block *sb) { enum { ARG_FILENAME = 0, ARG_PERMISSION = 1, ARG_UID = 2, ARG_GID = 3, ARG_MAY_CREATE = 4, ARG_MAY_DELETE = 5, ARG_DEV_TYPE = 6, ARG_SYMLINK_DATA = 7, ARG_DEV_MAJOR = 7, ARG_DEV_MINOR = 8, MAX_ARG = 9 }; char *args[MAX_ARG]; int i; int error = -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("<%s> <%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], args[8]); */ if (!args[ARG_FILENAME] || !args[ARG_PERMISSION] || !args[ARG_UID] || !args[ARG_GID] || !args[ARG_DEV_TYPE] || !args[ARG_MAY_CREATE] || !args[ARG_MAY_DELETE]) goto out; mode_t perm; uid_t uid; gid_t gid; unsigned short int major = 0, minor = 0; int may_create, may_delete; if (sscanf(args[ARG_PERMISSION], "%ho", &perm) != 1 || !(perm <= 0777) || sscanf(args[ARG_UID], "%u", &uid) != 1 || sscanf(args[ARG_GID], "%u", &gid) != 1 || sscanf(args[ARG_MAY_CREATE], "%d", &may_create) != 1 || sscanf(args[ARG_MAY_DELETE], "%d", &may_delete) != 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], "%hu", &major) != 1 || !args[ARG_DEV_MINOR] || sscanf(args[ARG_DEV_MINOR], "%hu", &minor) != 1) goto out; break; case 'b': perm |= S_IFBLK; if (!args[ARG_DEV_MAJOR] || sscanf(args[ARG_DEV_MAJOR], "%hu", &major) != 1 || !args[ARG_DEV_MINOR] || sscanf(args[ARG_DEV_MINOR], "%hu", &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; default: goto out; } error = -ENOMEM; struct syaoran_sb_info *info = (struct syaoran_sb_info *) sb->u.generic_sbp; if (!info->entry) { if ((info->entry = kmalloc(PAGE_SIZE, GFP_KERNEL)) == NULL) goto out; info->buffer_size = PAGE_SIZE; info->entry_count = 0; } if ((info->entry_count + 1) * sizeof(DEVLIST) > info->buffer_size) { if ((buffer = kmalloc(info->buffer_size * 2, GFP_KERNEL)) == NULL) goto out; info->buffer_size *= 2; memmove(buffer, info->entry, info->entry_count * sizeof(DEVLIST)); info->entry = (DEVLIST *) buffer; } DEVLIST *entry = &info->entry[info->entry_count]; memset(entry, 0, sizeof(*entry)); entry->symlink_data = NULL; if (S_ISLNK(perm)) { if ((entry->symlink_data = strdup(args[ARG_SYMLINK_DATA])) == NULL) goto out; } if ((entry->name = strdup(args[ARG_FILENAME])) == NULL) { kfree(entry->symlink_data); goto out; } entry->mode = perm; entry->uid = uid; entry->gid = gid; entry->kdev = S_ISCHR(perm) || S_ISBLK(perm) ? MKDEV(major, minor) : 0; entry->may_create = may_create; entry->may_delete = may_delete; info->entry_count++; /* printk("Entry added.\n"); */ error = 0; out: ; return error; }
/* Free the entry[]. */ static void syaoran_put_super(struct super_block *sb) { if (!sb) return; struct syaoran_sb_info *info = (struct syaoran_sb_info *) sb->u.generic_sbp; if (!info) return; int i; for (i = 0; i < info->entry_count; i++) { DEVLIST *entry = &info->entry[i]; kfree(entry->name); entry->name = NULL; kfree(entry->symlink_data); entry->symlink_data = NULL; /* printk("Entry removed.\n"); */ } kfree(info->entry); info->entry = NULL; info->buffer_size = 0; info->entry_count = 0; kfree(info); sb->u.generic_sbp = NULL; printk("syaoran_put_super: Unused memory freed.\n"); }
/* Read a config file. */ static int ReadConfigFile(struct file *file, struct super_block *sb) { char *buffer; int error = -ENOMEM; if (!file) return -EINVAL; if ((buffer = kmalloc(PAGE_SIZE, GFP_KERNEL)) != NULL) { int avail_len = 0; int len; unsigned long offset = 0; memset(buffer, 0, PAGE_SIZE); while ((len = kernel_read(file, offset, buffer + avail_len, PAGE_SIZE - avail_len - 1)) > 0) { char *cp; avail_len += len; offset += len; while ((cp = memchr(buffer, '\n', avail_len)) != NULL) { *cp = '\0'; NormalizeLine((unsigned char *) buffer); if (RegisterNodeInfo(buffer, sb) == -ENOMEM) goto out; cp++; len = cp - buffer; avail_len -= len; memmove(buffer, cp, avail_len); } } kfree(buffer); error = 0; } out: ; return 0; }
/* Create a file at mount time. */ static void MakeNode(DEVLIST *entry, struct dentry *root) { struct dentry *base = dget(root); char *filename = entry->name; unsigned long hash; struct qstr this; unsigned int c; goto start; while ((c = * (unsigned char *) filename) != '\0') { if (c == '/') { this.len = filename - (char *) this.name; *filename = '\0'; filename++; this.hash = end_name_hash(hash); struct dentry *new_base; down(&base->d_inode->i_sem); new_base = lookup_hash(&this, base); up(&base->d_inode->i_sem); dput(base); if (IS_ERR(new_base)) { /* printk("'%s' not found. (1)\n", this.name); */ return; } else if (!new_base->d_inode) { dput(new_base); /* printk("'%s' not found. (2)\n", this.name); */ return; } /* printk("'%s' found.\n", this.name); */ base = new_base; start: ; this.name = filename; hash = init_name_hash(); } else { filename++; hash = partial_name_hash(c, hash); } } filename = (char *) this.name; mode_t perm = entry->mode; uid_t uid = entry->uid; gid_t gid = entry->gid; if (S_ISLNK(perm)) { fs_symlink(filename, base, entry->symlink_data); } else if (S_ISDIR(perm)) { fs_mkdir(filename, base, perm ^ S_IFDIR, uid, gid); } else if (S_ISSOCK(perm) || S_ISFIFO(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 at mount time according to entry[]. */ static void MakeInitialNodes(struct super_block *sb) { if (!sb) return; struct syaoran_sb_info *info = (struct syaoran_sb_info *) sb->u.generic_sbp; if (!info) return; int i; for (i = 0; i < info->entry_count; i++) { MakeNode(&info->entry[i], sb->s_root); } info->initialize_done = 1; }
/* Open a config file and read. */ static int Syaoran_Initialize(struct super_block *sb, void *data) { int error = -EINVAL; if (data) { struct file *f = filp_open((char *) data, O_RDONLY, 0600); if (!IS_ERR(f)) { if (f->f_dentry->d_inode->i_size > 0) { if ((sb->u.generic_sbp = kmalloc(sizeof(struct syaoran_sb_info), GFP_KERNEL)) != NULL) { memset(sb->u.generic_sbp, 0, sizeof(struct syaoran_sb_info)); printk("Reading '%s'\n", (char *) data); if (ReadConfigFile(f, sb) == 0) error = 0; } } filp_close(f, NULL); } } return error; }
/* Based on __d_path(), but doesn't go over mount point. */ static char *GetAbsolutePath(struct dentry *dentry, char *buffer, int buflen) { char * end = buffer+buflen; char * retval; int namelen; *--end = '\0'; buflen--; /* Get '/' right */ retval = end-1; *retval = '/'; for (;;) { if (IS_ROOT(dentry)) break; struct dentry *parent = dentry->d_parent; namelen = dentry->d_name.len; buflen -= namelen + 1; if (buflen < 0) return ERR_PTR(-ENAMETOOLONG); end -= namelen; memcpy(end, dentry->d_name.name, namelen); *--end = '/'; retval = end; dentry = parent; } namelen = dentry->d_name.len; buflen -= namelen; if (buflen >= 0) { retval -= namelen-1;/* hit the slash */ memcpy(retval, dentry->d_name.name, namelen); } else retval = ERR_PTR(-ENAMETOOLONG); return retval; }
/* Based on sys_getcwd(). Get an absolute pathname. */ static int realpath_from_dentry(struct dentry *dentry, char *newname, int newname_len) { int error; char *page; char *cwd = NULL; struct dentry *d_dentry; if (!dentry || !newname || newname_len <= 0) return -EINVAL; if (!current->fs) { printk("realpath: current->fs == NULL for pid=%d\n", current->pid); return -ENOENT; } page = (char *) kmalloc(PAGE_SIZE, GFP_KERNEL); if (!page) return -ENOMEM; d_dentry = dget(dentry); error = -ENOENT; /* CRITICAL SECTION START */ spin_lock(&dcache_lock); if (IS_ROOT(d_dentry) || !list_empty(&d_dentry->d_hash)) cwd = GetAbsolutePath(d_dentry, page, PAGE_SIZE); spin_unlock(&dcache_lock); /* CRITICAL SECTION END */ if (cwd) { if (!IS_ERR(cwd)) { unsigned long len = PAGE_SIZE + page - cwd; if (len <= newname_len) { memmove(newname, cwd, len); error = 0; } else { error = -ERANGE; } } else { error = PTR_ERR(cwd); } } dput(d_dentry); kfree(page); return error; }
/* Check whether a file with specified attributes are permitted to create. */ static int MayCreateNode(struct dentry *dentry, int mode, int dev) { switch (mode & S_IFMT) { case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: case S_IFDIR: case S_IFLNK: break; default: return -EPERM; } struct syaoran_sb_info *info = (struct syaoran_sb_info *) dentry->d_sb->u.generic_sbp; if (!info) { printk("MayCreateNode: dentry->d_sb->u.generic_sbp == NULL\n"); return -EPERM; } if (!info->initialize_done) return 0; int error = -EPERM; char *filename = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!filename) return -ENOMEM; memset(filename, 0, PAGE_SIZE); if (realpath_from_dentry(dentry, filename, PAGE_SIZE - 1) == 0) { int i; for (i = 0; i < info->entry_count; i++) { DEVLIST *entry = &info->entry[i]; 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 (entry->may_create) error = 0; break; } } kfree(filename); return error; }
/* Check whether a file with specified attributes are permitted to delete. */ static int MayDeleteNode(struct dentry *dentry) { struct syaoran_sb_info *info = (struct syaoran_sb_info *) dentry->d_sb->u.generic_sbp; if (!info) { printk("MayDeleteNode: dentry->d_sb->u.generic_sbp == NULL\n"); return -EPERM; } if (!dentry->d_inode) return -ENOENT; const mode_t mode = dentry->d_inode->i_mode; const kdev_t dev = dentry->d_inode->i_rdev; int error = -EPERM; char *filename = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!filename) return -ENOMEM; memset(filename, 0, PAGE_SIZE); if (realpath_from_dentry(dentry, filename, PAGE_SIZE - 1) == 0) { int i; for (i = 0; i < info->entry_count; i++) { DEVLIST *entry = &info->entry[i]; 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 (entry->may_delete) error = 0; break; } } kfree(filename); return error; }
/***** SYAORAN end. *****/ - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |