summaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-04-19 04:00:00 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-04-19 04:00:00 +0000
commit46e045034336a2cc90c1798cd7cc07af744ddfd6 (patch)
tree3b9b51fc482e729f663d25333e77fbed9aaa939a /fs/nfs
parent31dc59d503a02e84c4de98826452acaeb56dc15a (diff)
Merge with Linux 2.3.99-pre4.
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/Makefile3
-rw-r--r--fs/nfs/dir.c845
-rw-r--r--fs/nfs/file.c5
-rw-r--r--fs/nfs/flushd.c1
-rw-r--r--fs/nfs/inode.c494
-rw-r--r--fs/nfs/mount_clnt.c65
-rw-r--r--fs/nfs/nfs2xdr.c390
-rw-r--r--fs/nfs/nfs3proc.c477
-rw-r--r--fs/nfs/nfs3xdr.c1147
-rw-r--r--fs/nfs/nfsroot.c45
-rw-r--r--fs/nfs/proc.c328
-rw-r--r--fs/nfs/read.c56
-rw-r--r--fs/nfs/symlink.c22
-rw-r--r--fs/nfs/write.c181
14 files changed, 2691 insertions, 1368 deletions
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index 3c8aac510..64ef9274b 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -14,6 +14,9 @@ O_OBJS := inode.o file.o read.o write.o dir.o symlink.o proc.o \
ifdef CONFIG_ROOT_NFS
O_OBJS += nfsroot.o mount_clnt.o
endif
+ifdef CONFIG_NFS_V3
+ O_OBJS += nfs3proc.o nfs3xdr.o
+endif
M_OBJS := $(O_TARGET)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 3ca240129..7d80e6468 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -17,7 +17,6 @@
* 6 Jun 1999 Cache readdir lookups in the page cache. -DaveM
*/
-#define NFS_NEED_XDR_TYPES
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/stat.h>
@@ -28,7 +27,7 @@
#include <linux/mm.h>
#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
-#include <linux/nfs.h>
+#include <linux/nfs_mount.h>
#include <linux/pagemap.h>
#include <asm/segment.h> /* for fs functions */
@@ -71,202 +70,131 @@ struct inode_operations nfs_dir_inode_operations = {
setattr: nfs_notify_change,
};
-/* Each readdir response is composed of entries which look
- * like the following, as per the NFSv2 RFC:
- *
- * __u32 not_end zero if end of response
- * __u32 file ID opaque ino_t
- * __u32 namelen size of name string
- * VAR name string the string, padded to modulo 4 bytes
- * __u32 cookie opaque ID of next entry
- *
- * When you hit not_end being zero, the next __u32 is non-zero if
- * this is the end of the complete set of readdir entires for this
- * directory. This can be used, for example, to initiate pre-fetch.
- *
- * In order to know what to ask the server for, we only need to know
- * the final cookie of the previous page, and offset zero has cookie
- * zero, so we cache cookie to page offset translations in chunks.
- */
-#define COOKIES_PER_CHUNK (8 - ((sizeof(void *) / sizeof(__u32))))
-struct nfs_cookie_table {
- struct nfs_cookie_table *next;
- __u32 cookies[COOKIES_PER_CHUNK];
-};
-static kmem_cache_t *nfs_cookie_cachep;
+typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int);
-/* This whole scheme relies on the fact that dirent cookies
- * are monotonically increasing.
- *
- * Another invariant is that once we have a valid non-zero
- * EOF marker cached, we also have the complete set of cookie
- * table entries.
+/*
+ * Given a pointer to a buffer that has already been filled by a call
+ * to readdir, find the next entry.
*
- * We return the page offset assosciated with the page where
- * cookie must be if it exists at all, however if we can not
- * figure that out conclusively, we return < 0.
+ * If the end of the buffer has been reached, return -EAGAIN, if not,
+ * return the offset within the buffer of the next entry to be
+ * read.
*/
-static long __nfs_readdir_offset(struct inode *inode, __u32 cookie)
+static inline
+long find_dirent(struct page *page, loff_t offset,
+ struct nfs_entry *entry,
+ decode_dirent_t decode, int plus, int use_cookie)
{
- struct nfs_cookie_table *p;
- unsigned long ret = 0;
-
- for(p = NFS_COOKIES(inode); p != NULL; p = p->next) {
- int i;
-
- for (i = 0; i < COOKIES_PER_CHUNK; i++) {
- __u32 this_cookie = p->cookies[i];
-
- /* End of known cookies, EOF is our only hope. */
- if (!this_cookie)
- goto check_eof;
-
- /* Next cookie is larger, must be in previous page. */
- if (this_cookie > cookie)
- return ret;
-
- ret += 1;
-
- /* Exact cookie match, it must be in this page :-) */
- if (this_cookie == cookie)
- return ret;
+ u8 *p = (u8 *)kmap(page),
+ *start = p;
+ unsigned long base = page_offset(page),
+ pg_offset = 0;
+ int loop_count = 0;
+
+ if (!p)
+ return -EIO;
+ for(;;) {
+ p = (u8*)decode((__u32*)p, entry, plus);
+ if (IS_ERR(p))
+ break;
+ pg_offset = p - start;
+ entry->prev = entry->offset;
+ entry->offset = base + pg_offset;
+ if ((use_cookie ? entry->cookie : entry->offset) > offset)
+ break;
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
}
}
-check_eof:
- if (NFS_DIREOF(inode) != 0)
- return ret;
- return -1L;
-}
-
-static __inline__ long nfs_readdir_offset(struct inode *inode, __u32 cookie)
-{
- /* Cookie zero is always at page offset zero. Optimize the
- * other common case since most directories fit entirely
- * in one page.
- */
- if (!cookie || (!NFS_COOKIES(inode) && NFS_DIREOF(inode)))
- return 0;
- return __nfs_readdir_offset(inode, cookie);
+ kunmap(page);
+ return (IS_ERR(p)) ? PTR_ERR(p) : (long)pg_offset;
}
-/* Since a cookie of zero is declared special by the NFS
- * protocol, we easily can tell if a cookie in an existing
- * table chunk is valid or not.
+/*
+ * Find the given page, and call find_dirent() in order to try to
+ * return the next entry.
*
- * NOTE: The cookies are indexed off-by-one because zero
- * need not an entry.
+ * Returns -EIO if the page is not available, or up to date.
*/
-static __inline__ __u32 *find_cookie(struct inode *inode, unsigned long off)
+static inline
+long find_dirent_page(struct inode *inode, loff_t offset,
+ struct nfs_entry *entry)
{
- static __u32 cookie_zero = 0;
- struct nfs_cookie_table *p;
- __u32 *ret;
-
- if (!off)
- return &cookie_zero;
- off -= 1;
- p = NFS_COOKIES(inode);
- while(off >= COOKIES_PER_CHUNK && p) {
- off -= COOKIES_PER_CHUNK;
- p = p->next;
- }
- ret = NULL;
- if (p) {
- ret = &p->cookies[off];
- if (!*ret)
- ret = NULL;
- }
- return ret;
-}
+ decode_dirent_t decode = NFS_PROTO(inode)->decode_dirent;
+ struct page *page;
+ unsigned long index = entry->offset >> PAGE_CACHE_SHIFT;
+ long status = -EIO;
+ int plus = NFS_USE_READDIRPLUS(inode),
+ use_cookie = NFS_MONOTONE_COOKIES(inode);
-#define NFS_NAMELEN_ALIGN(__len) ((((__len)+3)>>2)<<2)
-static int create_cookie(__u32 cookie, unsigned long off, struct inode *inode)
-{
- struct nfs_cookie_table **cpp;
+ dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", entry->offset & PAGE_CACHE_MASK);
- cpp = (struct nfs_cookie_table **) &NFS_COOKIES(inode);
- while (off >= COOKIES_PER_CHUNK && *cpp) {
- off -= COOKIES_PER_CHUNK;
- cpp = &(*cpp)->next;
- }
- if (*cpp) {
- (*cpp)->cookies[off] = cookie;
- } else {
- struct nfs_cookie_table *new;
- int i;
-
- new = kmem_cache_alloc(nfs_cookie_cachep, SLAB_ATOMIC);
- if(!new)
- return -1;
- *cpp = new;
- new->next = NULL;
- for(i = 0; i < COOKIES_PER_CHUNK; i++) {
- if (i == off) {
- new->cookies[i] = cookie;
- } else {
- new->cookies[i] = 0;
- }
- }
- }
- return 0;
+ if (entry->page)
+ page_cache_release(entry->page);
+
+ page = find_get_page(&inode->i_data, index);
+
+ if (page && Page_Uptodate(page))
+ status = find_dirent(page, offset, entry, decode, plus, use_cookie);
+
+ /* NB: on successful return we will be holding the page */
+ if (status < 0) {
+ entry->page = NULL;
+ if (page)
+ page_cache_release(page);
+ } else
+ entry->page = page;
+
+ dfprintk(VFS, "NFS: find_dirent_page() returns %ld\n", status);
+ return status;
}
-static struct page *try_to_get_dirent_page(struct file *, __u32, int);
-/* Recover from a revalidation flush. The case here is that
- * the inode for the directory got invalidated somehow, and
- * all of our cached information is lost. In order to get
- * a correct cookie for the current readdir request from the
- * user, we must (re-)fetch older readdir page cache entries.
+/*
+ * Recurse through the page cache pages, and return a
+ * filled nfs_entry structure of the next directory entry if possible.
*
- * Returns < 0 if some error occurrs, else it is the page offset
- * to fetch.
+ * The target for the search is position 'offset'.
+ * The latter may either be an offset into the page cache, or (better)
+ * a cookie depending on whether we're interested in strictly following
+ * the RFC wrt. not assuming monotonicity of cookies or not.
+ *
+ * For most systems, the latter is more reliable since it naturally
+ * copes with holes in the directory.
*/
-static long refetch_to_readdir_cookie(struct file *file, struct inode *inode)
+static inline
+long search_cached_dirent_pages(struct inode *inode, loff_t offset,
+ struct nfs_entry *entry)
{
- struct page *page;
- u32 goal_cookie = file->f_pos;
- long cur_off, ret = -1L;
+ long res = 0;
+ int loop_count = 0;
-again:
- cur_off = 0;
+ dfprintk(VFS, "NFS: search_cached_dirent_pages() searching for cookie %Ld\n", (long long)offset);
for (;;) {
- page = find_get_page(&inode->i_data, cur_off);
- if (page) {
- if (!Page_Uptodate(page))
- goto out_error;
- } else {
- __u32 *cp = find_cookie(inode, cur_off);
-
- if (!cp)
- goto out_error;
-
- page = try_to_get_dirent_page(file, *cp, 0);
- if (!page) {
- if (!cur_off)
- goto out_error;
-
- /* Someone touched the dir on us. */
- goto again;
- }
+ res = find_dirent_page(inode, offset, entry);
+ if (res == -EAGAIN) {
+ /* Align to beginning of next page */
+ entry->offset &= PAGE_CACHE_MASK;
+ entry->offset += PAGE_CACHE_SIZE;
+ }
+ if (res != -EAGAIN)
+ break;
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
}
- page_cache_release(page);
-
- if ((ret = nfs_readdir_offset(inode, goal_cookie)) >= 0)
- goto out;
-
- cur_off += 1;
}
-out:
- return ret;
-
-out_error:
- if (page)
- page_cache_release(page);
- goto out;
+ if (res < 0 && entry->page) {
+ page_cache_release(entry->page);
+ entry->page = NULL;
+ }
+ dfprintk(VFS, "NFS: search_cached_dirent_pages() returned %ld\n", res);
+ return res;
}
+
/* Now we cache directories properly, by stuffing the dirent
* data directly in the page cache.
*
@@ -279,198 +207,240 @@ out_error:
* page-in of the RPC reply, nowhere else, this simplies
* things substantially.
*/
-
-static int nfs_dir_filler(struct dentry *dentry, struct page *page)
+static inline
+long try_to_get_dirent_page(struct file *file, struct inode *inode,
+ struct nfs_entry *entry)
{
- struct nfs_readdirargs rd_args;
- struct nfs_readdirres rd_res;
- struct inode *inode = dentry->d_inode;
- long offset = page->index;
- __u32 *cookiep;
- int err;
+ struct dentry *dir = file->f_dentry;
+ struct page *page;
+ __u32 *p;
+ unsigned long index = entry->offset >> PAGE_CACHE_SHIFT;
+ long res = 0;
+ unsigned int dtsize = NFS_SERVER(inode)->dtsize;
+ int plus = NFS_USE_READDIRPLUS(inode);
- kmap(page);
+ dfprintk(VFS, "NFS: try_to_get_dirent_page() reading directory page @ index %ld\n", index);
- err = -EIO;
- cookiep = find_cookie(inode, offset);
- if (!cookiep)
- goto fail;
+ page = grab_cache_page(&inode->i_data, index);
- rd_args.fh = NFS_FH(dentry);
- rd_res.buffer = (char *)page_address(page);
- rd_res.bufsiz = PAGE_CACHE_SIZE;
- rd_res.cookie = *cookiep;
- do {
- rd_args.buffer = rd_res.buffer;
- rd_args.bufsiz = rd_res.bufsiz;
- rd_args.cookie = rd_res.cookie;
- err = rpc_call(NFS_CLIENT(inode),
- NFSPROC_READDIR, &rd_args, &rd_res, 0);
- if (err < 0)
- goto fail;
- } while(rd_res.bufsiz > 0);
-
- err = -EIO;
- if (rd_res.bufsiz < 0)
- NFS_DIREOF(inode) = rd_res.cookie;
- else if (create_cookie(rd_res.cookie, offset, inode))
- goto fail;
+ if (!page) {
+ res = -ENOMEM;
+ goto out;
+ }
- SetPageUptodate(page);
- kunmap(page);
- UnlockPage(page);
- return 0;
-fail:
- SetPageError(page);
- kunmap(page);
- UnlockPage(page);
- return err;
-}
+ if (Page_Uptodate(page)) {
+ dfprintk(VFS, "NFS: try_to_get_dirent_page(): page already up to date.\n");
+ goto unlock_out;
+ }
-static struct page *try_to_get_dirent_page(struct file *file, __u32 cookie, int refetch_ok)
-{
- struct dentry *dentry = file->f_dentry;
- struct inode *inode = dentry->d_inode;
- struct page *page;
- long offset;
+ p = (__u32 *)kmap(page);
- if ((offset = nfs_readdir_offset(inode, cookie)) < 0) {
- if (!refetch_ok ||
- (offset = refetch_to_readdir_cookie(file, inode)) < 0) {
- goto fail;
- }
- }
+ if (dtsize > PAGE_CACHE_SIZE)
+ dtsize = PAGE_CACHE_SIZE;
+ res = NFS_PROTO(inode)->readdir(dir, entry->cookie, p, dtsize, plus);
+
+ kunmap(page);
- page = read_cache_page(&inode->i_data, offset,
- (filler_t *)nfs_dir_filler, dentry);
- if (IS_ERR(page))
- goto fail;
- if (!Page_Uptodate(page))
- goto fail2;
- return page;
+ if (res < 0)
+ goto error;
+ if (PageError(page))
+ ClearPageError(page);
+ SetPageUptodate(page);
-fail2:
+ unlock_out:
+ UnlockPage(page);
page_cache_release(page);
-fail:
- return NULL;
+ out:
+ dfprintk(VFS, "NFS: try_to_get_dirent_page() returns %ld\n", res);
+ return res;
+ error:
+ SetPageError(page);
+ goto unlock_out;
}
-/* Seek up to dirent assosciated with the passed in cookie,
- * then fill in dirents found. Return the last cookie
- * actually given to the user, to update the file position.
+/* Recover from a revalidation flush. The case here is that
+ * the inode for the directory got invalidated somehow, and
+ * all of our cached information is lost. In order to get
+ * a correct cookie for the current readdir request from the
+ * user, we must (re-)fetch all the older readdir page cache
+ * entries.
+ *
+ * Returns < 0 if some error occurs.
*/
-static __inline__ u32 nfs_do_filldir(__u32 *p, u32 cookie,
- void *dirent, filldir_t filldir)
+static inline
+long refetch_to_readdir(struct file *file, struct inode *inode,
+ loff_t off, struct nfs_entry *entry)
{
- u32 end;
-
- while((end = *p++) != 0) {
- __u32 fileid, len, skip, this_cookie;
- char *name;
+ struct nfs_entry my_dirent,
+ *dirent = &my_dirent;
+ long res;
+ int plus = NFS_USE_READDIRPLUS(inode),
+ use_cookie = NFS_MONOTONE_COOKIES(inode),
+ loop_count = 0;
+
+ dfprintk(VFS, "NFS: refetch_to_readdir() searching for cookie %Ld\n", (long long)off);
+ *dirent = *entry;
+ entry->page = NULL;
+
+ for (res = 0;res >= 0;) {
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
+ }
- fileid = *p++;
- len = *p++;
- name = (char *) p;
- skip = NFS_NAMELEN_ALIGN(len);
- p += (skip >> 2);
- this_cookie = *p++;
+ /* Search for last cookie in page cache */
+ res = search_cached_dirent_pages(inode, off, dirent);
- if (this_cookie < cookie)
+ if (res >= 0) {
+ /* Cookie was found */
+ if ((use_cookie?dirent->cookie:dirent->offset) > off) {
+ *entry = *dirent;
+ dirent->page = NULL;
+ break;
+ }
continue;
+ }
+
+ if (dirent->page)
+ page_cache_release(dirent->page);
+ dirent->page = NULL;
- cookie = this_cookie;
- if (filldir(dirent, name, len, cookie, fileid) < 0)
+ if (res != -EIO) {
+ *entry = *dirent;
break;
+ }
+
+ /* Read in a new page */
+ res = try_to_get_dirent_page(file, inode, dirent);
+ if (res == -EBADCOOKIE) {
+ memset(dirent, 0, sizeof(*dirent));
+ nfs_zap_caches(inode);
+ res = 0;
+ }
+ /* We requested READDIRPLUS, but the server doesn't grok it */
+ if (plus && res == -ENOTSUPP) {
+ NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS;
+ memset(dirent, 0, sizeof(*dirent));
+ nfs_zap_caches(inode);
+ plus = 0;
+ res = 0;
+ }
}
+ if (dirent->page)
+ page_cache_release(dirent->page);
- return cookie;
+ dfprintk(VFS, "NFS: refetch_to_readdir() returns %ld\n", res);
+ return res;
}
-/* The file offset position is represented in pure bytes, to
- * make the page cache interface straight forward.
- *
- * However, some way is needed to make the connection between the
- * opaque NFS directory entry cookies and our offsets, so a per-inode
- * cookie cache table is used.
+/*
+ * Once we've found the start of the dirent within a page: fill 'er up...
*/
-static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+static
+int nfs_do_filldir(struct file *file, struct inode *inode,
+ struct nfs_entry *entry, void *dirent, filldir_t filldir)
{
- struct dentry *dentry = filp->f_dentry;
- struct inode *inode = dentry->d_inode;
- struct page *page;
- long offset;
- int res;
-
- res = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
- if (res < 0)
- return res;
-
- if (NFS_DIREOF(inode) && filp->f_pos >= NFS_DIREOF(inode))
- return 0;
-
- if ((offset = nfs_readdir_offset(inode, filp->f_pos)) < 0)
- goto no_dirent_page;
-
- page = find_get_page(&inode->i_data, offset);
- if (!page)
- goto no_dirent_page;
- if (!Page_Uptodate(page))
- goto dirent_read_error;
-success:
- kmap(page);
- filp->f_pos = nfs_do_filldir((__u32 *) page_address(page),
- filp->f_pos, dirent, filldir);
+ decode_dirent_t decode = NFS_PROTO(inode)->decode_dirent;
+ struct page *page = entry->page;
+ __u8 *p,
+ *start;
+ unsigned long base = page_offset(page),
+ offset = entry->offset,
+ pg_offset,
+ fileid;
+ int plus = NFS_USE_READDIRPLUS(inode),
+ use_cookie = NFS_MONOTONE_COOKIES(inode),
+ loop_count = 0,
+ res = 0;
+
+ dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ offset %ld\n", entry->offset);
+ pg_offset = offset & ~PAGE_CACHE_MASK;
+ start = (u8*)kmap(page);
+ p = start + pg_offset;
+
+ for(;;) {
+ /* Note: entry->prev contains the offset of the start of the
+ * current dirent */
+ fileid = nfs_fileid_to_ino_t(entry->ino);
+ if (use_cookie)
+ res = filldir(dirent, entry->name, entry->len, entry->prev_cookie, fileid);
+ else
+ res = filldir(dirent, entry->name, entry->len, entry->prev, fileid);
+ if (res < 0)
+ break;
+ file->f_pos = (use_cookie) ? entry->cookie : entry->offset;
+ p = (u8*)decode((__u32*)p, entry, plus);
+ if (!p || IS_ERR(p))
+ break;
+ pg_offset = p - start;
+ entry->prev = entry->offset;
+ entry->offset = base + pg_offset;
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
+ }
+ }
kunmap(page);
- page_cache_release(page);
- return 0;
-
-no_dirent_page:
- page = try_to_get_dirent_page(filp, filp->f_pos, 1);
- if (!page)
- goto no_page;
- if (Page_Uptodate(page))
- goto success;
-dirent_read_error:
- page_cache_release(page);
-no_page:
- return -EIO;
+ dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ offset %ld; returning = %d\n", entry->offset, res);
+ return res;
}
-/* Flush directory cookie and EOF caches for an inode.
- * So we don't thrash allocating/freeing cookie tables,
- * we keep the cookies around until the inode is
- * deleted/reused.
+/* The file offset position is now represented as a true offset into the
+ * page cache as is the case in most of the other filesystems.
*/
-__inline__ void nfs_flush_dircache(struct inode *inode)
+static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
- struct nfs_cookie_table *p = NFS_COOKIES(inode);
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ struct page *page;
+ struct nfs_entry my_entry,
+ *entry = &my_entry;
+ loff_t offset;
+ long res;
+
+ res = nfs_revalidate(dentry);
+ if (res < 0)
+ return res;
- while (p != NULL) {
- int i;
+ /*
+ * filp->f_pos points to the file offset in the page cache.
+ * but if the cache has meanwhile been zapped, we need to
+ * read from the last dirent to revalidate f_pos
+ * itself.
+ */
+ memset(entry, 0, sizeof(*entry));
- for(i = 0; i < COOKIES_PER_CHUNK; i++)
- p->cookies[i] = 0;
+ offset = filp->f_pos;
- p = p->next;
- }
- NFS_DIREOF(inode) = 0;
-}
+ while(!entry->eof) {
+ res = search_cached_dirent_pages(inode, offset, entry);
-/* Free up directory cache state, this happens when
- * nfs_delete_inode is called on an NFS directory.
- */
-void nfs_free_dircache(struct inode *inode)
-{
- struct nfs_cookie_table *p = NFS_COOKIES(inode);
+ if (res < 0) {
+ if (entry->eof)
+ break;
+ res = refetch_to_readdir(filp, inode, offset, entry);
+ if (res < 0)
+ break;
+ }
- while (p != NULL) {
- struct nfs_cookie_table *next = p->next;
- kmem_cache_free(nfs_cookie_cachep, p);
- p = next;
+ page = entry->page;
+ if (!page)
+ printk(KERN_ERR "NFS: Missing page...\n");
+ res = nfs_do_filldir(filp, inode, entry, dirent, filldir);
+ page_cache_release(page);
+ entry->page = NULL;
+ if (res < 0) {
+ res = 0;
+ break;
+ }
+ offset = filp->f_pos;
}
- NFS_COOKIES(inode) = NULL;
- NFS_DIREOF(inode) = 0;
+ if (entry->page)
+ page_cache_release(entry->page);
+ if (res < 0 && res != -EBADCOOKIE)
+ return res;
+ return 0;
}
/*
@@ -540,7 +510,8 @@ static inline int nfs_neg_need_reval(struct dentry *dentry)
*/
static int nfs_lookup_revalidate(struct dentry * dentry, int flags)
{
- struct dentry * parent = dentry->d_parent;
+ struct dentry *dir = dentry->d_parent;
+ struct inode *dir_i = dir->d_inode;
struct inode * inode = dentry->d_inode;
int error;
struct nfs_fh fhandle;
@@ -559,7 +530,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, int flags)
if (is_bad_inode(inode)) {
dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n",
- parent->d_name.name, dentry->d_name.name);
+ dir->d_name.name, dentry->d_name.name);
goto out_bad;
}
@@ -574,13 +545,14 @@ static int nfs_lookup_revalidate(struct dentry * dentry, int flags)
/*
* Do a new lookup and check the dentry attributes.
*/
- error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent),
- dentry->d_name.name, &fhandle, &fattr);
+ error = NFS_PROTO(dir_i)->lookup(dir, &dentry->d_name, &fhandle,
+ &fattr);
if (error)
goto out_bad;
/* Inode number matches? */
- if (NFS_FSID(inode) != fattr.fsid ||
+ if (!(fattr.valid & NFS_ATTR_FATTR) ||
+ NFS_FSID(inode) != fattr.fsid ||
NFS_FILEID(inode) != fattr.fileid)
goto out_bad;
@@ -603,10 +575,9 @@ out_bad:
goto out_valid;
d_drop(dentry);
/* Purge readdir caches. */
- if (dentry->d_parent->d_inode) {
- nfs_zap_caches(dentry->d_parent->d_inode);
- NFS_CACHEINV(dentry->d_parent->d_inode);
- }
+ nfs_zap_caches(dir_i);
+ if (inode && S_ISDIR(inode->i_mode))
+ nfs_zap_caches(inode);
return 0;
}
@@ -687,18 +658,19 @@ static void show_dentry(struct list_head * dlist)
#endif /* NFS_PARANOIA */
#endif /* 0 */
-static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry)
+static struct dentry *nfs_lookup(struct inode *dir_i, struct dentry * dentry)
{
+ struct dentry *dir = dentry->d_parent;
struct inode *inode;
int error;
struct nfs_fh fhandle;
struct nfs_fattr fattr;
dfprintk(VFS, "NFS: lookup(%s/%s)\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
+ dir->d_name.name, dentry->d_name.name);
error = -ENAMETOOLONG;
- if (dentry->d_name.len > NFS_MAXNAMLEN)
+ if (dentry->d_name.len > NFS_SERVER(dir_i)->namelen)
goto out;
error = -ENOMEM;
@@ -709,8 +681,8 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry)
}
dentry->d_op = &nfs_dentry_operations;
- error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
- dentry->d_name.name, &fhandle, &fattr);
+ error = NFS_PROTO(dir_i)->lookup(dir, &dentry->d_name, &fhandle,
+ &fattr);
inode = NULL;
if (error == -ENOENT)
goto no_entry;
@@ -743,6 +715,7 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
nfs_renew_times(dentry);
error = 0;
}
+ NFS_CACHEINV(dentry->d_parent->d_inode);
return error;
}
@@ -752,29 +725,32 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
* that the operation succeeded on the server, but an error in the
* reply path made it appear to have failed.
*/
-static int nfs_create(struct inode *dir, struct dentry *dentry, int mode)
+static int nfs_create(struct inode *dir_i, struct dentry *dentry, int mode)
{
- int error;
+ struct dentry *dir = dentry->d_parent;
struct iattr attr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
+ int error;
dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
attr.ia_mode = mode;
attr.ia_valid = ATTR_MODE;
/*
- * Invalidate the dir cache before the operation to avoid a race.
+ * The 0 argument passed into the create function should one day
+ * contain the O_EXCL flag if requested. This allows NFSv3 to
+ * select the appropriate create strategy. Currently open_namei
+ * does not pass the create flags.
*/
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
- error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
- dentry->d_name.name, &attr, &fhandle, &fattr);
- if (!error)
+ nfs_zap_caches(dir_i);
+ error = NFS_PROTO(dir_i)->create(dir, &dentry->d_name,
+ &attr, 0, &fhandle, &fattr);
+ if (!error && fhandle.size != 0)
error = nfs_instantiate(dentry, &fhandle, &fattr);
- if (error)
+ if (error || fhandle.size == 0)
d_drop(dentry);
return error;
}
@@ -782,31 +758,26 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode)
/*
* See comments for nfs_proc_create regarding failed operations.
*/
-static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+static int nfs_mknod(struct inode *dir_i, struct dentry *dentry, int mode, int rdev)
{
- int error;
+ struct dentry *dir = dentry->d_parent;
struct iattr attr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
+ int error;
dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
attr.ia_mode = mode;
attr.ia_valid = ATTR_MODE;
- /* FIXME: move this to a special nfs_proc_mknod() */
- if (S_ISCHR(mode) || S_ISBLK(mode)) {
- attr.ia_size = rdev; /* get out your barf bag */
- attr.ia_valid |= ATTR_SIZE;
- }
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
- error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
- dentry->d_name.name, &attr, &fhandle, &fattr);
- if (!error)
+ nfs_zap_caches(dir_i);
+ error = NFS_PROTO(dir_i)->mknod(dir, &dentry->d_name, &attr, rdev,
+ &fhandle, &fattr);
+ if (!error && fhandle.size != 0)
error = nfs_instantiate(dentry, &fhandle, &fattr);
- if (error)
+ if (error || fhandle.size == 0)
d_drop(dentry);
return error;
}
@@ -814,19 +785,21 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde
/*
* See comments for nfs_proc_create regarding failed operations.
*/
-static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+static int nfs_mkdir(struct inode *dir_i, struct dentry *dentry, int mode)
{
- int error;
+ struct dentry *dir = dentry->d_parent;
struct iattr attr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
+ int error;
dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
attr.ia_valid = ATTR_MODE;
attr.ia_mode = mode | S_IFDIR;
+#if 0
/*
* Always drop the dentry, we can't always depend on
* the fattr returned by the server (AIX seems to be
@@ -834,44 +807,48 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
* depending on potentially bogus information.
*/
d_drop(dentry);
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
- error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent),
- dentry->d_name.name, &attr, &fhandle, &fattr);
- if (!error)
- dir->i_nlink++;
+#endif
+ nfs_zap_caches(dir_i);
+ dir_i->i_nlink++;
+ error = NFS_PROTO(dir_i)->mkdir(dir, &dentry->d_name, &attr, &fhandle,
+ &fattr);
+ if (!error && fhandle.size != 0)
+ error = nfs_instantiate(dentry, &fhandle, &fattr);
+ if (error || fhandle.size == 0)
+ d_drop(dentry);
return error;
}
-static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
+static int nfs_rmdir(struct inode *dir_i, struct dentry *dentry)
{
+ struct dentry *dir = dentry->d_parent;
int error;
dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
- error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
- dentry->d_name.name);
+ nfs_zap_caches(dir_i);
+ error = NFS_PROTO(dir_i)->rmdir(dir, &dentry->d_name);
/* Update i_nlink and invalidate dentry. */
if (!error) {
d_drop(dentry);
- if (dir->i_nlink)
- dir->i_nlink--;
+ if (dir_i->i_nlink)
+ dir_i->i_nlink--;
}
return error;
}
-static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
+static int nfs_sillyrename(struct inode *dir_i, struct dentry *dentry)
{
+ struct dentry *dir = dentry->d_parent;
static unsigned int sillycounter = 0;
- const int i_inosize = sizeof(dir->i_ino)*2;
+ const int i_inosize = sizeof(dir_i->i_ino)*2;
const int countersize = sizeof(sillycounter)*2;
const int slen = strlen(".nfs") + i_inosize + countersize;
char silly[slen+1];
+ struct qstr qsilly;
struct dentry *sdentry;
int error = -EIO;
@@ -923,11 +900,10 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
goto out;
} while(sdentry->d_inode != NULL); /* need negative lookup */
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
- error = nfs_proc_rename(NFS_SERVER(dir),
- NFS_FH(dentry->d_parent), dentry->d_name.name,
- NFS_FH(dentry->d_parent), silly);
+ nfs_zap_caches(dir_i);
+ qsilly.name = silly;
+ qsilly.len = strlen(silly);
+ error = NFS_PROTO(dir_i)->rename(dir, &dentry->d_name, dir, &qsilly);
if (!error) {
nfs_renew_times(dentry);
d_move(dentry, sdentry);
@@ -948,7 +924,8 @@ out:
*/
static int nfs_safe_remove(struct dentry *dentry)
{
- struct inode *dir = dentry->d_parent->d_inode;
+ struct dentry *dir = dentry->d_parent;
+ struct inode *dir_i = dir->d_inode;
struct inode *inode = dentry->d_inode;
int error, rehash = 0;
@@ -979,22 +956,22 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
d_drop(dentry);
rehash = 1;
}
+ nfs_zap_caches(dir_i);
+ error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name);
+ if (error < 0)
+ goto out;
/*
- * Update i_nlink and free the inode before unlinking.
+ * Update i_nlink and free the inode
*/
if (inode) {
if (inode->i_nlink)
inode->i_nlink --;
d_delete(dentry);
}
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
- error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
- dentry->d_name.name);
/*
* Rehash the negative dentry if the operation succeeded.
*/
- if (!error && rehash)
+ if (rehash)
d_add(dentry, NULL);
out:
return error;
@@ -1023,16 +1000,22 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
}
static int
-nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+nfs_symlink(struct inode *dir_i, struct dentry *dentry, const char *symname)
{
+ struct dentry *dir = dentry->d_parent;
struct iattr attr;
+ struct nfs_fattr sym_attr;
+ struct nfs_fh sym_fh;
+ struct qstr qsymname;
+ unsigned int maxlen;
int error;
dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
+ dir_i->i_dev, dir_i->i_ino, dentry->d_name.name, symname);
error = -ENAMETOOLONG;
- if (strlen(symname) > NFS_MAXPATHLEN)
+ maxlen = (NFS_PROTO(dir_i)->version==2) ? NFS2_MAXPATHLEN : NFS3_MAXPATHLEN;
+ if (strlen(symname) > maxlen)
goto out;
#ifdef NFS_PARANOIA
@@ -1047,21 +1030,19 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
attr.ia_valid = ATTR_MODE;
attr.ia_mode = S_IFLNK | S_IRWXUGO;
- /*
- * Drop the dentry in advance to force a new lookup.
- * Since nfs_proc_symlink doesn't return a fattr, we
- * can't instantiate the new inode.
- */
- d_drop(dentry);
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
- error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
- dentry->d_name.name, symname, &attr);
- if (!error) {
- nfs_renew_times(dentry->d_parent);
- } else if (error == -EEXIST) {
- printk("nfs_proc_symlink: %s/%s already exists??\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
+ qsymname.name = symname;
+ qsymname.len = strlen(symname);
+
+ nfs_zap_caches(dir_i);
+ error = NFS_PROTO(dir_i)->symlink(dir, &dentry->d_name, &qsymname,
+ &attr, &sym_fh, &sym_attr);
+ if (!error && sym_fh.size != 0 && (sym_attr.valid & NFS_ATTR_FATTR)) {
+ error = nfs_instantiate(dentry, &sym_fh, &sym_attr);
+ } else {
+ if (error == -EEXIST)
+ printk("nfs_proc_symlink: %s/%s already exists??\n",
+ dir->d_name.name, dentry->d_name.name);
+ d_drop(dentry);
}
out:
@@ -1069,8 +1050,9 @@ out:
}
static int
-nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
+nfs_link(struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry)
{
+ struct dentry *dir = dentry->d_parent;
struct inode *inode = old_dentry->d_inode;
int error;
@@ -1084,10 +1066,8 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
* we can't use the existing dentry.
*/
d_drop(dentry);
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
- error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry),
- NFS_FH(dentry->d_parent), dentry->d_name.name);
+ nfs_zap_caches(dir_i);
+ error = NFS_PROTO(dir_i)->link(old_dentry, dir, &dentry->d_name);
if (!error) {
/*
* Update the link count immediately, as some apps
@@ -1197,14 +1177,12 @@ go_ahead:
if (new_inode)
d_delete(new_dentry);
- invalidate_inode_pages(new_dir);
- nfs_flush_dircache(new_dir);
- invalidate_inode_pages(old_dir);
- nfs_flush_dircache(old_dir);
- error = nfs_proc_rename(NFS_DSERVER(old_dentry),
- NFS_FH(old_dentry->d_parent), old_dentry->d_name.name,
- NFS_FH(new_dentry->d_parent), new_dentry->d_name.name);
-
+ nfs_zap_caches(new_dir);
+ nfs_zap_caches(old_dir);
+ error = NFS_PROTO(old_dir)->rename(old_dentry->d_parent,
+ &old_dentry->d_name,
+ new_dentry->d_parent,
+ &new_dentry->d_name);
NFS_CACHEINV(old_dir);
NFS_CACHEINV(new_dir);
/* Update the dcache if needed */
@@ -1229,16 +1207,15 @@ int nfs_init_fhcache(void)
if (nfs_fh_cachep == NULL)
return -ENOMEM;
- nfs_cookie_cachep = kmem_cache_create("nfs_dcookie",
- sizeof(struct nfs_cookie_table),
- 0, SLAB_HWCACHE_ALIGN,
- NULL, NULL);
- if (nfs_cookie_cachep == NULL)
- return -ENOMEM;
-
return 0;
}
+void nfs_destroy_fhcache(void)
+{
+ if (kmem_cache_destroy(nfs_fh_cachep))
+ printk(KERN_INFO "nfs_fh: not all structures were freed\n");
+}
+
/*
* Local variables:
* version-control: t
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 32d290c73..d5c3d0944 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -22,6 +22,7 @@
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/pagemap.h>
@@ -215,10 +216,10 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
struct inode * inode = filp->f_dentry->d_inode;
int status = 0;
- dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%ld:%ld)\n",
+ dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n",
inode->i_dev, inode->i_ino,
fl->fl_type, fl->fl_flags,
- fl->fl_start, fl->fl_end);
+ (long long)fl->fl_start, (long long)fl->fl_end);
if (!inode)
return -EINVAL;
diff --git a/fs/nfs/flushd.c b/fs/nfs/flushd.c
index d36c3a9ae..800a42171 100644
--- a/fs/nfs/flushd.c
+++ b/fs/nfs/flushd.c
@@ -299,6 +299,5 @@ nfs_flushd_exit(struct rpc_task *task)
cache->task = NULL;
spin_unlock(&nfs_flushd_lock);
wake_up(&cache->request_wait);
- rpc_release_task(task);
}
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index ca7e1b944..14c43cd24 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -27,6 +27,7 @@
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/stats.h>
#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
#include <linux/nfs_flushd.h>
#include <linux/lockd/bind.h>
#include <linux/smp_lock.h>
@@ -58,7 +59,32 @@ static struct super_operations nfs_sops = {
umount_begin: nfs_umount_begin,
};
+/*
+ * RPC cruft for NFS
+ */
struct rpc_stat nfs_rpcstat = { &nfs_program };
+static struct rpc_version * nfs_version[] = {
+ NULL,
+ NULL,
+ &nfs_version2,
+#ifdef CONFIG_NFS_V3
+ &nfs_version3,
+#endif
+};
+
+struct rpc_program nfs_program = {
+ "nfs",
+ NFS_PROGRAM,
+ sizeof(nfs_version) / sizeof(nfs_version[0]),
+ nfs_version,
+ &nfs_rpcstat,
+};
+
+static inline unsigned long
+nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
+{
+ return nfs_fileid_to_ino_t(fattr->fileid);
+}
/*
* The "read_inode" function doesn't actually do anything:
@@ -83,6 +109,7 @@ nfs_read_inode(struct inode * inode)
inode->u.nfs_i.npages = 0;
NFS_CACHEINV(inode);
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+ NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
}
static void
@@ -101,18 +128,12 @@ nfs_delete_inode(struct inode * inode)
{
dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
- lock_kernel();
- if (S_ISDIR(inode->i_mode)) {
- nfs_free_dircache(inode);
- } else {
- /*
- * The following can never actually happen...
- */
- if (nfs_have_writebacks(inode)) {
- printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
- }
+ /*
+ * The following can never actually happen...
+ */
+ if (nfs_have_writebacks(inode)) {
+ printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
}
- unlock_kernel();
clear_inode(inode);
}
@@ -153,33 +174,68 @@ nfs_umount_begin(struct super_block *sb)
rpc_killall_tasks(rpc);
}
-/*
- * Compute and set NFS server blocksize
- */
-static unsigned int
-nfs_block_size(unsigned int bsize, unsigned char *nrbitsp)
-{
- if (bsize < 1024)
- bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
- else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
- bsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
+static inline unsigned long
+nfs_block_bits(unsigned long bsize, unsigned char *nrbitsp)
+{
/* make sure blocksize is a power of two */
if ((bsize & (bsize - 1)) || nrbitsp) {
- unsigned int nrbits;
+ unsigned char nrbits;
for (nrbits = 31; nrbits && !(bsize & (1 << nrbits)); nrbits--)
;
bsize = 1 << nrbits;
if (nrbitsp)
*nrbitsp = nrbits;
- if (bsize < NFS_DEF_FILE_IO_BUFFER_SIZE)
- bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
}
return bsize;
}
+/*
+ * Calculate the number of 512byte blocks used.
+ */
+static inline unsigned long
+nfs_calc_block_size(u64 tsize)
+{
+ loff_t used = (tsize + 511) / 512;
+ return (used > ULONG_MAX) ? ULONG_MAX : used;
+}
+
+/*
+ * Compute and set NFS server blocksize
+ */
+static inline unsigned long
+nfs_block_size(unsigned long bsize, unsigned char *nrbitsp)
+{
+ if (bsize < 1024)
+ bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
+ else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
+ bsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
+
+ return nfs_block_bits(bsize, nrbitsp);
+}
+
+/*
+ * Obtain the root inode of the file system.
+ */
+static struct inode *
+nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh)
+{
+ struct nfs_server *server = &sb->u.nfs_sb.s_server;
+ struct nfs_fattr fattr;
+ struct inode *inode;
+ int error;
+
+ if ((error = server->rpc_ops->getroot(server, rootfh, &fattr)) < 0) {
+ printk(KERN_NOTICE "nfs_get_root: getattr error = %d\n", -error);
+ return NULL;
+ }
+
+ inode = __nfs_fhget(sb, &fattr);
+ return inode;
+}
+
extern struct nfs_fh *nfs_fh_alloc(void);
extern void nfs_fh_free(struct nfs_fh *p);
@@ -194,19 +250,20 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
{
struct nfs_mount_data *data = (struct nfs_mount_data *) raw_data;
struct nfs_server *server;
- struct rpc_xprt *xprt;
- struct rpc_clnt *clnt;
- struct nfs_fh *root_fh;
- struct inode *root_inode;
+ struct rpc_xprt *xprt = NULL;
+ struct rpc_clnt *clnt = NULL;
+ struct nfs_fh *root = &data->root, *root_fh, fh;
+ struct inode *root_inode = NULL;
unsigned int authflavor;
- int tcp;
struct sockaddr_in srvaddr;
struct rpc_timeout timeparms;
- struct nfs_fattr fattr;
+ struct nfs_fsinfo fsinfo;
+ int tcp, version, maxlen;
if (!data)
goto out_miss_args;
+ memset(&fh, 0, sizeof(fh));
if (data->version != NFS_MOUNT_VERSION) {
printk("nfs warning: mount version %s than kernel\n",
data->version < NFS_MOUNT_VERSION ? "older" : "newer");
@@ -214,6 +271,12 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
data->namlen = 0;
if (data->version < 3)
data->bsize = 0;
+ if (data->version < 4) {
+ data->flags &= ~NFS_MOUNT_VER3;
+ root = &fh;
+ root->size = NFS2_FHSIZE;
+ memcpy(root->data, data->old_root.data, NFS2_FHSIZE);
+ }
}
/* We now require that the mount process passes the remote address */
@@ -225,12 +288,12 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
sb->s_magic = NFS_SUPER_MAGIC;
sb->s_op = &nfs_sops;
+ sb->s_blocksize_bits = 0;
sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
- sb->u.nfs_sb.s_root = data->root;
server = &sb->u.nfs_sb.s_server;
server->rsize = nfs_block_size(data->rsize, NULL);
server->wsize = nfs_block_size(data->wsize, NULL);
- server->flags = data->flags;
+ server->flags = data->flags & NFS_MOUNT_FLAGMASK;
if (data->flags & NFS_MOUNT_NOAC) {
data->acregmin = data->acregmax = 0;
@@ -241,11 +304,32 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
server->acdirmin = data->acdirmin*HZ;
server->acdirmax = data->acdirmax*HZ;
+ server->namelen = data->namlen;
server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL);
if (!server->hostname)
goto out_unlock;
strcpy(server->hostname, data->hostname);
+ nfsv3_try_again:
+ /* Check NFS protocol revision and initialize RPC op vector
+ * and file handle pool. */
+ if (data->flags & NFS_MOUNT_VER3) {
+#ifdef CONFIG_NFS_V3
+ server->rpc_ops = &nfs_v3_clientops;
+ version = 3;
+ if (data->version < 4) {
+ printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n");
+ goto out_unlock;
+ }
+#else
+ printk(KERN_NOTICE "NFS: NFSv3 not supported.\n");
+ goto out_unlock;
+#endif
+ } else {
+ server->rpc_ops = &nfs_v2_clientops;
+ version = 2;
+ }
+
/* Which protocol do we use? */
tcp = (data->flags & NFS_MOUNT_TCP);
@@ -255,6 +339,11 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
timeparms.to_maxval = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT;
timeparms.to_exponential = 1;
+ if (!timeparms.to_initval)
+ timeparms.to_initval = (tcp ? 600 : 11) * HZ / 10;
+ if (!timeparms.to_retries)
+ timeparms.to_retries = 5;
+
/* Now create transport and client */
xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP,
&srvaddr, &timeparms);
@@ -269,7 +358,7 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
authflavor = RPC_AUTH_KRB;
clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
- NFS_VERSION, authflavor);
+ version, authflavor);
if (clnt == NULL)
goto out_no_client;
@@ -289,20 +378,68 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
root_fh = nfs_fh_alloc();
if (!root_fh)
goto out_no_fh;
- *root_fh = data->root;
-
- if (nfs_proc_getattr(server, root_fh, &fattr) != 0)
- goto out_no_fattr;
+ memcpy((u8*)root_fh, (u8*)root, sizeof(*root));
+
+ /* Did getting the root inode fail? */
+ if (!(root_inode = nfs_get_root(sb, root))
+ && (data->flags & NFS_MOUNT_VER3)) {
+ data->flags &= ~NFS_MOUNT_VER3;
+ nfs_fh_free(root_fh);
+ rpciod_down();
+ rpc_shutdown_client(server->client);
+ goto nfsv3_try_again;
+ }
- root_inode = __nfs_fhget(sb, &fattr);
if (!root_inode)
goto out_no_root;
sb->s_root = d_alloc_root(root_inode);
if (!sb->s_root)
goto out_no_root;
+
sb->s_root->d_op = &nfs_dentry_operations;
sb->s_root->d_fsdata = root_fh;
+ /* Get some general file system info */
+ if (server->rpc_ops->statfs(server, root, &fsinfo) >= 0) {
+ if (server->namelen == 0)
+ server->namelen = fsinfo.namelen;
+ } else {
+ printk(KERN_NOTICE "NFS: cannot retrieve file system info.\n");
+ goto out_no_root;
+ }
+
+ /* Work out a lot of parameters */
+ if (data->rsize == 0)
+ server->rsize = nfs_block_size(fsinfo.rtpref, NULL);
+ if (data->wsize == 0)
+ server->wsize = nfs_block_size(fsinfo.wtpref, NULL);
+ server->dtsize = nfs_block_size(fsinfo.dtpref, NULL);
+ /* NFSv3: we don't have bsize, but rather rtmult and wtmult... */
+ if (!fsinfo.bsize)
+ fsinfo.bsize = (fsinfo.rtmult>fsinfo.wtmult) ? fsinfo.rtmult : fsinfo.wtmult;
+ /* Also make sure we don't go below rsize/wsize since
+ * RPC calls are expensive */
+ if (fsinfo.bsize < server->rsize)
+ fsinfo.bsize = server->rsize;
+ if (fsinfo.bsize < server->wsize)
+ fsinfo.bsize = server->wsize;
+
+ if (data->bsize == 0)
+ sb->s_blocksize = nfs_block_bits(fsinfo.bsize, &sb->s_blocksize_bits);
+ if (server->rsize > fsinfo.rtmax)
+ server->rsize = fsinfo.rtmax;
+ if (server->rsize > PAGE_CACHE_SIZE)
+ server->rsize = PAGE_CACHE_SIZE;
+ if (server->wsize > fsinfo.wtmax)
+ server->wsize = fsinfo.wtmax;
+ if (server->wsize > NFS_WRITE_MAXIOV << PAGE_CACHE_SHIFT)
+ server->wsize = NFS_WRITE_MAXIOV << PAGE_CACHE_SHIFT;
+
+ maxlen = (version == 2) ? NFS2_MAXNAMLEN : NFS3_MAXNAMLEN;
+
+ if (server->namelen == 0 || server->namelen > maxlen)
+ server->namelen = maxlen;
+
/* Fire up the writeback cache */
if (nfs_reqlist_alloc(server) < 0) {
printk(KERN_NOTICE "NFS: cannot initialize writeback cache.\n");
@@ -322,11 +459,6 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
out_no_root:
printk("nfs_read_super: get root inode failed\n");
iput(root_inode);
- goto out_free_fh;
-
-out_no_fattr:
- printk("nfs_read_super: get root fattr failed\n");
-out_free_fh:
nfs_fh_free(root_fh);
out_no_fh:
rpciod_down();
@@ -366,21 +498,33 @@ out_fail:
static int
nfs_statfs(struct super_block *sb, struct statfs *buf)
{
- int error;
+ struct nfs_server *server = &sb->u.nfs_sb.s_server;
+ unsigned char blockbits;
+ unsigned long blockres;
struct nfs_fsinfo res;
+ int error;
- error = nfs_proc_statfs(&sb->u.nfs_sb.s_server, &sb->u.nfs_sb.s_root,
- &res);
- if (error) {
- printk("nfs_statfs: statfs error = %d\n", -error);
- res.bsize = res.blocks = res.bfree = res.bavail = -1;
- }
+ error = server->rpc_ops->statfs(server, NFS_FH(sb->s_root), &res);
buf->f_type = NFS_SUPER_MAGIC;
- buf->f_bsize = res.bsize;
- buf->f_blocks = res.blocks;
- buf->f_bfree = res.bfree;
- buf->f_bavail = res.bavail;
- buf->f_namelen = NAME_MAX;
+ if (error < 0)
+ goto out_err;
+
+ if (res.bsize == 0)
+ res.bsize = sb->s_blocksize;
+ buf->f_bsize = nfs_block_bits(res.bsize, &blockbits);
+ blockres = (1 << blockbits) - 1;
+ buf->f_blocks = (res.tbytes + blockres) >> blockbits;
+ buf->f_bfree = (res.fbytes + blockres) >> blockbits;
+ buf->f_bavail = (res.abytes + blockres) >> blockbits;
+ buf->f_files = res.tfiles;
+ buf->f_ffree = res.afiles;
+ if (res.namelen == 0 || res.namelen > server->namelen)
+ res.namelen = server->namelen;
+ buf->f_namelen = res.namelen;
+ return 0;
+ out_err:
+ printk("nfs_statfs: statfs error = %d\n", -error);
+ buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1;
return 0;
}
@@ -429,11 +573,12 @@ void
nfs_zap_caches(struct inode *inode)
{
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
- NFS_CACHEINV(inode);
+ NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
invalidate_inode_pages(inode);
- if (S_ISDIR(inode->i_mode))
- nfs_flush_dircache(inode);
+
+ memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
+ NFS_CACHEINV(inode);
}
/*
@@ -481,9 +626,16 @@ nfs_fill_inode(struct inode *inode, struct nfs_fattr *fattr)
* Preset the size and mtime, as there's no need
* to invalidate the caches.
*/
- inode->i_size = fattr->size;
- inode->i_mtime = fattr->mtime.seconds;
- NFS_OLDMTIME(inode) = fattr->mtime.seconds;
+ inode->i_size = nfs_size_to_loff_t(fattr->size);
+ inode->i_mtime = nfs_time_to_secs(fattr->mtime);
+ inode->i_atime = nfs_time_to_secs(fattr->atime);
+ inode->i_ctime = nfs_time_to_secs(fattr->ctime);
+ NFS_CACHE_CTIME(inode) = fattr->ctime;
+ NFS_CACHE_MTIME(inode) = fattr->mtime;
+ NFS_CACHE_ATIME(inode) = fattr->atime;
+ NFS_CACHE_ISIZE(inode) = fattr->size;
+ NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+ NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
}
nfs_refresh_inode(inode, fattr);
}
@@ -551,9 +703,9 @@ nfs_fhget(struct dentry *dentry, struct nfs_fh *fhandle,
{
struct super_block *sb = dentry->d_sb;
- dprintk("NFS: nfs_fhget(%s/%s fileid=%d)\n",
+ dprintk("NFS: nfs_fhget(%s/%s fileid=%Ld)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
- fattr->fileid);
+ (long long)fattr->fileid);
/* Install the file handle in the dentry */
*((struct nfs_fh *) dentry->d_fsdata) = *fhandle;
@@ -572,7 +724,7 @@ nfs_fhget(struct dentry *dentry, struct nfs_fh *fhandle,
inode->i_sb = sb;
inode->i_dev = sb->s_dev;
inode->i_flags = 0;
- inode->i_ino = fattr->fileid;
+ inode->i_ino = nfs_fattr_to_ino_t(fattr);
nfs_read_inode(inode);
nfs_fill_inode(inode, fattr);
inode->u.nfs_i.flags |= NFS_IS_SNAPSHOT;
@@ -598,12 +750,15 @@ __nfs_fhget(struct super_block *sb, struct nfs_fattr *fattr)
struct inode *inode = NULL;
unsigned long ino;
+ if ((fattr->valid & NFS_ATTR_FATTR) == 0)
+ goto out_no_inode;
+
if (!fattr->nlink) {
printk("NFS: Buggy server - nlink == 0!\n");
goto out_no_inode;
}
- ino = fattr->fileid;
+ ino = nfs_fattr_to_ino_t(fattr);
while((inode = iget4(sb, ino, nfs_find_actor, fattr)) != NULL) {
@@ -666,8 +821,7 @@ printk("nfs_notify_change: revalidate failed, error=%d\n", error);
if (error)
goto out;
- error = nfs_proc_setattr(NFS_DSERVER(dentry), NFS_FH(dentry),
- &fattr, attr);
+ error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
if (error)
goto out;
/*
@@ -676,13 +830,21 @@ printk("nfs_notify_change: revalidate failed, error=%d\n", error);
*/
if (attr->ia_valid & ATTR_SIZE) {
if (attr->ia_size != fattr.size)
- printk("nfs_notify_change: attr=%Ld, fattr=%d??\n",
- (long long) attr->ia_size, fattr.size);
- inode->i_mtime = fattr.mtime.seconds;
+ printk("nfs_notify_change: attr=%Ld, fattr=%Ld??\n",
+ (long long) attr->ia_size, (long long)fattr.size);
vmtruncate(inode, attr->ia_size);
}
- if (attr->ia_valid & ATTR_MTIME)
- inode->i_mtime = fattr.mtime.seconds;
+
+ /*
+ * If we changed the size or mtime, update the inode
+ * now to avoid invalidating the page cache.
+ */
+ if (!(fattr.valid & NFS_ATTR_WCC)) {
+ fattr.pre_size = NFS_CACHE_ISIZE(inode);
+ fattr.pre_mtime = NFS_CACHE_MTIME(inode);
+ fattr.pre_ctime = NFS_CACHE_CTIME(inode);
+ fattr.valid |= NFS_ATTR_WCC;
+ }
error = nfs_refresh_inode(inode, &fattr);
out:
return error;
@@ -695,24 +857,13 @@ out:
int
nfs_wait_on_inode(struct inode *inode, int flag)
{
- struct task_struct *tsk = current;
- DECLARE_WAITQUEUE(wait, tsk);
- int intr, error = 0;
-
- intr = NFS_SERVER(inode)->flags & NFS_MOUNT_INTR;
- add_wait_queue(&inode->i_wait, &wait);
- for (;;) {
- set_task_state(tsk, (intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE));
- error = 0;
- if (!(NFS_FLAGS(inode) & flag))
- break;
- error = -ERESTARTSYS;
- if (intr && signalled())
- break;
- schedule();
- }
- set_task_state(tsk, TASK_RUNNING);
- remove_wait_queue(&inode->i_wait, &wait);
+ struct rpc_clnt *clnt = NFS_CLIENT(inode);
+ int error;
+ if (!(NFS_FLAGS(inode) & flag))
+ return 0;
+ inode->i_count++;
+ error = nfs_wait_event(clnt, inode->i_wait, !(NFS_FLAGS(inode) & flag));
+ iput(inode);
return error;
}
@@ -768,30 +919,32 @@ __nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
}
NFS_FLAGS(inode) |= NFS_INO_REVALIDATING;
- status = nfs_proc_getattr(server, NFS_FH(dentry), &fattr);
+ status = NFS_PROTO(inode)->getattr(dentry, &fattr);
if (status) {
+ struct dentry *dir = dentry->d_parent;
+ struct inode *dir_i = dir->d_inode;
int error;
u32 *fh;
struct nfs_fh fhandle;
dfprintk(PAGECACHE, "nfs_revalidate_inode: %s/%s getattr failed, ino=%ld, error=%d\n",
- dentry->d_parent->d_name.name,
- dentry->d_name.name, inode->i_ino, status);
+ dir->d_name.name, dentry->d_name.name,
+ inode->i_ino, status);
if (status != -ESTALE)
goto out;
/*
* A "stale filehandle" error ... show the current fh
* and find out what the filehandle should be.
*/
- fh = (u32 *) NFS_FH(dentry);
+ fh = (u32 *) NFS_FH(dentry)->data;
dfprintk(PAGECACHE, "NFS: bad fh %08x%08x%08x%08x%08x%08x%08x%08x\n",
fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]);
- error = nfs_proc_lookup(server, NFS_FH(dentry->d_parent),
- dentry->d_name.name, &fhandle, &fattr);
+ error = NFS_PROTO(dir_i)->lookup(dir, &dentry->d_name,
+ &fhandle, &fattr);
if (error) {
dfprintk(PAGECACHE, "NFS: lookup failed, error=%d\n", error);
goto out;
}
- fh = (u32 *) &fhandle;
+ fh = (u32 *) fhandle.data;
dfprintk(PAGECACHE, " %08x%08x%08x%08x%08x%08x%08x%08x\n",
fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]);
goto out;
@@ -827,19 +980,37 @@ out:
int
nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{
- int invalid = 0;
- int error = -EIO;
-
- dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n",
- inode->i_dev, inode->i_ino, inode->i_count);
+ __u64 new_size, new_mtime;
+ loff_t new_isize;
+ int invalid = 0;
+ int error = -EIO;
if (!inode || !fattr) {
- printk("nfs_refresh_inode: inode or fattr is NULL\n");
+ printk(KERN_ERR "nfs_refresh_inode: inode or fattr is NULL\n");
+ goto out;
+ }
+ if (inode->i_mode == 0) {
+ printk(KERN_ERR "nfs_refresh_inode: empty inode\n");
goto out;
}
- if (inode->i_ino != fattr->fileid) {
- printk("nfs_refresh_inode: mismatch, ino=%ld, fattr=%d\n",
- inode->i_ino, fattr->fileid);
+
+ if ((fattr->valid & NFS_ATTR_FATTR) == 0)
+ goto out;
+
+ if (is_bad_inode(inode))
+ goto out;
+
+ dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d info=0x%x)\n",
+ inode->i_dev, inode->i_ino, inode->i_count,
+ fattr->valid);
+
+
+ if (NFS_FSID(inode) != fattr->fsid ||
+ NFS_FILEID(inode) != fattr->fileid) {
+ printk(KERN_ERR "nfs_refresh_inode: inode number mismatch\n"
+ "expected (0x%Lx/0x%Lx), got (0x%Lx/0x%Lx)\n",
+ (long long)NFS_FSID(inode), (long long)NFS_FILEID(inode),
+ (long long)fattr->fsid, (long long)fattr->fileid);
goto out;
}
@@ -849,54 +1020,101 @@ nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
goto out_changed;
- inode->i_mode = fattr->mode;
- inode->i_nlink = fattr->nlink;
- inode->i_uid = fattr->uid;
- inode->i_gid = fattr->gid;
+ new_mtime = fattr->mtime;
+ new_size = fattr->size;
+ new_isize = nfs_size_to_loff_t(fattr->size);
- inode->i_blocks = fattr->blocks;
- inode->i_atime = fattr->atime.seconds;
- inode->i_ctime = fattr->ctime.seconds;
+ error = 0;
/*
* Update the read time so we don't revalidate too often.
*/
NFS_READTIME(inode) = jiffies;
- error = 0;
/*
- * If we have pending write-back entries, we don't want
- * to look at the size or the mtime the server sends us
- * too closely, as we're in the middle of modifying them.
+ * Note: NFS_CACHE_ISIZE(inode) reflects the state of the cache.
+ * NOT inode->i_size!!!
*/
- if (nfs_have_writebacks(inode))
- goto out;
-
- if (inode->i_size != fattr->size) {
+ if (NFS_CACHE_ISIZE(inode) != new_size) {
#ifdef NFS_DEBUG_VERBOSE
-printk("NFS: size change on %x/%ld\n", inode->i_dev, inode->i_ino);
+ printk(KERN_DEBUG "NFS: isize change on %x/%ld\n", inode->i_dev, inode->i_ino);
#endif
- inode->i_size = fattr->size;
invalid = 1;
}
- if (inode->i_mtime != fattr->mtime.seconds) {
+ /*
+ * Note: we don't check inode->i_mtime since pipes etc.
+ * can change this value in VFS without requiring a
+ * cache revalidation.
+ */
+ if (NFS_CACHE_MTIME(inode) != new_mtime) {
#ifdef NFS_DEBUG_VERBOSE
-printk("NFS: mtime change on %x/%ld\n", inode->i_dev, inode->i_ino);
+ printk(KERN_DEBUG "NFS: mtime change on %x/%ld\n", inode->i_dev, inode->i_ino);
#endif
- inode->i_mtime = fattr->mtime.seconds;
invalid = 1;
}
- if (invalid)
- goto out_invalid;
+ /* Check Weak Cache Consistency data.
+ * If size and mtime match the pre-operation values, we can
+ * assume that any attribute changes were caused by our NFS
+ * operation, so there's no need to invalidate the caches.
+ */
+ if ((fattr->valid & NFS_ATTR_WCC)
+ && NFS_CACHE_ISIZE(inode) == fattr->pre_size
+ && NFS_CACHE_MTIME(inode) == fattr->pre_mtime) {
+ invalid = 0;
+ }
+
+ /*
+ * If we have pending writebacks, things can get
+ * messy.
+ */
+ if (nfs_have_writebacks(inode) && new_isize < inode->i_size)
+ new_isize = inode->i_size;
+
+ NFS_CACHE_CTIME(inode) = fattr->ctime;
+ inode->i_ctime = nfs_time_to_secs(fattr->ctime);
+ /* If we've been messing around with atime, don't
+ * update it. Save the server value in NFS_CACHE_ATIME.
+ */
+ NFS_CACHE_ATIME(inode) = fattr->atime;
+ if (time_before(inode->i_atime, nfs_time_to_secs(fattr->atime)))
+ inode->i_atime = nfs_time_to_secs(fattr->atime);
+ NFS_CACHE_MTIME(inode) = new_mtime;
+ inode->i_mtime = nfs_time_to_secs(new_mtime);
+
+ NFS_CACHE_ISIZE(inode) = new_size;
+ inode->i_size = new_isize;
+
+ inode->i_mode = fattr->mode;
+ inode->i_nlink = fattr->nlink;
+ inode->i_uid = fattr->uid;
+ inode->i_gid = fattr->gid;
+
+ if (fattr->valid & NFS_ATTR_FATTR_V3) {
+ /*
+ * report the blocks in 512byte units
+ */
+ inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
+ inode->i_blksize = inode->i_sb->s_blocksize;
+ } else {
+ inode->i_blocks = fattr->du.nfs2.blocks;
+ inode->i_blksize = fattr->du.nfs2.blocksize;
+ }
+ inode->i_rdev = 0;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ inode->i_rdev = to_kdev_t(fattr->rdev);
+
/* Update attrtimeo value */
- if (fattr->mtime.seconds == NFS_OLDMTIME(inode)) {
+ if (!invalid && time_after(jiffies, NFS_ATTRTIMEO_UPDATE(inode)+NFS_ATTRTIMEO(inode))) {
if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
+ NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
}
- NFS_OLDMTIME(inode) = fattr->mtime.seconds;
+
+ if (invalid)
+ nfs_zap_caches(inode);
out:
return error;
@@ -906,22 +1124,16 @@ out_changed:
* Big trouble! The inode has become a different object.
*/
#ifdef NFS_PARANOIA
-printk("nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n",
-inode->i_ino, inode->i_mode, fattr->mode);
+ printk(KERN_DEBUG "nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n",
+ inode->i_ino, inode->i_mode, fattr->mode);
#endif
/*
* No need to worry about unhashing the dentry, as the
* lookup validation will know that the inode is bad.
+ * (But we fall through to invalidate the caches.)
*/
nfs_invalidate_inode(inode);
goto out;
-
-out_invalid:
-#ifdef NFS_DEBUG_VERBOSE
-printk("nfs_refresh_inode: invalidating %ld pages\n", inode->i_nrpages);
-#endif
- nfs_zap_caches(inode);
- goto out;
}
/*
@@ -930,7 +1142,9 @@ printk("nfs_refresh_inode: invalidating %ld pages\n", inode->i_nrpages);
static DECLARE_FSTYPE(nfs_fs_type, "nfs", nfs_read_super, 0);
extern int nfs_init_fhcache(void);
+extern void nfs_destroy_fhcache(void);
extern int nfs_init_nfspagecache(void);
+extern void nfs_destroy_nfspagecache(void);
/*
* Initialize NFS
@@ -972,6 +1186,8 @@ init_module(void)
void
cleanup_module(void)
{
+ nfs_destroy_nfspagecache();
+ nfs_destroy_fhcache();
#ifdef CONFIG_PROC_FS
rpc_proc_unregister("nfs");
#endif
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index f74984de2..0adfacd3e 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -30,7 +30,9 @@
#define MOUNT_UMNT 3
*/
-static struct rpc_clnt * mnt_create(char *, struct sockaddr_in *);
+static int nfs_gen_mount(struct sockaddr_in *,
+ char *, struct nfs_fh *, int);
+static struct rpc_clnt * mnt_create(char *, struct sockaddr_in *, int);
extern struct rpc_program mnt_program;
struct mnt_fhstatus {
@@ -44,24 +46,38 @@ struct mnt_fhstatus {
int
nfs_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh)
{
+ return nfs_gen_mount(addr, path, fh, NFS_MNT_VERSION);
+}
+
+int
+nfs3_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh)
+{
+ return nfs_gen_mount(addr, path, fh, NFS_MNT3_VERSION);
+}
+
+static int
+nfs_gen_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh, int version)
+{
struct rpc_clnt *mnt_clnt;
struct mnt_fhstatus result = { 0, fh };
char hostname[32];
int status;
+ int call;
dprintk("NFS: nfs_mount(%08x:%s)\n",
(unsigned)ntohl(addr->sin_addr.s_addr), path);
strcpy(hostname, in_ntoa(addr->sin_addr.s_addr));
- if (!(mnt_clnt = mnt_create(hostname, addr)))
+ if (!(mnt_clnt = mnt_create(hostname, addr, version)))
return -EACCES;
- status = rpc_call(mnt_clnt, NFS_MNTPROC_MNT, path, &result, 0);
+ call = (version == 3) ? MOUNTPROC3_MNT : MNTPROC_MNT;
+ status = rpc_call(mnt_clnt, call, path, &result, 0);
return status < 0? status : (result.status? -EACCES : 0);
}
static struct rpc_clnt *
-mnt_create(char *hostname, struct sockaddr_in *srvaddr)
+mnt_create(char *hostname, struct sockaddr_in *srvaddr, int version)
{
struct rpc_xprt *xprt;
struct rpc_clnt *clnt;
@@ -70,7 +86,7 @@ mnt_create(char *hostname, struct sockaddr_in *srvaddr)
return NULL;
clnt = rpc_create_client(xprt, hostname,
- &mnt_program, NFS_MNT_VERSION,
+ &mnt_program, version,
RPC_AUTH_NULL);
if (!clnt) {
xprt_destroy(xprt);
@@ -104,8 +120,26 @@ xdr_encode_dirpath(struct rpc_rqst *req, u32 *p, const char *path)
static int
xdr_decode_fhstatus(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
{
- if ((res->status = ntohl(*p++)) == 0)
- memcpy(res->fh, p, sizeof(*res->fh));
+ memset((u8 *)res, 0, sizeof(*res));
+ if ((res->status = ntohl(*p++)) == 0) {
+ res->fh->size = NFS2_FHSIZE;
+ memcpy(res->fh->data, p, NFS2_FHSIZE);
+ }
+ return 0;
+}
+
+static int
+xdr_decode_fhstatus3(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
+{
+ memset((u8 *)res, 0, sizeof(*res));
+ if ((res->status = ntohl(*p++)) == 0) {
+ int size = ntohl(*p++);
+ if (size <= NFS3_FHSIZE) {
+ res->fh->size = size;
+ memcpy(res->fh->data, p, res->fh->size);
+ } else
+ res->status = -EBADHANDLE;
+ }
return 0;
}
@@ -122,13 +156,30 @@ static struct rpc_procinfo mnt_procedures[2] = {
MNT_dirpath_sz << 2, 0 },
};
+static struct rpc_procinfo mnt3_procedures[2] = {
+ { "mnt3_null",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+ { "mnt3_mount",
+ (kxdrproc_t) xdr_encode_dirpath,
+ (kxdrproc_t) xdr_decode_fhstatus3,
+ MNT_dirpath_sz << 2, 0 },
+};
+
+
static struct rpc_version mnt_version1 = {
1, 2, mnt_procedures
};
+static struct rpc_version mnt_version3 = {
+ 3, 2, mnt3_procedures
+};
+
static struct rpc_version * mnt_version[] = {
NULL,
&mnt_version1,
+ NULL,
+ &mnt_version3,
};
static struct rpc_stat mnt_stats;
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index 5ad2aaa67..1dd1553ba 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -1,14 +1,14 @@
/*
- * linux/fs/nfs/xdr.c
+ * linux/fs/nfs/nfs2xdr.c
*
* XDR functions to encode/decode NFS RPC arguments and results.
*
* Copyright (C) 1992, 1993, 1994 Rick Sladkey
* Copyright (C) 1996 Olaf Kirch
+ * 04 Aug 1998 Ion Badulescu <ionut@cs.columbia.edu>
+ * FIFO's need special handling in NFSv2
*/
-#define NFS_NEED_XDR_TYPES
-
#include <linux/param.h>
#include <linux/sched.h>
#include <linux/mm.h>
@@ -20,6 +20,8 @@
#include <linux/pagemap.h>
#include <linux/proc_fs.h>
#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
#include <linux/nfs_fs.h>
/* Uncomment this to support servers requiring longword lengths */
@@ -28,8 +30,7 @@
#define NFSDBG_FACILITY NFSDBG_XDR
/* #define NFS_PARANOIA 1 */
-#define QUADLEN(len) (((len) + 3) >> 2)
-static int nfs_stat_to_errno(int stat);
+extern int nfs_stat_to_errno(int stat);
/* Mapping from NFS error code to "errno" error code. */
#define errno_NFSERR_IO EIO
@@ -40,8 +41,8 @@ static int nfs_stat_to_errno(int stat);
*/
#define NFS_fhandle_sz 8
#define NFS_sattr_sz 8
-#define NFS_filename_sz 1+(NFS_MAXNAMLEN>>2)
-#define NFS_path_sz 1+(NFS_MAXPATHLEN>>2)
+#define NFS_filename_sz 1+(NFS2_MAXNAMLEN>>2)
+#define NFS_path_sz 1+(NFS2_MAXPATHLEN>>2)
#define NFS_fattr_sz 17
#define NFS_info_sz 5
#define NFS_entry_sz NFS_filename_sz+3
@@ -49,6 +50,7 @@ static int nfs_stat_to_errno(int stat);
#define NFS_enc_void_sz 0
#define NFS_diropargs_sz NFS_fhandle_sz+NFS_filename_sz
#define NFS_sattrargs_sz NFS_fhandle_sz+NFS_sattr_sz
+#define NFS_readlinkargs_sz NFS_fhandle_sz
#define NFS_readargs_sz NFS_fhandle_sz+3
#define NFS_writeargs_sz NFS_fhandle_sz+4
#define NFS_createargs_sz NFS_diropargs_sz+NFS_sattr_sz
@@ -56,14 +58,13 @@ static int nfs_stat_to_errno(int stat);
#define NFS_linkargs_sz NFS_fhandle_sz+NFS_diropargs_sz
#define NFS_symlinkargs_sz NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz
#define NFS_readdirargs_sz NFS_fhandle_sz+2
-#define NFS_readlinkargs_sz NFS_fhandle_sz
#define NFS_dec_void_sz 0
#define NFS_attrstat_sz 1+NFS_fattr_sz
#define NFS_diropres_sz 1+NFS_fhandle_sz+NFS_fattr_sz
#define NFS_readlinkres_sz 1
#define NFS_readres_sz 1+NFS_fattr_sz+1
-#define NFS_writeres_sz NFS_attrstat_sz
+#define NFS_writeres_sz NFS_attrstat_sz
#define NFS_stat_sz 1
#define NFS_readdirres_sz 1
#define NFS_statfsres_sz 1+NFS_info_sz
@@ -74,15 +75,19 @@ static int nfs_stat_to_errno(int stat);
static inline u32 *
xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle)
{
- *((struct nfs_fh *) p) = *fhandle;
- return p + QUADLEN(sizeof(*fhandle));
+ memcpy(p, fhandle->data, NFS2_FHSIZE);
+ return p + XDR_QUADLEN(NFS2_FHSIZE);
}
static inline u32 *
xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle)
{
- *fhandle = *((struct nfs_fh *) p);
- return p + QUADLEN(sizeof(*fhandle));
+ /* Zero handle first to allow comparisons */
+ memset(fhandle, 0, sizeof(*fhandle));
+ /* NFSv2 handles have a fixed length */
+ fhandle->size = NFS2_FHSIZE;
+ memcpy(fhandle->data, p, NFS2_FHSIZE);
+ return p + XDR_QUADLEN(NFS2_FHSIZE);
}
static inline u32 *
@@ -93,7 +98,14 @@ xdr_decode_string2(u32 *p, char **string, unsigned int *len,
if (*len > maxlen)
return NULL;
*string = (char *) p;
- return p + QUADLEN(*len);
+ return p + XDR_QUADLEN(*len);
+}
+
+static inline u32*
+xdr_decode_time(u32 *p, u64 *timep)
+{
+ *timep = ((u64)ntohl(*p++) << 32) + (u64)ntohl(*p++);
+ return p;
}
static inline u32 *
@@ -105,21 +117,23 @@ xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
fattr->uid = ntohl(*p++);
fattr->gid = ntohl(*p++);
fattr->size = ntohl(*p++);
- fattr->blocksize = ntohl(*p++);
+ fattr->du.nfs2.blocksize = ntohl(*p++);
fattr->rdev = ntohl(*p++);
- fattr->blocks = ntohl(*p++);
+ fattr->du.nfs2.blocks = ntohl(*p++);
fattr->fsid = ntohl(*p++);
fattr->fileid = ntohl(*p++);
- fattr->atime.seconds = ntohl(*p++);
- fattr->atime.useconds = ntohl(*p++);
- fattr->mtime.seconds = ntohl(*p++);
- fattr->mtime.useconds = ntohl(*p++);
- fattr->ctime.seconds = ntohl(*p++);
- fattr->ctime.useconds = ntohl(*p++);
+ p = xdr_decode_time(p, &fattr->atime);
+ p = xdr_decode_time(p, &fattr->mtime);
+ p = xdr_decode_time(p, &fattr->ctime);
+ fattr->valid |= NFS_ATTR_FATTR;
+ if (fattr->type == NFCHR && fattr->rdev == NFS2_FIFO_DEV) {
+ fattr->type = NFFIFO;
+ fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
+ fattr->rdev = 0;
+ }
return p;
}
-
#define SATTR(p, attr, flag, field) \
*p++ = (attr->ia_valid & flag) ? htonl(attr->field) : ~(u32) 0
static inline u32 *
@@ -194,7 +208,7 @@ static int
nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args)
{
p = xdr_encode_fhandle(p, args->fh);
- p = xdr_encode_string(p, args->name);
+ p = xdr_encode_array(p, args->name, args->len);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
@@ -208,7 +222,8 @@ static int
nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
{
struct rpc_auth *auth = req->rq_task->tk_auth;
- int replen, buflen;
+ int buflen, replen;
+ unsigned int nr;
p = xdr_encode_fhandle(p, args->fh);
*p++ = htonl(args->offset);
@@ -216,16 +231,25 @@ nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
*p++ = htonl(args->count);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ /* Get the number of buffers in the receive iovec */
+ nr = args->nriov;
+
+ if (nr+2 > MAX_IOVEC) {
+ printk(KERN_ERR "NFS: Bad number of iov's in xdr_readargs\n");
+ return -EINVAL;
+ }
+
/* set up reply iovec */
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
buflen = req->rq_rvec[0].iov_len;
req->rq_rvec[0].iov_len = replen;
- req->rq_rvec[1].iov_base = args->buffer;
- req->rq_rvec[1].iov_len = args->count;
- req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
- req->rq_rvec[2].iov_len = buflen - replen;
+ /* Copy the iovec */
+ memcpy(req->rq_rvec + 1, args->iov, nr * sizeof(struct iovec));
+
+ req->rq_rvec[nr+1].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
+ req->rq_rvec[nr+1].iov_len = buflen - replen;
req->rq_rlen = args->count + buflen;
- req->rq_rnr = 3;
+ req->rq_rnr += nr+1;
return 0;
}
@@ -239,7 +263,6 @@ nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
struct iovec *iov = req->rq_rvec;
int status, count, recvd, hdrlen;
- dprintk("RPC: readres OK status %lx\n", (long)ntohl(*p));
if ((status = ntohl(*p++)))
return -nfs_stat_to_errno(status);
p = xdr_decode_fattr(p, res->fattr);
@@ -247,22 +270,26 @@ nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
count = ntohl(*p++);
hdrlen = (u8 *) p - (u8 *) iov->iov_base;
recvd = req->rq_rlen - hdrlen;
- if (p != iov[2].iov_base) {
+ if (p != iov[req->rq_rnr-1].iov_base) {
/* Unexpected reply header size. Punt.
* XXX: Move iovec contents to align data on page
* boundary and adjust RPC header size guess */
- printk("NFS: Odd RPC header size in read reply: %d\n", hdrlen);
+ printk(KERN_WARNING "NFS: Odd RPC header size in read reply: %d\n", hdrlen);
return -errno_NFSERR_IO;
}
if (count > recvd) {
- printk("NFS: server cheating in read reply: "
+ printk(KERN_WARNING "NFS: server cheating in read reply: "
"count %d > recvd %d\n", count, recvd);
count = recvd;
}
dprintk("RPC: readres OK count %d\n", count);
- if (count < res->count)
- memset((u8 *)(iov[1].iov_base+count), 0, res->count-count);
+ if (count < res->count) {
+ xdr_zero_iovec(iov+1, req->rq_rnr-2, res->count - count);
+ res->count = count;
+ res->eof = 1; /* Silly NFSv3ism which can't be helped */
+ } else
+ res->eof = 0;
return count;
}
@@ -288,13 +315,13 @@ nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
nr = args->nriov;
if (nr+2 > MAX_IOVEC) {
- printk(KERN_ERR "NFS: Bad number of iov's in xdr_writeargs "
- "(nr %d max %d)\n", nr, MAX_IOVEC);
- return -EINVAL;
- }
+ printk(KERN_ERR "NFS: Bad number of iov's in xdr_writeargs "
+ "(nr %d max %d)\n", nr, MAX_IOVEC);
+ return -EINVAL;
+ }
/* Copy the iovec */
- memcpy(req->rq_svec + 1, args->iov, nr * sizeof(struct iovec));
+ memcpy(req->rq_svec + 1, args->iov, nr * sizeof(struct iovec));
#ifdef NFS_PAD_WRITES
/*
@@ -325,7 +352,7 @@ static int
nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args)
{
p = xdr_encode_fhandle(p, args->fh);
- p = xdr_encode_string(p, args->name);
+ p = xdr_encode_array(p, args->name, args->len);
p = xdr_encode_sattr(p, args->sattr);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
@@ -338,9 +365,9 @@ static int
nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args)
{
p = xdr_encode_fhandle(p, args->fromfh);
- p = xdr_encode_string(p, args->fromname);
+ p = xdr_encode_array(p, args->fromname, args->fromlen);
p = xdr_encode_fhandle(p, args->tofh);
- p = xdr_encode_string(p, args->toname);
+ p = xdr_encode_array(p, args->toname, args->tolen);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
@@ -353,7 +380,7 @@ nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args)
{
p = xdr_encode_fhandle(p, args->fromfh);
p = xdr_encode_fhandle(p, args->tofh);
- p = xdr_encode_string(p, args->toname);
+ p = xdr_encode_array(p, args->toname, args->tolen);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
@@ -365,8 +392,8 @@ static int
nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
{
p = xdr_encode_fhandle(p, args->fromfh);
- p = xdr_encode_string(p, args->fromname);
- p = xdr_encode_string(p, args->topath);
+ p = xdr_encode_array(p, args->fromname, args->fromlen);
+ p = xdr_encode_array(p, args->topath, args->tolen);
p = xdr_encode_sattr(p, args->sattr);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
@@ -380,108 +407,113 @@ nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
{
struct rpc_task *task = req->rq_task;
struct rpc_auth *auth = task->tk_auth;
- int bufsiz = args->bufsiz;
- int replen;
-
- p = xdr_encode_fhandle(p, args->fh);
- *p++ = htonl(args->cookie);
+ u32 bufsiz = args->bufsiz;
+ int buflen, replen;
- /* Some servers (e.g. HP OS 9.5) seem to expect the buffer size
+ /*
+ * Some servers (e.g. HP OS 9.5) seem to expect the buffer size
* to be in longwords ... check whether to convert the size.
*/
if (task->tk_client->cl_flags & NFS_CLNTF_BUFSIZE)
- *p++ = htonl(bufsiz >> 2);
- else
- *p++ = htonl(bufsiz);
+ bufsiz = bufsiz >> 2;
+ p = xdr_encode_fhandle(p, args->fh);
+ *p++ = htonl(args->cookie);
+ *p++ = htonl(bufsiz); /* see above */
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
/* set up reply iovec */
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
+ buflen = req->rq_rvec[0].iov_len;
req->rq_rvec[0].iov_len = replen;
req->rq_rvec[1].iov_base = args->buffer;
- req->rq_rvec[1].iov_len = bufsiz;
- req->rq_rlen = replen + bufsiz;
- req->rq_rnr = 2;
+ req->rq_rvec[1].iov_len = args->bufsiz;
+ req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
+ req->rq_rvec[2].iov_len = buflen - replen;
+ req->rq_rlen = buflen + args->bufsiz;
+ req->rq_rnr += 2;
return 0;
}
/*
* Decode the result of a readdir call.
+ * We're not really decoding anymore, we just leave the buffer untouched
+ * and only check that it is syntactically correct.
+ * The real decoding happens in nfs_decode_entry below, called directly
+ * from nfs_readdir for each entry.
*/
-#define NFS_DIRENT_MAXLEN (5 * sizeof(u32) + (NFS_MAXNAMLEN + 1))
static int
nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
{
struct iovec *iov = req->rq_rvec;
int status, nr;
- u32 *end;
- u32 last_cookie = res->cookie;
+ u32 *end, *entry, len;
- status = ntohl(*p++);
- if (status) {
- nr = -nfs_stat_to_errno(status);
- goto error;
- }
+ if ((status = ntohl(*p++)))
+ return -nfs_stat_to_errno(status);
if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
/* Unexpected reply header size. Punt. */
- printk("NFS: Odd RPC header size in readdirres reply\n");
- nr = -errno_NFSERR_IO;
- goto error;
+ printk(KERN_WARNING "NFS: Odd RPC header size in readdirres reply\n");
+ return -errno_NFSERR_IO;
}
- /* Get start and end address of XDR readdir response. */
+ /* Get start and end address of XDR data */
p = (u32 *) iov[1].iov_base;
end = (u32 *) ((u8 *) p + iov[1].iov_len);
- for (nr = 0; *p++; nr++) {
- __u32 len;
-
- /* Convert fileid. */
- *p = ntohl(*p);
- p++;
- /* Convert and capture len */
- len = *p = ntohl(*p);
- p++;
-
- if ((p + QUADLEN(len) + 3) > end) {
- struct rpc_clnt *clnt = req->rq_task->tk_client;
+ /* Get start and end of dirent buffer */
+ if (res->buffer != p) {
+ printk(KERN_ERR "NFS: Bad result buffer in readdir\n");
+ return -errno_NFSERR_IO;
+ }
- clnt->cl_flags |= NFS_CLNTF_BUFSIZE;
- p -= 2;
- p[-1] = 0;
- p[0] = 0;
- break;
+ for (nr = 0; *p++; nr++) {
+ entry = p - 1;
+ p++; /* fileid */
+ len = ntohl(*p++);
+ p += XDR_QUADLEN(len) + 1; /* name plus cookie */
+ if (len > NFS2_MAXNAMLEN) {
+ printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)!\n",
+ len);
+ return -errno_NFSERR_IO;
}
- if (len > NFS_MAXNAMLEN) {
- nr = -errno_NFSERR_IO;
- goto error;
+ if (p + 2 > end) {
+ printk(KERN_NOTICE
+ "NFS: short packet in readdir reply!\n");
+ entry[0] = entry[1] = 0;
+ break;
}
- p += QUADLEN(len);
-
- /* Convert and capture cookie. */
- last_cookie = *p = ntohl(*p);
- p++;
}
- p -= 1;
- status = ((end - p) << 2);
- if (!p[1] && (status >= NFS_DIRENT_MAXLEN)) {
- status = ((__u8 *)p - (__u8 *)iov[1].iov_base);
- res->buffer += status;
- res->bufsiz -= status;
- } else if (p[1]) {
- status = (int)((long)p & ~PAGE_CACHE_MASK);
- res->bufsiz = -status;
- } else {
- res->bufsiz = 0;
+ p++; /* EOF flag */
+
+ if (p > end) {
+ printk(KERN_NOTICE
+ "NFS: short packet in readdir reply!\n");
+ return -errno_NFSERR_IO;
}
- res->cookie = last_cookie;
return nr;
+}
-error:
- res->bufsiz = 0;
- return nr;
+u32 *
+nfs_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
+{
+ if (!*p++) {
+ if (!*p)
+ return ERR_PTR(-EAGAIN);
+ entry->eof = 1;
+ return ERR_PTR(-EBADCOOKIE);
+ }
+
+ entry->ino = ntohl(*p++);
+ entry->len = ntohl(*p++);
+ entry->name = (const char *) p;
+ p += XDR_QUADLEN(entry->len);
+ entry->prev_cookie = entry->cookie;
+ entry->cookie = ntohl(*p++);
+ entry->eof = !p[0] && p[1];
+
+ return p;
}
/*
@@ -518,12 +550,9 @@ nfs_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
{
int status;
- dprintk("RPC: attrstat status %lx\n", (long)ntohl(*p));
if ((status = ntohl(*p++)))
return -nfs_stat_to_errno(status);
xdr_decode_fattr(p, fattr);
- dprintk("RPC: attrstat OK type %d mode %o dev %x ino %x\n",
- fattr->type, fattr->mode, fattr->fsid, fattr->fileid);
return 0;
}
@@ -536,36 +565,34 @@ nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res)
{
int status;
- dprintk("RPC: diropres status %lx\n", (long)ntohl(*p));
if ((status = ntohl(*p++)))
return -nfs_stat_to_errno(status);
p = xdr_decode_fhandle(p, res->fh);
xdr_decode_fattr(p, res->fattr);
- dprintk("RPC: diropres OK type %x mode %o dev %x ino %x\n",
- res->fattr->type, res->fattr->mode,
- res->fattr->fsid, res->fattr->fileid);
return 0;
}
/*
- * Encode arguments to readlink call
+ * Encode READLINK args
*/
-static int nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args)
+static int
+nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args)
{
struct rpc_task *task = req->rq_task;
struct rpc_auth *auth = task->tk_auth;
- int bufsiz = NFS_MAXPATHLEN;
- int replen;
+ int buflen, replen;
p = xdr_encode_fhandle(p, args->fh);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2;
- req->rq_rvec[0].iov_len = replen;
- req->rq_rvec[1].iov_base = (void *) args->buffer;
- req->rq_rvec[1].iov_len = bufsiz;
- req->rq_rlen = replen + bufsiz;
- req->rq_rnr = 2;
-
+ buflen = req->rq_rvec[0].iov_len;
+ req->rq_rvec[0].iov_len = replen;
+ req->rq_rvec[1].iov_base = args->buffer;
+ req->rq_rvec[1].iov_len = args->bufsiz;
+ req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
+ req->rq_rvec[2].iov_len = buflen - replen;
+ req->rq_rlen = buflen + args->bufsiz;
+ req->rq_rnr += 2;
return 0;
}
@@ -573,31 +600,24 @@ static int nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlin
* Decode READLINK reply
*/
static int
-nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, void *dummy)
+nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
{
- struct iovec *iov = req->rq_rvec;
- int status, len;
- char *name;
+ u32 *strlen;
+ char *string;
+ int status;
+ unsigned int len;
- /* Verify OK status. */
- if ((status = ntohl(*p++)) != 0)
+ if ((status = ntohl(*p++)))
return -nfs_stat_to_errno(status);
-
- /* Verify OK response length. */
- if ((__u8 *)p != ((u8 *) iov->iov_base + iov->iov_len))
- return -errno_NFSERR_IO;
-
- /* Convert and verify that string length is in range. */
- p = iov[1].iov_base;
- len = *p = ntohl(*p);
- p++;
- if (len > iov[1].iov_len)
- return -errno_NFSERR_IO;
-
- /* NULL terminate the string we got. */
- name = (char *) p;
- name[len] = 0;
-
+ strlen = (u32*)res->buffer;
+ /* Convert length of symlink */
+ len = ntohl(*strlen);
+ if (len > res->bufsiz - 5)
+ len = res->bufsiz - 5;
+ *strlen = len;
+ /* NULL terminate the string we got */
+ string = (char *)(strlen + 1);
+ string[len] = 0;
return 0;
}
@@ -618,14 +638,34 @@ static int
nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
{
int status;
+ u32 xfer_size;
if ((status = ntohl(*p++)))
return -nfs_stat_to_errno(status);
- res->tsize = ntohl(*p++);
- res->bsize = ntohl(*p++);
- res->blocks = ntohl(*p++);
- res->bfree = ntohl(*p++);
- res->bavail = ntohl(*p++);
+
+ /* For NFSv2, we more or less have to guess the preferred
+ * read/write/readdir sizes from the single 'transfer size'
+ * value.
+ */
+ xfer_size = ntohl(*p++); /* tsize */
+ res->rtmax = 8 * 1024;
+ res->rtpref = xfer_size;
+ res->rtmult = xfer_size;
+ res->wtmax = 8 * 1024;
+ res->wtpref = xfer_size;
+ res->wtmult = xfer_size;
+ res->dtpref = PAGE_CACHE_SIZE;
+ res->maxfilesize = 0x7FFFFFFF; /* just a guess */
+ res->bsize = ntohl(*p++);
+
+ res->tbytes = ntohl(*p++) * res->bsize;
+ res->fbytes = ntohl(*p++) * res->bsize;
+ res->abytes = ntohl(*p++) * res->bsize;
+ res->tfiles = 0;
+ res->ffiles = 0;
+ res->afiles = 0;
+ res->namelen = 0;
+
return 0;
}
@@ -642,7 +682,7 @@ static struct {
{ NFSERR_NOENT, ENOENT },
{ NFSERR_IO, errno_NFSERR_IO },
{ NFSERR_NXIO, ENXIO },
- { NFSERR_EAGAIN, EAGAIN },
+/* { NFSERR_EAGAIN, EAGAIN }, */
{ NFSERR_ACCES, EACCES },
{ NFSERR_EXIST, EEXIST },
{ NFSERR_XDEV, EXDEV },
@@ -653,18 +693,31 @@ static struct {
{ NFSERR_FBIG, EFBIG },
{ NFSERR_NOSPC, ENOSPC },
{ NFSERR_ROFS, EROFS },
- { NFSERR_OPNOTSUPP, EOPNOTSUPP },
+ { NFSERR_MLINK, EMLINK },
{ NFSERR_NAMETOOLONG, ENAMETOOLONG },
{ NFSERR_NOTEMPTY, ENOTEMPTY },
{ NFSERR_DQUOT, EDQUOT },
{ NFSERR_STALE, ESTALE },
+ { NFSERR_REMOTE, EREMOTE },
#ifdef EWFLUSH
{ NFSERR_WFLUSH, EWFLUSH },
#endif
+ { NFSERR_BADHANDLE, EBADHANDLE },
+ { NFSERR_NOT_SYNC, ENOTSYNC },
+ { NFSERR_BAD_COOKIE, EBADCOOKIE },
+ { NFSERR_NOTSUPP, ENOTSUPP },
+ { NFSERR_TOOSMALL, ETOOSMALL },
+ { NFSERR_SERVERFAULT, ESERVERFAULT },
+ { NFSERR_BADTYPE, EBADTYPE },
+ { NFSERR_JUKEBOX, EJUKEBOX },
{ -1, EIO }
};
-static int
+/*
+ * Convert an NFS error code to a local one.
+ * This one is used jointly by NFSv2 and NFSv3.
+ */
+int
nfs_stat_to_errno(int stat)
{
int i;
@@ -673,7 +726,7 @@ nfs_stat_to_errno(int stat)
if (nfs_errtbl[i].stat == stat)
return nfs_errtbl[i].errno;
}
- printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
+ printk(KERN_ERR "nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
return nfs_errtbl[i].errno;
}
@@ -685,7 +738,8 @@ nfs_stat_to_errno(int stat)
{ "nfs_" #proc, \
(kxdrproc_t) nfs_xdr_##argtype, \
(kxdrproc_t) nfs_xdr_##restype, \
- MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2 \
+ MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2, \
+ 0 \
}
static struct rpc_procinfo nfs_procedures[18] = {
@@ -709,22 +763,8 @@ static struct rpc_procinfo nfs_procedures[18] = {
PROC(statfs, fhandle, statfsres),
};
-static struct rpc_version nfs_version2 = {
+struct rpc_version nfs_version2 = {
2,
sizeof(nfs_procedures)/sizeof(nfs_procedures[0]),
nfs_procedures
};
-
-static struct rpc_version * nfs_version[] = {
- NULL,
- NULL,
- &nfs_version2
-};
-
-struct rpc_program nfs_program = {
- "nfs",
- NFS_PROGRAM,
- sizeof(nfs_version) / sizeof(nfs_version[0]),
- nfs_version,
- &nfs_rpcstat,
-};
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
new file mode 100644
index 000000000..67de662a6
--- /dev/null
+++ b/fs/nfs/nfs3proc.c
@@ -0,0 +1,477 @@
+/*
+ * linux/fs/nfs/nfs3proc.c
+ *
+ * Client-side NFSv3 procedures stubs.
+ *
+ * Copyright (C) 1997, Olaf Kirch
+ */
+
+#include <linux/mm.h>
+#include <linux/utsname.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_fs.h>
+
+#include <asm/segment.h>
+
+#define NFSDBG_FACILITY NFSDBG_PROC
+
+/*
+ * Bare-bones access to getattr: this is for nfs_read_super.
+ */
+static int
+nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ int status;
+
+ dprintk("NFS call getroot\n");
+ fattr->valid = 0;
+ status = rpc_call(server->client, NFS3PROC_GETATTR, fhandle, fattr, 0);
+ dprintk("NFS reply getroot\n");
+ return status;
+}
+
+/*
+ * One function for each procedure in the NFS protocol.
+ */
+static int
+nfs3_proc_getattr(struct dentry *dentry, struct nfs_fattr *fattr)
+{
+ int status;
+
+ dprintk("NFS call getattr\n");
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_GETATTR,
+ NFS_FH(dentry), fattr, 0);
+ dprintk("NFS reply getattr\n");
+ return status;
+}
+
+static int
+nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
+ struct iattr *sattr)
+{
+ struct nfs3_sattrargs arg = { NFS_FH(dentry), sattr, 0, 0 };
+ int status;
+
+ dprintk("NFS call setattr\n");
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_SETATTR, &arg, fattr, 0);
+ dprintk("NFS reply setattr\n");
+ return status;
+}
+
+static int
+nfs3_proc_lookup(struct dentry *dir, struct qstr *name,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_diropargs arg = { NFS_FH(dir), name->name, name->len };
+ struct nfs3_diropres res = { &dir_attr, fhandle, fattr };
+ int status;
+
+ dprintk("NFS call lookup %s\n", name->name);
+ dir_attr.valid = 0;
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_LOOKUP, &arg, &res, 0);
+ if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR))
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_GETATTR,
+ fhandle, fattr, 0);
+ dprintk("NFS reply lookup: %d\n", status);
+ nfs_refresh_inode(dir->d_inode, &dir_attr);
+ return status;
+}
+
+static int
+nfs3_proc_access(struct dentry *dentry, int mode, int ruid)
+{
+ struct nfs_fattr fattr;
+ struct nfs3_accessargs arg = { NFS_FH(dentry), 0 };
+ struct nfs3_accessres res = { &fattr, 0 };
+ int status, flags;
+
+ dprintk("NFS call access\n");
+ fattr.valid = 0;
+
+ if (mode & MAY_READ)
+ arg.access |= NFS3_ACCESS_READ;
+ if (S_ISDIR(dentry->d_inode->i_mode)) {
+ if (mode & MAY_WRITE)
+ arg.access |= NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND | NFS3_ACCESS_DELETE;
+ if (mode & MAY_EXEC)
+ arg.access |= NFS3_ACCESS_LOOKUP;
+ } else {
+ if (mode & MAY_WRITE)
+ arg.access |= NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND;
+ if (mode & MAY_EXEC)
+ arg.access |= NFS3_ACCESS_EXECUTE;
+ }
+ flags = (ruid) ? RPC_CALL_REALUID : 0;
+ status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_ACCESS, &arg, &res, flags);
+ nfs_refresh_inode(dentry->d_inode, &fattr);
+ dprintk("NFS reply access\n");
+
+ if (status == 0 && (arg.access & res.access) != arg.access)
+ status = -EACCES;
+ return status;
+}
+
+static int
+nfs3_proc_readlink(struct dentry *dentry, void *buffer, unsigned int buflen)
+{
+ struct nfs_fattr fattr;
+ struct nfs3_readlinkargs args = { NFS_FH(dentry), buffer, buflen };
+ struct nfs3_readlinkres res = { &fattr, buffer, buflen };
+ int status;
+
+ dprintk("NFS call readlink\n");
+ fattr.valid = 0;
+ status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_READLINK,
+ &args, &res, 0);
+ nfs_refresh_inode(dentry->d_inode, &fattr);
+ dprintk("NFS reply readlink: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_read(struct dentry *dentry, struct nfs_fattr *fattr, int flags,
+ loff_t offset, unsigned int count, void *buffer, int *eofp)
+{
+ struct nfs_readargs arg = { NFS_FH(dentry), offset, count, 1,
+ {{buffer, count}, {0,0}, {0,0}, {0,0},
+ {0,0}, {0,0}, {0,0}, {0,0}} };
+ struct nfs_readres res = { fattr, count, 0 };
+ struct rpc_message msg = { NFS3PROC_READ, &arg, &res, NULL };
+ int status;
+
+ dprintk("NFS call read %d @ %Ld\n", count, (long long)offset);
+ fattr->valid = 0;
+ status = rpc_call_sync(NFS_CLIENT(dentry->d_inode), &msg, flags);
+ dprintk("NFS reply read: %d\n", status);
+ *eofp = res.eof;
+ return status;
+}
+
+static int
+nfs3_proc_write(struct dentry *dentry, struct nfs_fattr *fattr, int flags,
+ loff_t offset, unsigned int count,
+ void *buffer, struct nfs_writeverf *verf)
+{
+ struct nfs_writeargs arg = { NFS_FH(dentry), offset, count,
+ NFS_FILE_SYNC, 1,
+ {{buffer, count}, {0,0}, {0,0}, {0,0},
+ {0,0}, {0,0}, {0,0}, {0,0}} };
+ struct nfs_writeres res = { fattr, verf, 0 };
+ struct rpc_message msg = { NFS3PROC_WRITE, &arg, &res, NULL };
+ int status, rpcflags = 0;
+
+ dprintk("NFS call write %d @ %Ld\n", count, (long long)offset);
+ fattr->valid = 0;
+ if (flags & NFS_RW_SWAP)
+ rpcflags |= NFS_RPC_SWAPFLAGS;
+ arg.stable = (flags & NFS_RW_SYNC) ? NFS_FILE_SYNC : NFS_UNSTABLE;
+
+ status = rpc_call_sync(NFS_CLIENT(dentry->d_inode), &msg, rpcflags);
+
+ dprintk("NFS reply read: %d\n", status);
+ return status < 0? status : res.count;
+}
+
+/*
+ * Create a regular file.
+ * For now, we don't implement O_EXCL.
+ */
+static int
+nfs3_proc_create(struct dentry *dir, struct qstr *name, struct iattr *sattr,
+ int flags, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_createargs arg = { NFS_FH(dir), name->name, name->len,
+ sattr, 0, { 0, 0 } };
+ struct nfs3_diropres res = { &dir_attr, fhandle, fattr };
+ int status;
+
+ dprintk("NFS call create %s\n", name->name);
+ arg.createmode = NFS3_CREATE_UNCHECKED;
+ if (flags & O_EXCL) {
+ arg.createmode = NFS3_CREATE_EXCLUSIVE;
+ arg.verifier[0] = jiffies;
+ arg.verifier[1] = current->pid;
+ }
+
+again:
+ dir_attr.valid = 0;
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_CREATE, &arg, &res, 0);
+ nfs_refresh_inode(dir->d_inode, &dir_attr);
+
+ /* If the server doesn't support the exclusive creation semantics,
+ * try again with simple 'guarded' mode. */
+ if (status == NFSERR_NOTSUPP) {
+ switch (arg.createmode) {
+ case NFS3_CREATE_EXCLUSIVE:
+ arg.createmode = NFS3_CREATE_GUARDED;
+ break;
+
+ case NFS3_CREATE_GUARDED:
+ arg.createmode = NFS3_CREATE_UNCHECKED;
+ break;
+
+ case NFS3_CREATE_UNCHECKED:
+ goto exit;
+ }
+ goto again;
+ }
+
+exit:
+ dprintk("NFS reply create: %d\n", status);
+
+ /* When we created the file with exclusive semantics, make
+ * sure we set the attributes afterwards. */
+ if (status == 0 && arg.createmode == NFS3_CREATE_EXCLUSIVE) {
+ struct nfs3_sattrargs arg = { fhandle, sattr, 0, 0 };
+ dprintk("NFS call setattr (post-create)\n");
+
+ /* Note: we could use a guarded setattr here, but I'm
+ * not sure this buys us anything (and I'd have
+ * to revamp the NFSv3 XDR code) */
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_SETATTR,
+ &arg, fattr, 0);
+ dprintk("NFS reply setattr (post-create): %d\n", status);
+ }
+
+ return status;
+}
+
+static int
+nfs3_proc_remove(struct dentry *dir, struct qstr *name)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_diropargs arg = { NFS_FH(dir), name->name, name->len };
+ struct rpc_message msg = { NFS3PROC_REMOVE, &arg, &dir_attr, NULL };
+ int status;
+
+ dprintk("NFS call remove %s\n", name->name);
+ dir_attr.valid = 0;
+ status = rpc_call_sync(NFS_CLIENT(dir->d_inode), &msg, 0);
+ nfs_refresh_inode(dir->d_inode, &dir_attr);
+ dprintk("NFS reply remove: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_rename(struct dentry *old_dir, struct qstr *old_name,
+ struct dentry *new_dir, struct qstr *new_name)
+{
+ struct nfs_fattr old_dir_attr, new_dir_attr;
+ struct nfs3_renameargs arg = { NFS_FH(old_dir),
+ old_name->name, old_name->len,
+ NFS_FH(new_dir),
+ new_name->name, new_name->len };
+ struct nfs3_renameres res = { &old_dir_attr, &new_dir_attr };
+ int status;
+
+ dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name);
+ old_dir_attr.valid = 0;
+ new_dir_attr.valid = 0;
+ status = rpc_call(NFS_CLIENT(old_dir->d_inode), NFS3PROC_RENAME, &arg, &res, 0);
+ nfs_refresh_inode(old_dir->d_inode, &old_dir_attr);
+ nfs_refresh_inode(new_dir->d_inode, &new_dir_attr);
+ dprintk("NFS reply rename: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_link(struct dentry *dentry, struct dentry *dir, struct qstr *name)
+{
+ struct nfs_fattr dir_attr, fattr;
+ struct nfs3_linkargs arg = { NFS_FH(dentry), NFS_FH(dir),
+ name->name, name->len };
+ struct nfs3_linkres res = { &dir_attr, &fattr };
+ int status;
+
+ dprintk("NFS call link %s\n", name->name);
+ dir_attr.valid = 0;
+ fattr.valid = 0;
+ status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_LINK, &arg, &res, 0);
+ nfs_refresh_inode(dir->d_inode, &dir_attr);
+ nfs_refresh_inode(dentry->d_inode, &fattr);
+ dprintk("NFS reply link: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_symlink(struct dentry *dir, struct qstr *name, struct qstr *path,
+ struct iattr *sattr, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_symlinkargs arg = { NFS_FH(dir), name->name, name->len,
+ path->name, path->len, sattr };
+ struct nfs3_diropres res = { &dir_attr, fhandle, fattr };
+ int status;
+
+ dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
+ dir_attr.valid = 0;
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_SYMLINK, &arg, &res, 0);
+ nfs_refresh_inode(dir->d_inode, &dir_attr);
+ dprintk("NFS reply symlink: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_mkdir(struct dentry *dir, struct qstr *name, struct iattr *sattr,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_createargs arg = { NFS_FH(dir), name->name, name->len,
+ sattr, 0, { 0, 0 } };
+ struct nfs3_diropres res = { &dir_attr, fhandle, fattr };
+ int status;
+
+ dprintk("NFS call mkdir %s\n", name->name);
+ dir_attr.valid = 0;
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_MKDIR, &arg, &res, 0);
+ nfs_refresh_inode(dir->d_inode, &dir_attr);
+ dprintk("NFS reply mkdir: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_rmdir(struct dentry *dir, struct qstr *name)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_diropargs arg = { NFS_FH(dir), name->name, name->len };
+ int status;
+
+ dprintk("NFS call rmdir %s\n", name->name);
+ dir_attr.valid = 0;
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_RMDIR, &arg, &dir_attr, 0);
+ nfs_refresh_inode(dir->d_inode, &dir_attr);
+ dprintk("NFS reply rmdir: %d\n", status);
+ return status;
+}
+
+/*
+ * The READDIR implementation is somewhat hackish - we pass the user buffer
+ * to the encode function, which installs it in the receive iovec.
+ * The decode function itself doesn't perform any decoding, it just makes
+ * sure the reply is syntactically correct.
+ *
+ * Also note that this implementation handles both plain readdir and
+ * readdirplus.
+ */
+static int
+nfs3_proc_readdir(struct dentry *dir, u64 cookie, void *entry,
+ unsigned int size, int plus)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_readdirargs arg = { NFS_FH(dir), cookie, {0, 0}, 0, 0, 0 };
+ struct nfs3_readdirres res = { &dir_attr, 0, 0, 0, 0 };
+ struct rpc_message msg = { NFS3PROC_READDIR, &arg, &res, NULL };
+ u32 *verf = NFS_COOKIEVERF(dir->d_inode);
+ int status;
+
+ arg.buffer = entry;
+ arg.bufsiz = size;
+ arg.verf[0] = verf[0];
+ arg.verf[1] = verf[1];
+ arg.plus = plus;
+ res.buffer = entry;
+ res.bufsiz = size;
+ res.verf = verf;
+ res.plus = plus;
+
+ if (plus)
+ msg.rpc_proc = NFS3PROC_READDIRPLUS;
+
+ dprintk("NFS call readdir%s %d\n",
+ plus? "plus" : "", (unsigned int) cookie);
+
+ dir_attr.valid = 0;
+ status = rpc_call_sync(NFS_CLIENT(dir->d_inode), &msg, 0);
+ nfs_refresh_inode(dir->d_inode, &dir_attr);
+ dprintk("NFS reply readdir: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_mknod(struct dentry *dir, struct qstr *name, struct iattr *sattr,
+ dev_t rdev, struct nfs_fh *fh, struct nfs_fattr *fattr)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_mknodargs arg = { NFS_FH(dir), name->name, name->len, 0,
+ sattr, rdev };
+ struct nfs3_diropres res = { &dir_attr, fh, fattr };
+ int status;
+
+ switch (sattr->ia_mode & S_IFMT) {
+ case S_IFBLK: arg.type = NF3BLK; break;
+ case S_IFCHR: arg.type = NF3CHR; break;
+ case S_IFIFO: arg.type = NF3FIFO; break;
+ case S_IFSOCK: arg.type = NF3SOCK; break;
+ default: return -EINVAL;
+ }
+
+ dprintk("NFS call mknod %s %x\n", name->name, rdev);
+ dir_attr.valid = 0;
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_MKNOD, &arg, &res, 0);
+ nfs_refresh_inode(dir->d_inode, &dir_attr);
+ dprintk("NFS reply mknod: %d\n", status);
+ return status;
+}
+
+/*
+ * This is a combo call of fsstat and fsinfo
+ */
+static int
+nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fsinfo *info)
+{
+ int status;
+
+ dprintk("NFS call fsstat\n");
+ memset((char *)info, 0, sizeof(*info));
+ status = rpc_call(server->client, NFS3PROC_FSSTAT, fhandle, info, 0);
+ if (status < 0)
+ goto error;
+ status = rpc_call(server->client, NFS3PROC_FSINFO, fhandle, info, 0);
+
+error:
+ dprintk("NFS reply statfs: %d\n", status);
+ return status;
+}
+
+extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int);
+
+struct nfs_rpc_ops nfs_v3_clientops = {
+ 3, /* protocol version */
+ nfs3_proc_get_root,
+ nfs3_proc_getattr,
+ nfs3_proc_setattr,
+ nfs3_proc_lookup,
+ nfs3_proc_access,
+ nfs3_proc_readlink,
+ nfs3_proc_read,
+ nfs3_proc_write,
+ NULL, /* commit */
+ nfs3_proc_create,
+ nfs3_proc_remove,
+ nfs3_proc_rename,
+ nfs3_proc_link,
+ nfs3_proc_symlink,
+ nfs3_proc_mkdir,
+ nfs3_proc_rmdir,
+ nfs3_proc_readdir,
+ nfs3_proc_mknod,
+ nfs3_proc_statfs,
+ nfs3_decode_dirent,
+};
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index beed6ec1e..ef8580c02 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -1,94 +1,158 @@
/*
- * linux/fs/nfs/nfs2xdr.c
+ * linux/fs/nfs/nfs3xdr.c
*
* XDR functions to encode/decode NFSv3 RPC arguments and results.
- * Note: this is incomplete!
*
- * Copyright (C) 1996 Olaf Kirch
+ * Copyright (C) 1996, 1997 Olaf Kirch
*/
-#define NFS_NEED_XDR_TYPES
-
#include <linux/param.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/malloc.h>
-#include <linux/nfs_fs.h>
#include <linux/utsname.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/in.h>
#include <linux/pagemap.h>
#include <linux/proc_fs.h>
+#include <linux/kdev_t.h>
#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_fs.h>
-#ifdef RPC_DEBUG
-# define RPC_FACILITY RPCDBG_NFS
-#endif
+/* Uncomment this to support servers requiring longword lengths */
+#define NFS_PAD_WRITES 1
-#define QUADLEN(len) (((len) + 3) >> 2)
-static int nfs_stat_to_errno(int stat);
+#define NFSDBG_FACILITY NFSDBG_XDR
/* Mapping from NFS error code to "errno" error code. */
#define errno_NFSERR_IO EIO
+extern int nfs_stat_to_errno(int);
+
/*
* Declare the space requirements for NFS arguments and replies as
* number of 32bit-words
*/
-#define NFS_fhandle_sz (1+16)
-#define NFS_sattr_sz 8
-#define NFS_filename_sz 1+(NFS_MAXNAMLEN>>2)
-#define NFS_path_sz 1+(NFS_MAXPATHLEN>>2)
-#define NFS_fattr_sz 17
-#define NFS_info_sz 5
-#define NFS_entry_sz NFS_filename_sz+3
-
-#define NFS_enc_void_sz 0
-#define NFS_diropargs_sz NFS_fhandle_sz+NFS_filename_sz
-#define NFS_sattrargs_sz NFS_fhandle_sz+NFS_sattr_sz
-#define NFS_readargs_sz NFS_fhandle_sz+3
-#define NFS_writeargs_sz NFS_fhandle_sz+4
-#define NFS_createargs_sz NFS_diropargs_sz+NFS_sattr_sz
-#define NFS_renameargs_sz NFS_diropargs_sz+NFS_diropargs_sz
-#define NFS_linkargs_sz NFS_fhandle_sz+NFS_diropargs_sz
-#define NFS_symlinkargs_sz NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz
-#define NFS_readdirargs_sz NFS_fhandle_sz+2
-
-#define NFS_dec_void_sz 0
-#define NFS_attrstat_sz 1+NFS_fattr_sz
-#define NFS_diropres_sz 1+NFS_fhandle_sz+NFS_fattr_sz
-#define NFS_readlinkres_sz 1+NFS_path_sz
-#define NFS_readres_sz 1+NFS_fattr_sz+1
-#define NFS_stat_sz 1
-#define NFS_readdirres_sz 1
-#define NFS_statfsres_sz 1+NFS_info_sz
+#define NFS3_fhandle_sz 1+16
+#define NFS3_fh_sz NFS3_fhandle_sz /* shorthand */
+#define NFS3_sattr_sz 15
+#define NFS3_filename_sz 1+(NFS3_MAXNAMLEN>>2)
+#define NFS3_path_sz 1+(NFS3_MAXPATHLEN>>2)
+#define NFS3_fattr_sz 21
+#define NFS3_wcc_attr_sz 6
+#define NFS3_pre_op_attr_sz 1+NFS3_wcc_attr_sz
+#define NFS3_post_op_attr_sz 1+NFS3_fattr_sz
+#define NFS3_wcc_data_sz NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz
+#define NFS3_fsstat_sz
+#define NFS3_fsinfo_sz
+#define NFS3_pathconf_sz
+#define NFS3_entry_sz NFS3_filename_sz+3
+
+#define NFS3_enc_void_sz 0
+#define NFS3_sattrargs_sz NFS3_fh_sz+NFS3_sattr_sz+3
+#define NFS3_diropargs_sz NFS3_fh_sz+NFS3_filename_sz
+#define NFS3_accessargs_sz NFS3_fh_sz+1
+#define NFS3_readlinkargs_sz NFS3_fh_sz
+#define NFS3_readargs_sz NFS3_fh_sz+3
+#define NFS3_writeargs_sz NFS3_fh_sz+5
+#define NFS3_createargs_sz NFS3_diropargs_sz+NFS3_sattr_sz
+#define NFS3_mkdirargs_sz NFS3_diropargs_sz+NFS3_sattr_sz
+#define NFS3_symlinkargs_sz NFS3_diropargs_sz+NFS3_path_sz+NFS3_sattr_sz
+#define NFS3_mknodargs_sz NFS3_diropargs_sz+2+NFS3_sattr_sz
+#define NFS3_renameargs_sz NFS3_diropargs_sz+NFS3_diropargs_sz
+#define NFS3_linkargs_sz NFS3_fh_sz+NFS3_diropargs_sz
+#define NFS3_readdirargs_sz NFS3_fh_sz+2
+#define NFS3_commitargs_sz NFS3_fh_sz+3
+
+#define NFS3_dec_void_sz 0
+#define NFS3_attrstat_sz 1+NFS3_fattr_sz
+#define NFS3_wccstat_sz 1+NFS3_wcc_data_sz
+#define NFS3_lookupres_sz 1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz)
+#define NFS3_accessres_sz 1+NFS3_post_op_attr_sz+1
+#define NFS3_readlinkres_sz 1+NFS3_post_op_attr_sz
+#define NFS3_readres_sz 1+NFS3_post_op_attr_sz+3
+#define NFS3_writeres_sz 1+NFS3_wcc_data_sz+4
+#define NFS3_createres_sz 1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz
+#define NFS3_renameres_sz 1+(2 * NFS3_wcc_data_sz)
+#define NFS3_linkres_sz 1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz
+#define NFS3_readdirres_sz 1+NFS3_post_op_attr_sz+2
+#define NFS3_fsstatres_sz 1+NFS3_post_op_attr_sz+13
+#define NFS3_fsinfores_sz 1+NFS3_post_op_attr_sz+12
+#define NFS3_pathconfres_sz 1+NFS3_post_op_attr_sz+6
+#define NFS3_commitres_sz 1+NFS3_wcc_data_sz+2
+
+/*
+ * Map file type to S_IFMT bits
+ */
+static struct {
+ unsigned int mode;
+ unsigned int nfs2type;
+} nfs_type2fmt[] = {
+ { 0, NFNON },
+ { S_IFREG, NFREG },
+ { S_IFDIR, NFDIR },
+ { S_IFBLK, NFBLK },
+ { S_IFCHR, NFCHR },
+ { S_IFLNK, NFLNK },
+ { S_IFSOCK, NFSOCK },
+ { S_IFIFO, NFFIFO },
+ { 0, NFBAD }
+};
/*
* Common NFS XDR functions as inlines
*/
static inline u32 *
-xdr_encode_fhandle(u32 *p, struct nfs3_fh *fh)
+xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
{
*p++ = htonl(fh->size);
memcpy(p, fh->data, fh->size);
- return p + QUADLEN(fh->size);
+ return p + XDR_QUADLEN(fh->size);
}
static inline u32 *
-xdr_decode_fhandle(u32 *p, struct nfs3_fh *fh)
+xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
{
+ /*
+ * Zero all nonused bytes
+ */
+ memset((u8 *)fh, 0, sizeof(*fh));
if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
memcpy(fh->data, p, fh->size);
- return p + QUADLEN(fh->size);
+ return p + XDR_QUADLEN(fh->size);
}
return NULL;
}
-static inline enum nfs_ftype
-xdr_decode_ftype(u32 type)
+/*
+ * Encode/decode time.
+ * Since the VFS doesn't care for fractional times, we ignore the
+ * nanosecond field.
+ */
+static inline u32 *
+xdr_encode_time(u32 *p, time_t time)
+{
+ *p++ = htonl(time);
+ *p++ = 0;
+ return p;
+}
+
+static inline u32 *
+xdr_decode_time3(u32 *p, u64 *timep)
{
- return (type == NF3FIFO)? NFFIFO : (enum nfs_ftype) type;
+ *timep = ((u64)ntohl(*p++) << 32) + (u64)ntohl(*p++);
+ return p;
+}
+
+static inline u32 *
+xdr_encode_time3(u32 *p, u64 time)
+{
+ *p++ = htonl(time >> 32);
+ *p++ = htonl(time & 0xFFFFFFFF);
+ return p;
}
static inline u32 *
@@ -99,47 +163,119 @@ xdr_decode_string2(u32 *p, char **string, unsigned int *len,
if (*len > maxlen)
return NULL;
*string = (char *) p;
- return p + QUADLEN(*len);
+ return p + XDR_QUADLEN(*len);
}
static inline u32 *
-xdr_decode_fattr(u32 *p, struct nfs3_fattr *fattr)
+xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
{
- fattr->type = xdr_decode_ftype(ntohl(*p++));
- fattr->mode = ntohl(*p++);
+ unsigned int type;
+ int fmode;
+
+ type = ntohl(*p++);
+ if (type >= NF3BAD)
+ type = NF3BAD;
+ fmode = nfs_type2fmt[type].mode;
+ fattr->type = nfs_type2fmt[type].nfs2type;
+ fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
fattr->nlink = ntohl(*p++);
fattr->uid = ntohl(*p++);
fattr->gid = ntohl(*p++);
- fattr->size = ((u64) ntohl(*p++) << 32) | ntohl(*p++);
- fattr->used = ((u64) ntohl(*p++) << 32) | ntohl(*p++);
- fattr->rdev_maj = ntohl(*p++);
- fattr->rdev_min = ntohl(*p++);
- fattr->fsid = ntohl(*p++);
- fattr->fileid = ntohl(*p++);
- fattr->atime.seconds = ntohl(*p++);
- fattr->atime.useconds = ntohl(*p++);
- fattr->mtime.seconds = ntohl(*p++);
- fattr->mtime.useconds = ntohl(*p++);
- fattr->ctime.seconds = ntohl(*p++);
- fattr->ctime.useconds = ntohl(*p++);
+ p = xdr_decode_hyper(p, &fattr->size);
+ p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
+ /* Turn remote device info into Linux-specific dev_t */
+ fattr->rdev = (ntohl(*p++) << MINORBITS) | (ntohl(*p++) & MINORMASK);
+ p = xdr_decode_hyper(p, &fattr->fsid);
+ p = xdr_decode_hyper(p, &fattr->fileid);
+ p = xdr_decode_time3(p, &fattr->atime);
+ p = xdr_decode_time3(p, &fattr->mtime);
+ p = xdr_decode_time3(p, &fattr->ctime);
+
+ /* Update the mode bits */
+ fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
+ return p;
+}
+
+static inline u32 *
+xdr_encode_sattr(u32 *p, struct iattr *attr)
+{
+ if (attr->ia_valid & ATTR_MODE) {
+ *p++ = xdr_one;
+ *p++ = htonl(attr->ia_mode);
+ } else {
+ *p++ = xdr_zero;
+ }
+ if (attr->ia_valid & ATTR_UID) {
+ *p++ = xdr_one;
+ *p++ = htonl(attr->ia_uid);
+ } else {
+ *p++ = xdr_zero;
+ }
+ if (attr->ia_valid & ATTR_GID) {
+ *p++ = xdr_one;
+ *p++ = htonl(attr->ia_gid);
+ } else {
+ *p++ = xdr_zero;
+ }
+ if (attr->ia_valid & ATTR_SIZE) {
+ *p++ = xdr_one;
+ p = xdr_encode_hyper(p, (__u64) attr->ia_size);
+ } else {
+ *p++ = xdr_zero;
+ }
+ if (attr->ia_valid & ATTR_ATIME_SET) {
+ *p++ = xdr_two;
+ p = xdr_encode_time(p, attr->ia_atime);
+ } else if (attr->ia_valid & ATTR_ATIME) {
+ *p++ = xdr_one;
+ } else {
+ *p++ = xdr_zero;
+ }
+ if (attr->ia_valid & ATTR_MTIME_SET) {
+ *p++ = xdr_two;
+ p = xdr_encode_time(p, attr->ia_mtime);
+ } else if (attr->ia_valid & ATTR_MTIME) {
+ *p++ = xdr_one;
+ } else {
+ *p++ = xdr_zero;
+ }
+ return p;
+}
+
+static inline u32 *
+xdr_decode_wcc_attr(u32 *p, struct nfs_fattr *fattr)
+{
+ p = xdr_decode_hyper(p, &fattr->pre_size);
+ p = xdr_decode_time3(p, &fattr->pre_mtime);
+ p = xdr_decode_time3(p, &fattr->pre_ctime);
+ fattr->valid |= NFS_ATTR_WCC;
return p;
}
static inline u32 *
-xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr)
-{
- *p++ = htonl(sattr->mode);
- *p++ = htonl(sattr->uid);
- *p++ = htonl(sattr->gid);
- *p++ = htonl(sattr->size >> 32);
- *p++ = htonl(sattr->size & 0xFFFFFFFF);
- *p++ = htonl(sattr->atime.seconds);
- *p++ = htonl(sattr->atime.useconds);
- *p++ = htonl(sattr->mtime.seconds);
- *p++ = htonl(sattr->mtime.useconds);
+xdr_decode_post_op_attr(u32 *p, struct nfs_fattr *fattr)
+{
+ if (*p++)
+ p = xdr_decode_fattr(p, fattr);
return p;
}
+static inline u32 *
+xdr_decode_pre_op_attr(u32 *p, struct nfs_fattr *fattr)
+{
+ if (*p++)
+ return xdr_decode_wcc_attr(p, fattr);
+ return p;
+}
+
+
+static inline u32 *
+xdr_decode_wcc_data(u32 *p, struct nfs_fattr *fattr)
+{
+ p = xdr_decode_pre_op_attr(p, fattr);
+ return xdr_decode_post_op_attr(p, fattr);
+}
+
/*
* NFS encode functions
*/
@@ -147,7 +283,7 @@ xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr)
* Encode void argument
*/
static int
-nfs_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
+nfs3_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
{
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
@@ -155,10 +291,9 @@ nfs_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
/*
* Encode file handle argument
- * GETATTR, READLINK, STATFS
*/
static int
-nfs_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs3_fh *fh)
+nfs3_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs_fh *fh)
{
p = xdr_encode_fhandle(p, fh);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
@@ -169,23 +304,37 @@ nfs_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs3_fh *fh)
* Encode SETATTR arguments
*/
static int
-nfs_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs_sattrargs *args)
+nfs3_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs3_sattrargs *args)
{
p = xdr_encode_fhandle(p, args->fh);
p = xdr_encode_sattr(p, args->sattr);
+ *p++ = htonl(args->guard);
+ if (args->guard)
+ p = xdr_encode_time3(p, args->guardtime);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
/*
* Encode directory ops argument
- * LOOKUP, REMOVE, RMDIR
*/
static int
-nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args)
+nfs3_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs3_diropargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_array(p, args->name, args->len);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode access() argument
+ */
+static int
+nfs3_xdr_accessargs(struct rpc_rqst *req, u32 *p, struct nfs3_accessargs *args)
{
p = xdr_encode_fhandle(p, args->fh);
- p = xdr_encode_string(p, args->name);
+ *p++ = htonl(args->access);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
@@ -193,149 +342,181 @@ nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args)
/*
* Arguments to a READ call. Since we read data directly into the page
* cache, we also set up the reply iovec here so that iov[1] points
- * exactly to the page wewant to fetch.
+ * exactly to the page we want to fetch.
*/
static int
-nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
+nfs3_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
{
struct rpc_auth *auth = req->rq_task->tk_auth;
- int replen, buflen;
+ int buflen, replen;
+ unsigned int nr;
p = xdr_encode_fhandle(p, args->fh);
- *p++ = htonl(args->offset);
- *p++ = htonl(args->count);
+ p = xdr_encode_hyper(p, args->offset);
*p++ = htonl(args->count);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-#if 1
+ /* Get the number of buffers in the receive iovec */
+ nr = args->nriov;
+
+ if (nr+2 > MAX_IOVEC) {
+ printk(KERN_ERR "NFS: Bad number of iov's in xdr_readargs\n");
+ return -EINVAL;
+ }
+
/* set up reply iovec */
- replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
+ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
buflen = req->rq_rvec[0].iov_len;
req->rq_rvec[0].iov_len = replen;
- req->rq_rvec[1].iov_base = args->buffer;
- req->rq_rvec[1].iov_len = args->count;
- req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
- req->rq_rvec[2].iov_len = buflen - replen;
+
+ /* Copy the iovec */
+ memcpy(req->rq_rvec + 1, args->iov, nr * sizeof(struct iovec));
+
+ req->rq_rvec[nr+1].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
+ req->rq_rvec[nr+1].iov_len = buflen - replen;
req->rq_rlen = args->count + buflen;
- req->rq_rnr = 3;
-#else
- replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
- req->rq_rvec[0].iov_len = replen;
-#endif
+ req->rq_rnr += nr+1;
return 0;
}
/*
- * Decode READ reply
+ * Write arguments. Splice the buffer to be written into the iovec.
*/
static int
-nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
+nfs3_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
{
- struct iovec *iov = req->rq_rvec;
- int status, count, recvd, hdrlen;
+ unsigned int nr;
+ u32 count = args->count;
- dprintk("RPC: readres OK status %lx\n", ntohl(*p));
- if ((status = ntohl(*p++)))
- return -nfs_stat_to_errno(status);
- p = xdr_decode_fattr(p, res->fattr);
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_hyper(p, args->offset);
+ *p++ = htonl(count);
+ *p++ = htonl(args->stable);
+ *p++ = htonl(count);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
- count = ntohl(*p++);
- hdrlen = (u8 *) p - (u8 *) iov->iov_base;
- recvd = req->rq_rlen - hdrlen;
- if (p != iov[2].iov_base) {
- /* Unexpected reply header size. Punt.
- * XXX: Move iovec contents to align data on page
- * boundary and adjust RPC header size guess */
- printk("NFS: Odd RPC header size in read reply: %d\n", hdrlen);
- return -errno_NFSERR_IO;
- }
- if (count > recvd) {
- printk("NFS: server cheating in read reply: "
- "count %d > recvd %d\n", count, recvd);
- count = recvd;
+ /* Get the number of buffers in the send iovec */
+ nr = args->nriov;
+
+ if (nr+2 > MAX_IOVEC) {
+ printk(KERN_ERR "NFS: Bad number of iov's in xdr_writeargs\n");
+ return -EINVAL;
}
- dprintk("RPC: readres OK count %d\n", count);
- if (count < res->count)
- memset((u8 *)(iov[1].iov_base+count), 0, res->count-count);
+ /* Copy the iovec */
+ memcpy(req->rq_svec + 1, args->iov, nr * sizeof(struct iovec));
- return count;
-}
+#ifdef NFS_PAD_WRITES
+ /*
+ * Some old servers require that the message length
+ * be a multiple of 4, so we pad it here if needed.
+ */
+ if (count & 3) {
+ struct iovec *iov = req->rq_svec + nr + 1;
+ int pad = 4 - (count & 3);
+
+ iov->iov_base = (void *) "\0\0\0";
+ iov->iov_len = pad;
+ count += pad;
+ nr++;
+ }
+#endif
+ req->rq_slen += count;
+ req->rq_snr += nr;
+ return 0;
+}
/*
- * Write arguments. Splice the buffer to be written into the iovec.
+ * Encode CREATE arguments
*/
static int
-nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+nfs3_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs3_createargs *args)
{
p = xdr_encode_fhandle(p, args->fh);
- *p++ = htonl(args->offset);
- *p++ = htonl(args->offset);
- *p++ = htonl(args->count);
- *p++ = htonl(args->count);
- req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ p = xdr_encode_array(p, args->name, args->len);
- req->rq_svec[1].iov_base = (void *) args->buffer;
- req->rq_svec[1].iov_len = args->count;
- req->rq_slen += args->count;
- req->rq_snr = 2;
+ *p++ = htonl(args->createmode);
+ if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
+ *p++ = args->verifier[0];
+ *p++ = args->verifier[1];
+ } else
+ p = xdr_encode_sattr(p, args->sattr);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
/*
- * Encode create arguments
- * CREATE, MKDIR
+ * Encode MKDIR arguments
*/
static int
-nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args)
+nfs3_xdr_mkdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_mkdirargs *args)
{
p = xdr_encode_fhandle(p, args->fh);
- p = xdr_encode_string(p, args->name);
+ p = xdr_encode_array(p, args->name, args->len);
p = xdr_encode_sattr(p, args->sattr);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
/*
- * Encode RENAME arguments
+ * Encode SYMLINK arguments
*/
static int
-nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args)
+nfs3_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_symlinkargs *args)
{
p = xdr_encode_fhandle(p, args->fromfh);
- p = xdr_encode_string(p, args->fromname);
- p = xdr_encode_fhandle(p, args->tofh);
- p = xdr_encode_string(p, args->toname);
+ p = xdr_encode_array(p, args->fromname, args->fromlen);
+ p = xdr_encode_sattr(p, args->sattr);
+ p = xdr_encode_array(p, args->topath, args->tolen);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
/*
- * Encode LINK arguments
+ * Encode MKNOD arguments
+ */
+static int
+nfs3_xdr_mknodargs(struct rpc_rqst *req, u32 *p, struct nfs3_mknodargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_array(p, args->name, args->len);
+ *p++ = htonl(args->type);
+ p = xdr_encode_sattr(p, args->sattr);
+ if (args->type == NF3CHR || args->type == NF3BLK) {
+ *p++ = htonl(args->rdev >> MINORBITS);
+ *p++ = htonl(args->rdev & MINORMASK);
+ }
+
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode RENAME arguments
*/
static int
-nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args)
+nfs3_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs3_renameargs *args)
{
p = xdr_encode_fhandle(p, args->fromfh);
+ p = xdr_encode_array(p, args->fromname, args->fromlen);
p = xdr_encode_fhandle(p, args->tofh);
- p = xdr_encode_string(p, args->toname);
+ p = xdr_encode_array(p, args->toname, args->tolen);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
/*
- * Encode SYMLINK arguments
+ * Encode LINK arguments
*/
static int
-nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
+nfs3_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs3_linkargs *args)
{
p = xdr_encode_fhandle(p, args->fromfh);
- p = xdr_encode_string(p, args->fromname);
- p = xdr_encode_string(p, args->topath);
- p = xdr_encode_sattr(p, args->sattr);
+ p = xdr_encode_fhandle(p, args->tofh);
+ p = xdr_encode_array(p, args->toname, args->tolen);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
@@ -344,131 +525,164 @@ nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
* Encode arguments to readdir call
*/
static int
-nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
+nfs3_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_readdirargs *args)
{
struct rpc_auth *auth = req->rq_task->tk_auth;
- int replen;
+ int buflen, replen;
p = xdr_encode_fhandle(p, args->fh);
- *p++ = htonl(args->cookie);
+ p = xdr_encode_hyper(p, args->cookie);
+ *p++ = args->verf[0];
+ *p++ = args->verf[1];
+ if (args->plus) {
+ /* readdirplus: need dircount + buffer size.
+ * We just make sure we make dircount big enough */
+ *p++ = htonl(args->bufsiz >> 3);
+ }
*p++ = htonl(args->bufsiz);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
/* set up reply iovec */
- replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
- /*
- dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n",
- RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen);
- */
+ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
+ buflen = req->rq_rvec[0].iov_len;
req->rq_rvec[0].iov_len = replen;
req->rq_rvec[1].iov_base = args->buffer;
req->rq_rvec[1].iov_len = args->bufsiz;
- req->rq_rlen = replen + args->bufsiz;
- req->rq_rnr = 2;
-
- /*
- dprintk("RPC: readdirargs set up reply vec:\n");
- dprintk(" rvec[0] = %p/%d\n",
- req->rq_rvec[0].iov_base,
- req->rq_rvec[0].iov_len);
- dprintk(" rvec[1] = %p/%d\n",
- req->rq_rvec[1].iov_base,
- req->rq_rvec[1].iov_len);
- */
+ req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
+ req->rq_rvec[2].iov_len = buflen - replen;
+ req->rq_rlen = buflen + args->bufsiz;
+ req->rq_rnr += 2;
return 0;
}
/*
- * Decode the result of a readdir call. We decode the result in place
- * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
- * After decoding, the layout in memory looks like this:
- * entry1 entry2 ... entryN <space> stringN ... string2 string1
- * Each entry consists of three __u32 values, the same space as NFS uses.
- * Note that the strings are not null-terminated so that the entire number
- * of entries returned by the server should fit into the buffer.
+ * Decode the result of a readdir call.
+ * We just check for syntactical correctness.
*/
static int
-nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
+nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
{
- struct iovec *iov = req->rq_rvec;
- int status, nr, len;
- char *string, *start;
- u32 *end;
- __u32 fileid, cookie, *entry;
-
- if ((status = ntohl(*p++)))
+ struct iovec *iov = req->rq_rvec;
+ int hdrlen;
+ int status, nr;
+ unsigned int len;
+ u32 *entry, *end;
+
+ status = ntohl(*p++);
+ /* Decode post_op_attrs */
+ p = xdr_decode_post_op_attr(p, res->dir_attr);
+ if (status)
return -nfs_stat_to_errno(status);
- if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
- /* Unexpected reply header size. Punt. */
- printk("NFS: Odd RPC header size in readdirres reply\n");
- return -errno_NFSERR_IO;
+ /* Decode verifier cookie */
+ if (res->verf) {
+ res->verf[0] = *p++;
+ res->verf[1] = *p++;
+ } else {
+ p += 2;
}
- p = (u32 *) iov[1].iov_base;
- end = (u32 *) ((u8 *) p + iov[1].iov_len);
-
- if (p != res->buffer) {
- printk("NFS: p != res->buffer in %s:%d!!!\n",
- __FILE__, __LINE__);
- return -errno_NFSERR_IO;
+ hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+ if (iov->iov_len > hdrlen) {
+ dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
+ xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
}
- entry = (__u32 *) res->buffer;
- start = (char *) res->buffer;
- string = start + res->bufsiz;
+ p = (u32 *) iov[1].iov_base;
+ end = (u32 *) ((u8 *) p + iov[1].iov_len);
for (nr = 0; *p++; nr++) {
- fileid = ntohl(*p++);
-
- len = ntohl(*p++);
- if ((p + QUADLEN(len) + 3) > end) {
- printk(KERN_NOTICE
- "NFS: short packet in readdir reply!\n");
- break;
- }
- if (len > NFS_MAXNAMLEN) {
- printk("NFS: giant filename in readdir (len %x)!\n",
+ entry = p - 1;
+ p += 2; /* inode # */
+ len = ntohl(*p++); /* string length */
+ p += XDR_QUADLEN(len) + 2; /* name + cookie */
+ if (len > NFS3_MAXNAMLEN) {
+ printk(KERN_WARNING "NFS: giant filename in readdir (len %x)!\n",
len);
return -errno_NFSERR_IO;
}
- string -= len;
- if ((void *) (entry+3) > (void *) string) {
- /*
- * This error is impossible as long as the temp
- * buffer is no larger than the user buffer. The
- * current packing algorithm uses the same amount
- * of space in the user buffer as in the XDR data,
- * so it's guaranteed to fit.
- */
- printk("NFS: incorrect buffer size in %s!\n",
- __FUNCTION__);
- break;
+
+ if (res->plus) {
+ /* post_op_attr */
+ if (*p++)
+ p += 21;
+ /* post_op_fh3 */
+ if (*p++) {
+ len = ntohl(*p++);
+ if (len > NFS3_FHSIZE) {
+ printk(KERN_WARNING "NFS: giant filehandle in "
+ "readdir (len %x)!\n", len);
+ return -errno_NFSERR_IO;
+ }
+ p += XDR_QUADLEN(len);
+ }
}
- memmove(string, p, len);
- p += QUADLEN(len);
- cookie = ntohl(*p++);
- /*
- * To make everything fit, we encode the length, offset,
- * and eof flag into 32 bits. This works for filenames
- * up to 32K and PAGE_SIZE up to 64K.
- */
- status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
- *entry++ = fileid;
- *entry++ = cookie;
- *entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
- /*
- dprintk("NFS: decoded dirent %.*s cookie %d eof %d\n",
- len, string, cookie, status);
- */
+ if (p + 2 > end) {
+ printk(KERN_NOTICE
+ "NFS: short packet in readdir reply!\n");
+ /* truncate listing */
+ entry[0] = entry[1] = 0;
+ break;
+ }
}
-#ifdef NFS_PARANOIA
-printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
-nr, ((char *) entry - start), (start + res->bufsiz - string));
-#endif
+
return nr;
}
+u32 *
+nfs3_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
+{
+ struct nfs_entry old = *entry;
+
+ if (!*p++) {
+ if (!*p)
+ return ERR_PTR(-EAGAIN);
+ entry->eof = 1;
+ return ERR_PTR(-EBADCOOKIE);
+ }
+
+ p = xdr_decode_hyper(p, &entry->ino);
+ entry->len = ntohl(*p++);
+ entry->name = (const char *) p;
+ p += XDR_QUADLEN(entry->len);
+ entry->prev_cookie = entry->cookie;
+ p = xdr_decode_hyper(p, &entry->cookie);
+
+ if (plus) {
+ p = xdr_decode_post_op_attr(p, &entry->fattr);
+ /* In fact, a post_op_fh3: */
+ if (*p++) {
+ p = xdr_decode_fhandle(p, &entry->fh);
+ /* Ugh -- server reply was truncated */
+ if (p == NULL) {
+ dprintk("NFS: FH truncated\n");
+ *entry = old;
+ return ERR_PTR(-EAGAIN);
+ }
+ } else {
+ /* If we don't get a file handle, the attrs
+ * aren't worth a lot. */
+ entry->fattr.valid = 0;
+ }
+ }
+
+ entry->eof = !p[0] && p[1];
+ return p;
+}
+
+/*
+ * Encode COMMIT arguments
+ */
+static int
+nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_hyper(p, args->offset);
+ *p++ = htonl(args->count);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
/*
* NFS XDR decode functions
*/
@@ -476,59 +690,92 @@ nr, ((char *) entry - start), (start + res->bufsiz - string));
* Decode void reply
*/
static int
-nfs_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
+nfs3_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
{
return 0;
}
/*
- * Decode simple status reply
+ * Decode attrstat reply.
*/
static int
-nfs_xdr_stat(struct rpc_rqst *req, u32 *p, void *dummy)
+nfs3_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
{
int status;
- if ((status = ntohl(*p++)) != 0)
- status = -nfs_stat_to_errno(status);
- return status;
+ if ((status = ntohl(*p++)))
+ return -nfs_stat_to_errno(status);
+ xdr_decode_fattr(p, fattr);
+ return 0;
}
/*
- * Decode attrstat reply
- * GETATTR, SETATTR, WRITE
+ * Decode status+wcc_data reply
+ * SATTR, REMOVE, RMDIR
*/
static int
-nfs_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+nfs3_xdr_wccstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
{
int status;
- dprintk("RPC: attrstat status %lx\n", ntohl(*p));
if ((status = ntohl(*p++)))
- return -nfs_stat_to_errno(status);
- xdr_decode_fattr(p, fattr);
- dprintk("RPC: attrstat OK type %d mode %o dev %x ino %x\n",
- fattr->type, fattr->mode, fattr->fsid, fattr->fileid);
- return 0;
+ status = -nfs_stat_to_errno(status);
+ xdr_decode_wcc_data(p, fattr);
+ return status;
}
/*
- * Decode diropres reply
- * LOOKUP, CREATE, MKDIR
+ * Decode LOOKUP reply
*/
static int
-nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res)
+nfs3_xdr_lookupres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
{
int status;
- dprintk("RPC: diropres status %lx\n", ntohl(*p));
- if ((status = ntohl(*p++)))
+ if ((status = ntohl(*p++))) {
+ status = -nfs_stat_to_errno(status);
+ } else {
+ if (!(p = xdr_decode_fhandle(p, res->fh)))
+ return -errno_NFSERR_IO;
+ p = xdr_decode_post_op_attr(p, res->fattr);
+ }
+ xdr_decode_post_op_attr(p, res->dir_attr);
+ return status;
+}
+
+/*
+ * Decode ACCESS reply
+ */
+static int
+nfs3_xdr_accessres(struct rpc_rqst *req, u32 *p, struct nfs3_accessres *res)
+{
+ int status = ntohl(*p++);
+
+ p = xdr_decode_post_op_attr(p, res->fattr);
+ if (status)
return -nfs_stat_to_errno(status);
- p = xdr_decode_fhandle(p, res->fh);
- xdr_decode_fattr(p, res->fattr);
- dprintk("RPC: diropres OK type %x mode %o dev %x ino %x\n",
- res->fattr->type, res->fattr->mode,
- res->fattr->fsid, res->fattr->fileid);
+ res->access = ntohl(*p++);
+ return 0;
+}
+
+static int
+nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *args)
+{
+ struct rpc_task *task = req->rq_task;
+ struct rpc_auth *auth = task->tk_auth;
+ int buflen, replen;
+
+ p = xdr_encode_fhandle(p, args->fh);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
+ buflen = req->rq_rvec[0].iov_len;
+ req->rq_rvec[0].iov_len = replen;
+ req->rq_rvec[1].iov_base = args->buffer;
+ req->rq_rvec[1].iov_len = args->bufsiz;
+ req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
+ req->rq_rvec[2].iov_len = buflen - replen;
+ req->rq_rlen = buflen + args->bufsiz;
+ req->rq_rnr += 2;
return 0;
}
@@ -536,155 +783,299 @@ nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res)
* Decode READLINK reply
*/
static int
-nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
+nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkres *res)
{
+ struct iovec *iov = req->rq_rvec;
+ int hdrlen;
+ u32 *strlen;
+ char *string;
int status;
+ unsigned int len;
- if ((status = ntohl(*p++)))
+ status = ntohl(*p++);
+ p = xdr_decode_post_op_attr(p, res->fattr);
+
+ if (status != 0)
return -nfs_stat_to_errno(status);
- xdr_decode_string2(p, res->string, res->lenp, res->maxlen);
- /* Caller takes over the buffer here to avoid extra copy */
- res->buffer = req->rq_task->tk_buffer;
- req->rq_task->tk_buffer = NULL;
+ hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+ if (iov->iov_len > hdrlen) {
+ dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
+ xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+ }
+
+ strlen = (u32*)res->buffer;
+ /* Convert length of symlink */
+ len = ntohl(*strlen);
+ if (len > res->bufsiz - 5)
+ len = res->bufsiz - 5;
+ *strlen = len;
+ /* NULL terminate the string we got */
+ string = (char *)(strlen + 1);
+ string[len] = 0;
return 0;
}
/*
- * Decode STATFS reply
+ * Decode READ reply
*/
static int
-nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
{
- int status;
+ struct iovec *iov = req->rq_rvec;
+ int status, count, ocount, recvd, hdrlen;
- if ((status = ntohl(*p++)))
+ status = ntohl(*p++);
+ p = xdr_decode_post_op_attr(p, res->fattr);
+
+ if (status != 0)
return -nfs_stat_to_errno(status);
- res->tsize = ntohl(*p++);
- res->bsize = ntohl(*p++);
- res->blocks = ntohl(*p++);
- res->bfree = ntohl(*p++);
- res->bavail = ntohl(*p++);
- return 0;
+
+ /* Decode reply could and EOF flag. NFSv3 is somewhat redundant
+ * in that it puts the count both in the res struct and in the
+ * opaque data count. */
+ count = ntohl(*p++);
+ res->eof = ntohl(*p++);
+ ocount = ntohl(*p++);
+
+ if (ocount != count) {
+ printk(KERN_WARNING "NFS: READ count doesn't match RPC opaque count.\n");
+ return -errno_NFSERR_IO;
+ }
+
+ hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+ if (iov->iov_len > hdrlen) {
+ dprintk("NFS: READ header is short. iovec will be shifted.\n");
+ xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+ }
+
+ recvd = req->rq_rlen - hdrlen;
+ if (count > recvd) {
+ printk(KERN_WARNING "NFS: server cheating in read reply: "
+ "count %d > recvd %d\n", count, recvd);
+ count = recvd;
+ }
+
+ if (count < res->count) {
+ xdr_zero_iovec(iov+1, req->rq_rnr-2, res->count - count);
+ res->count = count;
+ }
+
+ return count;
}
/*
- * We need to translate between nfs status return values and
- * the local errno values which may not be the same.
+ * Decode WRITE response
*/
-static struct {
- int stat;
- int errno;
-} nfs_errtbl[] = {
- { NFS_OK, 0 },
- { NFSERR_PERM, EPERM },
- { NFSERR_NOENT, ENOENT },
- { NFSERR_IO, errno_NFSERR_IO },
- { NFSERR_NXIO, ENXIO },
- { NFSERR_EAGAIN, EAGAIN },
- { NFSERR_ACCES, EACCES },
- { NFSERR_EXIST, EEXIST },
- { NFSERR_XDEV, EXDEV },
- { NFSERR_NODEV, ENODEV },
- { NFSERR_NOTDIR, ENOTDIR },
- { NFSERR_ISDIR, EISDIR },
- { NFSERR_INVAL, EINVAL },
- { NFSERR_FBIG, EFBIG },
- { NFSERR_NOSPC, ENOSPC },
- { NFSERR_ROFS, EROFS },
- { NFSERR_NAMETOOLONG, ENAMETOOLONG },
- { NFSERR_NOTEMPTY, ENOTEMPTY },
- { NFSERR_DQUOT, EDQUOT },
- { NFSERR_STALE, ESTALE },
-#ifdef EWFLUSH
- { NFSERR_WFLUSH, EWFLUSH },
-#endif
- { -1, EIO }
-};
+static int
+nfs3_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+{
+ int status;
+
+ status = ntohl(*p++);
+ p = xdr_decode_wcc_data(p, res->fattr);
+ if (status != 0)
+ return -nfs_stat_to_errno(status);
+
+ res->count = ntohl(*p++);
+ res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
+ res->verf->verifier[0] = *p++;
+ res->verf->verifier[1] = *p++;
+
+ return res->count;
+}
+
+/*
+ * Decode a CREATE response
+ */
static int
-nfs_stat_to_errno(int stat)
+nfs3_xdr_createres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
{
- int i;
+ int status;
- for (i = 0; nfs_errtbl[i].stat != -1; i++) {
- if (nfs_errtbl[i].stat == stat)
- return nfs_errtbl[i].errno;
+ status = ntohl(*p++);
+ if (status == 0) {
+ if (*p++) {
+ if (!(p = xdr_decode_fhandle(p, res->fh)))
+ return -errno_NFSERR_IO;
+ p = xdr_decode_post_op_attr(p, res->fattr);
+ } else {
+ memset(res->fh, 0, sizeof(*res->fh));
+ /* Do decode post_op_attr but set it to NULL */
+ p = xdr_decode_post_op_attr(p, res->fattr);
+ res->fattr->valid = 0;
+ }
+ } else {
+ status = -nfs_stat_to_errno(status);
}
- printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
- return nfs_errtbl[i].errno;
+ p = xdr_decode_wcc_data(p, res->dir_attr);
+ return status;
}
-#ifndef MAX
-# define MAX(a, b) (((a) > (b))? (a) : (b))
-#endif
+/*
+ * Decode RENAME reply
+ */
+static int
+nfs3_xdr_renameres(struct rpc_rqst *req, u32 *p, struct nfs3_renameres *res)
+{
+ int status;
-#define PROC(proc, argtype, restype) \
- { "nfs_" #proc, \
- (kxdrproc_t) nfs_xdr_##argtype, \
- (kxdrproc_t) nfs_xdr_##restype, \
- MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2 \
- }
+ if ((status = ntohl(*p++)) != 0)
+ status = -nfs_stat_to_errno(status);
+ p = xdr_decode_wcc_data(p, res->fromattr);
+ p = xdr_decode_wcc_data(p, res->toattr);
+ return status;
+}
-static struct rpc_procinfo nfs_procedures[18] = {
- PROC(null, enc_void, dec_void),
- PROC(getattr, fhandle, attrstat),
- PROC(setattr, sattrargs, attrstat),
- PROC(root, enc_void, dec_void),
- PROC(lookup, diropargs, diropres),
- PROC(readlink, fhandle, readlinkres),
- PROC(read, readargs, readres),
- PROC(writecache, enc_void, dec_void),
- PROC(write, writeargs, attrstat),
- PROC(create, createargs, diropres),
- PROC(remove, diropargs, stat),
- PROC(rename, renameargs, stat),
- PROC(link, linkargs, stat),
- PROC(symlink, symlinkargs, stat),
- PROC(mkdir, createargs, diropres),
- PROC(rmdir, diropargs, stat),
- PROC(readdir, readdirargs, readdirres),
- PROC(statfs, fhandle, statfsres),
-};
+/*
+ * Decode LINK reply
+ */
+static int
+nfs3_xdr_linkres(struct rpc_rqst *req, u32 *p, struct nfs3_linkres *res)
+{
+ int status;
-static struct rpc_version nfs_version2 = {
- 2,
- sizeof(nfs_procedures)/sizeof(nfs_procedures[0]),
- nfs_procedures
-};
+ if ((status = ntohl(*p++)) != 0)
+ status = -nfs_stat_to_errno(status);
+ p = xdr_decode_post_op_attr(p, res->fattr);
+ p = xdr_decode_wcc_data(p, res->dir_attr);
+ return status;
+}
-static struct rpc_version * nfs_version[] = {
- NULL,
- NULL,
- &nfs_version2
-};
+/*
+ * Decode FSSTAT reply
+ */
+static int
+nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+ struct nfs_fattr dummy;
+ int status;
-struct rpc_program nfs_program = {
- "nfs",
- NFS_PROGRAM,
- sizeof(nfs_version) / sizeof(nfs_version[0]),
- nfs_version,
- &nfs_rpcstat,
-};
+ status = ntohl(*p++);
+
+ p = xdr_decode_post_op_attr(p, &dummy);
+ if (status != 0)
+ return -nfs_stat_to_errno(status);
+
+ p = xdr_decode_hyper(p, &res->tbytes);
+ p = xdr_decode_hyper(p, &res->fbytes);
+ p = xdr_decode_hyper(p, &res->abytes);
+ p = xdr_decode_hyper(p, &res->tfiles);
+ p = xdr_decode_hyper(p, &res->ffiles);
+ p = xdr_decode_hyper(p, &res->afiles);
+
+ /* ignore invarsec */
+ return 0;
+}
+
+/*
+ * Decode FSINFO reply
+ */
+static int
+nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+ struct nfs_fattr dummy;
+ int status;
+
+ status = ntohl(*p++);
+
+ p = xdr_decode_post_op_attr(p, &dummy);
+ if (status != 0)
+ return -nfs_stat_to_errno(status);
+
+ res->rtmax = ntohl(*p++);
+ res->rtpref = ntohl(*p++);
+ res->rtmult = ntohl(*p++);
+ res->wtmax = ntohl(*p++);
+ res->wtpref = ntohl(*p++);
+ res->wtmult = ntohl(*p++);
+ res->dtpref = ntohl(*p++);
+ p = xdr_decode_hyper(p, &res->maxfilesize);
+
+ /* ignore time_delta and properties */
+ return 0;
+}
+
+/*
+ * Decode PATHCONF reply
+ */
+static int
+nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+ struct nfs_fattr dummy;
+ int status;
+
+ status = ntohl(*p++);
+
+ p = xdr_decode_post_op_attr(p, &dummy);
+ if (status != 0)
+ return -nfs_stat_to_errno(status);
+ res->linkmax = ntohl(*p++);
+ res->namelen = ntohl(*p++);
+
+ /* ignore remaining fields */
+ return 0;
+}
/*
- * RPC stats support
+ * Decode COMMIT reply
*/
static int
-nfs_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
{
- return rpcstat_get_info(&nfs_rpcstat, buffer, start, offset, length);
+ int status;
+
+ status = ntohl(*p++);
+ p = xdr_decode_wcc_data(p, res->fattr);
+ if (status != 0)
+ return -nfs_stat_to_errno(status);
+
+ res->verf->verifier[0] = *p++;
+ res->verf->verifier[1] = *p++;
+ return 0;
}
-static struct proc_dir_entry proc_nfsclnt = {
- 0, 3, "nfs",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 6, NULL,
- nfs_get_info
+#ifndef MAX
+# define MAX(a, b) (((a) > (b))? (a) : (b))
+#endif
+
+#define PROC(proc, argtype, restype) \
+ { "nfs3_" #proc, \
+ (kxdrproc_t) nfs3_xdr_##argtype, \
+ (kxdrproc_t) nfs3_xdr_##restype, \
+ MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2, \
+ 0 \
+ }
+
+static struct rpc_procinfo nfs3_procedures[22] = {
+ PROC(null, enc_void, dec_void),
+ PROC(getattr, fhandle, attrstat),
+ PROC(setattr, sattrargs, wccstat),
+ PROC(lookup, diropargs, lookupres),
+ PROC(access, accessargs, accessres),
+ PROC(readlink, readlinkargs, readlinkres),
+ PROC(read, readargs, readres),
+ PROC(write, writeargs, writeres),
+ PROC(create, createargs, createres),
+ PROC(mkdir, mkdirargs, createres),
+ PROC(symlink, symlinkargs, createres),
+ PROC(mknod, mknodargs, createres),
+ PROC(remove, diropargs, wccstat),
+ PROC(rmdir, diropargs, wccstat),
+ PROC(rename, renameargs, renameres),
+ PROC(link, linkargs, linkres),
+ PROC(readdir, readdirargs, readdirres),
+ PROC(readdirplus, readdirargs, readdirres),
+ PROC(fsstat, fhandle, fsstatres),
+ PROC(fsinfo, fhandle, fsinfores),
+ PROC(pathconf, fhandle, pathconfres),
+ PROC(commit, commitargs, commitres),
};
-struct rpc_stat nfs_rpcstat = {
- NULL, /* next */
- &proc_nfsclnt, /* /proc/net directory entry */
- &nfs_program, /* RPC program */
+struct rpc_version nfs_version3 = {
+ 3,
+ sizeof(nfs3_procedures)/sizeof(nfs3_procedures[0]),
+ nfs3_procedures
};
+
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
index a592608be..b632cf175 100644
--- a/fs/nfs/nfsroot.c
+++ b/fs/nfs/nfsroot.c
@@ -61,8 +61,12 @@
* Martin Mares : Use root_server_addr appropriately during setup.
* Martin Mares : Rewrote parameter parsing, now hopefully giving
* correct overriding.
+ * Trond Myklebust : Add in preliminary support for NFSv3 and TCP.
+ * Fix bug in root_nfs_addr(). nfs_data.namlen
+ * is NOT for the length of the hostname.
*/
+#include <linux/config.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
@@ -147,6 +151,12 @@ static struct nfs_bool_opts {
{ "noac", ~NFS_MOUNT_NOAC, NFS_MOUNT_NOAC },
{ "lock", ~NFS_MOUNT_NONLM, 0 },
{ "nolock", ~NFS_MOUNT_NONLM, NFS_MOUNT_NONLM },
+#ifdef CONFIG_NFS_V3
+ { "v2", ~NFS_MOUNT_VER3, 0 },
+ { "v3", ~NFS_MOUNT_VER3, NFS_MOUNT_VER3 },
+#endif
+ { "udp", ~NFS_MOUNT_TCP, 0 },
+ { "tcp", ~NFS_MOUNT_TCP, NFS_MOUNT_TCP },
{ NULL, 0, 0 }
};
@@ -271,7 +281,6 @@ static int __init root_nfs_addr(void)
}
strncpy(nfs_data.hostname, in_ntoa(servaddr), sizeof(nfs_data.hostname)-1);
- nfs_data.namlen = strlen(nfs_data.hostname);
return 0;
}
@@ -360,14 +369,14 @@ set_sockaddr(struct sockaddr_in *sin, __u32 addr, __u16 port)
/*
* Query server portmapper for the port of a daemon program.
*/
-static int __init root_nfs_getport(int program, int version)
+static int __init root_nfs_getport(int program, int version, int proto)
{
struct sockaddr_in sin;
printk(KERN_NOTICE "Looking up port of RPC %d/%d on %s\n",
program, version, in_ntoa(servaddr));
set_sockaddr(&sin, servaddr, 0);
- return rpc_getport_external(&sin, program, version, IPPROTO_UDP);
+ return rpc_getport_external(&sin, program, version, proto);
}
@@ -379,22 +388,39 @@ static int __init root_nfs_getport(int program, int version)
static int __init root_nfs_ports(void)
{
int port;
+ int nfsd_ver, mountd_ver;
+ int nfsd_port, mountd_port;
+ int proto;
+
+ if (nfs_data.flags & NFS_MOUNT_VER3) {
+ nfsd_ver = NFS3_VERSION;
+ mountd_ver = NFS_MNT3_VERSION;
+ nfsd_port = NFS_PORT;
+ mountd_port = NFS_MNT_PORT;
+ } else {
+ nfsd_ver = NFS2_VERSION;
+ mountd_ver = NFS_MNT_VERSION;
+ nfsd_port = NFS_PORT;
+ mountd_port = NFS_MNT_PORT;
+ }
+
+ proto = (nfs_data.flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;
if (nfs_port < 0) {
- if ((port = root_nfs_getport(NFS_PROGRAM, NFS_VERSION)) < 0) {
+ if ((port = root_nfs_getport(NFS_PROGRAM, nfsd_ver, proto)) < 0) {
printk(KERN_ERR "Root-NFS: Unable to get nfsd port "
"number from server, using default\n");
- port = NFS_PORT;
+ port = nfsd_port;
}
nfs_port = htons(port);
dprintk("Root-NFS: Portmapper on server returned %d "
"as nfsd port\n", port);
}
- if ((port = root_nfs_getport(NFS_MNT_PROGRAM, NFS_MNT_VERSION)) < 0) {
+ if ((port = root_nfs_getport(NFS_MNT_PROGRAM, nfsd_ver, proto)) < 0) {
printk(KERN_ERR "Root-NFS: Unable to get mountd port "
"number from server, using default\n");
- port = NFS_MNT_PORT;
+ port = mountd_port;
}
mount_port = htons(port);
dprintk("Root-NFS: mountd port is %d\n", port);
@@ -413,7 +439,10 @@ static int __init root_nfs_get_handle(void)
int status;
set_sockaddr(&sin, servaddr, mount_port);
- status = nfs_mount(&sin, nfs_path, &nfs_data.root);
+ if (nfs_data.flags & NFS_MOUNT_VER3)
+ status = nfs3_mount(&sin, nfs_path, &nfs_data.root);
+ else
+ status = nfs_mount(&sin, nfs_path, &nfs_data.root);
if (status < 0)
printk(KERN_ERR "Root-NFS: Server returned error %d "
"while mounting %s\n", status, nfs_path);
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 3823c3118..d6d532217 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -13,10 +13,6 @@
* Note: Error returns are optimized for NFS_OK, which isn't translated via
* nfs_stat_to_errno(), but happens to be already the right return code.
*
- * FixMe: We ought to define a sensible small max size for
- * things like getattr that are tiny packets and use the
- * old get_free_page stuff with it.
- *
* Also, the code currently doesn't check the size of the packet, when
* it decodes the packet.
*
@@ -25,209 +21,357 @@
* Completely rewritten to support the new RPC call interface;
* rewrote and moved the entire XDR stuff to xdr.c
* --Olaf Kirch June 1996
+ *
+ * The code below initializes all auto variables explicitly, otherwise
+ * it will fail to work as a module (gcc generates a memset call for an
+ * incomplete struct).
*/
-#define NFS_NEED_XDR_TYPES
-
+#include <linux/types.h>
#include <linux/param.h>
+#include <linux/malloc.h>
#include <linux/sched.h>
#include <linux/mm.h>
-#include <linux/malloc.h>
#include <linux/utsname.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/in.h>
#include <linux/pagemap.h>
#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
#include <linux/nfs_fs.h>
#include <asm/segment.h>
-#ifdef NFS_DEBUG
-# define NFSDBG_FACILITY NFSDBG_PROC
-#endif
+#define NFSDBG_FACILITY NFSDBG_PROC
+
+/*
+ * Bare-bones access to getattr: this is for nfs_read_super.
+ */
+static int
+nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ int status;
+ dprintk("NFS call getroot\n");
+ fattr->valid = 0;
+ status = rpc_call(server->client, NFSPROC_GETATTR, fhandle, fattr, 0);
+ dprintk("NFS reply getroot\n");
+ return status;
+}
/*
* One function for each procedure in the NFS protocol.
*/
-int
-nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr)
+static int
+nfs_proc_getattr(struct dentry *dentry, struct nfs_fattr *fattr)
{
int status;
dprintk("NFS call getattr\n");
- status = rpc_call(server->client, NFSPROC_GETATTR, fhandle, fattr, 0);
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dentry->d_inode), NFSPROC_GETATTR,
+ NFS_FH(dentry), fattr, 0);
dprintk("NFS reply getattr\n");
return status;
}
-int
-nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr, struct iattr *sattr)
+static int
+nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
+ struct iattr *sattr)
{
- struct nfs_sattrargs arg = { fhandle, sattr };
+ struct nfs_sattrargs arg = { NFS_FH(dentry), sattr };
int status;
dprintk("NFS call setattr\n");
- status = rpc_call(server->client, NFSPROC_SETATTR, &arg, fattr, 0);
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dentry->d_inode), NFSPROC_SETATTR, &arg, fattr, 0);
dprintk("NFS reply setattr\n");
return status;
}
-int
-nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name,
- struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int
+nfs_proc_lookup(struct dentry *dir, struct qstr *name,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
- struct nfs_diropargs arg = { dir, name };
+ struct nfs_diropargs arg = { NFS_FH(dir), name->name, name->len };
struct nfs_diropok res = { fhandle, fattr };
int status;
- dprintk("NFS call lookup %s\n", name);
- status = rpc_call(server->client, NFSPROC_LOOKUP, &arg, &res, 0);
+ dprintk("NFS call lookup %s\n", name->name);
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_LOOKUP, &arg, &res, 0);
dprintk("NFS reply lookup: %d\n", status);
return status;
}
-int
-nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle, int swap,
- unsigned long offset, unsigned int count,
- void *buffer, struct nfs_fattr *fattr)
+static int
+nfs_proc_readlink(struct dentry *dentry, void *buffer, unsigned int bufsiz)
{
- struct nfs_readargs arg = { fhandle, offset, count, buffer };
- struct nfs_readres res = { fattr, count };
+ struct nfs_readlinkargs args = { NFS_FH(dentry), buffer, bufsiz };
+ struct nfs_readlinkres res = { buffer, bufsiz };
int status;
- dprintk("NFS call read %d @ %ld\n", count, offset);
- status = rpc_call(server->client, NFSPROC_READ, &arg, &res,
- swap? NFS_RPC_SWAPFLAGS : 0);
- dprintk("NFS reply read: %d\n", status);
+ dprintk("NFS call readlink\n");
+ status = rpc_call(NFS_CLIENT(dentry->d_inode), NFSPROC_READLINK,
+ &args, &res, 0);
+ dprintk("NFS reply readlink: %d\n", status);
return status;
}
-int
-nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle, int swap,
- unsigned long offset, unsigned int count,
- const void *buffer, struct nfs_fattr *fattr)
+static int
+nfs_proc_read(struct dentry *dentry, struct nfs_fattr *fattr, int flags,
+ loff_t offset, unsigned int count, void *buffer, int *eofp)
{
- struct nfs_writeargs arg = { fhandle, offset, count, 1, 1,
- {{(void *) buffer, count}, {0,0}, {0,0}, {0,0},
- {0,0}, {0,0}, {0,0}, {0,0}}};
- struct nfs_writeverf verf;
- struct nfs_writeres res = {fattr, &verf, count};
+ struct nfs_readargs arg = { NFS_FH(dentry), offset, count, 1,
+ {{ buffer, count }, {0,0}, {0,0}, {0,0},
+ {0,0}, {0,0}, {0,0}, {0,0}} };
+ struct nfs_readres res = { fattr, count, 0};
+ struct rpc_message msg = { NFSPROC_READ, &arg, &res, NULL };
int status;
- dprintk("NFS call write %d @ %ld\n", count, offset);
- status = rpc_call(server->client, NFSPROC_WRITE, &arg, &res,
- swap? (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS) : 0);
+ dprintk("NFS call read %d @ %Ld\n", count, (long long)offset);
+ fattr->valid = 0;
+ status = rpc_call_sync(NFS_CLIENT(dentry->d_inode), &msg, flags);
+
dprintk("NFS reply read: %d\n", status);
+ *eofp = res.eof;
+ return status;
+}
+
+static int
+nfs_proc_write(struct dentry *dentry, struct nfs_fattr *fattr, int how,
+ loff_t offset, unsigned int count,
+ void *buffer, struct nfs_writeverf *verf)
+{
+ struct nfs_writeargs arg = {NFS_FH(dentry), offset, count,
+ NFS_FILE_SYNC, 1,
+ {{buffer, count}, {0,0}, {0,0}, {0,0},
+ {0,0}, {0,0}, {0,0}, {0,0}}};
+ struct nfs_writeres res = {fattr, verf, count};
+ struct rpc_message msg = { NFSPROC_WRITE, &arg, &res, NULL };
+ int status, flags = 0;
+
+ dprintk("NFS call write %d @ %Ld\n", count, (long long)offset);
+ fattr->valid = 0;
+ if (how & NFS_RW_SWAP)
+ flags |= NFS_RPC_SWAPFLAGS;
+ status = rpc_call_sync(NFS_CLIENT(dentry->d_inode), &msg, flags);
+
+ dprintk("NFS reply write: %d\n", status);
+ verf->committed = NFS_FILE_SYNC; /* NFSv2 always syncs data */
return status < 0? status : count;
}
-int
-nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
- const char *name, struct iattr *sattr,
- struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int
+nfs_proc_create(struct dentry *dir, struct qstr *name, struct iattr *sattr,
+ int flags, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
- struct nfs_createargs arg = { dir, name, sattr };
+ struct nfs_createargs arg = { NFS_FH(dir), name->name,
+ name->len, sattr };
struct nfs_diropok res = { fhandle, fattr };
int status;
- dprintk("NFS call create %s\n", name);
- status = rpc_call(server->client, NFSPROC_CREATE, &arg, &res, 0);
+ fattr->valid = 0;
+ dprintk("NFS call create %s\n", name->name);
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_CREATE, &arg, &res, 0);
dprintk("NFS reply create: %d\n", status);
return status;
}
-int
-nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *name)
+/*
+ * In NFSv2, mknod is grafted onto the create call.
+ */
+static int
+nfs_proc_mknod(struct dentry *dir, struct qstr *name, struct iattr *sattr,
+ dev_t rdev, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
- struct nfs_diropargs arg = { dir, name };
+ struct nfs_createargs arg = { NFS_FH(dir), name->name,
+ name->len, sattr };
+ struct nfs_diropok res = { fhandle, fattr };
+ int status, mode;
+
+ dprintk("NFS call mknod %s\n", name->name);
+
+ mode = sattr->ia_mode;
+ if (S_ISFIFO(mode)) {
+ sattr->ia_mode = (mode & ~S_IFMT) | S_IFCHR;
+ sattr->ia_valid &= ~ATTR_SIZE;
+ } else if (S_ISCHR(rdev) || S_ISBLK(rdev)) {
+ sattr->ia_valid |= ATTR_SIZE;
+ sattr->ia_size = rdev; /* get out your barf bag */
+ }
+
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_CREATE, &arg, &res, 0);
+
+ if (status == -EINVAL && S_ISFIFO(mode)) {
+ sattr->ia_mode = mode;
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_CREATE, &arg, &res, 0);
+ }
+ dprintk("NFS reply mknod: %d\n", status);
+ return status;
+}
+
+static int
+nfs_proc_remove(struct dentry *dir, struct qstr *name)
+{
+ struct nfs_diropargs arg = { NFS_FH(dir), name->name, name->len };
+ struct rpc_message msg = { NFSPROC_REMOVE, &arg, NULL, NULL };
int status;
- dprintk("NFS call remove %s\n", name);
- status = rpc_call(server->client, NFSPROC_REMOVE, &arg, NULL, 0);
+ dprintk("NFS call remove %s\n", name->name);
+ status = rpc_call_sync(NFS_CLIENT(dir->d_inode), &msg, 0);
+
dprintk("NFS reply remove: %d\n", status);
return status;
}
-int
-nfs_proc_rename(struct nfs_server *server,
- struct nfs_fh *old_dir, const char *old_name,
- struct nfs_fh *new_dir, const char *new_name)
+static int
+nfs_proc_rename(struct dentry *old_dir, struct qstr *old_name,
+ struct dentry *new_dir, struct qstr *new_name)
{
- struct nfs_renameargs arg = { old_dir, old_name, new_dir, new_name };
+ struct nfs_renameargs arg = { NFS_FH(old_dir), old_name->name,
+ old_name->len,
+ NFS_FH(new_dir), new_name->name,
+ new_name->len};
int status;
- dprintk("NFS call rename %s -> %s\n", old_name, new_name);
- status = rpc_call(server->client, NFSPROC_RENAME, &arg, NULL, 0);
+ dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name);
+ status = rpc_call(NFS_CLIENT(old_dir->d_inode), NFSPROC_RENAME, &arg, NULL, 0);
dprintk("NFS reply rename: %d\n", status);
return status;
}
-int
-nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
- struct nfs_fh *dir, const char *name)
+static int
+nfs_proc_link(struct dentry *dentry, struct dentry *dir, struct qstr *name)
{
- struct nfs_linkargs arg = { fhandle, dir, name };
+ struct nfs_linkargs arg = { NFS_FH(dentry), NFS_FH(dir),
+ name->name, name->len };
int status;
- dprintk("NFS call link %s\n", name);
- status = rpc_call(server->client, NFSPROC_LINK, &arg, NULL, 0);
+ dprintk("NFS call link %s\n", name->name);
+ status = rpc_call(NFS_CLIENT(dentry->d_inode), NFSPROC_LINK, &arg, NULL, 0);
dprintk("NFS reply link: %d\n", status);
return status;
}
-int
-nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
- const char *name, const char *path,
- struct iattr *sattr)
+static int
+nfs_proc_symlink(struct dentry *dir, struct qstr *name, struct qstr *path,
+ struct iattr *sattr, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
{
- struct nfs_symlinkargs arg = { dir, name, path, sattr };
+ struct nfs_symlinkargs arg = { NFS_FH(dir), name->name, name->len,
+ path->name, path->len, sattr };
int status;
- dprintk("NFS call symlink %s -> %s\n", name, path);
- status = rpc_call(server->client, NFSPROC_SYMLINK, &arg, NULL, 0);
+ dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_SYMLINK, &arg, NULL, 0);
dprintk("NFS reply symlink: %d\n", status);
return status;
}
-int
-nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
- const char *name, struct iattr *sattr,
- struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int
+nfs_proc_mkdir(struct dentry *dir, struct qstr *name, struct iattr *sattr,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
- struct nfs_createargs arg = { dir, name, sattr };
+ struct nfs_createargs arg = { NFS_FH(dir), name->name, name->len,
+ sattr };
struct nfs_diropok res = { fhandle, fattr };
int status;
- dprintk("NFS call mkdir %s\n", name);
- status = rpc_call(server->client, NFSPROC_MKDIR, &arg, &res, 0);
+ dprintk("NFS call mkdir %s\n", name->name);
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_MKDIR, &arg, &res, 0);
dprintk("NFS reply mkdir: %d\n", status);
return status;
}
-int
-nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name)
+static int
+nfs_proc_rmdir(struct dentry *dir, struct qstr *name)
{
- struct nfs_diropargs arg = { dir, name };
+ struct nfs_diropargs arg = { NFS_FH(dir), name->name, name->len };
int status;
- dprintk("NFS call rmdir %s\n", name);
- status = rpc_call(server->client, NFSPROC_RMDIR, &arg, NULL, 0);
+ dprintk("NFS call rmdir %s\n", name->name);
+ status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_RMDIR, &arg, NULL, 0);
dprintk("NFS reply rmdir: %d\n", status);
return status;
}
-int
+/*
+ * The READDIR implementation is somewhat hackish - we pass a temporary
+ * buffer to the encode function, which installs it in the receive
+ * the receive iovec. The decode function just parses the reply to make
+ * sure it is syntactically correct; the entries itself are decoded
+ * from nfs_readdir by calling the decode_entry function directly.
+ */
+static int
+nfs_proc_readdir(struct dentry *dir, __u64 cookie, void *entry,
+ unsigned int size, int plus)
+{
+ struct nfs_readdirargs arg;
+ struct nfs_readdirres res;
+ struct rpc_message msg = { NFSPROC_READDIR, &arg, &res, NULL };
+ struct nfs_server *server = NFS_DSERVER(dir);
+ int status;
+
+ if (server->rsize < size)
+ size = server->rsize;
+
+ arg.fh = NFS_FH(dir);
+ arg.cookie = cookie;
+ arg.buffer = entry;
+ arg.bufsiz = size;
+ res.buffer = entry;
+ res.bufsiz = size;
+
+ dprintk("NFS call readdir %d\n", (unsigned int)cookie);
+ status = rpc_call_sync(NFS_CLIENT(dir->d_inode), &msg, 0);
+
+ dprintk("NFS reply readdir: %d\n", status);
+ return status;
+}
+
+static int
nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info)
{
int status;
dprintk("NFS call statfs\n");
+ memset((char *)info, 0, sizeof(*info));
status = rpc_call(server->client, NFSPROC_STATFS, fhandle, info, 0);
dprintk("NFS reply statfs: %d\n", status);
return status;
}
+
+extern u32 * nfs_decode_dirent(u32 *, struct nfs_entry *, int);
+
+struct nfs_rpc_ops nfs_v2_clientops = {
+ 2, /* protocol version */
+ nfs_proc_get_root,
+ nfs_proc_getattr,
+ nfs_proc_setattr,
+ nfs_proc_lookup,
+ NULL, /* access */
+ nfs_proc_readlink,
+ nfs_proc_read,
+ nfs_proc_write,
+ NULL, /* commit */
+ nfs_proc_create,
+ nfs_proc_remove,
+ nfs_proc_rename,
+ nfs_proc_link,
+ nfs_proc_symlink,
+ nfs_proc_mkdir,
+ nfs_proc_rmdir,
+ nfs_proc_readdir,
+ nfs_proc_mknod,
+ nfs_proc_statfs,
+ nfs_decode_dirent,
+};
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index aa17780e5..4cf51ec8d 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -15,7 +15,6 @@
* within the RPC code when root squashing is suspected.
*/
-#define NFS_NEED_XDR_TYPES
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -52,14 +51,18 @@ struct nfs_rreq {
*/
static inline void
nfs_readreq_setup(struct nfs_rreq *req, struct nfs_fh *fh,
- unsigned long offset, void *buffer, unsigned int rsize)
+ loff_t offset, void *buffer, unsigned int rsize)
{
req->ra_args.fh = fh;
req->ra_args.offset = offset;
req->ra_args.count = rsize;
- req->ra_args.buffer = buffer;
+ req->ra_args.iov[0].iov_base = (void *)buffer;
+ req->ra_args.iov[0].iov_len = rsize;
+ req->ra_args.nriov = 1;
+ req->ra_fattr.valid = 0;
req->ra_res.fattr = &req->ra_fattr;
req->ra_res.count = rsize;
+ req->ra_res.eof = 0;
}
@@ -70,11 +73,12 @@ static int
nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page)
{
struct nfs_rreq rqst;
- unsigned long offset = page->index << PAGE_CACHE_SHIFT;
+ struct rpc_message msg;
+ loff_t offset = page_offset(page);
char *buffer;
int rsize = NFS_SERVER(inode)->rsize;
int result, refresh = 0;
- int count = PAGE_SIZE;
+ int count = PAGE_CACHE_SIZE;
int flags = IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0;
dprintk("NFS: nfs_readpage_sync(%p)\n", page);
@@ -89,16 +93,21 @@ nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page)
if (count < rsize)
rsize = count;
- dprintk("NFS: nfs_proc_read(%s, (%s/%s), %ld, %d, %p)\n",
+ dprintk("NFS: nfs_proc_read(%s, (%s/%s), %Ld, %d, %p)\n",
NFS_SERVER(inode)->hostname,
dentry->d_parent->d_name.name, dentry->d_name.name,
- offset, rsize, buffer);
+ (long long)offset, rsize, buffer);
/* Set up arguments and perform rpc call */
nfs_readreq_setup(&rqst, NFS_FH(dentry), offset, buffer, rsize);
lock_kernel();
- result = rpc_call(NFS_CLIENT(inode), NFSPROC_READ, &rqst.ra_args, &rqst.ra_res, flags);
+ msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? NFS3PROC_READ : NFSPROC_READ;
+ msg.rpc_argp = &rqst.ra_args;
+ msg.rpc_resp = &rqst.ra_res;
+ msg.rpc_cred = NULL;
+ result = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
unlock_kernel();
+ nfs_refresh_inode(inode, &rqst.ra_fattr);
/*
* Even if we had a partial success we can't mark the page
@@ -124,9 +133,6 @@ nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page)
io_error:
kunmap(page);
UnlockPage(page);
- /* Note: we don't refresh if the call returned error */
- if (refresh && result >= 0)
- nfs_refresh_inode(inode, &rqst.ra_fattr);
return result;
}
@@ -139,19 +145,18 @@ nfs_readpage_result(struct rpc_task *task)
{
struct nfs_rreq *req = (struct nfs_rreq *) task->tk_calldata;
struct page *page = req->ra_page;
- unsigned long address = page_address(page);
+ char *address = req->ra_args.iov[0].iov_base;
int result = task->tk_status;
static int succ = 0, fail = 0;
- dprintk("NFS: %4d received callback for page %lx, result %d\n",
+ dprintk("NFS: %4d received callback for page %p, result %d\n",
task->tk_pid, address, result);
+ nfs_refresh_inode(req->ra_inode, &req->ra_fattr);
if (result >= 0) {
result = req->ra_res.count;
- if (result < PAGE_SIZE) {
- memset((char *) address + result, 0, PAGE_SIZE - result);
- }
- nfs_refresh_inode(req->ra_inode, &req->ra_fattr);
+ if (result < PAGE_CACHE_SIZE)
+ memset(address + result, 0, PAGE_CACHE_SIZE - result);
SetPageUptodate(page);
succ++;
} else {
@@ -161,9 +166,8 @@ nfs_readpage_result(struct rpc_task *task)
}
kunmap(page);
UnlockPage(page);
- __free_page(page);
+ page_cache_release(page);
- rpc_release_task(task);
kfree(req);
}
@@ -189,15 +193,15 @@ nfs_readpage_async(struct dentry *dentry, struct inode *inode,
address = kmap(page);
/* Initialize request */
/* N.B. Will the dentry remain valid for life of request? */
- nfs_readreq_setup(req, NFS_FH(dentry), page->index << PAGE_CACHE_SHIFT,
- (void *) address, PAGE_SIZE);
+ nfs_readreq_setup(req, NFS_FH(dentry), page_offset(page),
+ (void *) address, PAGE_CACHE_SIZE);
req->ra_inode = inode;
req->ra_page = page; /* count has been incremented by caller */
/* Start the async call */
dprintk("NFS: executing async READ request.\n");
- msg.rpc_proc = NFSPROC_READ;
+ msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? NFS3PROC_READ : NFSPROC_READ;
msg.rpc_argp = &req->ra_args;
msg.rpc_resp = &req->ra_res;
msg.rpc_cred = NULL;
@@ -225,7 +229,7 @@ out_free:
* We read the page synchronously in the following cases:
* - The file is a swap file. Swap-ins are always sync operations,
* so there's no need bothering to make async reads 100% fail-safe.
- * - The NFS rsize is smaller than PAGE_SIZE. We could kludge our way
+ * - The NFS rsize is smaller than PAGE_CACHE_SIZE. We could kludge our way
* around this by creating several consecutive read requests, but
* that's hardly worth it.
* - The error flag is set for this page. This happens only when a
@@ -240,7 +244,7 @@ nfs_readpage(struct dentry *dentry, struct page *page)
lock_kernel();
dprintk("NFS: nfs_readpage (%p %ld@%lu)\n",
- page, PAGE_SIZE, page->index);
+ page, PAGE_CACHE_SIZE, page->index);
get_page(page);
/*
@@ -256,7 +260,7 @@ nfs_readpage(struct dentry *dentry, struct page *page)
error = -1;
if (!IS_SWAPFILE(inode) && !PageError(page) &&
- NFS_SERVER(inode)->rsize >= PAGE_SIZE)
+ NFS_SERVER(inode)->rsize >= PAGE_CACHE_SIZE)
error = nfs_readpage_async(dentry, inode, page);
if (error >= 0)
goto out;
@@ -269,7 +273,7 @@ nfs_readpage(struct dentry *dentry, struct page *page)
out_error:
UnlockPage(page);
out_free:
- __free_page(page);
+ page_cache_release(page);
out:
unlock_kernel();
return error;
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index 9f5a95ff9..6b4a94f44 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -14,8 +14,9 @@
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/sunrpc/clnt.h>
-#include <linux/nfs_fs.h>
#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs_fs.h>
#include <linux/pagemap.h>
#include <linux/stat.h>
#include <linux/mm.h>
@@ -27,16 +28,17 @@
*/
static int nfs_symlink_filler(struct dentry *dentry, struct page *page)
{
- struct nfs_readlinkargs rl_args;
- kmap(page);
+ struct inode *inode = dentry->d_inode;
+ void *buffer = (void *)kmap(page);
+ int error;
+
/* We place the length at the beginning of the page,
* in host byte order, followed by the string. The
* XDR response verification will NULL terminate it.
*/
- rl_args.fh = NFS_FH(dentry);
- rl_args.buffer = (const void *)page_address(page);
- if (rpc_call(NFS_CLIENT(dentry->d_inode), NFSPROC_READLINK,
- &rl_args, NULL, 0) < 0)
+ error = NFS_PROTO(inode)->readlink(dentry, buffer,
+ PAGE_CACHE_SIZE - sizeof(u32)-4);
+ if (error < 0)
goto error;
SetPageUptodate(page);
kunmap(page);
@@ -85,12 +87,10 @@ static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
return res;
}
-static struct dentry *
-nfs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow)
+static int nfs_follow_link(struct dentry *dentry, struct nameidata *nd)
{
struct page *page = NULL;
- struct dentry *res = vfs_follow_link(dentry, base, follow,
- nfs_getlink(dentry, &page));
+ int res = vfs_follow_link(nd, nfs_getlink(dentry,&page));
if (page) {
kunmap(page);
page_cache_release(page);
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index af023a121..651251548 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -55,6 +55,7 @@
#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
#include <linux/nfs_flushd.h>
#include <asm/uaccess.h>
#include <linux/smp_lock.h>
@@ -160,7 +161,6 @@ static __inline__ void nfs_writedata_free(struct nfs_write_data *p)
static void nfs_writedata_release(struct rpc_task *task)
{
struct nfs_write_data *wdata = (struct nfs_write_data *)task->tk_calldata;
- rpc_release_task(task);
nfs_writedata_free(wdata);
}
@@ -172,6 +172,12 @@ static void nfs_writedata_release(struct rpc_task *task)
static __inline__ int
nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr)
{
+ if ((fattr->valid & NFS_ATTR_FATTR) && !(fattr->valid & NFS_ATTR_WCC)) {
+ fattr->pre_size = NFS_CACHE_ISIZE(inode);
+ fattr->pre_mtime = NFS_CACHE_MTIME(inode);
+ fattr->pre_ctime = NFS_CACHE_CTIME(inode);
+ fattr->valid |= NFS_ATTR_WCC;
+ }
return nfs_refresh_inode(inode, fattr);
}
@@ -183,10 +189,12 @@ static int
nfs_writepage_sync(struct dentry *dentry, struct inode *inode,
struct page *page, unsigned long offset, unsigned int count)
{
+ loff_t base;
unsigned int wsize = NFS_SERVER(inode)->wsize;
- int result, refresh = 0, written = 0;
+ int result, refresh = 0, written = 0, flags;
u8 *buffer;
struct nfs_fattr fattr;
+ struct nfs_writeverf verf;
lock_kernel();
dprintk("NFS: nfs_writepage_sync(%s/%s %d@%lu/%ld)\n",
@@ -194,15 +202,17 @@ nfs_writepage_sync(struct dentry *dentry, struct inode *inode,
count, page->index, offset);
buffer = (u8 *) kmap(page) + offset;
- offset += page->index << PAGE_CACHE_SHIFT;
+ base = page_offset(page) + offset;
+
+ flags = ((IS_SWAPFILE(inode)) ? NFS_RW_SWAP : 0) | NFS_RW_SYNC;
do {
if (count < wsize && !IS_SWAPFILE(inode))
wsize = count;
- result = nfs_proc_write(NFS_DSERVER(dentry), NFS_FH(dentry),
- IS_SWAPFILE(inode), offset, wsize,
- buffer, &fattr);
+ result = NFS_PROTO(inode)->write(dentry, &fattr, flags,
+ base, wsize, buffer, &verf);
+ nfs_write_attributes(inode, &fattr);
if (result < 0) {
/* Must mark the page invalid after I/O error */
@@ -214,41 +224,19 @@ nfs_writepage_sync(struct dentry *dentry, struct inode *inode,
wsize, result);
refresh = 1;
buffer += wsize;
- offset += wsize;
+ base += wsize;
written += wsize;
count -= wsize;
/*
* If we've extended the file, update the inode
* now so we don't invalidate the cache.
*/
- if (offset > inode->i_size)
- inode->i_size = offset;
+ if (base > inode->i_size)
+ inode->i_size = base;
} while (count);
io_error:
kunmap(page);
- /* Note: we don't refresh if the call failed (fattr invalid) */
- if (refresh && result >= 0) {
- /* See comments in nfs_wback_result */
- /* N.B. I don't think this is right -- sync writes in order */
- if (fattr.size < inode->i_size)
- fattr.size = inode->i_size;
- if (fattr.mtime.seconds < inode->i_mtime)
- printk("nfs_writepage_sync: prior time??\n");
- /* Solaris 2.5 server seems to send garbled
- * fattrs occasionally */
- if (inode->i_ino == fattr.fileid) {
- /*
- * We expect the mtime value to change, and
- * don't want to invalidate the caches.
- */
- inode->i_mtime = fattr.mtime.seconds;
- nfs_refresh_inode(inode, &fattr);
- }
- else
- printk("nfs_writepage_sync: inode %ld, got %u?\n",
- inode->i_ino, fattr.fileid);
- }
unlock_kernel();
return written? written : result;
@@ -290,7 +278,7 @@ static int
region_locked(struct inode *inode, struct nfs_page *req)
{
struct file_lock *fl;
- unsigned long rqstart, rqend;
+ loff_t rqstart, rqend;
/* Don't optimize writes if we don't use NLM */
if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)
@@ -665,20 +653,18 @@ nfs_wait_on_request(struct nfs_page *req)
* Interruptible by signals only if mounted with intr flag.
*/
static int
-nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long start, unsigned int count)
+nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_start, unsigned int npages)
{
struct list_head *p, *head;
- unsigned long idx_start, idx_end;
- unsigned int pages = 0;
+ unsigned long idx_end;
+ unsigned int res = 0;
int error;
- idx_start = start >> PAGE_CACHE_SHIFT;
- if (count == 0)
+ if (npages == 0)
idx_end = ~0;
- else {
- unsigned long idx_count = (count-1) >> PAGE_CACHE_SHIFT;
- idx_end = idx_start + idx_count;
- }
+ else
+ idx_end = idx_start + npages - 1;
+
spin_lock(&nfs_wreq_lock);
head = &inode->u.nfs_i.writeback;
p = head->next;
@@ -705,10 +691,10 @@ nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long start
return error;
spin_lock(&nfs_wreq_lock);
p = head->next;
- pages++;
+ res++;
}
spin_unlock(&nfs_wreq_lock);
- return pages;
+ return res;
}
/*
@@ -769,19 +755,18 @@ nfs_scan_commit_timeout(struct inode *inode, struct list_head *dst)
#endif
static int
-nfs_scan_list(struct list_head *src, struct list_head *dst, struct file *file, unsigned long start, unsigned int count)
+nfs_scan_list(struct list_head *src, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
{
struct list_head *p;
struct nfs_page *req;
- unsigned long idx_start, idx_end;
- int pages;
+ unsigned long idx_end;
+ int res;
- pages = 0;
- idx_start = start >> PAGE_CACHE_SHIFT;
- if (count == 0)
+ res = 0;
+ if (npages == 0)
idx_end = ~0;
else
- idx_end = idx_start + ((count-1) >> PAGE_CACHE_SHIFT);
+ idx_end = idx_start + npages - 1;
p = src->next;
while (p != src) {
unsigned long pg_idx;
@@ -800,36 +785,36 @@ nfs_scan_list(struct list_head *src, struct list_head *dst, struct file *file, u
continue;
nfs_list_remove_request(req);
nfs_list_add_request(req, dst);
- pages++;
+ res++;
}
- return pages;
+ return res;
}
static int
-nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, unsigned long start, unsigned int count)
+nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
{
- int pages;
+ int res;
spin_lock(&nfs_wreq_lock);
- pages = nfs_scan_list(&inode->u.nfs_i.dirty, dst, file, start, count);
- inode->u.nfs_i.ndirty -= pages;
+ res = nfs_scan_list(&inode->u.nfs_i.dirty, dst, file, idx_start, npages);
+ inode->u.nfs_i.ndirty -= res;
if ((inode->u.nfs_i.ndirty == 0) != list_empty(&inode->u.nfs_i.dirty))
printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n");
spin_unlock(&nfs_wreq_lock);
- return pages;
+ return res;
}
#ifdef CONFIG_NFS_V3
static int
-nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, unsigned long start, unsigned int count)
+nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
{
- int pages;
+ int res;
spin_lock(&nfs_wreq_lock);
- pages = nfs_scan_list(&inode->u.nfs_i.commit, dst, file, start, count);
- inode->u.nfs_i.ncommit -= pages;
+ res = nfs_scan_list(&inode->u.nfs_i.commit, dst, file, idx_start, npages);
+ inode->u.nfs_i.ncommit -= res;
if ((inode->u.nfs_i.ncommit == 0) != list_empty(&inode->u.nfs_i.commit))
printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
spin_unlock(&nfs_wreq_lock);
- return pages;
+ return res;
}
#endif
@@ -1038,7 +1023,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig
dprintk("NFS: nfs_updatepage(%s/%s %d@%Ld)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
- count, page_offset(page) +offset);
+ count, (long long)(page_offset(page) +offset));
/*
* If wsize is smaller than page size, update and write
@@ -1071,7 +1056,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig
if (synchronous) {
int error;
- error = nfs_sync_file(inode, file, page_offset(page) + offset, count, FLUSH_SYNC|FLUSH_STABLE);
+ error = nfs_sync_file(inode, file, page_index(page), 1, FLUSH_SYNC|FLUSH_STABLE);
if (error < 0 || (error = file->f_error) < 0)
status = error;
file->f_error = 0;
@@ -1086,7 +1071,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig
nfs_release_request(req);
done:
dprintk("NFS: nfs_updatepage returns %d (isize %Ld)\n",
- status, inode->i_size);
+ status, (long long)inode->i_size);
if (status < 0)
clear_bit(PG_uptodate, &page->flags);
return status;
@@ -1173,6 +1158,8 @@ nfs_flush_one(struct list_head *head, struct file *file, int how)
/* Finalize the task. */
rpc_init_task(task, clnt, nfs_writeback_done, flags);
task->tk_calldata = data;
+ /* Release requests */
+ task->tk_release = nfs_writedata_release;
#ifdef CONFIG_NFS_V3
msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? NFS3PROC_WRITE : NFSPROC_WRITE;
@@ -1266,22 +1253,25 @@ nfs_writeback_done(struct rpc_task *task)
/* We tried a write call, but the server did not
* commit data to stable storage even though we
* requested it.
+ * Note: There is a known bug in Tru64 < 5.0 in which
+ * the server reports NFS_DATA_SYNC, but performs
+ * NFS_FILE_SYNC. We therefore implement this checking
+ * as a dprintk() in order to avoid filling syslog.
*/
static unsigned long complain = 0;
if (time_before(complain, jiffies)) {
- printk(KERN_NOTICE "NFS: faulty NFSv3 server %s:"
- " (committed = %d) != (stable = %d)\n",
- NFS_SERVER(inode)->hostname,
- resp->verf->committed, argp->stable);
+ dprintk("NFS: faulty NFSv3 server %s:"
+ " (committed = %d) != (stable = %d)\n",
+ NFS_SERVER(inode)->hostname,
+ resp->verf->committed, argp->stable);
complain = jiffies + 300 * HZ;
}
}
#endif
/* Update attributes as result of writeback. */
- if (task->tk_status >= 0)
- nfs_write_attributes(inode, resp->fattr);
+ nfs_write_attributes(inode, resp->fattr);
while (!list_empty(&data->pages)) {
req = nfs_list_entry(data->pages.next);
@@ -1293,7 +1283,7 @@ nfs_writeback_done(struct rpc_task *task)
req->wb_file->f_dentry->d_parent->d_name.name,
req->wb_file->f_dentry->d_name.name,
req->wb_bytes,
- page_offset(req->wb_page) + req->wb_offset);
+ (long long)(page_offset(req->wb_page) + req->wb_offset));
if (task->tk_status < 0) {
req->wb_file->f_error = task->tk_status;
@@ -1318,7 +1308,6 @@ nfs_writeback_done(struct rpc_task *task)
next:
nfs_unlock_request(req);
}
- nfs_writedata_release(task);
}
@@ -1332,7 +1321,7 @@ nfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data)
struct nfs_page *req;
struct dentry *dentry;
struct inode *inode;
- unsigned long start, end, len;
+ loff_t start, end, len;
/* Set up the RPC argument and reply structs
* NB: take care not to mess about with data->commit et al. */
@@ -1346,7 +1335,7 @@ nfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data)
inode = dentry->d_inode;
while (!list_empty(head)) {
struct nfs_page *req;
- unsigned long rqstart, rqend;
+ loff_t rqstart, rqend;
req = nfs_list_entry(head->next);
nfs_list_remove_request(req);
nfs_list_add_request(req, &data->pages);
@@ -1360,6 +1349,7 @@ nfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data)
data->args.fh = NFS_FH(dentry);
data->args.offset = start;
len = end - start;
+ /* If 'len' is not a 32-bit quantity, pass '0' in the COMMIT call */
if (end >= inode->i_size || len > (~((u32)0) >> 1))
len = 0;
data->res.count = data->args.count = (u32)len;
@@ -1399,6 +1389,8 @@ nfs_commit_list(struct list_head *head, int how)
rpc_init_task(task, clnt, nfs_commit_done, flags);
task->tk_calldata = data;
+ /* Release requests */
+ task->tk_release = nfs_writedata_release;
msg.rpc_proc = NFS3PROC_COMMIT;
msg.rpc_argp = &data->args;
@@ -1441,11 +1433,11 @@ nfs_commit_done(struct rpc_task *task)
req = nfs_list_entry(data->pages.next);
nfs_list_remove_request(req);
- dprintk("NFS: commit (%s/%s %d@%ld)",
+ dprintk("NFS: commit (%s/%s %d@%Ld)",
req->wb_file->f_dentry->d_parent->d_name.name,
req->wb_file->f_dentry->d_name.name,
req->wb_bytes,
- page_offset(req->wb_page) + req->wb_offset);
+ (long long)(page_offset(req->wb_page) + req->wb_offset));
if (task->tk_status < 0) {
req->wb_file->f_error = task->tk_status;
nfs_inode_remove_request(req);
@@ -1467,23 +1459,22 @@ nfs_commit_done(struct rpc_task *task)
next:
nfs_unlock_request(req);
}
- nfs_writedata_release(task);
}
#endif
-int nfs_flush_file(struct inode *inode, struct file *file, unsigned long start,
- unsigned int count, int how)
+int nfs_flush_file(struct inode *inode, struct file *file, unsigned long idx_start,
+ unsigned int npages, int how)
{
LIST_HEAD(head);
- int pages,
+ int res,
error = 0;
- pages = nfs_scan_dirty(inode, &head, file, start, count);
- if (pages)
+ res = nfs_scan_dirty(inode, &head, file, idx_start, npages);
+ if (res)
error = nfs_flush_list(inode, &head, how);
if (error < 0)
return error;
- return pages;
+ return res;
}
int nfs_flush_timeout(struct inode *inode, int how)
@@ -1501,19 +1492,19 @@ int nfs_flush_timeout(struct inode *inode, int how)
}
#ifdef CONFIG_NFS_V3
-int nfs_commit_file(struct inode *inode, struct file *file, unsigned long start,
- unsigned int count, int how)
+int nfs_commit_file(struct inode *inode, struct file *file, unsigned long idx_start,
+ unsigned int npages, int how)
{
LIST_HEAD(head);
- int pages,
+ int res,
error = 0;
- pages = nfs_scan_commit(inode, &head, file, start, count);
- if (pages)
+ res = nfs_scan_commit(inode, &head, file, idx_start, npages);
+ if (res)
error = nfs_commit_list(&head, how);
if (error < 0)
return error;
- return pages;
+ return res;
}
int nfs_commit_timeout(struct inode *inode, int how)
@@ -1533,8 +1524,8 @@ int nfs_commit_timeout(struct inode *inode, int how)
}
#endif
-int nfs_sync_file(struct inode *inode, struct file *file, unsigned long start,
- unsigned int count, int how)
+int nfs_sync_file(struct inode *inode, struct file *file, unsigned long idx_start,
+ unsigned int npages, int how)
{
int error,
wait;
@@ -1548,12 +1539,12 @@ int nfs_sync_file(struct inode *inode, struct file *file, unsigned long start,
do {
error = 0;
if (wait)
- error = nfs_wait_on_requests(inode, file, start, count);
+ error = nfs_wait_on_requests(inode, file, idx_start, npages);
if (error == 0)
- error = nfs_flush_file(inode, file, start, count, how);
+ error = nfs_flush_file(inode, file, idx_start, npages, how);
#ifdef CONFIG_NFS_V3
if (error == 0)
- error = nfs_commit_file(inode, file, start, count, how);
+ error = nfs_commit_file(inode, file, idx_start, npages, how);
#endif
} while (error > 0);
return error;