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