lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1211340189-11813-29-git-send-email-hooanon05@yahoo.co.jp>
Date:	Wed, 21 May 2008 12:22:58 +0900
From:	hooanon05@...oo.co.jp
To:	linux-fsdevel@...r.kernel.org, linux-kernel@...r.kernel.org
Cc:	Junjiro Okajima <hooanon05@...oo.co.jp>
Subject: [AUFS PATCH v2.6.26-rc2-mm1 28/39] aufs inode other operations

From: Junjiro Okajima <hooanon05@...oo.co.jp>

initial commit
inode other operations
(permission/lookup/setattr/readlink/follow_link/put_link)

Signed-off-by: Junjiro Okajima <hooanon05@...oo.co.jp>
---
 fs/aufs/i_op.c |  728 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 728 insertions(+), 0 deletions(-)
 create mode 100644 fs/aufs/i_op.c

diff --git a/fs/aufs/i_op.c b/fs/aufs/i_op.c
new file mode 100644
index 0000000..deb25d8
--- /dev/null
+++ b/fs/aufs/i_op.c
@@ -0,0 +1,728 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/*
+ * inode operations (except add/del/rename)
+ */
+
+#include <linux/fs_stack.h>
+#include <linux/uaccess.h>
+#include "aufs.h"
+
+static int h_permission(struct inode *h_inode, int mask,
+			struct nameidata *fake_nd, int brperm, int dlgt)
+{
+	int err, submask;
+	const int write_mask = (mask & (MAY_WRITE | MAY_APPEND));
+
+	LKTRTrace("ino %lu, mask 0x%x, brperm 0x%x\n",
+		  h_inode->i_ino, mask, brperm);
+
+	err = -EACCES;
+	if (unlikely((write_mask && IS_IMMUTABLE(h_inode))
+		     || ((mask & MAY_EXEC) && S_ISREG(h_inode->i_mode)
+			 && fake_nd && fake_nd->path.mnt
+			 && (fake_nd->path.mnt->mnt_flags & MNT_NOEXEC))
+		    ))
+		goto out;
+
+	/* skip hidden fs test in the case of write to ro branch */
+	submask = mask & ~MAY_APPEND;
+	if (unlikely((write_mask && !au_br_writable(brperm))
+		     || !h_inode->i_op
+		     || !h_inode->i_op->permission)) {
+		/* LKTRLabel(generic_permission); */
+		err = generic_permission(h_inode, submask, NULL);
+	} else {
+		/* LKTRLabel(h_inode->permission); */
+		err = h_inode->i_op->permission(h_inode, submask, fake_nd);
+		AuTraceErr(err);
+	}
+
+#if 1 /* todo: export? */
+	if (!err)
+		err = au_security_inode_permission(h_inode, mask, fake_nd,
+						   dlgt);
+#endif
+
+ out:
+	AuTraceErr(err);
+	return err;
+}
+
+static int silly_lock(struct inode *inode, struct nameidata *nd)
+{
+	int locked = 0;
+	struct super_block *sb = inode->i_sb;
+
+	LKTRTrace("i%lu, nd %p\n", inode->i_ino, nd);
+
+	if (!nd || !nd->path.dentry) {
+		si_read_lock(sb, AuLock_FLUSH);
+		ii_read_lock_child(inode);
+	} else if (nd->path.dentry->d_inode != inode) {
+		locked = 1;
+		/* lock child first, then parent */
+		si_read_lock(sb, AuLock_FLUSH);
+		ii_read_lock_child(inode);
+		di_read_lock_parent(nd->path.dentry, 0);
+	} else {
+		locked = 2;
+		aufs_read_lock(nd->path.dentry, AuLock_FLUSH | AuLock_IR);
+	}
+	return locked;
+}
+
+static void silly_unlock(int locked, struct inode *inode, struct nameidata *nd)
+{
+	struct super_block *sb = inode->i_sb;
+
+	LKTRTrace("locked %d, i%lu, nd %p\n", locked, inode->i_ino, nd);
+
+	switch (locked) {
+	case 0:
+		ii_read_unlock(inode);
+		si_read_unlock(sb);
+		break;
+	case 1:
+		di_read_unlock(nd->path.dentry, 0);
+		ii_read_unlock(inode);
+		si_read_unlock(sb);
+		break;
+	case 2:
+		aufs_read_unlock(nd->path.dentry, AuLock_FLUSH | AuLock_IR);
+		break;
+	default:
+		BUG();
+	}
+}
+
+static int aufs_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+	int err, locked, dlgt;
+	aufs_bindex_t bindex, bend;
+	struct inode *h_inode;
+	struct super_block *sb;
+	unsigned int mnt_flags;
+	struct nameidata fake_nd, *p;
+	const int write_mask = (mask & (MAY_WRITE | MAY_APPEND));
+	const int nondir = !S_ISDIR(inode->i_mode);
+
+	LKTRTrace("ino %lu, mask 0x%x, nondir %d, write_mask %d, "
+		  "nd %d{%d, %d}\n",
+		  inode->i_ino, mask, nondir, write_mask,
+		  !!nd, nd ? !!nd->path.dentry : 0, nd ? !!nd->path.mnt : 0);
+
+	sb = inode->i_sb;
+	locked = silly_lock(inode, nd);
+	mnt_flags = au_mntflags(sb);
+	dlgt = !!au_opt_test_dlgt(mnt_flags);
+
+	if (nd)
+		fake_nd = *nd;
+	if (/* unlikely */(nondir || write_mask
+			   || au_opt_test_dirperm1(mnt_flags))) {
+		h_inode = au_h_iptr(inode, au_ibstart(inode));
+		AuDebugOn(!h_inode
+			  || ((h_inode->i_mode & S_IFMT)
+			      != (inode->i_mode & S_IFMT)));
+		err = 0;
+		bindex = au_ibstart(inode);
+		p = au_fake_dm(&fake_nd, nd, sb, bindex);
+		/* actual test will be delegated to LSM */
+		if (IS_ERR(p))
+			AuDebugOn(PTR_ERR(p) != -ENOENT);
+		else {
+			LKTRTrace("b%d\n", bindex);
+			err = h_permission(h_inode, mask, p,
+					   au_sbr_perm(sb, bindex), dlgt);
+			au_fake_dm_release(p);
+		}
+		if (write_mask && !err) {
+			/* test whether the upper writable branch exists */
+			err = -EROFS;
+			for (; bindex >= 0; bindex--)
+				if (!au_br_rdonly(au_sbr(sb, bindex))) {
+					err = 0;
+					break;
+				}
+		}
+		goto out;
+	}
+
+	/* non-write to dir */
+	err = 0;
+	bend = au_ibend(inode);
+	for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) {
+		h_inode = au_h_iptr(inode, bindex);
+		if (!h_inode)
+			continue;
+		AuDebugOn(!S_ISDIR(h_inode->i_mode));
+
+		p = au_fake_dm(&fake_nd, nd, sb, bindex);
+		/* actual test will be delegated to LSM */
+		if (IS_ERR(p))
+			AuDebugOn(PTR_ERR(p) != -ENOENT);
+		else {
+			LKTRTrace("b%d\n", bindex);
+			err = h_permission(h_inode, mask, p,
+					   au_sbr_perm(sb, bindex), dlgt);
+			au_fake_dm_release(p);
+		}
+	}
+
+ out:
+	silly_unlock(locked, inode, nd);
+	AuTraceErr(err);
+	return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
+				  struct nameidata *nd)
+{
+	struct dentry *ret, *parent;
+	int err, npositive;
+	struct inode *inode, *h_inode;
+	struct nameidata tmp_nd, *ndp;
+
+	LKTRTrace("dir %lu, %.*s, nd{0x%x}\n",
+		  dir->i_ino, AuDLNPair(dentry), nd ? nd->flags : 0);
+	AuDebugOn(IS_ROOT(dentry));
+	IMustLock(dir);
+
+	/* nd can be NULL */
+	parent = dentry->d_parent; /* dir inode is locked */
+	aufs_read_lock(parent, AuLock_FLUSH);
+	err = au_alloc_dinfo(dentry);
+	ret = ERR_PTR(err);
+	if (unlikely(err))
+		goto out;
+
+	ndp = au_dup_nd(au_sbi(dir->i_sb), &tmp_nd, nd);
+	npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0, ndp);
+	err = npositive;
+	ret = ERR_PTR(err);
+	if (unlikely(err < 0))
+		goto out_unlock;
+	inode = NULL;
+	if (npositive) {
+		/*
+		 * stop 'race'-ing between hardlinks under different parents.
+		 */
+		h_inode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode;
+		AuDebugOn(!h_inode);
+		if (h_inode->i_nlink == 1 || S_ISDIR(h_inode->i_mode))
+			inode = au_new_inode(dentry);
+		else {
+			static DEFINE_MUTEX(mtx);
+			mutex_lock(&mtx);
+			inode = au_new_inode(dentry);
+			mutex_unlock(&mtx);
+		}
+		ret = (void *)inode;
+	}
+	if (!IS_ERR(inode)) {
+		ret = d_splice_alias(inode, dentry);
+		if (unlikely(IS_ERR(ret) && inode))
+			ii_write_unlock(inode);
+		AuDebugOn(nd
+			  && (nd->flags & LOOKUP_OPEN)
+			  && nd->intent.open.file
+			  && nd->intent.open.file->f_dentry);
+		au_store_fmode_exec(nd, inode);
+	}
+
+ out_unlock:
+	di_write_unlock(dentry);
+ out:
+	aufs_read_unlock(parent, !AuLock_IR);
+	AuTraceErrPtr(ret);
+	return ret;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * decide the branch and the parent dir where we will create a new entry.
+ * returns new bindex or an error.
+ * copyup the parent dir if needed.
+ */
+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
+	      struct au_wr_dir_args *args)
+{
+	int err;
+	aufs_bindex_t bcpup, bstart, src_bstart;
+	struct super_block *sb;
+	struct dentry *parent, *src_parent;
+	struct au_sbinfo *sbinfo;
+	const int add_entry = au_ftest_wrdir(args->flags, ADD_ENTRY);
+	const int lock_srcdir = au_ftest_wrdir(args->flags, LOCK_SRCDIR);
+
+	LKTRTrace("%.*s, src %p, {%d, 0x%x}\n",
+		  AuDLNPair(dentry), src_dentry, args->force_btgt, args->flags);
+
+	src_parent = NULL;
+	sb = dentry->d_sb;
+	sbinfo = au_sbi(sb);
+	parent = dget_parent(dentry);
+	bstart = au_dbstart(dentry);
+	bcpup = bstart;
+	if (args->force_btgt < 0) {
+		if (src_dentry) {
+			src_bstart = au_dbstart(src_dentry);
+			if (src_bstart < bstart)
+				bcpup = src_bstart;
+		} else if (add_entry) {
+			err = AuWbrCreate(sbinfo, dentry,
+					  au_ftest_wrdir(args->flags, ISDIR));
+			bcpup = err;
+		}
+
+		if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) {
+			if (add_entry)
+				err = AuWbrCopyup(sbinfo, dentry);
+			else {
+				di_read_lock_parent(parent, !AuLock_IR);
+				err = AuWbrCopyup(sbinfo, dentry);
+				di_read_unlock(parent, !AuLock_IR);
+			}
+			bcpup = err;
+			if (unlikely(err < 0))
+				goto out;
+		}
+	} else {
+		bcpup = args->force_btgt;
+		AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode));
+	}
+	LKTRTrace("bstart %d, bcpup %d\n", bstart, bcpup);
+	if (bstart < bcpup)
+		au_update_dbrange(dentry, /*do_put_zero*/1);
+
+	err = bcpup;
+	if (bcpup == bstart)
+		goto out; /* success */
+
+	/* copyup the new parent into the branch we process */
+	if (src_dentry) {
+		src_parent = dget_parent(src_dentry);
+		if (lock_srcdir)
+			di_write_lock_parent2(src_parent);
+	}
+
+	if (add_entry) {
+		au_update_dbstart(dentry);
+		IMustLock(parent->d_inode);
+		DiMustWriteLock(parent);
+		IiMustWriteLock(parent->d_inode);
+	} else
+		di_write_lock_parent(parent);
+
+	err = 0;
+	if (!au_h_dptr(parent, bcpup)) {
+		if (bstart < bcpup)
+			err = au_cpdown_dirs(dentry, bcpup, src_parent);
+		else
+			err = au_cpup_dirs(dentry, bcpup, src_parent);
+	}
+	if (!err && add_entry) {
+		struct dentry *h_parent;
+		struct inode *h_dir;
+
+		h_parent = au_h_dptr(parent, bcpup);
+		AuDebugOn(!h_parent);
+		h_dir = h_parent->d_inode;
+		AuDebugOn(!h_dir);
+		mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT);
+		err = au_lkup_neg(dentry, bcpup);
+		mutex_unlock(&h_dir->i_mutex);
+		if (bstart < bcpup && au_dbstart(dentry) < 0) {
+			au_set_dbstart(dentry, 0);
+			au_update_dbrange(dentry, /*do_put_zero*/0);
+		}
+	}
+
+	if (!add_entry)
+		di_write_unlock(parent);
+	if (lock_srcdir)
+		di_write_unlock(src_parent);
+	dput(src_parent);
+	if (!err)
+		err = bcpup; /* success */
+ out:
+	dput(parent);
+	LKTRTrace("err %d\n", err);
+	AuTraceErr(err);
+	return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void au_hi_lock(struct inode *h_inode, int isdir, struct inode *inode,
+		       aufs_bindex_t bindex)
+{
+	if (!isdir)
+		mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
+	else
+		au_hdir2_lock(h_inode, inode, bindex);
+}
+
+static void au_hi_unlock(struct inode *h_inode, int isdir, struct inode *inode,
+			 aufs_bindex_t bindex)
+{
+	if (!isdir)
+		mutex_unlock(&h_inode->i_mutex);
+	else
+		au_hdir_unlock(h_inode, inode, bindex);
+}
+
+struct au_icpup_args {
+	aufs_bindex_t btgt;
+	unsigned char isdir, did_cpup; /* flags */
+	unsigned char hinotify;
+	struct dentry *parent, *gparent, *h_dentry;
+	struct inode *dir, *gdir, *h_inode, *h_dir;
+};
+
+static int au_lock_and_icpup(struct dentry *dentry, loff_t sz,
+			     struct au_icpup_args *rargs)
+{
+	int err;
+	aufs_bindex_t bstart;
+	struct super_block *sb;
+	struct dentry *hi_wh;
+	struct inode *inode;
+	struct au_wr_dir_args wr_dir_args = {
+		.force_btgt	= -1,
+		.flags		= 0
+	};
+
+	LKTRTrace("%.*s, %lld\n", AuDLNPair(dentry), sz);
+
+	di_write_lock_child(dentry);
+	bstart = au_dbstart(dentry);
+	sb = dentry->d_sb;
+	inode = dentry->d_inode;
+	rargs->isdir = S_ISDIR(inode->i_mode);
+	if (rargs->isdir)
+		au_fset_wrdir(wr_dir_args.flags, ISDIR);
+	err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args);
+	if (unlikely(err < 0))
+		goto out_dentry;
+	rargs->btgt = err;
+	rargs->did_cpup = (err != bstart);
+	err = 0;
+
+	/* crazy udba locks */
+	rargs->hinotify = !!au_opt_test(au_mntflags(sb), UDBA_INOTIFY);
+	if (unlikely(!IS_ROOT(dentry))) {
+		rargs->parent = dget_parent(dentry);
+		rargs->dir = rargs->parent->d_inode;
+		di_read_lock_parent(rargs->parent, AuLock_IR);
+	}
+	rargs->h_dentry = au_h_dptr(dentry, au_dbstart(dentry));
+	rargs->h_inode = rargs->h_dentry->d_inode;
+	AuDebugOn(!rargs->h_inode);
+
+	if (!rargs->did_cpup) {
+		au_hi_lock(rargs->h_inode, rargs->isdir, inode, rargs->btgt);
+		/* todo: revalidate the lower dentry? */
+		goto out; /* success */
+	}
+
+	if (unlikely(rargs->hinotify
+		     && rargs->parent
+		     && !IS_ROOT(rargs->parent))) {
+		rargs->gparent = dget_parent(rargs->parent);
+		rargs->gdir = rargs->gparent->d_inode;
+		ii_read_lock_parent2(rargs->gdir);
+	}
+
+	hi_wh = NULL;
+	rargs->h_dir = au_h_iptr(rargs->dir, rargs->btgt);
+	au_hdir_lock(rargs->h_dir, rargs->dir, rargs->btgt);
+	/* todo: revalidate the lower dentry? */
+	au_hi_lock(rargs->h_inode, rargs->isdir, inode, bstart);
+	if (!d_unhashed(dentry)) {
+		err = au_sio_cpup_simple(dentry, rargs->btgt, sz, AuCpup_DTIME);
+		if (!err)
+			rargs->h_dentry = au_h_dptr(dentry, au_dbstart(dentry));
+	} else {
+		hi_wh = au_hi_wh(inode, rargs->btgt);
+		if (!hi_wh) {
+			err = au_sio_cpup_wh(dentry, rargs->btgt, sz,
+					     /*file*/NULL);
+			if (!err)
+				hi_wh = au_hi_wh(inode, rargs->btgt);
+			/* todo: revalidate hi_wh? */
+		}
+		if (!hi_wh)
+			rargs->h_dentry = au_h_dptr(dentry, au_dbstart(dentry));
+		else
+			rargs->h_dentry = hi_wh; /* do not dget here */
+	}
+
+	au_hi_unlock(rargs->h_inode, rargs->isdir, inode, bstart);
+	rargs->h_inode = rargs->h_dentry->d_inode;
+	AuDebugOn(!rargs->h_inode);
+	if (!err)
+		au_hi_lock(rargs->h_inode, rargs->isdir, inode, rargs->btgt);
+	au_hdir_unlock(rargs->h_dir, rargs->dir, rargs->btgt);
+	if (!err)
+		goto out; /* success */
+
+	au_hi_unlock(rargs->h_inode, rargs->isdir, inode, rargs->btgt);
+	if (unlikely(rargs->gdir)) {
+		ii_read_unlock(rargs->gdir);
+		dput(rargs->gparent);
+	}
+	if (unlikely(rargs->dir)) {
+		di_read_unlock(rargs->parent, AuLock_IR);
+		dput(rargs->parent);
+	}
+
+ out_dentry:
+	di_write_unlock(dentry);
+ out:
+	AuTraceErr(err);
+	return err;
+}
+
+static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
+{
+	int err;
+	struct inode *inode;
+	struct au_hin_ignore ign;
+	struct vfsub_args vargs;
+	struct super_block *sb;
+	__u32 events;
+	struct file *file;
+	loff_t sz;
+	struct au_icpup_args rargs;
+
+	LKTRTrace("%.*s, ia_valid 0x%x\n", AuDLNPair(dentry), ia->ia_valid);
+	inode = dentry->d_inode;
+	IMustLock(inode);
+
+	sb = dentry->d_sb;
+	si_read_lock(sb, AuLock_FLUSH);
+
+	file = NULL;
+	if (ia->ia_valid & ATTR_FILE) {
+		/* currently ftruncate(2) only */
+		file = ia->ia_file;
+		fi_write_lock(file);
+		ia->ia_file = au_h_fptr(file, au_fbstart(file));
+	}
+
+	sz = -1;
+	if ((ia->ia_valid & ATTR_SIZE)
+	    && ia->ia_size < i_size_read(inode))
+		sz = ia->ia_size;
+	memset(&rargs, 0, sizeof(rargs));
+	err = au_lock_and_icpup(dentry, sz, &rargs);
+	if (unlikely(err < 0))
+		goto out;
+
+	if ((ia->ia_valid & ATTR_SIZE)
+	    && ia->ia_size < i_size_read(inode)) {
+		err = vmtruncate(inode, ia->ia_size);
+		if (unlikely(err))
+			goto out_unlock;
+	}
+
+	if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
+		ia->ia_valid &= ~ATTR_MODE;
+
+	events = 0;
+	vfsub_args_init(&vargs, &ign, au_opt_test_dlgt(au_mntflags(sb)), 0);
+	if (unlikely(rargs.hinotify && rargs.dir)) {
+		events = vfsub_events_notify_change(ia);
+		if (events)
+			vfsub_ign_hinode(&vargs, events,
+					 au_hi(rargs.dir, rargs.btgt));
+	}
+	err = vfsub_notify_change(rargs.h_dentry, ia, &vargs);
+	if (!err)
+		au_cpup_attr_changeable(inode);
+
+ out_unlock:
+	au_hi_unlock(rargs.h_inode, rargs.isdir, inode, rargs.btgt);
+	if (unlikely(rargs.gdir)) {
+		ii_read_unlock(rargs.gdir);
+		dput(rargs.gparent);
+	}
+	if (unlikely(rargs.dir)) {
+		di_read_unlock(rargs.parent, AuLock_IR);
+		dput(rargs.parent);
+	}
+	di_write_unlock(dentry);
+ out:
+	if (file) {
+		ia->ia_file = file;
+		/* ia->ia_valid |= ATTR_FILE; */
+		fi_write_unlock(file);
+	}
+	si_read_unlock(sb);
+	AuTraceErr(err);
+	return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int h_readlink(struct dentry *dentry, int bindex, char __user *buf,
+		      int bufsiz)
+{
+	struct super_block *sb;
+	struct dentry *h_dentry;
+
+	LKTRTrace("%.*s, b%d, %d\n", AuDLNPair(dentry), bindex, bufsiz);
+
+	h_dentry = au_h_dptr(dentry, bindex);
+	if (unlikely(!h_dentry->d_inode->i_op
+		     || !h_dentry->d_inode->i_op->readlink))
+		return -EINVAL;
+
+	sb = dentry->d_sb;
+	if (!au_test_ro(sb, bindex, dentry->d_inode)) {
+		touch_atime(au_sbr_mnt(sb, bindex), h_dentry);
+		au_update_fuse_h_inode(NULL, h_dentry); /*ignore*/
+		fsstack_copy_attr_atime(dentry->d_inode, h_dentry->d_inode);
+	}
+	return h_dentry->d_inode->i_op->readlink(h_dentry, buf, bufsiz);
+}
+
+static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
+{
+	int err;
+
+	LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), bufsiz);
+
+	aufs_read_lock(dentry, AuLock_IR);
+	err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz);
+	aufs_read_unlock(dentry, AuLock_IR);
+	AuTraceErr(err);
+	return err;
+}
+
+static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	int err;
+	char *buf;
+	mm_segment_t old_fs;
+
+	LKTRTrace("%.*s, nd %.*s\n",
+		  AuDLNPair(dentry), AuDLNPair(nd->path.dentry));
+
+	err = -ENOMEM;
+	buf = __getname();
+	if (unlikely(!buf))
+		goto out;
+
+	aufs_read_lock(dentry, AuLock_IR);
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	err = h_readlink(dentry, au_dbstart(dentry), (char __user *)buf,
+			 PATH_MAX);
+	set_fs(old_fs);
+	aufs_read_unlock(dentry, AuLock_IR);
+
+	if (err >= 0) {
+		buf[err] = 0;
+		/* will be freed by put_link */
+		nd_set_link(nd, buf);
+		return NULL; /* success */
+	}
+	__putname(buf);
+
+ out:
+	path_put(&nd->path);
+	AuTraceErr(err);
+	return ERR_PTR(err);
+}
+
+static void aufs_put_link(struct dentry *dentry, struct nameidata *nd,
+			  void *cookie)
+{
+	LKTRTrace("%.*s\n", AuDLNPair(dentry));
+	__putname(nd_get_link(nd));
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void aufs_truncate_range(struct inode *inode, loff_t start, loff_t end)
+{
+	AuUnsupport();
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct inode_operations aufs_symlink_iop = {
+	.permission	= aufs_permission,
+	.setattr	= aufs_setattr,
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+	.getattr	= aufs_getattr,
+#endif
+
+	.readlink	= aufs_readlink,
+	.follow_link	= aufs_follow_link,
+	.put_link	= aufs_put_link
+};
+
+struct inode_operations aufs_dir_iop = {
+	.create		= aufs_create,
+	.lookup		= aufs_lookup,
+	.link		= aufs_link,
+	.unlink		= aufs_unlink,
+	.symlink	= aufs_symlink,
+	.mkdir		= aufs_mkdir,
+	.rmdir		= aufs_rmdir,
+	.mknod		= aufs_mknod,
+	.rename		= aufs_rename,
+
+	.permission	= aufs_permission,
+	.setattr	= aufs_setattr,
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+	.getattr	= aufs_getattr,
+#endif
+
+#if 0 /* reserved for future use */
+	.setxattr	= aufs_setxattr,
+	.getxattr	= aufs_getxattr,
+	.listxattr	= aufs_listxattr,
+	.removexattr	= aufs_removexattr
+#endif
+};
+
+struct inode_operations aufs_iop = {
+	.permission	= aufs_permission,
+	.setattr	= aufs_setattr,
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+	.getattr	= aufs_getattr,
+#endif
+
+#if 0 /* reserved for future use */
+	.setxattr	= aufs_setxattr,
+	.getxattr	= aufs_getxattr,
+	.listxattr	= aufs_listxattr,
+	.removexattr	= aufs_removexattr,
+#endif
+
+	.truncate_range	= aufs_truncate_range
+};
-- 
1.5.5.1.308.g1fbb5.dirty

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ