/*
 * namei.c - NTFS kernel directory inode operations. Part of the Linux-NTFS
 * 	     project.
 *
 * Copyright (c) 2001,2002 Anton Altaparmakov.
 *
 * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
 * distribution in the file COPYING); if not, write to the Free Software
 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "ntfs.h"

/**
 * ntfs_lookup - find the inode represented by a dentry in a directory inode
 * @dir_ino:	directory inode in which to look for the inode
 * @dent:	dentry representing the inode to look for
 *
 * In short, ntfs_lookup() looks for the inode represented by the dentry @dent
 * in the directory inode @dir_ino and if found attaches the inode to the
 * dentry @dent.
 *
 * In more detail, the dentry @dent specifies which inode to look for by
 * supplying the name of the inode in @dent->d_name.name. ntfs_lookup()
 * converts the name to Unicode and walks the contents of the directory inode
 * @dir_ino looking for the converted Unicode name. If the name is found in the
 * directory, the corresponding inode is loaded by calling iget() on its inode
 * number and the inode is associated with the dentry @dent via a call to
 * d_add().
 *
 * If the name is not found in the directory, a NULL inode is inserted into the
 * dentry @dent. The dentry is then termed a negative dentry.
 *
 * Only if an actual error occurs, do we return an error via ERR_PTR().
 */
static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent)
{
	ntfs_volume *vol = NTFS_SB(dir_ino->i_sb);
	struct inode *dent_inode;
	u64 mref;
	unsigned long dent_ino;
	uchar_t *uname;
	int uname_len;

	ntfs_debug("Looking up %s in directory inode 0x%lx.",
			dent->d_name.name, dir_ino->i_ino);
	/* Convert the name of the dentry to Unicode. */
	uname_len = ntfs_nlstoucs(vol, dent->d_name.name, dent->d_name.len,
			&uname);
	if (uname_len < 0) {
		ntfs_error(vol->sb, "Failed to convert name to Unicode.");
		return ERR_PTR(uname_len);
	}
	mref = ntfs_lookup_inode_by_name(NTFS_I(dir_ino), uname, uname_len);
	kmem_cache_free(ntfs_name_cache, uname);
	if (!IS_ERR_MREF(mref)) {
		dent_ino = (unsigned long)MREF(mref);
		ntfs_debug("Found inode 0x%lx. Calling iget.", dent_ino);
		dent_inode = iget(vol->sb, dent_ino);
		if (dent_inode) {
			/* Consistency check. */
			if (MSEQNO(mref) == NTFS_I(dent_inode)->seq_no ||
					dent_ino == FILE_MFT) {
				d_add(dent, dent_inode);
				ntfs_debug("Done.");
				return NULL;
			}
			ntfs_error(vol->sb, "Found stale reference to inode "
					"0x%Lx (reference sequence number = "
					"0x%x, inode sequence number = 0x%x, "
					"returning -EACCES. Run chkdsk.",
					(unsigned long long)MREF(mref),
					MSEQNO(mref),
					NTFS_I(dent_inode)->seq_no);
			iput(dent_inode);
		} else
			ntfs_error(vol->sb, "iget(0x%Lx) failed, returning "
					"-EACCES.",
					(unsigned long long)MREF(mref));
		return ERR_PTR(-EACCES);
	}
	if (MREF_ERR(mref) == -ENOENT) {
		ntfs_debug("Entry was not found, adding negative dentry.");
		/* The dcache will handle negative entries. */
		d_add(dent, NULL);
		ntfs_debug("Done.");
		return NULL;
	}
	ntfs_error(vol->sb, "ntfs_lookup_ino_by_name() failed with error "
			"code %i.", -MREF_ERR(mref));
	return ERR_PTR(MREF_ERR(mref));
}

struct inode_operations ntfs_dir_inode_ops = {
	create:		NULL,		/* . */
	lookup:		ntfs_lookup,	/* lookup directory. */
	link:		NULL,		/* . */
	unlink:		NULL,		/* . */
	symlink:	NULL,		/* . */
	mkdir:		NULL,		/* . */
	rmdir:		NULL,		/* . */
	mknod:		NULL,		/* . */
	rename:		NULL,		/* . */
	readlink:	NULL,		/* . */
	follow_link:	NULL,		/* . */
	truncate:	NULL,		/* . */
	permission:	NULL,		/* . */
	revalidate:	NULL,		/* . */
	setattr:	NULL,		/* . */
	getattr:	NULL,		/* . */
};

#if 0
struct inode_operations {
	int (*create) (struct inode *,struct dentry *,int);
	struct dentry * (*lookup) (struct inode *,struct dentry *);
	int (*link) (struct dentry *,struct inode *,struct dentry *);
	int (*unlink) (struct inode *,struct dentry *);
	int (*symlink) (struct inode *,struct dentry *,const char *);
	int (*mkdir) (struct inode *,struct dentry *,int);
	int (*rmdir) (struct inode *,struct dentry *);
	int (*mknod) (struct inode *,struct dentry *,int,int);
	int (*rename) (struct inode *, struct dentry *,
			struct inode *, struct dentry *);
	int (*readlink) (struct dentry *, char *,int);
	int (*follow_link) (struct dentry *, struct nameidata *);
	void (*truncate) (struct inode *);
	int (*permission) (struct inode *, int);
	int (*revalidate) (struct dentry *);
	int (*setattr) (struct dentry *, struct iattr *);
	int (*getattr) (struct dentry *, struct iattr *);
};
#endif