1 Index: linux-2.6.18-128.1.6/fs/ext4/iopen.c
2 ===================================================================
4 +++ linux-2.6.18-128.1.6/fs/ext4/iopen.c
7 + * linux/fs/ext4/iopen.c
9 + * Special support for open by inode number
11 + * Copyright (C) 2001 by Theodore Ts'o (tytso@alum.mit.edu).
13 + * This file may be redistributed under the terms of the GNU General
18 + * - there is only ever a single DCACHE_NFSD_DISCONNECTED dentry alias
19 + * for an inode at one time.
20 + * - there are never both connected and DCACHE_NFSD_DISCONNECTED dentry
21 + * aliases on an inode at the same time.
23 + * If we have any connected dentry aliases for an inode, use one of those
24 + * in iopen_lookup(). Otherwise, we instantiate a single NFSD_DISCONNECTED
25 + * dentry for this inode, which thereafter will be found by the dcache
26 + * when looking up this inode number in __iopen__, so we don't return here
29 + * If we get an inode via a regular name lookup, then we "rename" the
30 + * NFSD_DISCONNECTED dentry to the proper name and parent. This ensures
31 + * existing users of the disconnected dentry will continue to use the same
32 + * dentry as the connected users, and there will never be both kinds of
33 + * dentry aliases at one time.
36 +#include <linux/sched.h>
37 +#include <linux/fs.h>
38 +#include <linux/smp_lock.h>
39 +#include <linux/dcache.h>
40 +#include <linux/security.h>
43 +#include "ext4_jbd2.h"
46 +#define assert(test) J_ASSERT(test)
49 +#define IOPEN_NAME_LEN 32
52 + * This implements looking up an inode by number.
54 +static struct dentry *iopen_lookup(struct inode * dir, struct dentry *dentry,
55 + struct nameidata *nd)
57 + struct inode *inode;
59 + struct list_head *lp;
60 + struct dentry *alternate;
61 + char buf[IOPEN_NAME_LEN];
63 + if (dentry->d_name.len >= IOPEN_NAME_LEN)
64 + return ERR_PTR(-ENAMETOOLONG);
66 + memcpy(buf, dentry->d_name.name, dentry->d_name.len);
67 + buf[dentry->d_name.len] = 0;
69 + if (strcmp(buf, ".") == 0)
71 + else if (strcmp(buf, "..") == 0)
72 + ino = EXT4_ROOT_INO;
74 + ino = simple_strtoul(buf, 0, 0);
76 + if ((ino != EXT4_ROOT_INO &&
77 + ino < EXT4_FIRST_INO(dir->i_sb)) ||
78 + ino > le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count))
79 + return ERR_PTR(-ENOENT);
81 + inode = ext4_iget(dir->i_sb, ino);
82 + if (IS_ERR(inode)) {
83 + /* Newer kernels return -ESTALE for inodes that are not in use,
84 + * but older kernels return a negative dentry. This can only
85 + * happen when doing a lookup in the __iopen__ dir, because the
86 + * "entry" will always be found even if inode is unallocated.
87 + * Handle this here instead of fixing the callers. b=19114 */
88 + if (PTR_ERR(inode) == -ESTALE)
89 + return (ERR_PTR(-ENOENT));
90 + return ERR_CAST(inode);
93 + assert(list_empty(&dentry->d_alias)); /* d_instantiate */
94 + assert(d_unhashed(dentry)); /* d_rehash */
96 + /* preferrably return a connected dentry */
97 + spin_lock(&dcache_lock);
98 + list_for_each(lp, &inode->i_dentry) {
99 + alternate = list_entry(lp, struct dentry, d_alias);
100 + assert(!(alternate->d_flags & DCACHE_DISCONNECTED));
103 + if (!list_empty(&inode->i_dentry)) {
104 + alternate = list_entry(inode->i_dentry.next,
105 + struct dentry, d_alias);
106 + dget_locked(alternate);
107 + spin_lock(&alternate->d_lock);
108 + alternate->d_flags |= DCACHE_REFERENCED;
109 + spin_unlock(&alternate->d_lock);
111 + spin_unlock(&dcache_lock);
114 + dentry->d_flags |= DCACHE_DISCONNECTED;
116 + /* d_add(), but don't drop dcache_lock before adding dentry to inode */
117 + list_add(&dentry->d_alias, &inode->i_dentry); /* d_instantiate */
118 + dentry->d_inode = inode;
120 + d_rehash_cond(dentry, 0);
121 + spin_unlock(&dcache_lock);
126 +/* This function is spliced into ext4_lookup and does the move of a
127 + * disconnected dentry (if it exists) to a connected dentry.
129 +struct dentry *iopen_connect_dentry(struct dentry *dentry, struct inode *inode,
132 + struct dentry *tmp, *goal = NULL;
133 + struct list_head *lp;
135 + /* verify this dentry is really new */
136 + assert(dentry->d_inode == NULL);
137 + assert(list_empty(&dentry->d_alias)); /* d_instantiate */
139 + assert(d_unhashed(dentry)); /* d_rehash */
140 + assert(list_empty(&dentry->d_subdirs));
142 + spin_lock(&dcache_lock);
146 + if (!test_opt(inode->i_sb, IOPEN))
147 + goto do_instantiate;
149 + /* preferrably return a connected dentry */
150 + list_for_each(lp, &inode->i_dentry) {
151 + tmp = list_entry(lp, struct dentry, d_alias);
152 + if (tmp->d_flags & DCACHE_DISCONNECTED) {
153 + assert(tmp->d_alias.next == &inode->i_dentry);
154 + assert(tmp->d_alias.prev == &inode->i_dentry);
162 + goto do_instantiate;
164 + /* Move the goal to the de hash queue */
165 + goal->d_flags &= ~DCACHE_DISCONNECTED;
166 + security_d_instantiate(goal, inode);
168 + d_rehash_cond(dentry, 0);
169 + d_move_locked(goal, dentry);
170 + spin_unlock(&dcache_lock);
175 + /* d_add(), but don't drop dcache_lock before adding dentry to inode */
177 + list_add(&dentry->d_alias, &inode->i_dentry); /* d_instantiate */
178 + dentry->d_inode = inode;
181 + d_rehash_cond(dentry, 0);
182 + spin_unlock(&dcache_lock);
188 + * Similar as d_instantiate() except that it drops the disconnected
191 +void iopen_d_instantiate(struct dentry *dentry, struct inode * inode)
193 + struct dentry *dis_dentry;
195 + /* verify this dentry is really new */
196 + assert(dentry->d_inode == NULL);
197 + assert(list_empty(&dentry->d_alias));
199 + spin_lock(&dcache_lock);
200 + if (!inode || !test_opt(inode->i_sb, IOPEN) ||
201 + list_empty(&inode->i_dentry))
202 + goto do_instantiate;
204 + /* a disconnected dentry has been added in our back,
205 + * we have to drop this dentry, see bug 16362/15713*/
206 + dis_dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
207 + spin_lock(&dis_dentry->d_lock);
208 + assert(dis_dentry->d_alias.next == &inode->i_dentry);
209 + assert(dis_dentry->d_alias.prev == &inode->i_dentry);
210 + assert(dis_dentry->d_flags & DCACHE_DISCONNECTED);
211 + __d_drop(dis_dentry);
212 + list_del_init(&dis_dentry->d_alias);
213 + spin_unlock(&dis_dentry->d_lock);
217 + list_add(&dentry->d_alias, &inode->i_dentry);
218 + dentry->d_inode = inode;
219 + spin_unlock(&dcache_lock);
220 + security_d_instantiate(dentry, inode);
224 + * These are the special structures for the iopen pseudo directory.
227 +static struct inode_operations iopen_inode_operations = {
228 + lookup: iopen_lookup, /* BKL held */
231 +static struct file_operations iopen_file_operations = {
232 + read: generic_read_dir,
235 +static int match_dentry(struct dentry *dentry, const char *name)
239 + len = strlen(name);
240 + if (dentry->d_name.len != len)
242 + if (strncmp(dentry->d_name.name, name, len))
248 + * This function is spliced into ext4_lookup and returns 1 the file
249 + * name is __iopen__ and dentry has been filled in appropriately.
251 +int ext4_check_for_iopen(struct inode *dir, struct dentry *dentry)
253 + struct inode *inode;
255 + if (dir->i_ino != EXT4_ROOT_INO ||
256 + !test_opt(dir->i_sb, IOPEN) ||
257 + !match_dentry(dentry, "__iopen__"))
260 + inode = ext4_iget(dir->i_sb, EXT4_BAD_INO);
264 + d_add(dentry, inode);
269 + * This function is spliced into read_inode; it returns 1 if inode
270 + * number is the one for /__iopen__, in which case the inode is filled
271 + * in appropriately. Otherwise, this fuction returns 0.
273 +int ext4_iopen_get_inode(struct inode *inode)
275 + if (inode->i_ino != EXT4_BAD_INO)
278 + inode->i_mode = S_IFDIR | S_IRUSR | S_IXUSR;
279 + if (test_opt(inode->i_sb, IOPEN_NOPRIV))
280 + inode->i_mode |= 0777;
283 + inode->i_nlink = 1;
284 + inode->i_size = 4096;
285 + inode->i_atime = inode->i_ctime = inode->i_mtime = ext4_current_time(inode);
286 + EXT4_I(inode)->i_dtime = 0;
287 + EXT4_I(inode)->i_file_acl = 0;
288 + inode->i_blocks = 0;
289 + inode->i_version = 1;
290 + inode->i_generation = 0;
292 + inode->i_op = &iopen_inode_operations;
293 + inode->i_fop = &iopen_file_operations;
294 + inode->i_mapping->a_ops = 0;
296 + if (inode->i_state & I_NEW)
297 + unlock_new_inode(inode);
301 Index: linux-2.6.18-128.1.6/fs/ext4/iopen.h
302 ===================================================================
304 +++ linux-2.6.18-128.1.6/fs/ext4/iopen.h
309 + * Special support for opening files by inode number.
311 + * Copyright (C) 2001 by Theodore Ts'o (tytso@alum.mit.edu).
313 + * This file may be redistributed under the terms of the GNU General
317 +extern int ext4_check_for_iopen(struct inode *dir, struct dentry *dentry);
318 +extern int ext4_iopen_get_inode(struct inode *inode);
319 +extern struct dentry *iopen_connect_dentry(struct dentry *dentry,
320 + struct inode *inode, int rehash);
321 +extern void iopen_d_instantiate(struct dentry *dentry, struct inode * inode);
322 Index: linux-2.6.18-128.1.6/fs/ext4/inode.c
323 ===================================================================
324 --- linux-2.6.18-128.1.6.orig/fs/ext4/inode.c
325 +++ linux-2.6.18-128.1.6/fs/ext4/inode.c
327 #include <linux/bio.h>
328 #include "ext4_jbd2.h"
334 @@ -2764,6 +2765,8 @@ struct inode *ext4_iget(struct super_blo
335 ei->i_acl = EXT4_ACL_NOT_CACHED;
336 ei->i_default_acl = EXT4_ACL_NOT_CACHED;
338 + if (ext4_iopen_get_inode(inode))
341 ret = __ext4_get_inode_loc(inode, &iloc, 0);
343 Index: linux-2.6.18-128.1.6/fs/ext4/super.c
344 ===================================================================
345 --- linux-2.6.18-128.1.6.orig/fs/ext4/super.c
346 +++ linux-2.6.18-128.1.6/fs/ext4/super.c
347 @@ -888,7 +888,8 @@ enum {
348 Opt_usrquota, Opt_grpquota, Opt_i_version,
349 Opt_stripe, Opt_delalloc, Opt_nodelalloc,
350 Opt_block_validity, Opt_noblock_validity,
351 - Opt_inode_readahead_blks, Opt_journal_ioprio
352 + Opt_inode_readahead_blks, Opt_journal_ioprio,
353 + Opt_iopen, Opt_noiopen, Opt_iopen_nopriv,
356 static match_table_t tokens = {
357 @@ -938,6 +939,9 @@ static match_table_t tokens = {
358 {Opt_noquota, "noquota"},
359 {Opt_quota, "quota"},
360 {Opt_usrquota, "usrquota"},
361 + {Opt_iopen, "iopen"},
362 + {Opt_noiopen, "noiopen"},
363 + {Opt_iopen_nopriv, "iopen_nopriv"},
364 {Opt_barrier, "barrier=%u"},
365 {Opt_extents, "extents"},
366 {Opt_noextents, "noextents"},
367 @@ -1270,6 +1274,18 @@ clear_qf_name:
369 clear_opt(sbi->s_mount_opt, BARRIER);
372 + set_opt (sbi->s_mount_opt, IOPEN);
373 + clear_opt (sbi->s_mount_opt, IOPEN_NOPRIV);
376 + clear_opt (sbi->s_mount_opt, IOPEN);
377 + clear_opt (sbi->s_mount_opt, IOPEN_NOPRIV);
379 + case Opt_iopen_nopriv:
380 + set_opt (sbi->s_mount_opt, IOPEN);
381 + set_opt (sbi->s_mount_opt, IOPEN_NOPRIV);
386 Index: linux-2.6.18-128.1.6/fs/ext4/namei.c
387 ===================================================================
388 --- linux-2.6.18-128.1.6.orig/fs/ext4/namei.c
389 +++ linux-2.6.18-128.1.6/fs/ext4/namei.c
398 @@ -1048,6 +1049,9 @@ static struct dentry *ext4_lookup(struct
399 if (dentry->d_name.len > EXT4_NAME_LEN)
400 return ERR_PTR(-ENAMETOOLONG);
402 + if (ext4_check_for_iopen(dir, dentry))
405 bh = ext4_find_entry(dir, &dentry->d_name, &de);
408 @@ -1062,7 +1066,8 @@ static struct dentry *ext4_lookup(struct
410 return ERR_CAST(inode);
412 - return d_splice_alias(inode, dentry);
414 + return iopen_connect_dentry(dentry, inode, 1);
418 @@ -1709,7 +1714,7 @@ static int ext4_add_nondir(handle_t *han
419 int err = ext4_add_entry(handle, dentry, inode);
421 ext4_mark_inode_dirty(handle, inode);
422 - d_instantiate(dentry, inode);
423 + iopen_d_instantiate(dentry, inode);
424 unlock_new_inode(inode);
427 @@ -1868,7 +1873,7 @@ out_clear_inode:
428 ext4_inc_count(handle, dir);
429 ext4_update_dx_flag(dir);
430 ext4_mark_inode_dirty(handle, dir);
431 - d_instantiate(dentry, inode);
432 + iopen_d_instantiate(dentry, inode);
433 unlock_new_inode(inode);
435 ext4_journal_stop(handle);
436 @@ -2134,10 +2139,6 @@ static int ext4_rmdir (struct inode * di
440 - /* There's no need to set i_disksize: the fact that i_nlink is
441 - * zero will ensure that the right thing happens during any
444 ext4_orphan_add(handle, inode);
445 inode->i_ctime = dir->i_ctime = dir->i_mtime = ext4_current_time(inode);
446 ext4_mark_inode_dirty(handle, inode);
447 @@ -2263,6 +2264,23 @@ out_stop:
451 +/* Like ext4_add_nondir() except for call to iopen_connect_dentry */
452 +static int ext4_add_link(handle_t *handle, struct dentry *dentry,
453 + struct inode *inode)
455 + int err = ext4_add_entry(handle, dentry, inode);
457 + err = ext4_mark_inode_dirty(handle, inode);
459 + dput(iopen_connect_dentry(dentry, inode, 0));
463 + ext4_dec_count(handle, inode);
468 static int ext4_link(struct dentry *old_dentry,
469 struct inode *dir, struct dentry *dentry)
471 @@ -2293,14 +2311,8 @@ retry:
472 ext4_inc_count(handle, inode);
473 atomic_inc(&inode->i_count);
475 - err = ext4_add_entry(handle, dentry, inode);
477 - ext4_mark_inode_dirty(handle, inode);
478 - d_instantiate(dentry, inode);
483 + err = ext4_add_link(handle, dentry, inode);
484 + ext4_orphan_del(handle, inode);
485 ext4_journal_stop(handle);
486 if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
488 Index: linux-2.6.18-128.1.6/fs/ext4/Makefile
489 ===================================================================
490 --- linux-2.6.18-128.1.6.orig/fs/ext4/Makefile
491 +++ linux-2.6.18-128.1.6/fs/ext4/Makefile
494 obj-$(CONFIG_EXT4_FS) += ext4.o
496 -ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
497 +ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o iopen.o \
498 ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \
499 ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o
501 Index: linux-2.6.18-128.1.6/fs/ext4/ext4.h
502 ===================================================================
503 --- linux-2.6.18-128.1.6.orig/fs/ext4/ext4.h
504 +++ linux-2.6.18-128.1.6/fs/ext4/ext4.h
505 @@ -537,6 +538,8 @@ do { \
506 #define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */
507 #define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */
508 #define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */
509 +#define EXT4_MOUNT_IOPEN 0x20000000 /* Allow access via iopen */
510 +#define EXT4_MOUNT_IOPEN_NOPRIV 0x40000000 /* Make iopen world-readable */
512 /* Compatibility, for having both ext2_fs.h and ext4_fs.h included at once */
513 #ifndef _LINUX_EXT2_FS_H