Charles Duffy:
> Reading broken symlinks (ie. /proc/*/fd/* entries corresponding with
> sockets or stdin/stdout) results in ENXIO.

Surprise!
I thought nobody was interested in this patch.
As you wrote, the patch was bad. In my environment, I had modified it
already. Honestly speaking, I don't touch around here recently.
Hope this helps.


Junjiro Okajima


diff -u -r1.1 -r1.4
--- unionfs-20050923-1803/main.c        26 Sep 2005 11:12:24 -0000      1.1
+++ unionfs-20050923-1803/main.c        1 Oct 2005 10:29:59 -0000       1.4
@@ -15,6 +15,260 @@
 #include "unionfs.h"
 #include <linux/module.h>
 
+/* ---------------------------------------------------------------------- */
+static int proc_pid_readlink_hook(struct dentry * dentry,
+                                 char __user * buffer, int buflen);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
+#define FollowLinkType int
+#define FLReturn(n) return (n)
+#else
+#define FollowLinkType void*
+#define FLReturn(n) return ERR_PTR(n)
+#endif
+static FollowLinkType proc_pid_follow_link_hook(struct dentry *dentry,
+                                               struct nameidata *nd);
+
+enum {RealOp, HookOp};
+static struct inode_operations proc_pid_link_inode_op_hook[]
+= {{NULL,},
+   {
+       .readlink       = proc_pid_readlink_hook,
+       .follow_link    = proc_pid_follow_link_hook
+   }
+};
+
+/*
+ * returns tri-state:
+ * 0: the realpath is replaced and buflen is set.
+ * plus: the realpath is not need to be replaced, and buflen is set.
+ * minus: error.
+ */
+static int proc_pid_readlink_hook_body(struct dentry * dentry,
+                             char __user * buffer, int *buflen)
+{
+       int error, len;
+       struct super_block *sb;
+       char *real, *tmp, *p;
+       int bstart, bend, i, found, hidden_len = 0;
+
+       PASSERT(proc_pid_link_inode_op_hook[RealOp].readlink);
+       len = *buflen = proc_pid_link_inode_op_hook[RealOp].readlink(dentry, 
buffer, *buflen);
+       if (!is_root_unionfs() || len < 4) /* /a/b is minimum length */
+               return 1;
+
+       /*
+        * hide the name of branch in the real path.
+        */
+       error = -ENOMEM;
+       real = (void*)__get_free_page(GFP_KERNEL);
+       if (!real)
+               return error;
+       if (copy_from_user(real, buffer, len))
+               goto err1;
+       real[len] = 0;
+       tmp = (void*)__get_free_page(GFP_KERNEL);
+       if (!tmp)
+               goto err1;
+
+       /* printk("%s:%d: unionfs root %s\n", __func__, __LINE__, real); */
+       sb = current->fs->rootmnt->mnt_sb;
+       lock_super(sb);
+       lock_dentry(sb->s_root);
+       bstart = sbstart(sb);
+       bend = sbend(sb);
+       for (found = 0, i = bstart; !found && i <= bend; i++) {
+               struct vfsmount *hidden_mnt;
+               char *p;
+
+               hidden_mnt = stohiddenmnt_index(sb, i);
+               PASSERT(hidden_mnt);
+               p = d_path(hidden_mnt->mnt_root, hidden_mnt, tmp, PAGE_SIZE);
+               if (IS_ERR(p)) {
+                       error = PTR_ERR(p);
+                       goto err2;
+               }
+
+               /* printk("%s:%d: p %s\n", __func__, __LINE__, p); */
+               hidden_len = strlen(p);
+               if (1+len < hidden_len) continue;
+               if (memcmp(p, real, hidden_len) == 0 && real[hidden_len] == '/')
+                       found = 1;
+       }
+
+       if (!found) {
+               /*
+                * realpath is a socket.
+                * this is not an error.
+                */
+               error = 1;
+               goto err2;
+       }
+
+       /* printk("%s:%d: real %s\n", __func__, __LINE__, real); */
+       for (p = real, i = hidden_len; i < len; i++, p++)
+               *p = real[i];
+       *p = 0;
+       /* printk("%s:%d: real %s\n", __func__, __LINE__, real); */
+       len = *buflen -= hidden_len;
+       ASSERT(len > 0);
+       error = 0;
+       if (copy_to_user(buffer, real, len))
+               error = -EFAULT;
+ err2:
+       unlock_dentry(sb->s_root);
+       unlock_super(sb);
+       free_page((unsigned long)tmp);
+ err1:
+       free_page((unsigned long)real);
+       return error;
+}
+
+static int proc_pid_readlink_hook(struct dentry * dentry,
+                             char __user * buffer, int buflen)
+{
+       int l = buflen;
+       int error;
+
+       error = proc_pid_readlink_hook_body(dentry, buffer, &l);
+       return (error < 0)?error:l;
+}
+
+static FollowLinkType proc_pid_follow_link_hook(struct dentry *dentry, struct 
nameidata *nd)
+{
+       int error, len;
+       char *tmp;
+       struct nameidata unionfs_nd;
+       mm_segment_t oldfs;
+       FollowLinkType oerr;
+
+       PASSERT(proc_pid_link_inode_op_hook[RealOp].follow_link);
+       oerr = proc_pid_link_inode_op_hook[RealOp].follow_link(dentry, nd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
+       if (oerr < 0)
+               return oerr;
+#else
+       if (IS_ERR(oerr))
+               return oerr;
+#endif
+       if (!is_root_unionfs())
+               return oerr;
+
+       /*
+        * replace some members of nameidata to hide the root branch.
+        */
+       /* printk("%s:%d: here\n", __func__, __LINE__); */
+       error = -ENOMEM;
+       tmp = (void*)__get_free_page(GFP_KERNEL);
+       if (!tmp)
+               FLReturn(error);
+       len = PAGE_SIZE;
+       oldfs = get_fs();
+       set_fs(get_ds());
+       error = proc_pid_readlink_hook_body(dentry, tmp, &len);
+       set_fs(oldfs);
+       if (error) /* plus is not an error, minus is a real error */
+               goto err;
+       tmp[len] = 0;
+       oldfs = get_fs();
+       set_fs(get_ds());
+       error = user_path_walk(tmp, &unionfs_nd);
+       set_fs(oldfs);
+       if (error)
+               goto err;
+
+       /*
+         * printk("%s:%d: nd i_no %lu, und i_no %lu\n", __func__, __LINE__,
+        *        nd->dentry->d_inode->i_ino, 
unionfs_nd.dentry->d_inode->i_ino);
+         */
+       mntput(nd->mnt);
+       dput(nd->dentry);
+       nd->mnt = unionfs_nd.mnt;
+       nd->dentry = unionfs_nd.dentry;
+       /* do not path_realease unionfs_nd */
+ err:
+       free_page((unsigned long)tmp);
+       FLReturn(error);
+}
+
+static int lookup_proc_pid_link(struct nameidata nd[])
+{
+       mm_segment_t oldfs;
+       int error, i;
+       char a[32];
+
+       snprintf(a, sizeof(a), "/proc/%d/exe", current->pid);
+       oldfs = get_fs();
+       set_fs(get_ds());
+       error = user_path_walk_link(a, nd);
+       set_fs(oldfs);
+       if (!error)
+               return error;
+
+       /* slow search */
+       for (i = 1; i < 32; i++) {
+               snprintf(a, sizeof(a), "/proc/%d/exe", i);
+               oldfs = get_fs();
+               set_fs(get_ds());
+               error = user_path_walk_link(a, nd);
+               set_fs(oldfs);
+               if (!error)
+                       break;
+       }
+
+       return error;
+}
+
+static int set_proc_hook(void)
+{
+       struct dentry * dentry;
+       struct nameidata nd;
+       struct inode *inode;
+       int error;
+       const int n = sizeof(proc_pid_link_inode_op_hook[0]);
+
+       if (proc_pid_link_inode_op_hook[RealOp].readlink)
+               return 0;
+
+       error = lookup_proc_pid_link(&nd);
+       if (error < 0)
+               goto err1;
+
+       error = -ENXIO;
+       dentry = nd.dentry;
+       if (!(dentry && dentry->d_inode && dentry->d_inode->i_op
+             && dentry->d_inode->i_op->readlink))
+               goto err2;
+
+       error = 0;
+       inode = dentry->d_inode;
+       // what to lock?
+       memcpy(proc_pid_link_inode_op_hook+RealOp, inode->i_op, n);
+       memcpy(inode->i_op, proc_pid_link_inode_op_hook+HookOp, n);
+
+ err2:
+       path_release(&nd);
+ err1:
+       if (error)
+               printk(KERN_WARNING "not found exe link under /proc\n");
+       return error;
+}
+
+static int unset_proc_hook(void)
+{
+#if 0
+       /* not implemented */
+       const int n = sizeof(proc_pid_link_inode_op_hook[0]);
+
+       // what to lock?
+       memcpy(inode->i_op, proc_pid_link_inode_op_hook+RealOp, n);
+       memset(proc_pid_link_inode_op_hook+HookOp, 0, n);
+#endif
+       return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
 /* sb we pass is unionfs's super_block */
 int unionfs_interpose(struct dentry *dentry, struct super_block *sb, int flag)
 {
@@ -877,11 +1131,15 @@
                                          int flags, const char *dev_name,
                                          void *raw_data)
 {
+       int error;
+
+       error = set_proc_hook();
        return get_sb_nodev(fs_type, flags, raw_data, unionfs_read_super);
 }
 
 void unionfs_kill_block_super(struct super_block *sb)
 {
+       unset_proc_hook();
        generic_shutdown_super(sb);
 }
 

_______________________________________________
unionfs mailing list
[email protected]
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs

Reply via email to