* [PATCH 1/8] NFS: Buffer overflow in ->decode_dirent() should not be fatal
@ 2010-11-21 19:21 Trond Myklebust
2010-11-21 19:21 ` [PATCH 2/8] NFS: Assume eof if the server returns no readdir records Trond Myklebust
0 siblings, 1 reply; 8+ messages in thread
From: Trond Myklebust @ 2010-11-21 19:21 UTC (permalink / raw)
To: linux-nfs
Overflowing the buffer in the readdir ->decode_dirent() should not lead to
a fatal error, but rather to an attempt to reread the record in question.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
---
fs/nfs/nfs2xdr.c | 2 +-
fs/nfs/nfs3xdr.c | 2 +-
fs/nfs/nfs4xdr.c | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index 2563f76..ab59377 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -495,7 +495,7 @@ nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_se
out_overflow:
print_overflow_msg(__func__, xdr);
- return ERR_PTR(-EIO);
+ return ERR_PTR(-EAGAIN);
}
/*
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index 748dc91..e79e4f5 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -656,7 +656,7 @@ nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_s
out_overflow:
print_overflow_msg(__func__, xdr);
out_overflow_exit:
- return ERR_PTR(-EIO);
+ return ERR_PTR(-EAGAIN);
}
/*
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index b7a204f..a3b39cb 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -6221,7 +6221,7 @@ __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
out_overflow:
print_overflow_msg(__func__, xdr);
- return ERR_PTR(-EIO);
+ return ERR_PTR(-EAGAIN);
}
/*
--
1.7.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/8] NFS: Assume eof if the server returns no readdir records
2010-11-21 19:21 [PATCH 1/8] NFS: Buffer overflow in ->decode_dirent() should not be fatal Trond Myklebust
@ 2010-11-21 19:21 ` Trond Myklebust
2010-11-21 19:21 ` [PATCH 3/8] NFS: Fix a page leak in nfs_do_filldir() Trond Myklebust
0 siblings, 1 reply; 8+ messages in thread
From: Trond Myklebust @ 2010-11-21 19:21 UTC (permalink / raw)
To: linux-nfs
Some servers are known to be buggy w.r.t. this. Deal with them...
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
---
fs/nfs/dir.c | 10 +++++++---
1 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 662df2a..2789cb3 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -466,8 +466,9 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
struct xdr_stream stream;
struct xdr_buf buf;
__be32 *ptr = xdr_page;
- int status;
struct nfs_cache_array *array;
+ unsigned int count = 0;
+ int status;
buf.head->iov_base = xdr_page;
buf.head->iov_len = buflen;
@@ -488,6 +489,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
break;
}
+ count++;
+
if (desc->plus == 1)
nfs_prime_dcache(desc->file->f_path.dentry, entry);
@@ -496,13 +499,14 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
break;
} while (!entry->eof);
- if (status == -EBADCOOKIE && entry->eof) {
+ if (count == 0 || (status == -EBADCOOKIE && entry->eof == 1)) {
array = nfs_readdir_get_array(page);
if (!IS_ERR(array)) {
array->eof_index = array->size;
status = 0;
nfs_readdir_release_array(page);
- }
+ } else
+ status = PTR_ERR(array);
}
return status;
}
--
1.7.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/8] NFS: Fix a page leak in nfs_do_filldir()
2010-11-21 19:21 ` [PATCH 2/8] NFS: Assume eof if the server returns no readdir records Trond Myklebust
@ 2010-11-21 19:21 ` Trond Myklebust
2010-11-21 19:21 ` [PATCH 4/8] NFS: Fix a page leak in uncached_readdir() Trond Myklebust
0 siblings, 1 reply; 8+ messages in thread
From: Trond Myklebust @ 2010-11-21 19:21 UTC (permalink / raw)
To: linux-nfs
nfs_do_filldir() must always free desc->page when it is done, otherwise
we end up leaking the page.
Also remove unused variable 'dentry'.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
---
fs/nfs/dir.c | 10 +++++-----
1 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 2789cb3..42e66e9 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -701,11 +701,12 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
int res = 0;
struct nfs_cache_array *array = NULL;
unsigned int d_type = DT_UNKNOWN;
- struct dentry *dentry = NULL;
array = nfs_readdir_get_array(desc->page);
- if (IS_ERR(array))
- return PTR_ERR(array);
+ if (IS_ERR(array)) {
+ res = PTR_ERR(array);
+ goto out;
+ }
for (i = desc->cache_entry_index; i < array->size; i++) {
d_type = DT_UNKNOWN;
@@ -726,9 +727,8 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
desc->eof = 1;
nfs_readdir_release_array(desc->page);
+out:
cache_page_release(desc);
- if (dentry != NULL)
- dput(dentry);
dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n",
(unsigned long long)*desc->dir_cookie, res);
return res;
--
1.7.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 4/8] NFS: Fix a page leak in uncached_readdir()
2010-11-21 19:21 ` [PATCH 3/8] NFS: Fix a page leak in nfs_do_filldir() Trond Myklebust
@ 2010-11-21 19:21 ` Trond Myklebust
2010-11-21 19:21 ` [PATCH 5/8] NFS: Fix the error handling in "uncached_readdir()" Trond Myklebust
0 siblings, 1 reply; 8+ messages in thread
From: Trond Myklebust @ 2010-11-21 19:21 UTC (permalink / raw)
To: linux-nfs
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
---
fs/nfs/dir.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 42e66e9..353f47c 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -763,13 +763,14 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
goto out;
}
+ desc->page_index = 0;
+ desc->page = page;
+
if (nfs_readdir_xdr_to_array(desc, page, inode) == -1) {
status = -EIO;
goto out_release;
}
- desc->page_index = 0;
- desc->page = page;
status = nfs_do_filldir(desc, dirent, filldir);
out:
--
1.7.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 5/8] NFS: Fix the error handling in "uncached_readdir()"
2010-11-21 19:21 ` [PATCH 4/8] NFS: Fix a page leak in uncached_readdir() Trond Myklebust
@ 2010-11-21 19:21 ` Trond Myklebust
2010-11-21 19:21 ` [PATCH 6/8] NFS: Don't ignore errors from nfs_do_filldir() Trond Myklebust
0 siblings, 1 reply; 8+ messages in thread
From: Trond Myklebust @ 2010-11-21 19:21 UTC (permalink / raw)
To: linux-nfs
Currently, uncached_readdir() is broken because if fails to handle
the results from nfs_readdir_xdr_to_array() correctly.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
---
fs/nfs/dir.c | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 353f47c..2492bac 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -766,10 +766,9 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
desc->page_index = 0;
desc->page = page;
- if (nfs_readdir_xdr_to_array(desc, page, inode) == -1) {
- status = -EIO;
+ status = nfs_readdir_xdr_to_array(desc, page, inode);
+ if (status < 0)
goto out_release;
- }
status = nfs_do_filldir(desc, dirent, filldir);
--
1.7.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 6/8] NFS: Don't ignore errors from nfs_do_filldir()
2010-11-21 19:21 ` [PATCH 5/8] NFS: Fix the error handling in "uncached_readdir()" Trond Myklebust
@ 2010-11-21 19:21 ` Trond Myklebust
2010-11-21 19:21 ` [PATCH 7/8] NFS: Correct the array bound calculation in nfs_readdir_add_to_array Trond Myklebust
0 siblings, 1 reply; 8+ messages in thread
From: Trond Myklebust @ 2010-11-21 19:21 UTC (permalink / raw)
To: linux-nfs
We should ignore the errors from the filldir callback, and just interpret
them as meaning we should exit, however we should definitely pass back
ENOMEM errors.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
---
fs/nfs/dir.c | 18 +++++++++---------
1 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 2492bac..ddc2e43 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -709,13 +709,15 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
}
for (i = desc->cache_entry_index; i < array->size; i++) {
+ struct nfs_cache_array_entry *ent;
d_type = DT_UNKNOWN;
- res = filldir(dirent, array->array[i].string.name,
- array->array[i].string.len, file->f_pos,
- nfs_compat_user_ino64(array->array[i].ino), d_type);
- if (res < 0)
+ ent = &array->array[i];
+ if (filldir(dirent, ent->string.name, ent->string.len,
+ file->f_pos, nfs_compat_user_ino64(ent->ino), d_type) < 0) {
+ desc->eof = 1;
break;
+ }
file->f_pos++;
desc->cache_entry_index = i;
if (i < (array->size-1))
@@ -820,14 +822,14 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
res = readdir_search_pagecache(desc);
if (res == -EBADCOOKIE) {
+ res = 0;
/* This means either end of directory */
if (*desc->dir_cookie && desc->eof == 0) {
/* Or that the server has 'lost' a cookie */
res = uncached_readdir(desc, dirent, filldir);
- if (res >= 0)
+ if (res == 0)
continue;
}
- res = 0;
break;
}
if (res == -ETOOSMALL && desc->plus) {
@@ -842,10 +844,8 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
break;
res = nfs_do_filldir(desc, dirent, filldir);
- if (res < 0) {
- res = 0;
+ if (res < 0)
break;
- }
}
out:
nfs_unblock_sillyrename(dentry);
--
1.7.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 7/8] NFS: Correct the array bound calculation in nfs_readdir_add_to_array
2010-11-21 19:21 ` [PATCH 6/8] NFS: Don't ignore errors from nfs_do_filldir() Trond Myklebust
@ 2010-11-21 19:21 ` Trond Myklebust
2010-11-21 19:21 ` [PATCH 8/8] NFS: Ensure we return the dirent->d_type when it is known Trond Myklebust
0 siblings, 1 reply; 8+ messages in thread
From: Trond Myklebust @ 2010-11-21 19:21 UTC (permalink / raw)
To: linux-nfs
It looks as if the array size calculation in MAX_READDIR_ARRAY does not
take the alignment of struct nfs_cache_array_entry into account.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
---
fs/nfs/dir.c | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index ddc2e43..ced7291 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -171,8 +171,6 @@ struct nfs_cache_array {
struct nfs_cache_array_entry array[0];
};
-#define MAX_READDIR_ARRAY ((PAGE_SIZE - sizeof(struct nfs_cache_array)) / sizeof(struct nfs_cache_array_entry))
-
typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
typedef struct {
struct file *file;
@@ -257,11 +255,14 @@ int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page)
if (IS_ERR(array))
return PTR_ERR(array);
+
+ cache_entry = &array->array[array->size];
+
+ /* Check that this entry lies within the page bounds */
ret = -ENOSPC;
- if (array->size >= MAX_READDIR_ARRAY)
+ if ((char *)&cache_entry[1] - (char *)page_address(page) > PAGE_SIZE)
goto out;
- cache_entry = &array->array[array->size];
cache_entry->cookie = entry->prev_cookie;
cache_entry->ino = entry->ino;
ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len);
--
1.7.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 8/8] NFS: Ensure we return the dirent->d_type when it is known
2010-11-21 19:21 ` [PATCH 7/8] NFS: Correct the array bound calculation in nfs_readdir_add_to_array Trond Myklebust
@ 2010-11-21 19:21 ` Trond Myklebust
0 siblings, 0 replies; 8+ messages in thread
From: Trond Myklebust @ 2010-11-21 19:21 UTC (permalink / raw)
To: linux-nfs
Store the dirent->d_type in the struct nfs_cache_array_entry so that we
can use it in getdents() calls.
This fixes a regression with the new readdir code.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
---
fs/nfs/dir.c | 7 ++++---
fs/nfs/internal.h | 9 +++++++++
fs/nfs/nfs2xdr.c | 2 ++
fs/nfs/nfs3xdr.c | 2 ++
fs/nfs/nfs4xdr.c | 4 ++++
include/linux/nfs_xdr.h | 1 +
6 files changed, 22 insertions(+), 3 deletions(-)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index ced7291..8ea4a41 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -162,6 +162,7 @@ struct nfs_cache_array_entry {
u64 cookie;
u64 ino;
struct qstr string;
+ unsigned char d_type;
};
struct nfs_cache_array {
@@ -265,6 +266,7 @@ int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page)
cache_entry->cookie = entry->prev_cookie;
cache_entry->ino = entry->ino;
+ cache_entry->d_type = entry->d_type;
ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len);
if (ret)
goto out;
@@ -701,7 +703,6 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
int i = 0;
int res = 0;
struct nfs_cache_array *array = NULL;
- unsigned int d_type = DT_UNKNOWN;
array = nfs_readdir_get_array(desc->page);
if (IS_ERR(array)) {
@@ -711,11 +712,11 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
for (i = desc->cache_entry_index; i < array->size; i++) {
struct nfs_cache_array_entry *ent;
- d_type = DT_UNKNOWN;
ent = &array->array[i];
if (filldir(dirent, ent->string.name, ent->string.len,
- file->f_pos, nfs_compat_user_ino64(ent->ino), d_type) < 0) {
+ file->f_pos, nfs_compat_user_ino64(ent->ino),
+ ent->d_type) < 0) {
desc->eof = 1;
break;
}
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index db08ff3..e6356b7 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -362,6 +362,15 @@ unsigned int nfs_page_length(struct page *page)
}
/*
+ * Convert a umode to a dirent->d_type
+ */
+static inline
+unsigned char nfs_umode_to_dtype(umode_t mode)
+{
+ return (mode >> 12) & 15;
+}
+
+/*
* Determine the number of pages in an array of length 'len' and
* with a base offset of 'base'
*/
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index ab59377..5914a19 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -485,6 +485,8 @@ nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_se
entry->prev_cookie = entry->cookie;
entry->cookie = ntohl(*p++);
+ entry->d_type = DT_UNKNOWN;
+
p = xdr_inline_peek(xdr, 8);
if (p != NULL)
entry->eof = !p[0] && p[1];
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index e79e4f5..f6cc60f 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -622,11 +622,13 @@ nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_s
entry->prev_cookie = entry->cookie;
p = xdr_decode_hyper(p, &entry->cookie);
+ entry->d_type = DT_UNKNOWN;
if (plus) {
entry->fattr->valid = 0;
p = xdr_decode_post_op_attr_stream(xdr, entry->fattr);
if (IS_ERR(p))
goto out_overflow_exit;
+ entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
/* In fact, a post_op_fh3: */
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index a3b39cb..9f1826b 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -6208,6 +6208,10 @@ __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID)
entry->ino = entry->fattr->fileid;
+ entry->d_type = DT_UNKNOWN;
+ if (entry->fattr->valid & NFS_ATTR_FATTR_TYPE)
+ entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
+
if (verify_attr_len(xdr, p, len) < 0)
goto out_overflow;
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index ba6cc8f..80f0719 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -483,6 +483,7 @@ struct nfs_entry {
int eof;
struct nfs_fh * fh;
struct nfs_fattr * fattr;
+ unsigned char d_type;
};
/*
--
1.7.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2010-11-21 19:21 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-11-21 19:21 [PATCH 1/8] NFS: Buffer overflow in ->decode_dirent() should not be fatal Trond Myklebust
2010-11-21 19:21 ` [PATCH 2/8] NFS: Assume eof if the server returns no readdir records Trond Myklebust
2010-11-21 19:21 ` [PATCH 3/8] NFS: Fix a page leak in nfs_do_filldir() Trond Myklebust
2010-11-21 19:21 ` [PATCH 4/8] NFS: Fix a page leak in uncached_readdir() Trond Myklebust
2010-11-21 19:21 ` [PATCH 5/8] NFS: Fix the error handling in "uncached_readdir()" Trond Myklebust
2010-11-21 19:21 ` [PATCH 6/8] NFS: Don't ignore errors from nfs_do_filldir() Trond Myklebust
2010-11-21 19:21 ` [PATCH 7/8] NFS: Correct the array bound calculation in nfs_readdir_add_to_array Trond Myklebust
2010-11-21 19:21 ` [PATCH 8/8] NFS: Ensure we return the dirent->d_type when it is known Trond Myklebust
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).