Merge branch 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
* 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6: NFS: Ensure we return the dirent->d_type when it is known NFS: Correct the array bound calculation in nfs_readdir_add_to_array NFS: Don't ignore errors from nfs_do_filldir() NFS: Fix the error handling in "uncached_readdir()" NFS: Fix a page leak in uncached_readdir() NFS: Fix a page leak in nfs_do_filldir() NFS: Assume eof if the server returns no readdir records NFS: Buffer overflow in ->decode_dirent() should not be fatal Pure nfs client performance using odirect. SUNRPC: Fix an infinite loop in call_refresh/call_refreshresult
This commit is contained in:
Коммит
19650e8580
64
fs/nfs/dir.c
64
fs/nfs/dir.c
|
@ -162,6 +162,7 @@ struct nfs_cache_array_entry {
|
||||||
u64 cookie;
|
u64 cookie;
|
||||||
u64 ino;
|
u64 ino;
|
||||||
struct qstr string;
|
struct qstr string;
|
||||||
|
unsigned char d_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nfs_cache_array {
|
struct nfs_cache_array {
|
||||||
|
@ -171,8 +172,6 @@ struct nfs_cache_array {
|
||||||
struct nfs_cache_array_entry array[0];
|
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 __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct file *file;
|
struct file *file;
|
||||||
|
@ -257,13 +256,17 @@ int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page)
|
||||||
|
|
||||||
if (IS_ERR(array))
|
if (IS_ERR(array))
|
||||||
return PTR_ERR(array);
|
return PTR_ERR(array);
|
||||||
ret = -ENOSPC;
|
|
||||||
if (array->size >= MAX_READDIR_ARRAY)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
cache_entry = &array->array[array->size];
|
cache_entry = &array->array[array->size];
|
||||||
|
|
||||||
|
/* Check that this entry lies within the page bounds */
|
||||||
|
ret = -ENOSPC;
|
||||||
|
if ((char *)&cache_entry[1] - (char *)page_address(page) > PAGE_SIZE)
|
||||||
|
goto out;
|
||||||
|
|
||||||
cache_entry->cookie = entry->prev_cookie;
|
cache_entry->cookie = entry->prev_cookie;
|
||||||
cache_entry->ino = entry->ino;
|
cache_entry->ino = entry->ino;
|
||||||
|
cache_entry->d_type = entry->d_type;
|
||||||
ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len);
|
ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -466,8 +469,9 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
|
||||||
struct xdr_stream stream;
|
struct xdr_stream stream;
|
||||||
struct xdr_buf buf;
|
struct xdr_buf buf;
|
||||||
__be32 *ptr = xdr_page;
|
__be32 *ptr = xdr_page;
|
||||||
int status;
|
|
||||||
struct nfs_cache_array *array;
|
struct nfs_cache_array *array;
|
||||||
|
unsigned int count = 0;
|
||||||
|
int status;
|
||||||
|
|
||||||
buf.head->iov_base = xdr_page;
|
buf.head->iov_base = xdr_page;
|
||||||
buf.head->iov_len = buflen;
|
buf.head->iov_len = buflen;
|
||||||
|
@ -488,6 +492,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
|
||||||
if (desc->plus == 1)
|
if (desc->plus == 1)
|
||||||
nfs_prime_dcache(desc->file->f_path.dentry, entry);
|
nfs_prime_dcache(desc->file->f_path.dentry, entry);
|
||||||
|
|
||||||
|
@ -496,13 +502,14 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
|
||||||
break;
|
break;
|
||||||
} while (!entry->eof);
|
} while (!entry->eof);
|
||||||
|
|
||||||
if (status == -EBADCOOKIE && entry->eof) {
|
if (count == 0 || (status == -EBADCOOKIE && entry->eof == 1)) {
|
||||||
array = nfs_readdir_get_array(page);
|
array = nfs_readdir_get_array(page);
|
||||||
if (!IS_ERR(array)) {
|
if (!IS_ERR(array)) {
|
||||||
array->eof_index = array->size;
|
array->eof_index = array->size;
|
||||||
status = 0;
|
status = 0;
|
||||||
nfs_readdir_release_array(page);
|
nfs_readdir_release_array(page);
|
||||||
}
|
} else
|
||||||
|
status = PTR_ERR(array);
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -696,21 +703,23 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
struct nfs_cache_array *array = NULL;
|
struct nfs_cache_array *array = NULL;
|
||||||
unsigned int d_type = DT_UNKNOWN;
|
|
||||||
struct dentry *dentry = NULL;
|
|
||||||
|
|
||||||
array = nfs_readdir_get_array(desc->page);
|
array = nfs_readdir_get_array(desc->page);
|
||||||
if (IS_ERR(array))
|
if (IS_ERR(array)) {
|
||||||
return PTR_ERR(array);
|
res = PTR_ERR(array);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = desc->cache_entry_index; i < array->size; i++) {
|
for (i = desc->cache_entry_index; i < array->size; i++) {
|
||||||
d_type = DT_UNKNOWN;
|
struct nfs_cache_array_entry *ent;
|
||||||
|
|
||||||
res = filldir(dirent, array->array[i].string.name,
|
ent = &array->array[i];
|
||||||
array->array[i].string.len, file->f_pos,
|
if (filldir(dirent, ent->string.name, ent->string.len,
|
||||||
nfs_compat_user_ino64(array->array[i].ino), d_type);
|
file->f_pos, nfs_compat_user_ino64(ent->ino),
|
||||||
if (res < 0)
|
ent->d_type) < 0) {
|
||||||
|
desc->eof = 1;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
file->f_pos++;
|
file->f_pos++;
|
||||||
desc->cache_entry_index = i;
|
desc->cache_entry_index = i;
|
||||||
if (i < (array->size-1))
|
if (i < (array->size-1))
|
||||||
|
@ -722,9 +731,8 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
|
||||||
desc->eof = 1;
|
desc->eof = 1;
|
||||||
|
|
||||||
nfs_readdir_release_array(desc->page);
|
nfs_readdir_release_array(desc->page);
|
||||||
|
out:
|
||||||
cache_page_release(desc);
|
cache_page_release(desc);
|
||||||
if (dentry != NULL)
|
|
||||||
dput(dentry);
|
|
||||||
dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n",
|
dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n",
|
||||||
(unsigned long long)*desc->dir_cookie, res);
|
(unsigned long long)*desc->dir_cookie, res);
|
||||||
return res;
|
return res;
|
||||||
|
@ -759,13 +767,13 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nfs_readdir_xdr_to_array(desc, page, inode) == -1) {
|
|
||||||
status = -EIO;
|
|
||||||
goto out_release;
|
|
||||||
}
|
|
||||||
|
|
||||||
desc->page_index = 0;
|
desc->page_index = 0;
|
||||||
desc->page = page;
|
desc->page = page;
|
||||||
|
|
||||||
|
status = nfs_readdir_xdr_to_array(desc, page, inode);
|
||||||
|
if (status < 0)
|
||||||
|
goto out_release;
|
||||||
|
|
||||||
status = nfs_do_filldir(desc, dirent, filldir);
|
status = nfs_do_filldir(desc, dirent, filldir);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -816,14 +824,14 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||||
res = readdir_search_pagecache(desc);
|
res = readdir_search_pagecache(desc);
|
||||||
|
|
||||||
if (res == -EBADCOOKIE) {
|
if (res == -EBADCOOKIE) {
|
||||||
|
res = 0;
|
||||||
/* This means either end of directory */
|
/* This means either end of directory */
|
||||||
if (*desc->dir_cookie && desc->eof == 0) {
|
if (*desc->dir_cookie && desc->eof == 0) {
|
||||||
/* Or that the server has 'lost' a cookie */
|
/* Or that the server has 'lost' a cookie */
|
||||||
res = uncached_readdir(desc, dirent, filldir);
|
res = uncached_readdir(desc, dirent, filldir);
|
||||||
if (res >= 0)
|
if (res == 0)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
res = 0;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (res == -ETOOSMALL && desc->plus) {
|
if (res == -ETOOSMALL && desc->plus) {
|
||||||
|
@ -838,10 +846,8 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
res = nfs_do_filldir(desc, dirent, filldir);
|
res = nfs_do_filldir(desc, dirent, filldir);
|
||||||
if (res < 0) {
|
if (res < 0)
|
||||||
res = 0;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
nfs_unblock_sillyrename(dentry);
|
nfs_unblock_sillyrename(dentry);
|
||||||
|
|
|
@ -867,7 +867,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
|
||||||
goto out;
|
goto out;
|
||||||
nfs_alloc_commit_data(dreq);
|
nfs_alloc_commit_data(dreq);
|
||||||
|
|
||||||
if (dreq->commit_data == NULL || count < wsize)
|
if (dreq->commit_data == NULL || count <= wsize)
|
||||||
sync = NFS_FILE_SYNC;
|
sync = NFS_FILE_SYNC;
|
||||||
|
|
||||||
dreq->inode = inode;
|
dreq->inode = inode;
|
||||||
|
|
|
@ -361,6 +361,15 @@ unsigned int nfs_page_length(struct page *page)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
* Determine the number of pages in an array of length 'len' and
|
||||||
* with a base offset of 'base'
|
* with a base offset of 'base'
|
||||||
|
|
|
@ -485,6 +485,8 @@ nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_se
|
||||||
entry->prev_cookie = entry->cookie;
|
entry->prev_cookie = entry->cookie;
|
||||||
entry->cookie = ntohl(*p++);
|
entry->cookie = ntohl(*p++);
|
||||||
|
|
||||||
|
entry->d_type = DT_UNKNOWN;
|
||||||
|
|
||||||
p = xdr_inline_peek(xdr, 8);
|
p = xdr_inline_peek(xdr, 8);
|
||||||
if (p != NULL)
|
if (p != NULL)
|
||||||
entry->eof = !p[0] && p[1];
|
entry->eof = !p[0] && p[1];
|
||||||
|
@ -495,7 +497,7 @@ nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_se
|
||||||
|
|
||||||
out_overflow:
|
out_overflow:
|
||||||
print_overflow_msg(__func__, xdr);
|
print_overflow_msg(__func__, xdr);
|
||||||
return ERR_PTR(-EIO);
|
return ERR_PTR(-EAGAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -622,11 +622,13 @@ nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_s
|
||||||
entry->prev_cookie = entry->cookie;
|
entry->prev_cookie = entry->cookie;
|
||||||
p = xdr_decode_hyper(p, &entry->cookie);
|
p = xdr_decode_hyper(p, &entry->cookie);
|
||||||
|
|
||||||
|
entry->d_type = DT_UNKNOWN;
|
||||||
if (plus) {
|
if (plus) {
|
||||||
entry->fattr->valid = 0;
|
entry->fattr->valid = 0;
|
||||||
p = xdr_decode_post_op_attr_stream(xdr, entry->fattr);
|
p = xdr_decode_post_op_attr_stream(xdr, entry->fattr);
|
||||||
if (IS_ERR(p))
|
if (IS_ERR(p))
|
||||||
goto out_overflow_exit;
|
goto out_overflow_exit;
|
||||||
|
entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
|
||||||
/* In fact, a post_op_fh3: */
|
/* In fact, a post_op_fh3: */
|
||||||
p = xdr_inline_decode(xdr, 4);
|
p = xdr_inline_decode(xdr, 4);
|
||||||
if (unlikely(!p))
|
if (unlikely(!p))
|
||||||
|
@ -656,7 +658,7 @@ nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_s
|
||||||
out_overflow:
|
out_overflow:
|
||||||
print_overflow_msg(__func__, xdr);
|
print_overflow_msg(__func__, xdr);
|
||||||
out_overflow_exit:
|
out_overflow_exit:
|
||||||
return ERR_PTR(-EIO);
|
return ERR_PTR(-EAGAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -6208,6 +6208,10 @@ __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
|
||||||
if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID)
|
if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID)
|
||||||
entry->ino = entry->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)
|
if (verify_attr_len(xdr, p, len) < 0)
|
||||||
goto out_overflow;
|
goto out_overflow;
|
||||||
|
|
||||||
|
@ -6221,7 +6225,7 @@ __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
|
||||||
|
|
||||||
out_overflow:
|
out_overflow:
|
||||||
print_overflow_msg(__func__, xdr);
|
print_overflow_msg(__func__, xdr);
|
||||||
return ERR_PTR(-EIO);
|
return ERR_PTR(-EAGAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -483,6 +483,7 @@ struct nfs_entry {
|
||||||
int eof;
|
int eof;
|
||||||
struct nfs_fh * fh;
|
struct nfs_fh * fh;
|
||||||
struct nfs_fattr * fattr;
|
struct nfs_fattr * fattr;
|
||||||
|
unsigned char d_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -989,20 +989,26 @@ call_refreshresult(struct rpc_task *task)
|
||||||
dprint_status(task);
|
dprint_status(task);
|
||||||
|
|
||||||
task->tk_status = 0;
|
task->tk_status = 0;
|
||||||
task->tk_action = call_allocate;
|
task->tk_action = call_refresh;
|
||||||
if (status >= 0 && rpcauth_uptodatecred(task))
|
|
||||||
return;
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case -EACCES:
|
case 0:
|
||||||
rpc_exit(task, -EACCES);
|
if (rpcauth_uptodatecred(task))
|
||||||
return;
|
task->tk_action = call_allocate;
|
||||||
case -ENOMEM:
|
|
||||||
rpc_exit(task, -ENOMEM);
|
|
||||||
return;
|
return;
|
||||||
case -ETIMEDOUT:
|
case -ETIMEDOUT:
|
||||||
rpc_delay(task, 3*HZ);
|
rpc_delay(task, 3*HZ);
|
||||||
|
case -EAGAIN:
|
||||||
|
status = -EACCES;
|
||||||
|
if (!task->tk_cred_retry)
|
||||||
|
break;
|
||||||
|
task->tk_cred_retry--;
|
||||||
|
dprintk("RPC: %5u %s: retry refresh creds\n",
|
||||||
|
task->tk_pid, __func__);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
task->tk_action = call_refresh;
|
dprintk("RPC: %5u %s: refresh creds failed with error %d\n",
|
||||||
|
task->tk_pid, __func__, status);
|
||||||
|
rpc_exit(task, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Загрузка…
Ссылка в новой задаче