Documentation/filesystems/ext2.txt | 16 ++ fs/ext3/Makefile | 2 fs/ext3/inode.c | 4 fs/ext3/iopen.c | 259 +++++++++++++++++++++++++++++++++++++ fs/ext3/iopen.h | 13 + fs/ext3/namei.c | 13 + fs/ext3/super.c | 11 + include/linux/ext3_fs.h | 2 8 files changed, 318 insertions(+), 2 deletions(-) Index: linux-ia64/Documentation/filesystems/ext2.txt =================================================================== --- linux-ia64.orig/Documentation/filesystems/ext2.txt 2004-03-17 15:47:15.000000000 -0800 +++ linux-ia64/Documentation/filesystems/ext2.txt 2004-03-17 18:03:15.000000000 -0800 @@ -35,6 +35,22 @@ resgid=n The group ID which may use th sb=n Use alternate superblock at this location. +iopen Makes an invisible pseudo-directory called + __iopen__ available in the root directory + of the filesystem. Allows open-by-inode- + number. i.e., inode 3145 can be accessed + via /mntpt/__iopen__/3145 + +iopen_nopriv This option makes the iopen directory be + world-readable. This may be safer since it + allows daemons to run as an unprivileged user, + however it significantly changes the security + model of a Unix filesystem, since previously + all files under a mode 700 directory were not + generally avilable even if the + permissions on the file itself is + world-readable. + grpquota,noquota,quota,usrquota Quota options are silently ignored by ext2. Index: linux-ia64/fs/ext3/Makefile =================================================================== --- linux-ia64.orig/fs/ext3/Makefile 2004-03-17 18:03:14.000000000 -0800 +++ linux-ia64/fs/ext3/Makefile 2004-03-17 18:03:15.000000000 -0800 @@ -11,7 +11,7 @@ O_TARGET := ext3.o export-objs := ext3-exports.o -obj-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ +obj-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o iopen.o \ ioctl.o namei.o super.o symlink.o hash.o ext3-exports.o obj-m := $(O_TARGET) Index: linux-ia64/fs/ext3/inode.c =================================================================== --- linux-ia64.orig/fs/ext3/inode.c 2004-03-17 18:03:15.000000000 -0800 +++ linux-ia64/fs/ext3/inode.c 2004-03-17 18:10:36.000000000 -0800 @@ -34,6 +34,7 @@ #include #include #include +#include "iopen.h" /* * SEARCH_FROM_ZERO forces each block allocation to search from the start @@ -2430,6 +2431,9 @@ void ext3_read_inode(struct inode * inod struct buffer_head *bh; int block; + if (ext3_iopen_get_inode(inode)) + return; + if(ext3_get_inode_loc(inode, &iloc)) goto bad_inode; bh = iloc.bh; Index: linux-ia64/fs/ext3/iopen.c =================================================================== --- linux-ia64.orig/fs/ext3/iopen.c 2004-03-17 18:02:08.000000000 -0800 +++ linux-ia64/fs/ext3/iopen.c 2004-03-17 18:10:58.000000000 -0800 @@ -0,0 +1,285 @@ +/* + * linux/fs/ext3/iopen.c + * + * Special support for open by inode number + * + * Copyright (C) 2001 by Theodore Ts'o (tytso@alum.mit.edu). + * + * This file may be redistributed under the terms of the GNU General + * Public License. + * + * + * Invariants: + * - there is only ever a single DCACHE_NFSD_DISCONNECTED dentry alias + * for an inode at one time. + * - there are never both connected and DCACHE_NFSD_DISCONNECTED dentry + * aliases on an inode at the same time. + * + * If we have any connected dentry aliases for an inode, use one of those + * in iopen_lookup(). Otherwise, we instantiate a single NFSD_DISCONNECTED + * dentry for this inode, which thereafter will be found by the dcache + * when looking up this inode number in __iopen__, so we don't return here + * until it is gone. + * + * If we get an inode via a regular name lookup, then we "rename" the + * NFSD_DISCONNECTED dentry to the proper name and parent. This ensures + * existing users of the disconnected dentry will continue to use the same + * dentry as the connected users, and there will never be both kinds of + * dentry aliases at one time. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "iopen.h" + +#ifndef assert +#define assert(test) J_ASSERT(test) +#endif + +#define IOPEN_NAME_LEN 32 + +/* + * This implements looking up an inode by number. + */ +static struct dentry *iopen_lookup(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode; + unsigned long ino; + struct list_head *lp; + struct dentry *alternate; + char buf[IOPEN_NAME_LEN]; + + if (dentry->d_name.len >= IOPEN_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + + memcpy(buf, dentry->d_name.name, dentry->d_name.len); + buf[dentry->d_name.len] = 0; + + if (strcmp(buf, ".") == 0) + ino = dir->i_ino; + else if (strcmp(buf, "..") == 0) + ino = EXT3_ROOT_INO; + else + ino = simple_strtoul(buf, 0, 0); + + if ((ino != EXT3_ROOT_INO && + //ino != EXT3_ACL_IDX_INO && + //ino != EXT3_ACL_DATA_INO && + ino < EXT3_FIRST_INO(dir->i_sb)) || + ino > le32_to_cpu(dir->i_sb->u.ext3_sb.s_es->s_inodes_count)) + return ERR_PTR(-ENOENT); + + inode = iget(dir->i_sb, ino); + if (!inode) + return ERR_PTR(-EACCES); + if (is_bad_inode(inode)) { + iput(inode); + return ERR_PTR(-ENOENT); + } + + assert(list_empty(&dentry->d_alias)); /* d_instantiate */ + assert(list_empty(&dentry->d_hash)); /* d_rehash */ + + /* preferrably return a connected dentry */ + spin_lock(&dcache_lock); + list_for_each(lp, &inode->i_dentry) { + alternate = list_entry(lp, struct dentry, d_alias); + assert(!(alternate->d_flags & DCACHE_NFSD_DISCONNECTED)); + } + + if (!list_empty(&inode->i_dentry)) { + alternate = list_entry(inode->i_dentry.next, + struct dentry, d_alias); + dget_locked(alternate); + alternate->d_vfs_flags |= DCACHE_REFERENCED; + iput(inode); + spin_unlock(&dcache_lock); + return alternate; + } + dentry->d_flags |= DCACHE_NFSD_DISCONNECTED; + + /* d_add(), but don't drop dcache_lock before adding dentry to inode */ + list_add(&dentry->d_alias, &inode->i_dentry); /* d_instantiate */ + dentry->d_inode = inode; + + __d_rehash(dentry, 0); /* d_rehash */ + spin_unlock(&dcache_lock); + + return NULL; +} + +#define do_switch(x,y) do { \ + __typeof__ (x) __tmp = x; \ + x = y; y = __tmp; } while (0) + +static inline void switch_names(struct dentry *dentry, struct dentry *target) +{ + const unsigned char *old_name, *new_name; + + memcpy(dentry->d_iname, target->d_iname, DNAME_INLINE_LEN); + old_name = target->d_name.name; + new_name = dentry->d_name.name; + if (old_name == target->d_iname) + old_name = dentry->d_iname; + if (new_name == dentry->d_iname) + new_name = target->d_iname; + target->d_name.name = new_name; + dentry->d_name.name = old_name; +} + +/* This function is spliced into ext3_lookup and does the move of a + * disconnected dentry (if it exists) to a connected dentry. + */ +struct dentry *iopen_connect_dentry(struct dentry *dentry, struct inode *inode, + int rehash) +{ + struct dentry *tmp, *goal = NULL; + struct list_head *lp; + + /* verify this dentry is really new */ + assert(dentry->d_inode == NULL); + assert(list_empty(&dentry->d_alias)); /* d_instantiate */ + if (rehash) + assert(list_empty(&dentry->d_hash)); /* d_rehash */ + assert(list_empty(&dentry->d_subdirs)); + + spin_lock(&dcache_lock); + if (!inode) + goto do_rehash; + + if (!test_opt(inode->i_sb, IOPEN)) + goto do_instantiate; + + /* preferrably return a connected dentry */ + list_for_each(lp, &inode->i_dentry) { + tmp = list_entry(lp, struct dentry, d_alias); + if (tmp->d_flags & DCACHE_NFSD_DISCONNECTED) { + assert(tmp->d_alias.next == &inode->i_dentry); + assert(tmp->d_alias.prev == &inode->i_dentry); + goal = tmp; + dget_locked(goal); + break; + } + } + + if (!goal) + goto do_instantiate; + + /* Move the goal to the de hash queue - like d_move() */ + goal->d_flags &= ~DCACHE_NFSD_DISCONNECTED; + list_del_init(&goal->d_hash); + + list_del(&goal->d_child); + list_del(&dentry->d_child); + + /* Switch the parents and the names.. */ + switch_names(goal, dentry); + do_switch(goal->d_parent, dentry->d_parent); + do_switch(goal->d_name.len, dentry->d_name.len); + do_switch(goal->d_name.hash, dentry->d_name.hash); + + /* And add them back to the (new) parent lists */ + list_add(&goal->d_child, &goal->d_parent->d_subdirs); + list_add(&dentry->d_child, &dentry->d_parent->d_subdirs); + __d_rehash(goal, 0); + spin_unlock(&dcache_lock); + iput(inode); + + return goal; + + /* d_add(), but don't drop dcache_lock before adding dentry to inode */ +do_instantiate: + list_add(&dentry->d_alias, &inode->i_dentry); /* d_instantiate */ + dentry->d_inode = inode; +do_rehash: + if (rehash) + __d_rehash(dentry, 0); /* d_rehash */ + spin_unlock(&dcache_lock); + + return NULL; +} + +/* + * These are the special structures for the iopen pseudo directory. + */ + +static struct inode_operations iopen_inode_operations = { + lookup: iopen_lookup, /* BKL held */ +}; + +static struct file_operations iopen_file_operations = { + read: generic_read_dir, +}; + +static int match_dentry(struct dentry *dentry, const char *name) +{ + int len; + + len = strlen(name); + if (dentry->d_name.len != len) + return 0; + if (strncmp(dentry->d_name.name, name, len)) + return 0; + return 1; +} + +/* + * This function is spliced into ext3_lookup and returns 1 the file + * name is __iopen__ and dentry has been filled in appropriately. + */ +int ext3_check_for_iopen(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode; + + if (dir->i_ino != EXT3_ROOT_INO || + !test_opt(dir->i_sb, IOPEN) || + !match_dentry(dentry, "__iopen__")) + return 0; + + inode = iget(dir->i_sb, EXT3_BAD_INO); + + if (!inode) + return 0; + d_add(dentry, inode); + return 1; +} + +/* + * This function is spliced into read_inode; it returns 1 if inode + * number is the one for /__iopen__, in which case the inode is filled + * in appropriately. Otherwise, this fuction returns 0. + */ +int ext3_iopen_get_inode(struct inode *inode) +{ + if (inode->i_ino != EXT3_BAD_INO) + return 0; + + inode->i_mode = S_IFDIR | S_IRUSR | S_IXUSR; + if (test_opt(inode->i_sb, IOPEN_NOPRIV)) + inode->i_mode |= 0777; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_nlink = 1; + inode->i_size = 4096; + inode->i_atime = CURRENT_TIME; + inode->i_ctime = CURRENT_TIME; + inode->i_mtime = CURRENT_TIME; + inode->u.ext3_i.i_dtime = 0; + inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size + * (for stat), not the fs block + * size */ + inode->i_blocks = 0; + inode->i_version = 1; + inode->i_generation = 0; + + inode->i_op = &iopen_inode_operations; + inode->i_fop = &iopen_file_operations; + inode->i_mapping->a_ops = 0; + + return 1; +} Index: linux-ia64/fs/ext3/iopen.h =================================================================== --- linux-ia64.orig/fs/ext3/iopen.h 2004-03-17 15:47:15.000000000 -0800 +++ linux-ia64/fs/ext3/iopen.h 2004-03-17 18:03:15.000000000 -0800 @@ -0,0 +1,15 @@ +/* + * iopen.h + * + * Special support for opening files by inode number. + * + * Copyright (C) 2001 by Theodore Ts'o (tytso@alum.mit.edu). + * + * This file may be redistributed under the terms of the GNU General + * Public License. + */ + +extern int ext3_check_for_iopen(struct inode *dir, struct dentry *dentry); +extern int ext3_iopen_get_inode(struct inode *inode); +extern struct dentry *iopen_connect_dentry(struct dentry *dentry, + struct inode *inode, int rehash); Index: linux-ia64/fs/ext3/namei.c =================================================================== --- linux-ia64.orig/fs/ext3/namei.c 2004-03-17 18:03:15.000000000 -0800 +++ linux-ia64/fs/ext3/namei.c 2004-03-17 18:10:35.000000000 -0800 @@ -36,7 +36,7 @@ #include #include #include - +#include "iopen.h" /* * define how far ahead to read directories while searching them. @@ -932,6 +932,9 @@ static struct dentry *ext3_lookup(struct if (dentry->d_name.len > EXT3_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); + if (ext3_check_for_iopen(dir, dentry)) + return NULL; + bh = ext3_find_entry(dentry, &de); inode = NULL; if (bh) { @@ -943,8 +946,8 @@ static struct dentry *ext3_lookup(struct return ERR_PTR(-EACCES); } } - d_add(dentry, inode); - return NULL; + + return iopen_connect_dentry(dentry, inode, 1); } #define S_SHIFT 12 @@ -1935,10 +1938,6 @@ static int ext3_rmdir (struct inode * di inode->i_nlink); inode->i_version = ++event; inode->i_nlink = 0; - /* There's no need to set i_disksize: the fact that i_nlink is - * zero will ensure that the right thing happens during any - * recovery. */ - inode->i_size = 0; ext3_orphan_add(handle, inode); dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; @@ -2057,6 +2056,23 @@ out_stop: return err; } +/* Like ext3_add_nondir() except for call to iopen_connect_dentry */ +static int ext3_add_link(handle_t *handle, struct dentry *dentry, + struct inode *inode) +{ + int err = ext3_add_entry(handle, dentry, inode); + if (!err) { + err = ext3_mark_inode_dirty(handle, inode); + if (err == 0) { + dput(iopen_connect_dentry(dentry, inode, 0)); + return 0; + } + } + ext3_dec_count(handle, inode); + iput(inode); + return err; +} + static int ext3_link (struct dentry * old_dentry, struct inode * dir, struct dentry *dentry) { @@ -2084,7 +2100,8 @@ static int ext3_link (struct dentry * ol ext3_inc_count(handle, inode); atomic_inc(&inode->i_count); - err = ext3_add_nondir(handle, dentry, inode); + err = ext3_add_link(handle, dentry, inode); + ext3_orphan_del(handle, inode); ext3_journal_stop(handle, dir); return err; } Index: linux-ia64/fs/ext3/super.c =================================================================== --- linux-ia64.orig/fs/ext3/super.c 2004-03-17 18:03:14.000000000 -0800 +++ linux-ia64/fs/ext3/super.c 2004-03-17 18:10:35.000000000 -0800 @@ -891,6 +891,18 @@ static int parse_options (char * options || !strcmp (this_char, "quota") || !strcmp (this_char, "usrquota")) /* Don't do anything ;-) */ ; + else if (!strcmp (this_char, "iopen")) { + set_opt (sbi->s_mount_opt, IOPEN); + clear_opt (sbi->s_mount_opt, IOPEN_NOPRIV); + } + else if (!strcmp (this_char, "noiopen")) { + clear_opt (sbi->s_mount_opt, IOPEN); + clear_opt (sbi->s_mount_opt, IOPEN_NOPRIV); + } + else if (!strcmp (this_char, "iopen_nopriv")) { + set_opt (sbi->s_mount_opt, IOPEN); + set_opt (sbi->s_mount_opt, IOPEN_NOPRIV); + } else if (!strcmp (this_char, "journal")) { /* @@@ FIXME */ /* Eventually we will want to be able to create Index: linux-ia64/include/linux/ext3_fs.h =================================================================== --- linux-ia64.orig/include/linux/ext3_fs.h 2004-03-17 18:03:15.000000000 -0800 +++ linux-ia64/include/linux/ext3_fs.h 2004-03-17 18:03:15.000000000 -0800 @@ -328,6 +328,8 @@ struct ext3_inode { #define EXT3_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */ #define EXT3_MOUNT_POSIX_ACL 0x8000 /* POSIX Access Control Lists */ #define EXT3_MOUNT_ASYNCDEL 0x20000 /* Delayed deletion */ +#define EXT3_MOUNT_IOPEN 0x80000 /* Allow access via iopen */ +#define EXT3_MOUNT_IOPEN_NOPRIV 0x100000/* Make iopen world-readable */ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */ #ifndef _LINUX_EXT2_FS_H