diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-04-19 04:00:00 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-04-19 04:00:00 +0000 |
commit | 46e045034336a2cc90c1798cd7cc07af744ddfd6 (patch) | |
tree | 3b9b51fc482e729f663d25333e77fbed9aaa939a /fs/nfs | |
parent | 31dc59d503a02e84c4de98826452acaeb56dc15a (diff) |
Merge with Linux 2.3.99-pre4.
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/Makefile | 3 | ||||
-rw-r--r-- | fs/nfs/dir.c | 845 | ||||
-rw-r--r-- | fs/nfs/file.c | 5 | ||||
-rw-r--r-- | fs/nfs/flushd.c | 1 | ||||
-rw-r--r-- | fs/nfs/inode.c | 494 | ||||
-rw-r--r-- | fs/nfs/mount_clnt.c | 65 | ||||
-rw-r--r-- | fs/nfs/nfs2xdr.c | 390 | ||||
-rw-r--r-- | fs/nfs/nfs3proc.c | 477 | ||||
-rw-r--r-- | fs/nfs/nfs3xdr.c | 1147 | ||||
-rw-r--r-- | fs/nfs/nfsroot.c | 45 | ||||
-rw-r--r-- | fs/nfs/proc.c | 328 | ||||
-rw-r--r-- | fs/nfs/read.c | 56 | ||||
-rw-r--r-- | fs/nfs/symlink.c | 22 | ||||
-rw-r--r-- | fs/nfs/write.c | 181 |
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; |