summaryrefslogtreecommitdiff
path: root/fs/ncpfs/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ncpfs/dir.c')
-rw-r--r--fs/ncpfs/dir.c156
1 files changed, 69 insertions, 87 deletions
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
index aeed93a6bde0..3be047474bfc 100644
--- a/fs/ncpfs/dir.c
+++ b/fs/ncpfs/dir.c
@@ -23,15 +23,15 @@
#include "ncp_fs.h"
-static void ncp_read_volume_list(struct file *, void *, filldir_t,
+static void ncp_read_volume_list(struct file *, struct dir_context *,
struct ncp_cache_control *);
-static void ncp_do_readdir(struct file *, void *, filldir_t,
+static void ncp_do_readdir(struct file *, struct dir_context *,
struct ncp_cache_control *);
-static int ncp_readdir(struct file *, void *, filldir_t);
+static int ncp_readdir(struct file *, struct dir_context *);
-static int ncp_create(struct inode *, struct dentry *, umode_t, struct nameidata *);
-static struct dentry *ncp_lookup(struct inode *, struct dentry *, struct nameidata *);
+static int ncp_create(struct inode *, struct dentry *, umode_t, bool);
+static struct dentry *ncp_lookup(struct inode *, struct dentry *, unsigned int);
static int ncp_unlink(struct inode *, struct dentry *);
static int ncp_mkdir(struct inode *, struct dentry *, umode_t);
static int ncp_rmdir(struct inode *, struct dentry *);
@@ -49,7 +49,7 @@ const struct file_operations ncp_dir_operations =
{
.llseek = generic_file_llseek,
.read = generic_read_dir,
- .readdir = ncp_readdir,
+ .iterate = ncp_readdir,
.unlocked_ioctl = ncp_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ncp_compat_ioctl,
@@ -72,11 +72,9 @@ const struct inode_operations ncp_dir_inode_operations =
/*
* Dentry operations routines
*/
-static int ncp_lookup_validate(struct dentry *, struct nameidata *);
-static int ncp_hash_dentry(const struct dentry *, const struct inode *,
- struct qstr *);
-static int ncp_compare_dentry(const struct dentry *, const struct inode *,
- const struct dentry *, const struct inode *,
+static int ncp_lookup_validate(struct dentry *, unsigned int);
+static int ncp_hash_dentry(const struct dentry *, struct qstr *);
+static int ncp_compare_dentry(const struct dentry *, const struct dentry *,
unsigned int, const char *, const struct qstr *);
static int ncp_delete_dentry(const struct dentry *);
@@ -119,11 +117,19 @@ static inline int ncp_case_sensitive(const struct inode *i)
/*
* Note: leave the hash unchanged if the directory
* is case-sensitive.
+ *
+ * Accessing the parent inode can be racy under RCU pathwalking.
+ * Use ACCESS_ONCE() to make sure we use _one_ particular inode,
+ * the callers will handle races.
*/
static int
-ncp_hash_dentry(const struct dentry *dentry, const struct inode *inode,
- struct qstr *this)
+ncp_hash_dentry(const struct dentry *dentry, struct qstr *this)
{
+ struct inode *inode = ACCESS_ONCE(dentry->d_inode);
+
+ if (!inode)
+ return 0;
+
if (!ncp_case_sensitive(inode)) {
struct super_block *sb = dentry->d_sb;
struct nls_table *t;
@@ -140,14 +146,24 @@ ncp_hash_dentry(const struct dentry *dentry, const struct inode *inode,
return 0;
}
+/*
+ * Accessing the parent inode can be racy under RCU pathwalking.
+ * Use ACCESS_ONCE() to make sure we use _one_ particular inode,
+ * the callers will handle races.
+ */
static int
-ncp_compare_dentry(const struct dentry *parent, const struct inode *pinode,
- const struct dentry *dentry, const struct inode *inode,
+ncp_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
unsigned int len, const char *str, const struct qstr *name)
{
+ struct inode *pinode;
+
if (len != name->len)
return 1;
+ pinode = ACCESS_ONCE(parent->d_inode);
+ if (!pinode)
+ return 1;
+
if (ncp_case_sensitive(pinode))
return strncmp(str, name->name, len);
@@ -290,7 +306,7 @@ leave_me:;
static int
-ncp_lookup_validate(struct dentry *dentry, struct nameidata *nd)
+ncp_lookup_validate(struct dentry *dentry, unsigned int flags)
{
struct ncp_server *server;
struct dentry *parent;
@@ -302,7 +318,7 @@ ncp_lookup_validate(struct dentry *dentry, struct nameidata *nd)
if (dentry == dentry->d_sb->s_root)
return 1;
- if (nd->flags & LOOKUP_RCU)
+ if (flags & LOOKUP_RCU)
return -ECHILD;
parent = dget_parent(dentry);
@@ -424,9 +440,9 @@ static time_t ncp_obtain_mtime(struct dentry *dentry)
return ncp_date_dos2unix(i.modifyTime, i.modifyDate);
}
-static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
+static int ncp_readdir(struct file *file, struct dir_context *ctx)
{
- struct dentry *dentry = filp->f_path.dentry;
+ struct dentry *dentry = file->f_path.dentry;
struct inode *inode = dentry->d_inode;
struct page *page = NULL;
struct ncp_server *server = NCP_SERVER(inode);
@@ -440,7 +456,7 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
DDPRINTK("ncp_readdir: reading %s/%s, pos=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
- (int) filp->f_pos);
+ (int) ctx->pos);
result = -EIO;
/* Do not generate '.' and '..' when server is dead. */
@@ -448,16 +464,8 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
goto out;
result = 0;
- if (filp->f_pos == 0) {
- if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
- goto out;
- filp->f_pos = 1;
- }
- if (filp->f_pos == 1) {
- if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR))
- goto out;
- filp->f_pos = 2;
- }
+ if (!dir_emit_dots(file, ctx))
+ goto out;
page = grab_cache_page(&inode->i_data, 0);
if (!page)
@@ -469,7 +477,7 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
if (!PageUptodate(page) || !ctl.head.eof)
goto init_cache;
- if (filp->f_pos == 2) {
+ if (ctx->pos == 2) {
if (jiffies - ctl.head.time >= NCP_MAX_AGE(server))
goto init_cache;
@@ -479,10 +487,10 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
goto init_cache;
}
- if (filp->f_pos > ctl.head.end)
+ if (ctx->pos > ctl.head.end)
goto finished;
- ctl.fpos = filp->f_pos + (NCP_DIRCACHE_START - 2);
+ ctl.fpos = ctx->pos + (NCP_DIRCACHE_START - 2);
ctl.ofs = ctl.fpos / NCP_DIRCACHE_SIZE;
ctl.idx = ctl.fpos % NCP_DIRCACHE_SIZE;
@@ -497,21 +505,21 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
}
while (ctl.idx < NCP_DIRCACHE_SIZE) {
struct dentry *dent;
- int res;
+ bool over;
dent = ncp_dget_fpos(ctl.cache->dentry[ctl.idx],
- dentry, filp->f_pos);
+ dentry, ctx->pos);
if (!dent)
goto invalid_cache;
- res = filldir(dirent, dent->d_name.name,
- dent->d_name.len, filp->f_pos,
+ over = !dir_emit(ctx, dent->d_name.name,
+ dent->d_name.len,
dent->d_inode->i_ino, DT_UNKNOWN);
dput(dent);
- if (res)
+ if (over)
goto finished;
- filp->f_pos += 1;
+ ctx->pos += 1;
ctl.idx += 1;
- if (filp->f_pos > ctl.head.end)
+ if (ctx->pos > ctl.head.end)
goto finished;
}
if (ctl.page) {
@@ -548,9 +556,9 @@ init_cache:
ctl.valid = 1;
read_really:
if (ncp_is_server_root(inode)) {
- ncp_read_volume_list(filp, dirent, filldir, &ctl);
+ ncp_read_volume_list(file, ctx, &ctl);
} else {
- ncp_do_readdir(filp, dirent, filldir, &ctl);
+ ncp_do_readdir(file, ctx, &ctl);
}
ctl.head.end = ctl.fpos - 1;
ctl.head.eof = ctl.valid;
@@ -573,11 +581,11 @@ out:
}
static int
-ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
+ncp_fill_cache(struct file *file, struct dir_context *ctx,
struct ncp_cache_control *ctrl, struct ncp_entry_info *entry,
int inval_childs)
{
- struct dentry *newdent, *dentry = filp->f_path.dentry;
+ struct dentry *newdent, *dentry = file->f_path.dentry;
struct inode *dir = dentry->d_inode;
struct ncp_cache_control ctl = *ctrl;
struct qstr qname;
@@ -593,14 +601,10 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
return 1; /* I'm not sure */
qname.name = __name;
- qname.hash = full_name_hash(qname.name, qname.len);
-
- if (dentry->d_op && dentry->d_op->d_hash)
- if (dentry->d_op->d_hash(dentry, dentry->d_inode, &qname) != 0)
- goto end_advance;
-
- newdent = d_lookup(dentry, &qname);
+ newdent = d_hash_and_lookup(dentry, &qname);
+ if (unlikely(IS_ERR(newdent)))
+ goto end_advance;
if (!newdent) {
newdent = d_alloc(dentry, &qname);
if (!newdent)
@@ -670,15 +674,13 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
end_advance:
if (!valid)
ctl.valid = 0;
- if (!ctl.filled && (ctl.fpos == filp->f_pos)) {
- if (!ino)
- ino = find_inode_number(dentry, &qname);
+ if (!ctl.filled && (ctl.fpos == ctx->pos)) {
if (!ino)
ino = iunique(dir->i_sb, 2);
- ctl.filled = filldir(dirent, qname.name, qname.len,
- filp->f_pos, ino, DT_UNKNOWN);
+ ctl.filled = !dir_emit(ctx, qname.name, qname.len,
+ ino, DT_UNKNOWN);
if (!ctl.filled)
- filp->f_pos += 1;
+ ctx->pos += 1;
}
ctl.fpos += 1;
ctl.idx += 1;
@@ -687,10 +689,10 @@ end_advance:
}
static void
-ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir,
+ncp_read_volume_list(struct file *file, struct dir_context *ctx,
struct ncp_cache_control *ctl)
{
- struct dentry *dentry = filp->f_path.dentry;
+ struct dentry *dentry = file->f_path.dentry;
struct inode *inode = dentry->d_inode;
struct ncp_server *server = NCP_SERVER(inode);
struct ncp_volume_info info;
@@ -698,7 +700,7 @@ ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir,
int i;
DPRINTK("ncp_read_volume_list: pos=%ld\n",
- (unsigned long) filp->f_pos);
+ (unsigned long) ctx->pos);
for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) {
int inval_dentry;
@@ -719,16 +721,16 @@ ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir,
}
inval_dentry = ncp_update_known_namespace(server, entry.i.volNumber, NULL);
entry.volume = entry.i.volNumber;
- if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, inval_dentry))
+ if (!ncp_fill_cache(file, ctx, ctl, &entry, inval_dentry))
return;
}
}
static void
-ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir,
+ncp_do_readdir(struct file *file, struct dir_context *ctx,
struct ncp_cache_control *ctl)
{
- struct dentry *dentry = filp->f_path.dentry;
+ struct dentry *dentry = file->f_path.dentry;
struct inode *dir = dentry->d_inode;
struct ncp_server *server = NCP_SERVER(dir);
struct nw_search_sequence seq;
@@ -740,7 +742,7 @@ ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir,
DPRINTK("ncp_do_readdir: %s/%s, fpos=%ld\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
- (unsigned long) filp->f_pos);
+ (unsigned long) ctx->pos);
PPRINTK("ncp_do_readdir: init %s, volnum=%d, dirent=%u\n",
dentry->d_name.name, NCP_FINFO(dir)->volNumber,
NCP_FINFO(dir)->dirEntNum);
@@ -782,7 +784,7 @@ ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir,
rpl += onerpl;
rpls -= onerpl;
entry.volume = entry.i.volNumber;
- if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, 0))
+ if (!ncp_fill_cache(file, ctx, ctl, &entry, 0))
break;
}
} while (more);
@@ -836,7 +838,7 @@ out:
return result;
}
-static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
+static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
{
struct ncp_server *server = NCP_SERVER(dir);
struct inode *inode = NULL;
@@ -980,7 +982,7 @@ out:
}
static int ncp_create(struct inode *dir, struct dentry *dentry, umode_t mode,
- struct nameidata *nd)
+ bool excl)
{
return ncp_create_new(dir, dentry, mode, 0, 0);
}
@@ -1033,15 +1035,6 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
DPRINTK("ncp_rmdir: removing %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
- /*
- * fail with EBUSY if there are still references to this
- * directory.
- */
- dentry_unhash(dentry);
- error = -EBUSY;
- if (!d_unhashed(dentry))
- goto out;
-
len = sizeof(__name);
error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
dentry->d_name.len, !ncp_preserve_case(dir));
@@ -1144,17 +1137,6 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
- if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) {
- /*
- * fail with EBUSY if there are still references to this
- * directory.
- */
- dentry_unhash(new_dentry);
- error = -EBUSY;
- if (!d_unhashed(new_dentry))
- goto out;
- }
-
ncp_age_dentry(server, old_dentry);
ncp_age_dentry(server, new_dentry);