Messages in this thread | | | Date | Thu, 10 Mar 2005 20:50:54 +0300 | From | Sergey Vlasov <> | Subject | Re: [PATCH] lookup traps, for compact autofs/devfs functionality |
| |
On Fri, Nov 05, 2004 at 11:38:22PM -0800, Adam J. Richter wrote: > The following patch implements a facility for invoking a user > level helper program whenever a program attempts to access a > nonexistent file on a given tmpfs file system. It can provide > functionality like autofs or devfs. At 1708 bytes of object code, it > should result in smaller implementations when it can be used, and > may avoid some potential recursion problems. A patch that shrinks > devfs by much more than the size of this facility depends on it, > so I would like to get it into the kernel soon.
What's the status of this patch - do you still plan to push it into the kernel?
> Documentation/filesystems/lookup-trap.txt provides cookbook > examples of how to use lookup traps with scripts or short C programs > to for a variety of uses. For example, here a way to create a create > a simple automounter for remote file systems: > > % cat > /usr/sbin/tmpfs-automount > #!/bin/sh > mkdir $3 > mount -t nfs $3:/ $1/$3 > ^D > % chmod a+x /usr/sbin/tmpfs-automount > % mkdir /auto > % mount -t tmpfs -o helper="/usr/sbin/tmpfs-automount /auto" x /auto > > > I will soon post a small devfs replacement based on this > facility. The lookup traps and the devfs patch are an evolution of > the shrunken devfs reimplementation that I posted almost two years > ago, which I have been running continuously on a number of computers > ever since, although the use of tmpfs is only a few days old. > > The implementation is small. The total increase in object > code size on x86 is 1708 bytes (mm/shmem.o change + fs/userhelper.o + > fs/lookuptrap.o), and I've made it a configuration option, so people > who don't want it will only be victimized by 180 object bytes of > changes I had to make outside of the ifdef's to mm/shmem.c (those > changes are counted in those 1708 byte tally, by the way). > > By line count, if you don't count the Documentation > subdirectory, the devfs replacement and lookup traps taken together > are actually a net reduction of more than 1800 lines of code from the > kernel. The lookup traps patch taken by itself is a net increase of > 533 lines of code, most of which is in two files outside the tmpfs > sources to facilitate possible use by other file systems. In > comparison, fs/autofs/*.[ch] is more than triple that line count. > > Over the past few days, I have posted earlier versions of > these patches on linux-fsdevel, originally under the name "trapfs." > This latest patch incorporate feedback expressed by a few people on > that list. Greg Kroah-Hartmann prefered the facility being a tmpfs > option, which I saw some advantages to as well. Mike Waychison and > Matthew Wilcox got me to take a harder look at a user level race > condition that I mentioned in the original posting, which eventually > led to an apparent solution that is incorporated into this version. > > Finally, I should mention a limitation and couple of > deficiencies that I'm aware of with respect to lookup traps to save > everyone some discussion time. > > 1. As Mike Waychison pointed out, this facility in tmpfs > cannot be used to trap accesses to the root of a file system or create > directories and then trap accesses when a program attempts to "cd" in. > Such functionality could provide a more consistent and informative > user interface for an automatic mounter of a small to moderate number > of remote file systems, in the style of autofs. The good news is that > this patch provides a facility in fs/userhelper.c that could be called > by another file system to provide functionality on > inode_operations->follow_link() events similar to what this patch does > for inode_operations->lookup() under tmpfs, hopefully allowing such an > implementation to be smaller. > > 2. Like sysfs and ramfs, tmpfs uses a struct inode and a > struct dentry for every file system node, consuming something like 500 > bytes per node. A backing store patch similar to the sysfs backing > store patch could dramatically reduce the memory used by the > tmpfs-based devfs, implementation. the devfs application of , > although devfs file systems tend to have fewer nodes, since they are > created on demand, and devfs memory consumption pales in comparison to > sysfs. I think that at some point in the future, it might be useful > to implement some kind of release of struct inode's for device files > at least, similar to the "sysfs backing store" patch that is supposed > to be on its way into the stock kernel. > > 3. Until the trapfs helper exits, it is impossible to > control-C out of the access that invoked the helper. This is a > deficiency of the synchronous call_usermodehelper interface. Every > kernel facility that uses call_usermodehelper has this problem. There > are a number of ways to fix synchronous call_usermodehelper, and I > surely expect trapfs to use whatever solution is implemented. > > Any feedback would be welcome. > > __ ______________ > Adam J. Richter \ / > adam@yggdrasil.com | g g d r a s i l > > > --- linux-2.6.10-rc1-bk14/include/linux/shmem_fs.h 2004-10-18 14:54:55.000000000 -0700 > +++ linux/include/linux/shmem_fs.h 2004-11-05 23:24:20.000000000 -0800 > @@ -1,8 +1,10 @@ > #ifndef __SHMEM_FS_H > #define __SHMEM_FS_H > > +#include <linux/config.h> > #include <linux/swap.h> > #include <linux/mempolicy.h> > +#include <linux/fsuserhelper.h> > > /* inode in-kernel data */ > > @@ -22,11 +24,15 @@ > }; > > struct shmem_sb_info { > + int limited; /* 0 = ignore max_blocks and max_inodes */ > unsigned long max_blocks; /* How many blocks are allowed */ > unsigned long free_blocks; /* How many are left for allocation */ > unsigned long max_inodes; /* How many inodes are allowed */ > unsigned long free_inodes; /* How many are left for allocation */ > spinlock_t stat_lock; > +#ifdef CONFIG_TMPFS_LOOKUP_TRAPS > + char *helper_shell_command; > +#endif > }; > > static inline struct shmem_inode_info *SHMEM_I(struct inode *inode) > @@ -34,4 +40,6 @@ > return container_of(inode, struct shmem_inode_info, vfs_inode); > } > > +extern int __init init_tmpfs(void); /* early initialization for devfs */
Adding this hook probably belongs to the devfs-replacement patch, which would be applied on top of the lookup-trap patch.
> + > #endif > --- linux-2.6.10-rc1-bk14/include/linux/fsuserhelper.h 1969-12-31 16:00:00.000000000 -0800 > +++ linux/include/linux/fsuserhelper.h 2004-11-05 23:24:21.000000000 -0800 > @@ -0,0 +1,15 @@ > +#ifndef _LINUX_FS_HELPER > +#define _LINUX_FS_HELPER > + > +#include <linux/dcache.h> > +#include <linux/rwsem.h> > + > +/* > + Note: call_fs_helper releases and retakes dentry->d_parent->d_inode->i_sem. > +*/
This comment style (which is used in your patch all over the place) does not match the style used in the rest of the kernel.
> +extern void call_fs_helper(char **comand_str_ptr,
Typo: command_str_ptr (and in the actual definition this parameter is named "command_ptr").
> + struct rw_semaphore *command_rwsem, > + const char *event, > + struct dentry *dentry); > + > +#endif /* _LINUX_FS_HELPER */ > --- linux-2.6.10-rc1-bk14/include/linux/lookuptrap.h 1969-12-31 16:00:00.000000000 -0800 > +++ linux/include/linux/lookuptrap.h 2004-11-05 23:24:22.000000000 -0800 > @@ -0,0 +1,12 @@ > +#ifndef _LINUX_LOOKUPTRAP_H > +#define _LINUX_LOOKUPTRAP_H > + > +#include <linux/fs.h> > +#include <linux/dcache.h> > + > +extern struct dentry *trapping_lookup(struct inode *dir, > + struct dentry *dentry, > + struct nameidata *nd, > + char **shell_command_ptr); > + > +#endif /* _LINUX_LOOKUPTRAP_H */ > --- linux-2.6.10-rc1-bk14/mm/shmem.c 2004-11-04 23:32:44.000000000 -0800 > +++ linux/mm/shmem.c 2004-11-05 23:24:23.000000000 -0800 > @@ -14,6 +14,9 @@ > * Copyright (c) 2004, Luke Kenneth Casson Leighton <lkcl@lkcl.net> > * Copyright (c) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com> > * > + * User level helper for directory lookups: > + * Copyright (C) 2004 Adam J. Richter, Yggdrasil Computing, Inc. > + * > * This file is released under the GPL. > */ > > @@ -46,6 +49,8 @@ > #include <linux/mempolicy.h> > #include <linux/namei.h> > #include <linux/xattr.h> > +#include <linux/lookuptrap.h> > +#include <linux/parser.h> > #include <asm/uaccess.h> > #include <asm/div64.h> > #include <asm/pgtable.h> > @@ -135,7 +140,20 @@ > > static inline struct shmem_sb_info *SHMEM_SB(struct super_block *sb) > { > +#ifdef CONFIG_TMPFS > return sb->s_fs_info; > +#else > + return NULL; /* compiler optimization */ > +#endif > +} > + > +static inline int shmem_have_quotas(struct shmem_sb_info *sb_info) > +{ > +#ifdef CONFIG_TMPFS > + return sb_info->limited; > +#else > + return 0; /* sb_info will be NULL. */ > +#endif > } > > /* > @@ -194,7 +212,8 @@ > static void shmem_free_blocks(struct inode *inode, long pages) > { > struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); > - if (sbinfo) { > + > + if (shmem_have_quotas(sbinfo)) { > spin_lock(&sbinfo->stat_lock); > sbinfo->free_blocks += pages; > inode->i_blocks -= pages*BLOCKS_PER_PAGE; > @@ -357,7 +376,7 @@ > * page (and perhaps indirect index pages) yet to allocate: > * a waste to allocate index if we cannot allocate data. > */ > - if (sbinfo) { > + if (shmem_have_quotas(sbinfo)) { > spin_lock(&sbinfo->stat_lock); > if (sbinfo->free_blocks <= 1) { > spin_unlock(&sbinfo->stat_lock); > @@ -678,7 +697,7 @@ > spin_unlock(&shmem_swaplist_lock); > } > } > - if (sbinfo) { > + if (shmem_have_quotas(sbinfo)) { > BUG_ON(inode->i_blocks); > spin_lock(&sbinfo->stat_lock); > sbinfo->free_inodes++; > @@ -1081,7 +1100,7 @@ > } else { > shmem_swp_unmap(entry); > sbinfo = SHMEM_SB(inode->i_sb); > - if (sbinfo) { > + if (shmem_have_quotas(sbinfo)) { > spin_lock(&sbinfo->stat_lock); > if (sbinfo->free_blocks == 0 || > shmem_acct_block(info->flags)) { > @@ -1269,7 +1288,7 @@ > struct shmem_inode_info *info; > struct shmem_sb_info *sbinfo = SHMEM_SB(sb); > > - if (sbinfo) { > + if (shmem_have_quotas(sbinfo)) { > spin_lock(&sbinfo->stat_lock); > if (!sbinfo->free_inodes) { > spin_unlock(&sbinfo->stat_lock); > @@ -1598,7 +1617,7 @@ > buf->f_type = TMPFS_MAGIC; > buf->f_bsize = PAGE_CACHE_SIZE; > buf->f_namelen = NAME_MAX; > - if (sbinfo) { > + if (shmem_have_quotas(sbinfo)) { > spin_lock(&sbinfo->stat_lock); > buf->f_blocks = sbinfo->max_blocks; > buf->f_bavail = buf->f_bfree = sbinfo->free_blocks; > @@ -1650,6 +1669,19 @@ > return shmem_mknod(dir, dentry, mode | S_IFREG, 0); > } > > +#ifdef CONFIG_TMPFS_LOOKUP_TRAPS > +static struct dentry * shmem_lookup(struct inode *dir, > + struct dentry *dentry, > + struct nameidata *nd) > +{ > + struct shmem_sb_info *sbinfo = SHMEM_SB(dir->i_sb); > + > + return trapping_lookup(dir, dentry, nd, &sbinfo->helper_shell_command); > +} > +#else > +# define shmem_lookup simple_lookup > +#endif > + > /* > * Link a file.. > */ > @@ -1663,7 +1695,7 @@ > * but each new link needs a new dentry, pinning lowmem, and > * tmpfs dentries cannot be pruned until they are unlinked. > */ > - if (sbinfo) { > + if (shmem_have_quotas(sbinfo)) { > spin_lock(&sbinfo->stat_lock); > if (!sbinfo->free_inodes) { > spin_unlock(&sbinfo->stat_lock); > @@ -1688,7 +1720,7 @@ > > if (inode->i_nlink > 1 && !S_ISDIR(inode->i_mode)) { > struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); > - if (sbinfo) { > + if (shmem_have_quotas(sbinfo)) { > spin_lock(&sbinfo->stat_lock); > sbinfo->free_inodes++; > spin_unlock(&sbinfo->stat_lock); > @@ -1840,7 +1872,7 @@ > #endif > }; > > -static int shmem_parse_options(char *options, int *mode, uid_t *uid, gid_t *gid, unsigned long *blocks, unsigned long *inodes) > +static int shmem_parse_options(char *options, int *mode, uid_t *uid, gid_t *gid, unsigned long *blocks, unsigned long *inodes, substring_t *helper) > { > char *this_char, *value, *rest; > > @@ -1894,6 +1926,9 @@ > *gid = simple_strtoul(value,&rest,0); > if (*rest) > goto bad_val; > + } else if (!strcmp(this_char,"helper")) { > + helper->from = value; > + helper->to = value + strlen(value); > } else { > printk(KERN_ERR "tmpfs: Bad mount option %s\n", > this_char); > @@ -1909,32 +1944,73 @@ > > } > > +#ifdef CONFIG_TMPFS_LOOKUP_TRAPS > +static int > +maybe_replace_from_substr(char **target, substring_t *substr) > +{ > + char *str; > + > + if (substr->from == NULL) > + return 0; > + > + if (substr->from == substr->to) > + str = NULL; > + else { > + str = match_strdup(substr); > + if (!str) > + return -ENOMEM; > + } > + kfree(*target); > + *target = str; > + return 0; > +} > +#endif /* CONFIG_TMPFS_LOOKUP_TRAPS */ > + > static int shmem_remount_fs(struct super_block *sb, int *flags, char *data) > { > struct shmem_sb_info *sbinfo = SHMEM_SB(sb); > unsigned long max_blocks = 0; > unsigned long max_inodes = 0; > + substring_t helper_str; > > - if (sbinfo) { > + if (shmem_have_quotas(sbinfo)) { > max_blocks = sbinfo->max_blocks; > max_inodes = sbinfo->max_inodes; > } > - if (shmem_parse_options(data, NULL, NULL, NULL, &max_blocks, &max_inodes)) > + helper_str.from = NULL; > + if (shmem_parse_options(data, NULL, NULL, NULL, &max_blocks, &max_inodes, &helper_str)) > return -EINVAL; > + > +#ifdef CONFIG_TMPFS_LOOKUP_TRAPS > + if (maybe_replace_from_substr(&sbinfo->helper_shell_command, > + &helper_str) != 0) > + return -ENOMEM; > +#endif > + > /* Keep it simple: disallow limited <-> unlimited remount */ > - if ((max_blocks || max_inodes) == !sbinfo) > + if ((max_blocks || max_inodes) != shmem_have_quotas(sbinfo)) > return -EINVAL;
At this point helper_shell_command was already changed. Having a possibility that a failed remount might still change some filesystem parameters does not seem good.
> + > /* But allow the pointless unlimited -> unlimited remount */ > - if (!sbinfo) > + if (!max_blocks && !max_inodes) > return 0; > + > return shmem_set_size(sbinfo, max_blocks, max_inodes); > } > -#endif > +#endif /* CONFIG_TMPFS */ > > static void shmem_put_super(struct super_block *sb) > { > - kfree(sb->s_fs_info); > - sb->s_fs_info = NULL; > +#ifdef CONFIG_TMPFS > + struct shmem_sb_info *sb_info = SHMEM_SB(sb); > + > +# ifdef CONFIG_TMPFS_LOOKUP_TRAPS > + kfree(sb_info->helper_shell_command); > +# endif > + kfree(sb_info); > +#endif
#ifdef inside functions are generally frowned upon; you have two nested ones here :(
> + > + sb->s_fs_info = NULL; /* FIXME. Is this line necessary? */ > } > > #ifdef CONFIG_TMPFS_XATTR > @@ -1954,14 +2030,17 @@ > int err = -ENOMEM; > > #ifdef CONFIG_TMPFS > + substring_t helper_str; > unsigned long blocks = 0; > unsigned long inodes = 0; > + struct shmem_sb_info *sbinfo; > > /* > * Per default we only allow half of the physical ram per > * tmpfs instance, limiting inodes to one per page of lowmem; > * but the internal instance is left unlimited. > */ > + helper_str.from = NULL;
This line divides apart the comment and the code it was attached to.
> if (!(sb->s_flags & MS_NOUSER)) { > blocks = totalram_pages / 2; > inodes = totalram_pages - totalhigh_pages; > @@ -1969,24 +2048,32 @@ > inodes = blocks; > > if (shmem_parse_options(data, &mode, > - &uid, &gid, &blocks, &inodes)) > + &uid, &gid, &blocks, &inodes, > + &helper_str)) > return -EINVAL; > } > > - if (blocks || inodes) { > - struct shmem_sb_info *sbinfo; > - sbinfo = kmalloc(sizeof(struct shmem_sb_info), GFP_KERNEL); > - if (!sbinfo) > - return -ENOMEM; > - sb->s_fs_info = sbinfo; > - spin_lock_init(&sbinfo->stat_lock); > - sbinfo->max_blocks = blocks; > - sbinfo->free_blocks = blocks; > - sbinfo->max_inodes = inodes; > - sbinfo->free_inodes = inodes; > - } > + sbinfo = kmalloc(sizeof(struct shmem_sb_info), GFP_KERNEL); > + if (!sbinfo) > + return -ENOMEM; > + > + sbinfo->limited = (blocks || inodes); > + sb->s_fs_info = sbinfo; > + spin_lock_init(&sbinfo->stat_lock); > + sbinfo->max_blocks = blocks; > + sbinfo->free_blocks = blocks; > + sbinfo->max_inodes = inodes; > + sbinfo->free_inodes = inodes; > + > +# ifdef CONFIG_TMPFS_LOOKUP_TRAPS > + sbinfo->helper_shell_command = NULL; > + if (maybe_replace_from_substr(&sbinfo->helper_shell_command, > + &helper_str) != 0) > + return -ENOMEM;
Leak of sbinfo.
> +# endif > + > sb->s_xattr = shmem_xattr_handlers; > -#endif > +#endif /* CONFIG_TMPFS */ > > sb->s_maxbytes = SHMEM_MAX_BYTES; > sb->s_blocksize = PAGE_CACHE_SIZE; > @@ -2088,7 +2175,7 @@ > static struct inode_operations shmem_dir_inode_operations = { > #ifdef CONFIG_TMPFS > .create = shmem_create, > - .lookup = simple_lookup, > + .lookup = shmem_lookup, > .link = shmem_link, > .unlink = shmem_unlink, > .symlink = shmem_symlink, > @@ -2192,9 +2279,15 @@ > }; > static struct vfsmount *shm_mnt; > > -static int __init init_tmpfs(void) > +/* init_tmpfs is exported so that devfs can get an earlier initialization > + if necessary. */ > +int __init init_tmpfs(void)
Again, making this function non-static belongs to another patch where it will actually be used.
> { > int error; > + static int initialized; /* = 0 */
__initdata (but as noted above, these additions should not be in this patch anyway).
> + > + if (initialized) > + return 0; > > error = init_inodecache(); > if (error) > @@ -2215,6 +2308,7 @@ > printk(KERN_ERR "Could not kern_mount tmpfs\n"); > goto out1; > } > + initialized = 1; > return 0; > > out1: > @@ -2310,3 +2404,5 @@ > vma->vm_ops = &shmem_vm_ops; > return 0; > } > +EXPORT_SYMBOL(shmem_lock); > +EXPORT_SYMBOL(shmem_nopage);
What do you need this for? These symbols are not used anythere in the patch.
> --- linux-2.6.10-rc1-bk14/fs/Kconfig 2004-11-04 23:32:39.000000000 -0800 > +++ linux/fs/Kconfig 2004-11-05 23:24:24.000000000 -0800 > @@ -940,6 +940,23 @@ > > If unsure, say N. > > +config TMPFS_LOOKUP_TRAPS > + bool "tmpfs lookup trapping" > + depends on TMPFS > + help > + This facility allows you to configure a tmpfs file system to > + invoke a user level helper program whenever an attempt is made > + to access a nonexistent file, enabling these helper programs > + to define file systems are filled in on demand. There > + is a version of devfs that requires it, and it can be > + used to implement certain types of automatic mounting > + of other file systems. It is a daemonless and, perhaps > + consequently, smaller (under 2kB object on x86) alternative > + to autofs for certain uses, although it cannot be used as a > + replacement in all cases, and it is not available as a loadable > + module (because tmpfs isn't). For examples and more information, > + please see <file:Documentations/filesystems/lookup-trap.txt>. > + > config TMPFS_SECURITY > bool "tmpfs Security Labels" > depends on TMPFS_XATTR > --- linux-2.6.10-rc1-bk14/fs/Makefile 2004-11-04 23:32:39.000000000 -0800 > +++ linux/fs/Makefile 2004-11-05 23:24:25.000000000 -0800 > @@ -44,6 +44,10 @@ > obj-y += devpts/ > > obj-$(CONFIG_PROFILING) += dcookies.o > + > +ifdef CONFIG_TMPFS_LOOKUP_TRAPS > + obj-$(CONFIG_TMPFS) += lookuptrap.o userhelper.o > +endif
Just obj-$(CONFIG_TMPFS_LOOKUP_TRAPS) will do - it depends on CONFIG_TMPFS anyway, no need to duplicate this logic in Makefile.
> > # Do not add any filesystems before this line > obj-$(CONFIG_REISERFS_FS) += reiserfs/ > --- linux-2.6.10-rc1-bk14/fs/lookuptrap.c 1969-12-31 16:00:00.000000000 -0800 > +++ linux/fs/lookuptrap.c 2004-11-05 23:24:26.000000000 -0800 > @@ -0,0 +1,190 @@ > +/* > + struct dentry *trapping_lookup(struct inode *dir, > + struct dentry *dentry, > + struct nameidata *nd, > + char **shell_command_ptr) > + > + trapping_lookup() is an alternative to simple_lookup() (from libfs.c), > + that adds the ability to invoke a user level helper program when > + an attempt is made to access a nonexistant file. It invokes the > + command via call_fs_helper() from fs/helper.c. > + > + trapping_lookup takes one parameter in addition to the parameters > + that an inode_operations->lookup method requires. This paratmer, > + shell_command_ptr is the address of a pointer to a string containing > + the command to be executed trapping_lookup will always take a > + read lock on superblock->s_umount, so it is multiprocessor-safe for your > + file system's superblock mount and remount routines to modify that > + address, since mount and remount will take a write lock on > + superblock->s_umount. > + > + See tmpfs in mm/shmem.c for an example of use of trapping_lookup. > + > + trapping_lookup() is the only symbol exported from this file. > + > + > + Written by Adam J. Richter > + Copyright (C) 2004 Yggdrasil Computing, Inc. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 2 of the License, or > + (at your option) any later version. > + > + 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. > + > + You should have received a copy of the GNU General Public License > + along with this program; if not, write to the Free Software > + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + */
Most other files use this format for the initial comment:
- one-line description of the file - copyright notice - GPL boilerplate (if present at all) - more verbose description if needed
> + > +#include <linux/module.h> > +#include <linux/fs.h> > +#include <linux/dcache.h> > +#include <linux/wait.h> > +#include <linux/fsuserhelper.h> > + > +static inline int want_to_trap(struct nameidata *nd) > +{ > + return (nd != NULL); > +} > + > + > +/* > + * Retaining negative dentries for an in-memory filesystem just wastes > + * memory and lookup time: arrange for them to be deleted immediately. > + */ > +static int always_delete_dentry(struct dentry *dentry) > +{ > + return 1; > +} > + > +/* Force revalidation of all negative dentries. Note that this routine > + only gets called when some other routine has a reference to the dentry > + or the dentry has an inode. Otherwise, unused negative dentries > + are immediately dropped, because of always_delete_dentry. Apparently, > + the only time blocking_dentry_valid ends up being called with an empty > + inode is when a trapped reference is blocking on the user level helper. > +*/ > +static int blocking_dentry_valid(struct dentry *dentry, > + struct nameidata *nd) > +{ > + struct wait_queue_head *queue_head; > + struct semaphore *parent_inode_sem; > + DEFINE_WAIT(my_wait); > + > + if (dentry->d_inode != NULL) > + return 1; > + > + if (!want_to_trap(nd)) > + return 0; > + > + /* FIXME? I think there is no need to use dget_parent or > + to take dcache_lock here, because dentry->d_parent never > + changes and the dentry holds a reference to its parent, > + and dentry->d_parent holds a reference to > + dentry->d_d_parent->d_inode. Right??? -AJR 2004.11.05 */ > + parent_inode_sem = &dentry->d_parent->d_inode->i_sem; > + > + down(parent_inode_sem); > + > + queue_head = dentry->d_fsdata; > + if (!queue_head) { > + up(parent_inode_sem); > + return (dentry->d_inode != NULL); > + } > + > + prepare_to_wait(queue_head, &my_wait, TASK_INTERRUPTIBLE); > + up(parent_inode_sem); > + schedule(); > + finish_wait(queue_head, &my_wait); > + > + /* At this point, maybe the queue was woken up or maybe we got a > + signal. There is no need to check, because we just return > + in either case. */ > + > + return (dentry->d_inode != NULL); > + > +} > + > + > +static struct dentry_operations trapping_dentry_ops = { > + .d_delete = always_delete_dentry, > + .d_revalidate = blocking_dentry_valid, > +}; > + > +struct dentry * > +trapping_lookup(struct inode *dir, > + struct dentry *dentry, > + struct nameidata *nd, > + char **shell_command_ptr) > +{ > + /* > + We must do d_add before call_fs_helper to prevent > + a duplicate dentry from being created if the helper > + program attempts to access the same file name in /dev. > + If simple_lookup returns non-NULL, then that is to an error > + like a malformed file name, so we do not invoke trapfs_event. > + If the file is not found but there was no other error, > + simple_lookup returns NULL, and that is the only case > + in which we want to generate a notification. > + > + We also filter out the final path element of mknod, mkdir > + and symlink, because invoking the helper for mknod and mkdir > + could lead to deadlock when trapfs loads a device driver > + kernel module than. One would think that the way to filter > + would be to look at nd->flags to check that LOOKUP_CREATE > + is set and LOOKUP_OPEN is clear, but instead, the vfs > + layers passed nd==NULL in these cases via a routine > + called lookup_create (without any leading underscores), > + so we filter out the case where nd == NULL. > + > + Filtering out nd==NULL has the unintented side-effect of > + filtering out the final path component of arguments to > + rmdir, unlink and rename (both source and destination). > + For rmdir, unlink, and the source arguement to rename, > + that's fine, since nobody cares about attempts to remove > + nonexistant files. We're probably also OK skipping the > + notifications with regard to the destination argument to > + rename, although that is less clear. > + */ > + > + struct dentry *result; > + wait_queue_head_t queue_head; > + struct dentry *new; > + > + if (!want_to_trap(nd)) > + return simple_lookup(dir, dentry, nd); > + > + if (dentry->d_name.len > NAME_MAX) > + return ERR_PTR(-ENAMETOOLONG); > + > + init_waitqueue_head(&queue_head); > + > + dentry->d_fsdata = &queue_head; > + dentry->d_op = &trapping_dentry_ops; > + > + d_add(dentry, NULL); > + > + call_fs_helper(shell_command_ptr, &dir->i_sb->s_umount, "LOOKUP", > + dentry); > + > + new = d_lookup(dentry->d_parent, &dentry->d_name); > + > + if (new != dentry) /* also handles new==NULL */ > + result = new; > + else { > + dput(new); > + result = NULL; > + } > + > + wake_up(&queue_head); > + dentry->d_fsdata = NULL; /* Yes, we really can call wake_up() first.*/ > + > + return result; > +} > + > +EXPORT_SYMBOL_GPL(trapping_lookup);
Currently tmpfs cannot be modular, therefore the export is unneeded. If more users of this function will appear, EXPORT_SYMBOL_GPL could be easily added.
> --- linux-2.6.10-rc1-bk14/fs/userhelper.c 1969-12-31 16:00:00.000000000 -0800 > +++ linux/fs/userhelper.c 2004-11-05 23:24:27.000000000 -0800 > @@ -0,0 +1,190 @@ > +/* > + helper.c -- Invoke user level helper command for a struct dentry. > + > + Written by Adam J. Richter > + Copyright (C) 2004 Yggdrasil Computing, Inc. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 2 of the License, or > + (at your option) any later version. > + > + 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. > + > + You should have received a copy of the GNU General Public License > + along with this program; if not, write to the Free Software > + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + */ > + > +#include <linux/module.h> > +#include <linux/fs.h> > +#include <linux/ctype.h> > +#include <linux/fsuserhelper.h> > + > +static int path_len (struct dentry *de, struct dentry *root) > +{ > + int len = 0; > + while (de != root) { > + len += de->d_name.len + 1; /* count the '/' */ > + de = de->d_parent; > + } > + return len; /* -1 because we omit the leading '/', > + +1 because we include trailing '\0' */ > +} > + > +static int write_path_from_mnt (struct dentry *de, char *path, int buflen) > +{ > + struct dentry *mnt_root = de->d_parent->d_inode->i_sb->s_root; > + int len; > + char *path_orig = path; > + > + if (de == NULL || de == mnt_root) > + return -EINVAL; > + > + spin_lock(&dcache_lock); > + len = path_len(de, mnt_root); > + if (len > buflen) { > + spin_unlock(&dcache_lock); > + return -ENAMETOOLONG; > + } > + > + path += len - 1; > + *path = '\0'; > + > + for (;;) { > + path -= de->d_name.len; > + memcpy(path, de->d_name.name, de->d_name.len); > + de = de->d_parent; > + if (de == mnt_root) > + break; > + *(--path) = '/'; > + } > + > + spin_unlock(&dcache_lock); > + > + BUG_ON(path != path_orig); > + > + return 0; > +} > + > +static inline int > +calc_argc(const char *str_in, int *str_len) > +{ > + const char *str = str_in; > + int argc = 0; > + while (*str) { > + while (*str == ' ' || *str == '\t') > + str++; > + argc++; > + while (*str != ' ' && *str != '\t' && *str) > + str++; > + } > + *str_len = str - str_in; > + return argc; > +} > + > +static char ** > +gen_argv(char *str_in, int argc_extra, int *argc_out) > +{ > + int argc; > + char **argv; > + char *str_out; > + int str_len; > + > + if (!str_in) > + return NULL; > + > + while (*str_in == ' ' || *str_in == '\t') > + str_in++; > + > + if (*str_in == '\0') > + return NULL; > + > + argc = calc_argc(str_in, &str_len); > + > + argv = kmalloc(((argc + argc_extra) * sizeof(char*)) + str_len + 1, > + GFP_KERNEL); > + if (!argv) > + return NULL; > + > + str_out = (char*) (argv + argc + argc_extra); > + > + argc = 0; > + while (*str_in) { > + argv[argc++] = str_out; > + > + while (*str_in != ' ' && *str_in != '\t' && *str_in) > + *(str_out++) = *(str_in++); > + > + *(str_out++) = '\0'; > + > + while (*str_in == ' ' || *str_in == '\t') > + str_in++; > + } > + *argc_out = argc; > + return argv; > +} > + > +/* > + Warning: dentry_usermodehelper releases and retakes > + dentry->d_parent->d_inode->i_sem. It must be called with this > + semaphore already held. > + > + command_p is a pointer to a single string. It is *not* in argv format. > + Instead, elements are separated by spaces. > +*/ > +void call_fs_helper(char **command_ptr, > + struct rw_semaphore *command_rwsem, > + const char *event, > + struct dentry *dentry) > +{ > + char path[64];
Having such arbitrary limits on the path length is bad.
> + int argc; > + char **argv; > + struct semaphore *parent_inode_sem = &dentry->d_parent->d_inode->i_sem; > + > + if (write_path_from_mnt(dentry, path, sizeof(path)) == 0) { > + > + /* > + FIXME. We would not need the extra memory allocation, > + string copying, error branch and lines of source code > + due to err_strdup(), and we could put gen_argv
What is err_strdup()?
> + into the set_fs_helper, if call_usermodehelper
set_fs_helper also does not seem to exist.
> + and execve had a callback to inform us when > + execve was done copying argv and envp. With > + such a facility, we could just hold helper->rw_sem
helper->rw_sem should be command_rwsem, right?
> + up to that point, without having to make a copy of the > + argument (which we currently do) or hold the semaphore > + until the helper process exits (which would cause a > + deadlock if a helper process ever tried to change > + the helper string of a file system, especially since > + there is not such a thing as rw_down_read_interruptible > + that would make the deadlock breakable). > + */ > + > + up(parent_inode_sem); > + > + down_read(command_rwsem); > + argv = gen_argv(*command_ptr, 3, &argc); > + up_read(command_rwsem); > + > + if (argv != NULL) { > + static char *envp[] = > + {"PATH=/bin:/sbin:/usr/bin:/usr/sbin", > + "HOME=/", NULL }; > + > + argv[argc++] = (char*) event; > + argv[argc++] = path; > + argv[argc] = NULL; > + > + call_usermodehelper(argv[0], argv, envp, 1); > + kfree(argv); > + } > + > + down(parent_inode_sem); > + } > +} > +EXPORT_SYMBOL_GPL(call_fs_helper);
Again, this export is not needed now.
> --- linux-2.6.10-rc1-bk14/Documentation/filesystems/lookup-trap.txt 1969-12-31 16:00:00.000000000 -0800 > +++ linux/Documentation/filesystems/lookup-trap.txt 2004-11-05 23:24:28.000000000 -0800 > @@ -0,0 +1,436 @@ > +User's Guide To Trapping Directory Lookup Operations in Tmpfs > +Version 0.2 > + > + > +1. INSTRUCTIONS FOR THE IMPATIENT > + > + % modprobe tmpfs > + % mount -t tmpfs -o helper=/path/to/helper/program whatever /mnt > + % ls /mnt/foo > + # Notice that "/path/to/helper/program LOOKUP foo" was executed. > + > + > +2. OVERVIEW (from the Kconfig help) > + > + Tmpfs now allows user level programs to implement file systems > +that are filled in on demand. This feature works by invoking a > +configurable helper program on attempts to open() or stat() a > +nonexistent file. The access waits until the helper finishes, so the > +helper can install the missing file if desired. > + > + Using this facility, a shell script or small C program can > +implement a file system that automatically mounts remote file systems > +or creates device files on demand, similar to autofs or devfs, > +respectively. Tmpfs is, however, daemonless and, perhaps > +consequently, smaller than either of these, and may avoid some > +recursion problems. > + > + Tmpfs might also be useful for debugging programs where you > +want to trap the first access a particular file or perhaps in > +automatic installation of missing command or libraries by specifying a > +tmpfs file system in certain search paths. > + > + This access trapping facility is designed to be easily ported > +to other file systems. Kernel developers should examine the following > +source files for more information: > + > + include/linux/fsuserhelper > + include/linux/lookuptrap.h > + fs/lookuptrap.c > + fs/userhelper.c > + mm/shmem.c (for an example of tmpfs using trapping_lookup) > + > + > +3. GETTING STARTED > + > +3.1 MOUNTING THE FILE SYSTEM > + > + First, build and boot a kernel with tmpfs either compiled in > +or built as a module. If you compile tmpfs as a module, you may have > +to load it, although, since the module name ("tmpfs.ko") and the file > +system name ("tmpfs") match, that may be unnecessary if you've > +configured modprobe automatically.
It is impossible to compile tmpfs as a module (at least for now).
> + > + % modprobe tmpfs > + > + Now let's mount a tmpfs file system on /mnt. > + > + % mount -t tmpfs blah /mnt > + > + The file system will behave exactly like a ramfs file system. > +In fact, tmpfs is derived from ramfs. You can create files, > +directories, symbolic links and device nodes in it, and they will > +exist only in the computer's main memory. The contents of the file > +system will disappear as soon as you unmount it. > + > + If you mount multiple instances of tmpfs, you will get > +separate file systems. > + > + > +3.2. THE HELPER PROGRAM > + > + What distinguishes tmpfs from ramfs is that it can invoke a > +user level helper program when an attempt is made to open or stat a > +nonexistent file for the first time (if the name is not already in the > +dcache). The user level program is set with the "helper" mount > +option. It is possible to set, clear or change the helper command at > +any time, so let's go back to our example and put a helper command on > +the tmpfs file system that we mounted on /mnt: > + > + % mount -o remount,helper=/tmp/helper /mnt > + > + If you use the file system in /mnt now, nothing appears to have > +changed. Now let's put a simple shell script in /tmp/helper: > + > + % cat > /tmp/helper > + #!/bin/sh > + echo "$*" > /dev/console > + ^D > + % chmod a+x /tmp/helper > + > + Now you should see console messages like "LOOKUP foo" when you > +try to access the file /mnt/foo for the first time. > + > + You can also pass arguments to the helper program by using > +spaces in the helper mount option, like so: > + > + % mount -o remount,helper='/tmp/helper my_argument' /mnt > + > + If you do this, your console messages will start to look > +something like "my_argument LOOKUP foo". The arguments that > +you specify come before "LOOKUP foo" to facilitate the use of > +command interpreters, like, say, helper='/usr/bin/perl handler.pl'. > +Arguments also make it easy to pass things like the mount point or > +configuration files, which should make it easier to write facilities > +that work on multiple mount points. > + > + You can also deactivate the helper at any time, like so: > + > + mount -o remount,helper='' /mnt > + > +4. PRACTICAL EXAMPLES > + > +4.1 AN NFS AUTOMOUNTER > + > + % cat > /usr/sbin/tmpfs-automount > + #!/bin/sh > + topdir=$1 > + host=${3%/*} > + dir=$topdir/$host > + mkdir $dir > + mount -t nfs $host:/ $dir > + ^D > + % chmod a+x /usr/sbin/tmpfs-automount > + % mkdir /auto > + % mount -t tmpfs -o helper="/usr/sbin/tmpfs-automount /auto" x /auto > + > + Notice how we pass the additional argument "/auto" to the > +tmpfs-automount command. > + > + If you want automatic unmount after a timeout, you'll probably > +want to do something a little more elaborate, perhaps with a script that > +runs from cron. > + > + > +4.2 DEMAND LOADING OF DEVICE DRIVERS > + > + A version of devfs that uses tmpfs is under development and > +running on the system I am using to write this document, but I am > +still cleaning it up. Here is how it should work, although I have > +not yet actually tried devfs_helper on it. > + > + The devfs_helper program was originally written for a stripped down > +rewrite of devfs, from which tmpfs is derived. It can read your > +/etc/devfs.conf file (the file previously used to configured > +devfsd) and load modules specified by "LOOKUP" commands. Other > +devfs.conf command are ignored. > + > + % ftp ftp.yggdrasil.com > + login: anonymous > + password; guest > + ftp> cd /pub/dist/device_control/devfs > + ftp> get devfs_helper-0.2.tar.gz > + ..... > + ftp> quit > + % tar xfpvz devfs_helper-0.2.tar.gz > + % cd devfs_helper-0.2 > + % make > + % make install > + % mkdir /tmp/tmpdev > + % mount -t devfs /tmp/tmpdev > + % cp -apRx /dev/* /tmpdev/ > + % mount -t devfs -o helper=/sbin/devfs_helper blah /dev > + % mount -t msods /dev/floppy/0 /mnt > + > + The above example should load the floppy.ko kernel module > +if you have a a line in your /etc/devfs.conf file like this: > + > + LOOKUP floppy EXECUTE modprobe floppy > + > + > + You should also be able to use execfs in this fashion to get
And what is execfs?
> +automatic loading of kernel modules on non-devfs systems, although > +you'll need something like udev the larger udev to create the
"udev the larger udev"?
> +device files once the device drivers are registered. > + > + > +4.3 DEBUGGING A PROGRAM TRYING TO ACCESS A FILE > + > + % cat > /tmp/call-sleep > + #!/bin/sh > + sleep 30 > + ^D > + % mount -t tmpfs -o helper=/tmp/call-sleep foo /mnt > + % mv .bashrc .bashrc- > + % ln -s /mnt/whatever .bashrc > + % gdb /bin/sh > + GNU gdb 5.2 > + [blah blah blah] > + (gdb) run > + [program eventually hangs. Switch to another terminal session. You > + cannot control-C out of it, a tmpfs bug from call_usermodehelper.] > + % ps axf > + [Find the process under gdb. Let say it's pid 1152.] > + % kill -SEGV 1152 > + % ps auxww | grep sleep > + [Find the sleeping tmpfs helper. Let's say it's pid 1120.] > + % kill -9 1120 > + [Now back at the first session, running gdb on /bin/sh.] > + Program received signal SIGSEGV, Segmentation fault. > + 0xb7f303d4 in __libc_open () at __libc_open:-1 > + -1 __libc_open: No such file or directory. > + in __libc_open > + (gdb) where > + #0 0xb7f303d4 in __libc_open () at __libc_open:-1 > + #1 0xb7f8b4c0 in __DTOR_END__ () from /lib/libc.so.6 > + #2 0x080921ef in _evalfile (filename=0x80dc788 "/tmp/junk/.bashrc", flags=9) > + at evalfile.c:85 > + #3 0x08092635 in maybe_execute_file ( > + fname=0xfffffffe <Address 0xfffffffe out of bounds>, > + force_noninteractive=1) at evalfile.c:218 > + #4 0x08059fe8 in run_startup_files () at shell.c:1019 > + #5 0x08059849 in main (argc=1, argv=0xbfffebc4, env=0xbfffebcc) at shell.c:581 > + #6 0xb7e88e02 in __libc_start_main (main=0x8059380 <main>, argc=1, > + ubp_av=0xbfffebc4, init=0x805897c <_init>, > + fini=0xb80005ac <_dl_debug_mask>, rtld_fini=0x8000, stack_end=0x0) > + at ../sysdeps/generic/libc-start.c:129 > + > + > +4.4 AUTOMATIC LOADING OF MISSING PROGRAMS > + > + % cat > /usr/sbin/missing-program > + #!/bin/sh > + my-automatic-network-downloader $2 > + ^D > + % chmod a+x /usr/sbin/missing-program > + % mount -t tmpfs -o helper=/usr/sbin/my-automatic-installer /mnt > + % PATH=$PATH:/mnt:$PATH > + # We include $PATH a second time so that the program can be > + # found after it is installed. > + % kdevelop # Or some other program you don't have... > + > + ...or maybe something like this... > + > + % cat > /usr/sbin/missing-program > + #!/bin/sh > + export DISPLAY=:0 > + konqueror http://www.google.com/search?q="download+$2" & > + ^D > + % chmod a+x /usr/sbin/missing-program > + % mount -t tmpfs -o helper=/usr/sbin/missing-program glorp /mnt > + % xhost localhost > + % PATH=$PATH:/mnt > + % kdevelop > + > +4.4.1 AUTOMATIC LOADING OF MISSING LIBRARIES > + > + Same as above, but with this line: > + % LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt:$LD_LIBRARY_PATH > + > +4.5 ADVANCED EXAMPLE: Generating plain files > + > + Doing automatic generation of plain files (as opposed to > +directories, device files and symbolic links) requires more care, > +because the helper program's attempt to open a file for writing > +will itself invoke another instance of the user level handler. > + > + One approach, perhaps the only one, is to define some temporary > +file name pattern that your user handler knows to ignore, create the > +file with a temporary name that matches that pattern, and then > +rename the temporary file to the real file name, since rename > +operations are not trapped (and if renames ever are trapped in future,
Misplaced comma.
> +releases you could filter out renames where the source matched the > +tempary file pattern). > + > + [FIXME. This example is untested! --Adam Richter, 2004.11.03] > + > + > + % cat > /usr/sbin/my-decrypter > + #!/bin/sh > + encrypted_dir=$2 > + decrypted_dir=$3 > + key=$4 > + # $5 is "LOOKUP" > + filename=$6 > + > + case $target in ( tmp/* ) ; exit ;; esac > + tmpfile=$decrypted_dir/$$ > + decrypt --key $key < $encrypted_dir/$filename > $tmpfile > + mv $tmpfile $decrypted_dir/$filename > + ^D > + % chmod a+x /usr/sbin/my-decrypter > + % mount -t tmpfs -o \ > + helper='/usr/sbin/my-decrypter /cryptdir /mnt key' x /mnt > + % cat /mnt/my-secret-file > + .... > + > + If you want to make the temporary directory completely > +inaccessible from the public directory, you can create two mount > +points, where the public directory for the file system is actually > +a subdirectory of a larger hidden directory, like so: > + > + % mount -t tmpfs /some/place/hidden > + % chmod go-rwx /someplace/hidden > + % mkdir /some/place/hidden/mirror > + % mount --bind /some/place/hidden/mirror /public > + > + Now you can set up a helper program that operates in some > +other directory of /some/place/hidden, and then renames the > +resultant files into /some/place/hidden/mirror. > + > + > +4.5.1 A WARNING ABOUT SYMBOLIC LINKS AND THE GNU "ln -s" COMMAND > + > + If your helper program invoke the shell command "ln" to create > +symbolic links, you may need to use "filter and rename" technique that > +was described above for plain files, or one of several other > +workarounds listed below. If you helper program just uses the > +symlink() system call directly, you don't have to worry about this. > + > + What is the problem, exactly? The problem is that although > +symlink system calls are not trapped, the "ln" command from version > +5.2.1 of the GNU coreutils package (latest version as of this writing) > +does a stat system call on the target before doing the symlink, > +because it is specified to behave differently if the destination path > +exists and is a directory. stat, unlink symlink, is trapped, in order > +to support automatic creation of files in case a program stats a file > +before deciding whether to open it, and for things like "ls > +/auto/fileserver1.mycompany.com/". Consequently, a user level helper > +shell script that simply does "ln -s whatever $target_file_name" will > +deadlock (which can be broken by interrupting the child helper > +program). > + > + There are several possible solutions to this problem. > + > + 1. You can use a simpler symlink program, such as ssln, > + instead of "ln -s". > + > + 2. If the final path element of the symlink's contents is > + the same as the final path element of the symlink's name, > + then you can just specify the directory name as the second > + argument to "ln -s". In other words, change > + > + ln -s foo/bar /the/target/bar > + ...to... > + ln -s foo/bar /the/target > + > + 3. You can use the same filtering techniques for symlink > + as discussed for plain files in the previous section > + (create them with speical temporary names that your helper > + program knows to ignore and then rename them into place). > + > + 4. Invoke perl to do the symlink, if you know you have it available: > + > + perl -e 'symlink("contents", "target");' > + > + 5. You can port shell script to C or perl or some other > + language that gives you direct access to the symlink() > + system call. > + > + > + Perhaps, in the future, the GNU ln command could be changed so > +that, when it is called with exactly two file names, it would try to > +do the symlink() and then check if the target is a directory only if > +the symlink attempt failed. > + > + > +4.6. OTHER USES? > + > + I would be interested in hearing about any other uses that you up > +with for tmpfs, especially if I can include them in this document. > + > +5. SERIALIZATION > + > + Note that many instances of the user level helper program > +can potentially be running at the same time. It is up to "you", > +the implementor of the helper program to determine what sort of > +serialization you need and implement it. A simple solution to > +enforce complete serialization would be to have every instance > +of the helper program take an exclusive flock on some common > +file. > + > +6. KERNEL DEVELOPER ANSWERS ABOUT IMPLEMENTATION DECISIONS > + > +6.1 Q: Why doesn't tmpfs provide REGISTER and UNREGISTER events when > + new nodes are created or deleted from the file system, as the > + mini-devfs implementation from which is derived did? > + > + A: {,UN}REGISTER in Richard Gooch's implementation of devfs enabled > + things like automatically setting permissions and sound settings > + on your sound device when the driver was loaded, even if > + the loading of the driver had not been caused by devfs. > + For tmpfs-based devfs, I expect to implement that in > + a more complex way by shadowing the real devfs file system > + and creating {,UN}REGISTER events as updates are propagated > + from the real devfs to /dev. The advantages of this would be > + that module initialization would not be blockable by a user > + level program, and events like a device quickly appearing and > + disappearing could be coalesced (i.e., ignored in this case). > + > + I'm not convinced that {,UN}REGISTER has to go, but I haven't > + seen any compelling uses for it, and I know it's politically > + easier to add a feature than to remove one, especially if anyone > + has developed a dependence on it and does not want to port. So, > + I'm starting out with tmpfs not providing {,UN}REGISTER events. > + > +6.2 Q: Why isn't this facility implemented as an overlay file system? I'd > + like to be able to apply it to, say, /dev, without having > + to start out with /dev being a devfs file system. I could > + also demand load certain facilities based on accesses to /proc. > + > + A: There are about two dozen routines in inode_operations and > + file_operations that would require pass-through versions, and > + they are not as trivial as you might think because of > + locking issues involved in going through the vfs layer again. > + Also, currently, tmpfs, like ramfs and sysfs, use a struct > + inode and a struct dentry in kernel low memory for every > + existing node in the file system, about half a kilobyte > + per entry. So, tmpfs would need to be converted to allow > + inode structures to be released if it were to overlay > + potentially large directory trees. Also there are issues > + related to the underlying file system changing "out from > + under" tmpfs. Perhaps in the future this an be implemented. > + > + > +6.3. Q: Why isn't tmpfs a built-in kernel facility that can be > + applied to any file, like dnotify? That would also have > + the above advantages and could eliminate the mount complexity > + of overlays. > + > + A: I thought about defining a separating inode->directory_operations > + from inode->inode_operations. Since all of those operations that > + I want to intercept are called with inode->i_sem held, it > + follows that it would be SMP-safe to change an inode->dir_ops > + pointer dynamically, which would allow stacking of inode > + operations. This approach could be used to remove the special > + case code that is used to implement dnotify. But what happens > + when the inode is freed? It would be necessary to intercept > + the victim superblock's superblock_operations.drop_inode > + routine, which could get pretty messy, especially, if, for > + example, more than one trap was being used on the same file > + system. Perhaps if drop_inode were moved to struct > + inode_operations this would be easier. > + > + > +Adam J. Richter (adam@yggdrasil.com) > +2004.11.02 > - > 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/ > [unhandled content-type:application/pgp-signature] | |