/*
* dir.c
*
* Copyright (C) 1995, 1996 by Volker Lendecke
* Modified for big endian by J.F. Chadima and David S. Miller
* Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
* Modified 1998, 1999 Wolfram Pienkoss for NLS
* Modified 1999 Wolfram Pienkoss for directory caching
* Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info
*
*/
#include <linux/config.h>
#include <linux/time.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
#include <linux/smp_lock.h>
#include <linux/ncp_fs.h>
#include "ncplib_kernel.h"
static void ncp_read_volume_list(struct file *, void *, filldir_t,
struct ncp_cache_control *);
static void ncp_do_readdir(struct file *, void *, filldir_t,
struct ncp_cache_control *);
static int ncp_readdir(struct file *, void *, filldir_t);
static int ncp_create(struct inode *, struct dentry *, int, struct nameidata *);
static struct dentry *ncp_lookup(struct inode *, struct dentry *, struct nameidata *);
static int ncp_unlink(struct inode *, struct dentry *);
static int ncp_mkdir(struct inode *, struct dentry *, int);
static int ncp_rmdir(struct inode *, struct dentry *);
static int ncp_rename(struct inode *, struct dentry *,
struct inode *, struct dentry *);
static int ncp_mknod(struct inode * dir, struct dentry *dentry,
int mode, dev_t rdev);
#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
extern int ncp_symlink(struct inode *, struct dentry *, const char *);
#else
#define ncp_symlink NULL
#endif
const struct file_operations ncp_dir_operations =
{
.read = generic_read_dir,
.readdir = ncp_readdir,
.ioctl = ncp_ioctl,
};
struct inode_operations ncp_dir_inode_operations =
{
.create = ncp_create,
.lookup = ncp_lookup,
.unlink = ncp_unlink,
.symlink = ncp_symlink,
.mkdir = ncp_mkdir,
.rmdir = ncp_rmdir,
.mknod = ncp_mknod,
.rename = ncp_rename,
.setattr = ncp_notify_change,
};
/*
* Dentry operations routines
*/
static int ncp_lookup_validate(struct dentry *, struct nameidata *);
static int ncp_hash_dentry(struct dentry *, struct qstr *);
static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *);
static int ncp_delete_dentry(struct dentry *);
static struct dentry_operations ncp_dentry_operations =
{
.d_revalidate = ncp_lookup_validate,
.d_hash = ncp_hash_dentry,
.d_compare = ncp_compare_dentry,
.d_delete = ncp_delete_dentry,
};
struct dentry_operations ncp_root_dentry_operations =
{
.d_hash = ncp_hash_dentry,
.d_compare = ncp_compare_dentry,
.d_delete = ncp_delete_dentry,
};
/*
* Note: leave the hash unchanged if the directory
* is case-sensitive.
*/
static int
ncp_hash_dentry(struct dentry *dentry, struct qstr *this)
{
struct nls_table *t;
unsigned long hash;
int i;
t = NCP_IO_TABLE(dentry);
if (!ncp_case_sensitive(dentry->d_inode)) {
hash = init_name_hash();
for (i=0; i<this->len ; i++)
hash = partial_name_hash(ncp_tolower(t, this->name[i]),
hash);
this->hash = end_name_hash(hash);
}
return 0;
}
static int
ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
{
if (a->len != b->len)
return 1;
if (ncp_case_sensitive(dentry->d_inode))
return strncmp(a->name, b->name, a->len);
return ncp_strnicmp(NCP_IO_TABLE(dentry), a->name, b->name, a->len);
}
/*
* This is the callback from dput() when d_count is going to 0.
* We use this to unhash dentries with bad inodes.
* Closing files can be safely postponed until iput() - it's done there anyway.
*/
static int
ncp_delete_dentry(struct dentry * dentry)
{
struct inode *inode = dentry->d_inode;
if (inode) {
if (is_bad_inode(inode))
return 1;
} else
{
/* N.B. Unhash negative dentries? */
}
return 0;
}
static inline int
ncp_single_volume(struct ncp_server *server)
{
return (server->m.mounted_vol[0] != '\0');
}
static inline int ncp_is_server_root(struct inode *inode)
{
return (!ncp_single_volume(NCP_SERVER(inode)) &&
inode == inode->i_sb->s_root->d_inode);
}
/*
* This is the callback when the dcache has a lookup hit.
*/
#ifdef CONFIG_NCPFS_STRONG
/* try to delete a readonly file (NW R bit set) */
static int
ncp_force_unlink(struct inode *dir, struct dentry* dentry)
{
int res=0x9c,res2;
struct nw_modify_dos_info info;
__le32 old_nwattr;
struct inode *inode;
memset(&info, 0, sizeof(info));
/* remove the Read-Only flag on the NW server */
inode = dentry->d_inode;
old_nwattr = NCP_FINFO(inode)->nwattr;
info.attributes = old_nwattr & ~(aRONLY|aDELETEINHIBIT|aRENAMEINHIBIT);
res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES
|