Documentation/filesystems/ext3.txt | 11 ++ fs/ext3/Makefile | 2 fs/ext3/inode.c | 5 + fs/ext3/iopen.c | 142 +++++++++++++++++++++++++++++++++++++ fs/ext3/iopen.h | 7 + fs/ext3/namei.c | 4 + fs/ext3/super.c | 21 ++++- include/linux/ext3_fs.h | 2 8 files changed, 190 insertions(+), 4 deletions(-) diff -urN linux-2.6.18-rc1/Documentation/filesystems/ext3.txt linux/Documentation/filesystems/ext3.txt --- linux-2.6.18-rc1/Documentation/filesystems/ext3.txt 2006-07-07 10:35:11.000000000 -0400 +++ linux/Documentation/filesystems/ext3.txt 2006-07-10 10:36:15.000000000 -0400 @@ -121,6 +121,17 @@ "nobh" option tries to avoid associating buffer heads (supported only for "writeback" mode). +iopen=no (*) Do not allow open-by-inode +iopen=yes Allow open by inode. A special invisible directory + is present in the root of all filesystems that allows + users to open files by their inode number. F.e., + /.inode/314159 corresponds to the file with inode + 314159 on the filesystem mounted at '/'. Only root + may open-by-inode. +iopen=all Allow all users to open-by-inode. This circumvents + directory-based security. A world-readable file in a + 0700 directory is accessible by inode. + Specification ============= diff -urN linux-2.6.18-rc1/fs/ext3/inode.c linux/fs/ext3/inode.c --- linux-2.6.18-rc1/fs/ext3/inode.c 2006-07-07 10:35:39.000000000 -0400 +++ linux/fs/ext3/inode.c 2006-07-07 12:51:54.000000000 -0400 @@ -36,6 +36,8 @@ #include #include #include + +#include "iopen.h" #include "xattr.h" #include "acl.h" @@ -2584,6 +2586,9 @@ struct buffer_head *bh; int block; + if (ext3_iopen_get_inode(inode)) + return; + #ifdef CONFIG_EXT3_FS_POSIX_ACL ei->i_acl = EXT3_ACL_NOT_CACHED; ei->i_default_acl = EXT3_ACL_NOT_CACHED; diff -urN linux-2.6.18-rc1/fs/ext3/iopen.c linux/fs/ext3/iopen.c --- linux-2.6.18-rc1/fs/ext3/iopen.c 1969-12-31 19:00:00.000000000 -0500 +++ linux/fs/ext3/iopen.c 2006-07-10 10:47:40.000000000 -0400 @@ -0,0 +1,142 @@ +/* + * fs/ext3/iopen.c - open-by-inode + * + * Copyright (C) 2001 Theodore Ts'o + * Copyright (C) 2006 Robert Love + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 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. + */ + +#include +#include +#include + +#include "iopen.h" + +#define IOPEN_NAME_LEN 32 + +#define IOPEN_DIR ".inode" /* special directory from which we iopen */ +#define IOPEN_DIR_LEN 6 /* strlen (IOPEN_DIR) */ + +/* + * iopen_lookup - lookup by inode number, which is the dentry->d_name + */ +static struct dentry *iopen_lookup(struct inode *dir, + struct dentry *dentry, + struct nameidata *nd) +{ + char dname[IOPEN_NAME_LEN]; + struct inode *inode; + unsigned long ino; + + if (dentry->d_name.len >= IOPEN_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + + memcpy(dname, dentry->d_name.name, dentry->d_name.len); + dname[dentry->d_name.len] = 0; + + if (!strcmp(dname, ".")) + ino = dir->i_ino; + else if (!strcmp(dname, "..")) + ino = EXT3_ROOT_INO; + else + ino = simple_strtoul(dname, NULL, 0); + + if (ino < EXT3_FIRST_INO(dir->i_sb) && ino != EXT3_ROOT_INO) + return ERR_PTR(-ENOENT); + if (ino > le32_to_cpu(EXT3_SB(dir->i_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); + } + + d_add(dentry, inode); + + return NULL; +} + +static struct inode_operations iopen_inode_operations = { + .lookup = iopen_lookup, +}; + +static struct file_operations iopen_file_operations = { + .read = generic_read_dir, +}; + +/* + * ext3_iopen_check - Called from fs/namei.c :: ext3_lookup(). Returns 1 if + * the filename is IOPEN_DIR and 0 otherwise. + */ +int ext3_iopen_check(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode; + + if (!test_opt(dir->i_sb, IOPEN)) + return 0; + if (dir->i_ino != EXT3_ROOT_INO) + return 0; + if (dentry->d_name.len != IOPEN_DIR_LEN) + return 0; + if (strncmp(dentry->d_name.name, IOPEN_DIR, IOPEN_DIR_LEN)) + return 0; + + inode = iget(dir->i_sb, EXT3_BAD_INO); + if (!inode) + return 0; + + d_add(dentry, inode); + + return 1; +} + +/* + * ext3_iopen_get_inode - Called from fs/inode.c :: ext3_read_inode(). Returns + * 1 if the inode number is that of /IOPEN_DIR, in which case the inode + * structure is filled in. Otherwise, the function returns 0. + */ +int ext3_iopen_get_inode(struct inode *inode) +{ + struct ext3_inode_info *ei = EXT3_I(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_ALL)) + 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->i_blksize = PAGE_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 = NULL; + + ei->i_state = 0; + ei->i_dir_start_lookup = 0; + ei->i_dtime = 0; + + return 1; +} diff -urN linux-2.6.18-rc1/fs/ext3/iopen.h linux/fs/ext3/iopen.h --- linux-2.6.18-rc1/fs/ext3/iopen.h 1969-12-31 19:00:00.000000000 -0500 +++ linux/fs/ext3/iopen.h 2006-07-07 14:34:44.000000000 -0400 @@ -0,0 +1,7 @@ +#ifndef _FS_EXT3_IOPEN_H +#define _FS_EXT3_IOPEN_H + +int ext3_iopen_check(struct inode *dir, struct dentry *dentry); +int ext3_iopen_get_inode(struct inode *inode); + +#endif /* _FS_EXT3_IOPEN_H */ diff -urN linux-2.6.18-rc1/fs/ext3/Makefile linux/fs/ext3/Makefile --- linux-2.6.18-rc1/fs/ext3/Makefile 2006-06-17 21:49:35.000000000 -0400 +++ linux/fs/ext3/Makefile 2006-07-07 10:56:34.000000000 -0400 @@ -5,7 +5,7 @@ obj-$(CONFIG_EXT3_FS) += ext3.o ext3-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ - ioctl.o namei.o super.o symlink.o hash.o resize.o + ioctl.o namei.o super.o symlink.o hash.o resize.o iopen.o ext3-$(CONFIG_EXT3_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o ext3-$(CONFIG_EXT3_FS_POSIX_ACL) += acl.o diff -urN linux-2.6.18-rc1/fs/ext3/namei.c linux/fs/ext3/namei.c --- linux-2.6.18-rc1/fs/ext3/namei.c 2006-07-07 10:35:39.000000000 -0400 +++ linux/fs/ext3/namei.c 2006-07-07 14:35:09.000000000 -0400 @@ -37,6 +37,7 @@ #include #include +#include "iopen.h" #include "namei.h" #include "xattr.h" #include "acl.h" @@ -995,6 +996,9 @@ if (dentry->d_name.len > EXT3_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); + if (ext3_iopen_check(dir, dentry)) + return NULL; + bh = ext3_find_entry(dentry, &de); inode = NULL; if (bh) { diff -urN linux-2.6.18-rc1/fs/ext3/super.c linux/fs/ext3/super.c --- linux-2.6.18-rc1/fs/ext3/super.c 2006-07-07 10:35:39.000000000 -0400 +++ linux/fs/ext3/super.c 2006-07-10 10:47:09.000000000 -0400 @@ -634,8 +634,8 @@ Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback, Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota, - Opt_ignore, Opt_barrier, Opt_err, Opt_resize, Opt_usrquota, - Opt_grpquota + Opt_ignore, Opt_barrier, Opt_resize, Opt_usrquota, Opt_grpquota, + Opt_iopen_yes, Opt_iopen_all, Opt_iopen_no, Opt_err }; static match_table_t tokens = { @@ -685,8 +685,11 @@ {Opt_quota, "quota"}, {Opt_usrquota, "usrquota"}, {Opt_barrier, "barrier=%u"}, - {Opt_err, NULL}, {Opt_resize, "resize"}, + {Opt_iopen_yes, "iopen=yes"}, + {Opt_iopen_all, "iopen=all"}, + {Opt_iopen_no, "iopen=no"}, + {Opt_err, NULL}, }; static ext3_fsblk_t get_sb_block(void **data) @@ -1017,6 +1020,18 @@ case Opt_bh: clear_opt(sbi->s_mount_opt, NOBH); break; + case Opt_iopen_yes: + set_opt(sbi->s_mount_opt, IOPEN); + clear_opt(sbi->s_mount_opt, IOPEN_ALL); + break; + case Opt_iopen_all: + set_opt(sbi->s_mount_opt, IOPEN); + set_opt(sbi->s_mount_opt, IOPEN_ALL); + break; + case Opt_iopen_no: + clear_opt(sbi->s_mount_opt, IOPEN); + clear_opt(sbi->s_mount_opt, IOPEN_ALL); + break; default: printk (KERN_ERR "EXT3-fs: Unrecognized mount option \"%s\" " diff -urN linux-2.6.18-rc1/include/linux/ext3_fs.h linux/include/linux/ext3_fs.h --- linux-2.6.18-rc1/include/linux/ext3_fs.h 2006-07-07 10:35:44.000000000 -0400 +++ linux/include/linux/ext3_fs.h 2006-07-07 12:51:01.000000000 -0400 @@ -371,6 +371,8 @@ #define EXT3_MOUNT_QUOTA 0x80000 /* Some quota option set */ #define EXT3_MOUNT_USRQUOTA 0x100000 /* "old" user quota */ #define EXT3_MOUNT_GRPQUOTA 0x200000 /* "old" group quota */ +#define EXT3_MOUNT_IOPEN 0x400000 /* allow open via inode */ +#define EXT3_MOUNT_IOPEN_ALL 0x800000 /* allow iopen for all */ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */ #ifndef _LINUX_EXT2_FS_H