diff options
Diffstat (limited to 'ANDROID_3.4.5/fs/nfs')
63 files changed, 0 insertions, 54043 deletions
diff --git a/ANDROID_3.4.5/fs/nfs/Kconfig b/ANDROID_3.4.5/fs/nfs/Kconfig deleted file mode 100644 index 2a0e6c59..00000000 --- a/ANDROID_3.4.5/fs/nfs/Kconfig +++ /dev/null @@ -1,152 +0,0 @@ -config NFS_FS - tristate "NFS client support" - depends on INET && FILE_LOCKING - select LOCKD - select SUNRPC - select NFS_ACL_SUPPORT if NFS_V3_ACL - help - Choose Y here if you want to access files residing on other - computers using Sun's Network File System protocol. To compile - this file system support as a module, choose M here: the module - will be called nfs. - - To mount file systems exported by NFS servers, you also need to - install the user space mount.nfs command which can be found in - the Linux nfs-utils package, available from http://linux-nfs.org/. - Information about using the mount command is available in the - mount(8) man page. More detail about the Linux NFS client - implementation is available via the nfs(5) man page. - - Below you can choose which versions of the NFS protocol are - available in the kernel to mount NFS servers. Support for NFS - version 2 (RFC 1094) is always available when NFS_FS is selected. - - To configure a system which mounts its root file system via NFS - at boot time, say Y here, select "Kernel level IP - autoconfiguration" in the NETWORK menu, and select "Root file - system on NFS" below. You cannot compile this file system as a - module in this case. - - If unsure, say N. - -config NFS_V3 - bool "NFS client support for NFS version 3" - depends on NFS_FS - help - This option enables support for version 3 of the NFS protocol - (RFC 1813) in the kernel's NFS client. - - If unsure, say Y. - -config NFS_V3_ACL - bool "NFS client support for the NFSv3 ACL protocol extension" - depends on NFS_V3 - help - Some NFS servers support an auxiliary NFSv3 ACL protocol that - Sun added to Solaris but never became an official part of the - NFS version 3 protocol. This protocol extension allows - applications on NFS clients to manipulate POSIX Access Control - Lists on files residing on NFS servers. NFS servers enforce - ACLs on local files whether this protocol is available or not. - - Choose Y here if your NFS server supports the Solaris NFSv3 ACL - protocol extension and you want your NFS client to allow - applications to access and modify ACLs on files on the server. - - Most NFS servers don't support the Solaris NFSv3 ACL protocol - extension. You can choose N here or specify the "noacl" mount - option to prevent your NFS client from trying to use the NFSv3 - ACL protocol. - - If unsure, say N. - -config NFS_V4 - bool "NFS client support for NFS version 4" - depends on NFS_FS - select SUNRPC_GSS - select KEYS - help - This option enables support for version 4 of the NFS protocol - (RFC 3530) in the kernel's NFS client. - - To mount NFS servers using NFSv4, you also need to install user - space programs which can be found in the Linux nfs-utils package, - available from http://linux-nfs.org/. - - If unsure, say Y. - -config NFS_V4_1 - bool "NFS client support for NFSv4.1 (EXPERIMENTAL)" - depends on NFS_FS && NFS_V4 && EXPERIMENTAL - select SUNRPC_BACKCHANNEL - select PNFS_FILE_LAYOUT - help - This option enables support for minor version 1 of the NFSv4 protocol - (RFC 5661) in the kernel's NFS client. - - If unsure, say N. - -config PNFS_FILE_LAYOUT - tristate - -config PNFS_BLOCK - tristate - depends on NFS_FS && NFS_V4_1 && BLK_DEV_DM - default m - -config PNFS_OBJLAYOUT - tristate - depends on NFS_FS && NFS_V4_1 && SCSI_OSD_ULD - default m - -config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN - string "NFSv4.1 Implementation ID Domain" - depends on NFS_V4_1 - default "kernel.org" - help - This option defines the domain portion of the implementation ID that - may be sent in the NFS exchange_id operation. The value must be in - the format of a DNS domain name and should be set to the DNS domain - name of the distribution. - If the NFS client is unchanged from the upstream kernel, this - option should be set to the default "kernel.org". - -config ROOT_NFS - bool "Root file system on NFS" - depends on NFS_FS=y && IP_PNP - help - If you want your system to mount its root file system via NFS, - choose Y here. This is common practice for managing systems - without local permanent storage. For details, read - <file:Documentation/filesystems/nfs/nfsroot.txt>. - - Most people say N here. - -config NFS_FSCACHE - bool "Provide NFS client caching support" - depends on NFS_FS=m && FSCACHE || NFS_FS=y && FSCACHE=y - help - Say Y here if you want NFS data to be cached locally on disc through - the general filesystem cache manager - -config NFS_USE_LEGACY_DNS - bool "Use the legacy NFS DNS resolver" - depends on NFS_V4 - help - The kernel now provides a method for translating a host name into an - IP address. Select Y here if you would rather use your own DNS - resolver script. - - If unsure, say N - -config NFS_USE_KERNEL_DNS - bool - depends on NFS_V4 && !NFS_USE_LEGACY_DNS - select DNS_RESOLVER - default y - -config NFS_DEBUG - bool - depends on NFS_FS && SUNRPC_DEBUG - select CRC32 - default y diff --git a/ANDROID_3.4.5/fs/nfs/Makefile b/ANDROID_3.4.5/fs/nfs/Makefile deleted file mode 100644 index b58613d0..00000000 --- a/ANDROID_3.4.5/fs/nfs/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# -# Makefile for the Linux nfs filesystem routines. -# - -obj-$(CONFIG_NFS_FS) += nfs.o - -nfs-y := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \ - direct.o pagelist.o proc.o read.o symlink.o unlink.o \ - write.o namespace.o mount_clnt.o \ - dns_resolve.o cache_lib.o -nfs-$(CONFIG_ROOT_NFS) += nfsroot.o -nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o -nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o -nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ - delegation.o idmap.o \ - callback.o callback_xdr.o callback_proc.o \ - nfs4namespace.o -nfs-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o -nfs-$(CONFIG_SYSCTL) += sysctl.o -nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o - -obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o -nfs_layout_nfsv41_files-y := nfs4filelayout.o nfs4filelayoutdev.o - -obj-$(CONFIG_PNFS_OBJLAYOUT) += objlayout/ -obj-$(CONFIG_PNFS_BLOCK) += blocklayout/ diff --git a/ANDROID_3.4.5/fs/nfs/blocklayout/Makefile b/ANDROID_3.4.5/fs/nfs/blocklayout/Makefile deleted file mode 100644 index d5815505..00000000 --- a/ANDROID_3.4.5/fs/nfs/blocklayout/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for the pNFS block layout driver kernel module -# -obj-$(CONFIG_PNFS_BLOCK) += blocklayoutdriver.o -blocklayoutdriver-objs := blocklayout.o extents.o blocklayoutdev.o blocklayoutdm.o diff --git a/ANDROID_3.4.5/fs/nfs/blocklayout/blocklayout.c b/ANDROID_3.4.5/fs/nfs/blocklayout/blocklayout.c deleted file mode 100644 index 7f6a23f0..00000000 --- a/ANDROID_3.4.5/fs/nfs/blocklayout/blocklayout.c +++ /dev/null @@ -1,1185 +0,0 @@ -/* - * linux/fs/nfs/blocklayout/blocklayout.c - * - * Module for the NFSv4.1 pNFS block layout driver. - * - * Copyright (c) 2006 The Regents of the University of Michigan. - * All rights reserved. - * - * Andy Adamson <andros@citi.umich.edu> - * Fred Isaman <iisaman@umich.edu> - * - * permission is granted to use, copy, create derivative works and - * redistribute this software and such derivative works for any purpose, - * so long as the name of the university of michigan is not used in - * any advertising or publicity pertaining to the use or distribution - * of this software without specific, written prior authorization. if - * the above copyright notice or any other identification of the - * university of michigan is included in any copy of any portion of - * this software, then the disclaimer below must also be included. - * - * this software is provided as is, without representation from the - * university of michigan as to its fitness for any purpose, and without - * warranty by the university of michigan of any kind, either express - * or implied, including without limitation the implied warranties of - * merchantability and fitness for a particular purpose. the regents - * of the university of michigan shall not be liable for any damages, - * including special, indirect, incidental, or consequential damages, - * with respect to any claim arising out or in connection with the use - * of the software, even if it has been or is hereafter advised of the - * possibility of such damages. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/mount.h> -#include <linux/namei.h> -#include <linux/bio.h> /* struct bio */ -#include <linux/buffer_head.h> /* various write calls */ -#include <linux/prefetch.h> - -#include "../pnfs.h" -#include "../internal.h" -#include "blocklayout.h" - -#define NFSDBG_FACILITY NFSDBG_PNFS_LD - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Andy Adamson <andros@citi.umich.edu>"); -MODULE_DESCRIPTION("The NFSv4.1 pNFS Block layout driver"); - -static void print_page(struct page *page) -{ - dprintk("PRINTPAGE page %p\n", page); - dprintk(" PagePrivate %d\n", PagePrivate(page)); - dprintk(" PageUptodate %d\n", PageUptodate(page)); - dprintk(" PageError %d\n", PageError(page)); - dprintk(" PageDirty %d\n", PageDirty(page)); - dprintk(" PageReferenced %d\n", PageReferenced(page)); - dprintk(" PageLocked %d\n", PageLocked(page)); - dprintk(" PageWriteback %d\n", PageWriteback(page)); - dprintk(" PageMappedToDisk %d\n", PageMappedToDisk(page)); - dprintk("\n"); -} - -/* Given the be associated with isect, determine if page data needs to be - * initialized. - */ -static int is_hole(struct pnfs_block_extent *be, sector_t isect) -{ - if (be->be_state == PNFS_BLOCK_NONE_DATA) - return 1; - else if (be->be_state != PNFS_BLOCK_INVALID_DATA) - return 0; - else - return !bl_is_sector_init(be->be_inval, isect); -} - -/* Given the be associated with isect, determine if page data can be - * written to disk. - */ -static int is_writable(struct pnfs_block_extent *be, sector_t isect) -{ - return (be->be_state == PNFS_BLOCK_READWRITE_DATA || - be->be_state == PNFS_BLOCK_INVALID_DATA); -} - -/* The data we are handed might be spread across several bios. We need - * to track when the last one is finished. - */ -struct parallel_io { - struct kref refcnt; - void (*pnfs_callback) (void *data, int num_se); - void *data; - int bse_count; -}; - -static inline struct parallel_io *alloc_parallel(void *data) -{ - struct parallel_io *rv; - - rv = kmalloc(sizeof(*rv), GFP_NOFS); - if (rv) { - rv->data = data; - kref_init(&rv->refcnt); - rv->bse_count = 0; - } - return rv; -} - -static inline void get_parallel(struct parallel_io *p) -{ - kref_get(&p->refcnt); -} - -static void destroy_parallel(struct kref *kref) -{ - struct parallel_io *p = container_of(kref, struct parallel_io, refcnt); - - dprintk("%s enter\n", __func__); - p->pnfs_callback(p->data, p->bse_count); - kfree(p); -} - -static inline void put_parallel(struct parallel_io *p) -{ - kref_put(&p->refcnt, destroy_parallel); -} - -static struct bio * -bl_submit_bio(int rw, struct bio *bio) -{ - if (bio) { - get_parallel(bio->bi_private); - dprintk("%s submitting %s bio %u@%llu\n", __func__, - rw == READ ? "read" : "write", - bio->bi_size, (unsigned long long)bio->bi_sector); - submit_bio(rw, bio); - } - return NULL; -} - -static struct bio *bl_alloc_init_bio(int npg, sector_t isect, - struct pnfs_block_extent *be, - void (*end_io)(struct bio *, int err), - struct parallel_io *par) -{ - struct bio *bio; - - npg = min(npg, BIO_MAX_PAGES); - bio = bio_alloc(GFP_NOIO, npg); - if (!bio && (current->flags & PF_MEMALLOC)) { - while (!bio && (npg /= 2)) - bio = bio_alloc(GFP_NOIO, npg); - } - - if (bio) { - bio->bi_sector = isect - be->be_f_offset + be->be_v_offset; - bio->bi_bdev = be->be_mdev; - bio->bi_end_io = end_io; - bio->bi_private = par; - } - return bio; -} - -static struct bio *bl_add_page_to_bio(struct bio *bio, int npg, int rw, - sector_t isect, struct page *page, - struct pnfs_block_extent *be, - void (*end_io)(struct bio *, int err), - struct parallel_io *par) -{ -retry: - if (!bio) { - bio = bl_alloc_init_bio(npg, isect, be, end_io, par); - if (!bio) - return ERR_PTR(-ENOMEM); - } - if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) { - bio = bl_submit_bio(rw, bio); - goto retry; - } - return bio; -} - -/* This is basically copied from mpage_end_io_read */ -static void bl_end_io_read(struct bio *bio, int err) -{ - struct parallel_io *par = bio->bi_private; - const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); - struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; - struct nfs_read_data *rdata = (struct nfs_read_data *)par->data; - - do { - struct page *page = bvec->bv_page; - - if (--bvec >= bio->bi_io_vec) - prefetchw(&bvec->bv_page->flags); - if (uptodate) - SetPageUptodate(page); - } while (bvec >= bio->bi_io_vec); - if (!uptodate) { - if (!rdata->pnfs_error) - rdata->pnfs_error = -EIO; - pnfs_set_lo_fail(rdata->lseg); - } - bio_put(bio); - put_parallel(par); -} - -static void bl_read_cleanup(struct work_struct *work) -{ - struct rpc_task *task; - struct nfs_read_data *rdata; - dprintk("%s enter\n", __func__); - task = container_of(work, struct rpc_task, u.tk_work); - rdata = container_of(task, struct nfs_read_data, task); - pnfs_ld_read_done(rdata); -} - -static void -bl_end_par_io_read(void *data, int unused) -{ - struct nfs_read_data *rdata = data; - - rdata->task.tk_status = rdata->pnfs_error; - INIT_WORK(&rdata->task.u.tk_work, bl_read_cleanup); - schedule_work(&rdata->task.u.tk_work); -} - -static enum pnfs_try_status -bl_read_pagelist(struct nfs_read_data *rdata) -{ - int i, hole; - struct bio *bio = NULL; - struct pnfs_block_extent *be = NULL, *cow_read = NULL; - sector_t isect, extent_length = 0; - struct parallel_io *par; - loff_t f_offset = rdata->args.offset; - struct page **pages = rdata->args.pages; - int pg_index = rdata->args.pgbase >> PAGE_CACHE_SHIFT; - - dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__, - rdata->npages, f_offset, (unsigned int)rdata->args.count); - - par = alloc_parallel(rdata); - if (!par) - goto use_mds; - par->pnfs_callback = bl_end_par_io_read; - /* At this point, we can no longer jump to use_mds */ - - isect = (sector_t) (f_offset >> SECTOR_SHIFT); - /* Code assumes extents are page-aligned */ - for (i = pg_index; i < rdata->npages; i++) { - if (!extent_length) { - /* We've used up the previous extent */ - bl_put_extent(be); - bl_put_extent(cow_read); - bio = bl_submit_bio(READ, bio); - /* Get the next one */ - be = bl_find_get_extent(BLK_LSEG2EXT(rdata->lseg), - isect, &cow_read); - if (!be) { - rdata->pnfs_error = -EIO; - goto out; - } - extent_length = be->be_length - - (isect - be->be_f_offset); - if (cow_read) { - sector_t cow_length = cow_read->be_length - - (isect - cow_read->be_f_offset); - extent_length = min(extent_length, cow_length); - } - } - hole = is_hole(be, isect); - if (hole && !cow_read) { - bio = bl_submit_bio(READ, bio); - /* Fill hole w/ zeroes w/o accessing device */ - dprintk("%s Zeroing page for hole\n", __func__); - zero_user_segment(pages[i], 0, PAGE_CACHE_SIZE); - print_page(pages[i]); - SetPageUptodate(pages[i]); - } else { - struct pnfs_block_extent *be_read; - - be_read = (hole && cow_read) ? cow_read : be; - bio = bl_add_page_to_bio(bio, rdata->npages - i, READ, - isect, pages[i], be_read, - bl_end_io_read, par); - if (IS_ERR(bio)) { - rdata->pnfs_error = PTR_ERR(bio); - bio = NULL; - goto out; - } - } - isect += PAGE_CACHE_SECTORS; - extent_length -= PAGE_CACHE_SECTORS; - } - if ((isect << SECTOR_SHIFT) >= rdata->inode->i_size) { - rdata->res.eof = 1; - rdata->res.count = rdata->inode->i_size - f_offset; - } else { - rdata->res.count = (isect << SECTOR_SHIFT) - f_offset; - } -out: - bl_put_extent(be); - bl_put_extent(cow_read); - bl_submit_bio(READ, bio); - put_parallel(par); - return PNFS_ATTEMPTED; - - use_mds: - dprintk("Giving up and using normal NFS\n"); - return PNFS_NOT_ATTEMPTED; -} - -static void mark_extents_written(struct pnfs_block_layout *bl, - __u64 offset, __u32 count) -{ - sector_t isect, end; - struct pnfs_block_extent *be; - struct pnfs_block_short_extent *se; - - dprintk("%s(%llu, %u)\n", __func__, offset, count); - if (count == 0) - return; - isect = (offset & (long)(PAGE_CACHE_MASK)) >> SECTOR_SHIFT; - end = (offset + count + PAGE_CACHE_SIZE - 1) & (long)(PAGE_CACHE_MASK); - end >>= SECTOR_SHIFT; - while (isect < end) { - sector_t len; - be = bl_find_get_extent(bl, isect, NULL); - BUG_ON(!be); /* FIXME */ - len = min(end, be->be_f_offset + be->be_length) - isect; - if (be->be_state == PNFS_BLOCK_INVALID_DATA) { - se = bl_pop_one_short_extent(be->be_inval); - BUG_ON(!se); - bl_mark_for_commit(be, isect, len, se); - } - isect += len; - bl_put_extent(be); - } -} - -static void bl_end_io_write_zero(struct bio *bio, int err) -{ - struct parallel_io *par = bio->bi_private; - const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); - struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; - struct nfs_write_data *wdata = (struct nfs_write_data *)par->data; - - do { - struct page *page = bvec->bv_page; - - if (--bvec >= bio->bi_io_vec) - prefetchw(&bvec->bv_page->flags); - /* This is the zeroing page we added */ - end_page_writeback(page); - page_cache_release(page); - } while (bvec >= bio->bi_io_vec); - - if (unlikely(!uptodate)) { - if (!wdata->pnfs_error) - wdata->pnfs_error = -EIO; - pnfs_set_lo_fail(wdata->lseg); - } - bio_put(bio); - put_parallel(par); -} - -static void bl_end_io_write(struct bio *bio, int err) -{ - struct parallel_io *par = bio->bi_private; - const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); - struct nfs_write_data *wdata = (struct nfs_write_data *)par->data; - - if (!uptodate) { - if (!wdata->pnfs_error) - wdata->pnfs_error = -EIO; - pnfs_set_lo_fail(wdata->lseg); - } - bio_put(bio); - put_parallel(par); -} - -/* Function scheduled for call during bl_end_par_io_write, - * it marks sectors as written and extends the commitlist. - */ -static void bl_write_cleanup(struct work_struct *work) -{ - struct rpc_task *task; - struct nfs_write_data *wdata; - dprintk("%s enter\n", __func__); - task = container_of(work, struct rpc_task, u.tk_work); - wdata = container_of(task, struct nfs_write_data, task); - if (likely(!wdata->pnfs_error)) { - /* Marks for LAYOUTCOMMIT */ - mark_extents_written(BLK_LSEG2EXT(wdata->lseg), - wdata->args.offset, wdata->args.count); - } - pnfs_ld_write_done(wdata); -} - -/* Called when last of bios associated with a bl_write_pagelist call finishes */ -static void bl_end_par_io_write(void *data, int num_se) -{ - struct nfs_write_data *wdata = data; - - if (unlikely(wdata->pnfs_error)) { - bl_free_short_extents(&BLK_LSEG2EXT(wdata->lseg)->bl_inval, - num_se); - } - - wdata->task.tk_status = wdata->pnfs_error; - wdata->verf.committed = NFS_FILE_SYNC; - INIT_WORK(&wdata->task.u.tk_work, bl_write_cleanup); - schedule_work(&wdata->task.u.tk_work); -} - -/* FIXME STUB - mark intersection of layout and page as bad, so is not - * used again. - */ -static void mark_bad_read(void) -{ - return; -} - -/* - * map_block: map a requested I/0 block (isect) into an offset in the LVM - * block_device - */ -static void -map_block(struct buffer_head *bh, sector_t isect, struct pnfs_block_extent *be) -{ - dprintk("%s enter be=%p\n", __func__, be); - - set_buffer_mapped(bh); - bh->b_bdev = be->be_mdev; - bh->b_blocknr = (isect - be->be_f_offset + be->be_v_offset) >> - (be->be_mdev->bd_inode->i_blkbits - SECTOR_SHIFT); - - dprintk("%s isect %llu, bh->b_blocknr %ld, using bsize %Zd\n", - __func__, (unsigned long long)isect, (long)bh->b_blocknr, - bh->b_size); - return; -} - -/* Given an unmapped page, zero it or read in page for COW, page is locked - * by caller. - */ -static int -init_page_for_write(struct page *page, struct pnfs_block_extent *cow_read) -{ - struct buffer_head *bh = NULL; - int ret = 0; - sector_t isect; - - dprintk("%s enter, %p\n", __func__, page); - BUG_ON(PageUptodate(page)); - if (!cow_read) { - zero_user_segment(page, 0, PAGE_SIZE); - SetPageUptodate(page); - goto cleanup; - } - - bh = alloc_page_buffers(page, PAGE_CACHE_SIZE, 0); - if (!bh) { - ret = -ENOMEM; - goto cleanup; - } - - isect = (sector_t) page->index << PAGE_CACHE_SECTOR_SHIFT; - map_block(bh, isect, cow_read); - if (!bh_uptodate_or_lock(bh)) - ret = bh_submit_read(bh); - if (ret) - goto cleanup; - SetPageUptodate(page); - -cleanup: - bl_put_extent(cow_read); - if (bh) - free_buffer_head(bh); - if (ret) { - /* Need to mark layout with bad read...should now - * just use nfs4 for reads and writes. - */ - mark_bad_read(); - } - return ret; -} - -/* Find or create a zeroing page marked being writeback. - * Return ERR_PTR on error, NULL to indicate skip this page and page itself - * to indicate write out. - */ -static struct page * -bl_find_get_zeroing_page(struct inode *inode, pgoff_t index, - struct pnfs_block_extent *cow_read) -{ - struct page *page; - int locked = 0; - page = find_get_page(inode->i_mapping, index); - if (page) - goto check_page; - - page = find_or_create_page(inode->i_mapping, index, GFP_NOFS); - if (unlikely(!page)) { - dprintk("%s oom\n", __func__); - return ERR_PTR(-ENOMEM); - } - locked = 1; - -check_page: - /* PageDirty: Other will write this out - * PageWriteback: Other is writing this out - * PageUptodate: It was read before - */ - if (PageDirty(page) || PageWriteback(page)) { - print_page(page); - if (locked) - unlock_page(page); - page_cache_release(page); - return NULL; - } - - if (!locked) { - lock_page(page); - locked = 1; - goto check_page; - } - if (!PageUptodate(page)) { - /* New page, readin or zero it */ - init_page_for_write(page, cow_read); - } - set_page_writeback(page); - unlock_page(page); - - return page; -} - -static enum pnfs_try_status -bl_write_pagelist(struct nfs_write_data *wdata, int sync) -{ - int i, ret, npg_zero, pg_index, last = 0; - struct bio *bio = NULL; - struct pnfs_block_extent *be = NULL, *cow_read = NULL; - sector_t isect, last_isect = 0, extent_length = 0; - struct parallel_io *par; - loff_t offset = wdata->args.offset; - size_t count = wdata->args.count; - struct page **pages = wdata->args.pages; - struct page *page; - pgoff_t index; - u64 temp; - int npg_per_block = - NFS_SERVER(wdata->inode)->pnfs_blksize >> PAGE_CACHE_SHIFT; - - dprintk("%s enter, %Zu@%lld\n", __func__, count, offset); - /* At this point, wdata->pages is a (sequential) list of nfs_pages. - * We want to write each, and if there is an error set pnfs_error - * to have it redone using nfs. - */ - par = alloc_parallel(wdata); - if (!par) - goto out_mds; - par->pnfs_callback = bl_end_par_io_write; - /* At this point, have to be more careful with error handling */ - - isect = (sector_t) ((offset & (long)PAGE_CACHE_MASK) >> SECTOR_SHIFT); - be = bl_find_get_extent(BLK_LSEG2EXT(wdata->lseg), isect, &cow_read); - if (!be || !is_writable(be, isect)) { - dprintk("%s no matching extents!\n", __func__); - goto out_mds; - } - - /* First page inside INVALID extent */ - if (be->be_state == PNFS_BLOCK_INVALID_DATA) { - if (likely(!bl_push_one_short_extent(be->be_inval))) - par->bse_count++; - else - goto out_mds; - temp = offset >> PAGE_CACHE_SHIFT; - npg_zero = do_div(temp, npg_per_block); - isect = (sector_t) (((offset - npg_zero * PAGE_CACHE_SIZE) & - (long)PAGE_CACHE_MASK) >> SECTOR_SHIFT); - extent_length = be->be_length - (isect - be->be_f_offset); - -fill_invalid_ext: - dprintk("%s need to zero %d pages\n", __func__, npg_zero); - for (;npg_zero > 0; npg_zero--) { - if (bl_is_sector_init(be->be_inval, isect)) { - dprintk("isect %llu already init\n", - (unsigned long long)isect); - goto next_page; - } - /* page ref released in bl_end_io_write_zero */ - index = isect >> PAGE_CACHE_SECTOR_SHIFT; - dprintk("%s zero %dth page: index %lu isect %llu\n", - __func__, npg_zero, index, - (unsigned long long)isect); - page = bl_find_get_zeroing_page(wdata->inode, index, - cow_read); - if (unlikely(IS_ERR(page))) { - wdata->pnfs_error = PTR_ERR(page); - goto out; - } else if (page == NULL) - goto next_page; - - ret = bl_mark_sectors_init(be->be_inval, isect, - PAGE_CACHE_SECTORS); - if (unlikely(ret)) { - dprintk("%s bl_mark_sectors_init fail %d\n", - __func__, ret); - end_page_writeback(page); - page_cache_release(page); - wdata->pnfs_error = ret; - goto out; - } - if (likely(!bl_push_one_short_extent(be->be_inval))) - par->bse_count++; - else { - end_page_writeback(page); - page_cache_release(page); - wdata->pnfs_error = -ENOMEM; - goto out; - } - /* FIXME: This should be done in bi_end_io */ - mark_extents_written(BLK_LSEG2EXT(wdata->lseg), - page->index << PAGE_CACHE_SHIFT, - PAGE_CACHE_SIZE); - - bio = bl_add_page_to_bio(bio, npg_zero, WRITE, - isect, page, be, - bl_end_io_write_zero, par); - if (IS_ERR(bio)) { - wdata->pnfs_error = PTR_ERR(bio); - bio = NULL; - goto out; - } -next_page: - isect += PAGE_CACHE_SECTORS; - extent_length -= PAGE_CACHE_SECTORS; - } - if (last) - goto write_done; - } - bio = bl_submit_bio(WRITE, bio); - - /* Middle pages */ - pg_index = wdata->args.pgbase >> PAGE_CACHE_SHIFT; - for (i = pg_index; i < wdata->npages; i++) { - if (!extent_length) { - /* We've used up the previous extent */ - bl_put_extent(be); - bio = bl_submit_bio(WRITE, bio); - /* Get the next one */ - be = bl_find_get_extent(BLK_LSEG2EXT(wdata->lseg), - isect, NULL); - if (!be || !is_writable(be, isect)) { - wdata->pnfs_error = -EINVAL; - goto out; - } - if (be->be_state == PNFS_BLOCK_INVALID_DATA) { - if (likely(!bl_push_one_short_extent( - be->be_inval))) - par->bse_count++; - else { - wdata->pnfs_error = -ENOMEM; - goto out; - } - } - extent_length = be->be_length - - (isect - be->be_f_offset); - } - if (be->be_state == PNFS_BLOCK_INVALID_DATA) { - ret = bl_mark_sectors_init(be->be_inval, isect, - PAGE_CACHE_SECTORS); - if (unlikely(ret)) { - dprintk("%s bl_mark_sectors_init fail %d\n", - __func__, ret); - wdata->pnfs_error = ret; - goto out; - } - } - bio = bl_add_page_to_bio(bio, wdata->npages - i, WRITE, - isect, pages[i], be, - bl_end_io_write, par); - if (IS_ERR(bio)) { - wdata->pnfs_error = PTR_ERR(bio); - bio = NULL; - goto out; - } - isect += PAGE_CACHE_SECTORS; - last_isect = isect; - extent_length -= PAGE_CACHE_SECTORS; - } - - /* Last page inside INVALID extent */ - if (be->be_state == PNFS_BLOCK_INVALID_DATA) { - bio = bl_submit_bio(WRITE, bio); - temp = last_isect >> PAGE_CACHE_SECTOR_SHIFT; - npg_zero = npg_per_block - do_div(temp, npg_per_block); - if (npg_zero < npg_per_block) { - last = 1; - goto fill_invalid_ext; - } - } - -write_done: - wdata->res.count = (last_isect << SECTOR_SHIFT) - (offset); - if (count < wdata->res.count) { - wdata->res.count = count; - } -out: - bl_put_extent(be); - bl_submit_bio(WRITE, bio); - put_parallel(par); - return PNFS_ATTEMPTED; -out_mds: - bl_put_extent(be); - kfree(par); - return PNFS_NOT_ATTEMPTED; -} - -/* FIXME - range ignored */ -static void -release_extents(struct pnfs_block_layout *bl, struct pnfs_layout_range *range) -{ - int i; - struct pnfs_block_extent *be; - - spin_lock(&bl->bl_ext_lock); - for (i = 0; i < EXTENT_LISTS; i++) { - while (!list_empty(&bl->bl_extents[i])) { - be = list_first_entry(&bl->bl_extents[i], - struct pnfs_block_extent, - be_node); - list_del(&be->be_node); - bl_put_extent(be); - } - } - spin_unlock(&bl->bl_ext_lock); -} - -static void -release_inval_marks(struct pnfs_inval_markings *marks) -{ - struct pnfs_inval_tracking *pos, *temp; - struct pnfs_block_short_extent *se, *stemp; - - list_for_each_entry_safe(pos, temp, &marks->im_tree.mtt_stub, it_link) { - list_del(&pos->it_link); - kfree(pos); - } - - list_for_each_entry_safe(se, stemp, &marks->im_extents, bse_node) { - list_del(&se->bse_node); - kfree(se); - } - return; -} - -static void bl_free_layout_hdr(struct pnfs_layout_hdr *lo) -{ - struct pnfs_block_layout *bl = BLK_LO2EXT(lo); - - dprintk("%s enter\n", __func__); - release_extents(bl, NULL); - release_inval_marks(&bl->bl_inval); - kfree(bl); -} - -static struct pnfs_layout_hdr *bl_alloc_layout_hdr(struct inode *inode, - gfp_t gfp_flags) -{ - struct pnfs_block_layout *bl; - - dprintk("%s enter\n", __func__); - bl = kzalloc(sizeof(*bl), gfp_flags); - if (!bl) - return NULL; - spin_lock_init(&bl->bl_ext_lock); - INIT_LIST_HEAD(&bl->bl_extents[0]); - INIT_LIST_HEAD(&bl->bl_extents[1]); - INIT_LIST_HEAD(&bl->bl_commit); - INIT_LIST_HEAD(&bl->bl_committing); - bl->bl_count = 0; - bl->bl_blocksize = NFS_SERVER(inode)->pnfs_blksize >> SECTOR_SHIFT; - BL_INIT_INVAL_MARKS(&bl->bl_inval, bl->bl_blocksize); - return &bl->bl_layout; -} - -static void bl_free_lseg(struct pnfs_layout_segment *lseg) -{ - dprintk("%s enter\n", __func__); - kfree(lseg); -} - -/* We pretty much ignore lseg, and store all data layout wide, so we - * can correctly merge. - */ -static struct pnfs_layout_segment *bl_alloc_lseg(struct pnfs_layout_hdr *lo, - struct nfs4_layoutget_res *lgr, - gfp_t gfp_flags) -{ - struct pnfs_layout_segment *lseg; - int status; - - dprintk("%s enter\n", __func__); - lseg = kzalloc(sizeof(*lseg), gfp_flags); - if (!lseg) - return ERR_PTR(-ENOMEM); - status = nfs4_blk_process_layoutget(lo, lgr, gfp_flags); - if (status) { - /* We don't want to call the full-blown bl_free_lseg, - * since on error extents were not touched. - */ - kfree(lseg); - return ERR_PTR(status); - } - return lseg; -} - -static void -bl_encode_layoutcommit(struct pnfs_layout_hdr *lo, struct xdr_stream *xdr, - const struct nfs4_layoutcommit_args *arg) -{ - dprintk("%s enter\n", __func__); - encode_pnfs_block_layoutupdate(BLK_LO2EXT(lo), xdr, arg); -} - -static void -bl_cleanup_layoutcommit(struct nfs4_layoutcommit_data *lcdata) -{ - struct pnfs_layout_hdr *lo = NFS_I(lcdata->args.inode)->layout; - - dprintk("%s enter\n", __func__); - clean_pnfs_block_layoutupdate(BLK_LO2EXT(lo), &lcdata->args, lcdata->res.status); -} - -static void free_blk_mountid(struct block_mount_id *mid) -{ - if (mid) { - struct pnfs_block_dev *dev, *tmp; - - /* No need to take bm_lock as we are last user freeing bm_devlist */ - list_for_each_entry_safe(dev, tmp, &mid->bm_devlist, bm_node) { - list_del(&dev->bm_node); - bl_free_block_dev(dev); - } - kfree(mid); - } -} - -/* This is mostly copied from the filelayout's get_device_info function. - * It seems much of this should be at the generic pnfs level. - */ -static struct pnfs_block_dev * -nfs4_blk_get_deviceinfo(struct nfs_server *server, const struct nfs_fh *fh, - struct nfs4_deviceid *d_id) -{ - struct pnfs_device *dev; - struct pnfs_block_dev *rv; - u32 max_resp_sz; - int max_pages; - struct page **pages = NULL; - int i, rc; - - /* - * Use the session max response size as the basis for setting - * GETDEVICEINFO's maxcount - */ - max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz; - max_pages = nfs_page_array_len(0, max_resp_sz); - dprintk("%s max_resp_sz %u max_pages %d\n", - __func__, max_resp_sz, max_pages); - - dev = kmalloc(sizeof(*dev), GFP_NOFS); - if (!dev) { - dprintk("%s kmalloc failed\n", __func__); - return ERR_PTR(-ENOMEM); - } - - pages = kzalloc(max_pages * sizeof(struct page *), GFP_NOFS); - if (pages == NULL) { - kfree(dev); - return ERR_PTR(-ENOMEM); - } - for (i = 0; i < max_pages; i++) { - pages[i] = alloc_page(GFP_NOFS); - if (!pages[i]) { - rv = ERR_PTR(-ENOMEM); - goto out_free; - } - } - - memcpy(&dev->dev_id, d_id, sizeof(*d_id)); - dev->layout_type = LAYOUT_BLOCK_VOLUME; - dev->pages = pages; - dev->pgbase = 0; - dev->pglen = PAGE_SIZE * max_pages; - dev->mincount = 0; - - dprintk("%s: dev_id: %s\n", __func__, dev->dev_id.data); - rc = nfs4_proc_getdeviceinfo(server, dev); - dprintk("%s getdevice info returns %d\n", __func__, rc); - if (rc) { - rv = ERR_PTR(rc); - goto out_free; - } - - rv = nfs4_blk_decode_device(server, dev); - out_free: - for (i = 0; i < max_pages; i++) - __free_page(pages[i]); - kfree(pages); - kfree(dev); - return rv; -} - -static int -bl_set_layoutdriver(struct nfs_server *server, const struct nfs_fh *fh) -{ - struct block_mount_id *b_mt_id = NULL; - struct pnfs_devicelist *dlist = NULL; - struct pnfs_block_dev *bdev; - LIST_HEAD(block_disklist); - int status, i; - - dprintk("%s enter\n", __func__); - - if (server->pnfs_blksize == 0) { - dprintk("%s Server did not return blksize\n", __func__); - return -EINVAL; - } - b_mt_id = kzalloc(sizeof(struct block_mount_id), GFP_NOFS); - if (!b_mt_id) { - status = -ENOMEM; - goto out_error; - } - /* Initialize nfs4 block layout mount id */ - spin_lock_init(&b_mt_id->bm_lock); - INIT_LIST_HEAD(&b_mt_id->bm_devlist); - - dlist = kmalloc(sizeof(struct pnfs_devicelist), GFP_NOFS); - if (!dlist) { - status = -ENOMEM; - goto out_error; - } - dlist->eof = 0; - while (!dlist->eof) { - status = nfs4_proc_getdevicelist(server, fh, dlist); - if (status) - goto out_error; - dprintk("%s GETDEVICELIST numdevs=%i, eof=%i\n", - __func__, dlist->num_devs, dlist->eof); - for (i = 0; i < dlist->num_devs; i++) { - bdev = nfs4_blk_get_deviceinfo(server, fh, - &dlist->dev_id[i]); - if (IS_ERR(bdev)) { - status = PTR_ERR(bdev); - goto out_error; - } - spin_lock(&b_mt_id->bm_lock); - list_add(&bdev->bm_node, &b_mt_id->bm_devlist); - spin_unlock(&b_mt_id->bm_lock); - } - } - dprintk("%s SUCCESS\n", __func__); - server->pnfs_ld_data = b_mt_id; - - out_return: - kfree(dlist); - return status; - - out_error: - free_blk_mountid(b_mt_id); - goto out_return; -} - -static int -bl_clear_layoutdriver(struct nfs_server *server) -{ - struct block_mount_id *b_mt_id = server->pnfs_ld_data; - - dprintk("%s enter\n", __func__); - free_blk_mountid(b_mt_id); - dprintk("%s RETURNS\n", __func__); - return 0; -} - -static const struct nfs_pageio_ops bl_pg_read_ops = { - .pg_init = pnfs_generic_pg_init_read, - .pg_test = pnfs_generic_pg_test, - .pg_doio = pnfs_generic_pg_readpages, -}; - -static const struct nfs_pageio_ops bl_pg_write_ops = { - .pg_init = pnfs_generic_pg_init_write, - .pg_test = pnfs_generic_pg_test, - .pg_doio = pnfs_generic_pg_writepages, -}; - -static struct pnfs_layoutdriver_type blocklayout_type = { - .id = LAYOUT_BLOCK_VOLUME, - .name = "LAYOUT_BLOCK_VOLUME", - .read_pagelist = bl_read_pagelist, - .write_pagelist = bl_write_pagelist, - .alloc_layout_hdr = bl_alloc_layout_hdr, - .free_layout_hdr = bl_free_layout_hdr, - .alloc_lseg = bl_alloc_lseg, - .free_lseg = bl_free_lseg, - .encode_layoutcommit = bl_encode_layoutcommit, - .cleanup_layoutcommit = bl_cleanup_layoutcommit, - .set_layoutdriver = bl_set_layoutdriver, - .clear_layoutdriver = bl_clear_layoutdriver, - .pg_read_ops = &bl_pg_read_ops, - .pg_write_ops = &bl_pg_write_ops, -}; - -static const struct rpc_pipe_ops bl_upcall_ops = { - .upcall = rpc_pipe_generic_upcall, - .downcall = bl_pipe_downcall, - .destroy_msg = bl_pipe_destroy_msg, -}; - -static struct dentry *nfs4blocklayout_register_sb(struct super_block *sb, - struct rpc_pipe *pipe) -{ - struct dentry *dir, *dentry; - - dir = rpc_d_lookup_sb(sb, NFS_PIPE_DIRNAME); - if (dir == NULL) - return ERR_PTR(-ENOENT); - dentry = rpc_mkpipe_dentry(dir, "blocklayout", NULL, pipe); - dput(dir); - return dentry; -} - -static void nfs4blocklayout_unregister_sb(struct super_block *sb, - struct rpc_pipe *pipe) -{ - if (pipe->dentry) - rpc_unlink(pipe->dentry); -} - -static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event, - void *ptr) -{ - struct super_block *sb = ptr; - struct net *net = sb->s_fs_info; - struct nfs_net *nn = net_generic(net, nfs_net_id); - struct dentry *dentry; - int ret = 0; - - if (!try_module_get(THIS_MODULE)) - return 0; - - if (nn->bl_device_pipe == NULL) { - module_put(THIS_MODULE); - return 0; - } - - switch (event) { - case RPC_PIPEFS_MOUNT: - dentry = nfs4blocklayout_register_sb(sb, nn->bl_device_pipe); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - break; - } - nn->bl_device_pipe->dentry = dentry; - break; - case RPC_PIPEFS_UMOUNT: - if (nn->bl_device_pipe->dentry) - nfs4blocklayout_unregister_sb(sb, nn->bl_device_pipe); - break; - default: - ret = -ENOTSUPP; - break; - } - module_put(THIS_MODULE); - return ret; -} - -static struct notifier_block nfs4blocklayout_block = { - .notifier_call = rpc_pipefs_event, -}; - -static struct dentry *nfs4blocklayout_register_net(struct net *net, - struct rpc_pipe *pipe) -{ - struct super_block *pipefs_sb; - struct dentry *dentry; - - pipefs_sb = rpc_get_sb_net(net); - if (!pipefs_sb) - return NULL; - dentry = nfs4blocklayout_register_sb(pipefs_sb, pipe); - rpc_put_sb_net(net); - return dentry; -} - -static void nfs4blocklayout_unregister_net(struct net *net, - struct rpc_pipe *pipe) -{ - struct super_block *pipefs_sb; - - pipefs_sb = rpc_get_sb_net(net); - if (pipefs_sb) { - nfs4blocklayout_unregister_sb(pipefs_sb, pipe); - rpc_put_sb_net(net); - } -} - -static int nfs4blocklayout_net_init(struct net *net) -{ - struct nfs_net *nn = net_generic(net, nfs_net_id); - struct dentry *dentry; - - init_waitqueue_head(&nn->bl_wq); - nn->bl_device_pipe = rpc_mkpipe_data(&bl_upcall_ops, 0); - if (IS_ERR(nn->bl_device_pipe)) - return PTR_ERR(nn->bl_device_pipe); - dentry = nfs4blocklayout_register_net(net, nn->bl_device_pipe); - if (IS_ERR(dentry)) { - rpc_destroy_pipe_data(nn->bl_device_pipe); - return PTR_ERR(dentry); - } - nn->bl_device_pipe->dentry = dentry; - return 0; -} - -static void nfs4blocklayout_net_exit(struct net *net) -{ - struct nfs_net *nn = net_generic(net, nfs_net_id); - - nfs4blocklayout_unregister_net(net, nn->bl_device_pipe); - rpc_destroy_pipe_data(nn->bl_device_pipe); - nn->bl_device_pipe = NULL; -} - -static struct pernet_operations nfs4blocklayout_net_ops = { - .init = nfs4blocklayout_net_init, - .exit = nfs4blocklayout_net_exit, -}; - -static int __init nfs4blocklayout_init(void) -{ - int ret; - - dprintk("%s: NFSv4 Block Layout Driver Registering...\n", __func__); - - ret = pnfs_register_layoutdriver(&blocklayout_type); - if (ret) - goto out; - - ret = rpc_pipefs_notifier_register(&nfs4blocklayout_block); - if (ret) - goto out_remove; - ret = register_pernet_subsys(&nfs4blocklayout_net_ops); - if (ret) - goto out_notifier; -out: - return ret; - -out_notifier: - rpc_pipefs_notifier_unregister(&nfs4blocklayout_block); -out_remove: - pnfs_unregister_layoutdriver(&blocklayout_type); - return ret; -} - -static void __exit nfs4blocklayout_exit(void) -{ - dprintk("%s: NFSv4 Block Layout Driver Unregistering...\n", - __func__); - - rpc_pipefs_notifier_unregister(&nfs4blocklayout_block); - unregister_pernet_subsys(&nfs4blocklayout_net_ops); - pnfs_unregister_layoutdriver(&blocklayout_type); -} - -MODULE_ALIAS("nfs-layouttype4-3"); - -module_init(nfs4blocklayout_init); -module_exit(nfs4blocklayout_exit); diff --git a/ANDROID_3.4.5/fs/nfs/blocklayout/blocklayout.h b/ANDROID_3.4.5/fs/nfs/blocklayout/blocklayout.h deleted file mode 100644 index 03350690..00000000 --- a/ANDROID_3.4.5/fs/nfs/blocklayout/blocklayout.h +++ /dev/null @@ -1,210 +0,0 @@ -/* - * linux/fs/nfs/blocklayout/blocklayout.h - * - * Module for the NFSv4.1 pNFS block layout driver. - * - * Copyright (c) 2006 The Regents of the University of Michigan. - * All rights reserved. - * - * Andy Adamson <andros@citi.umich.edu> - * Fred Isaman <iisaman@umich.edu> - * - * permission is granted to use, copy, create derivative works and - * redistribute this software and such derivative works for any purpose, - * so long as the name of the university of michigan is not used in - * any advertising or publicity pertaining to the use or distribution - * of this software without specific, written prior authorization. if - * the above copyright notice or any other identification of the - * university of michigan is included in any copy of any portion of - * this software, then the disclaimer below must also be included. - * - * this software is provided as is, without representation from the - * university of michigan as to its fitness for any purpose, and without - * warranty by the university of michigan of any kind, either express - * or implied, including without limitation the implied warranties of - * merchantability and fitness for a particular purpose. the regents - * of the university of michigan shall not be liable for any damages, - * including special, indirect, incidental, or consequential damages, - * with respect to any claim arising out or in connection with the use - * of the software, even if it has been or is hereafter advised of the - * possibility of such damages. - */ -#ifndef FS_NFS_NFS4BLOCKLAYOUT_H -#define FS_NFS_NFS4BLOCKLAYOUT_H - -#include <linux/device-mapper.h> -#include <linux/nfs_fs.h> -#include <linux/sunrpc/rpc_pipe_fs.h> - -#include "../pnfs.h" -#include "../netns.h" - -#define PAGE_CACHE_SECTORS (PAGE_CACHE_SIZE >> SECTOR_SHIFT) -#define PAGE_CACHE_SECTOR_SHIFT (PAGE_CACHE_SHIFT - SECTOR_SHIFT) - -struct block_mount_id { - spinlock_t bm_lock; /* protects list */ - struct list_head bm_devlist; /* holds pnfs_block_dev */ -}; - -struct pnfs_block_dev { - struct list_head bm_node; - struct nfs4_deviceid bm_mdevid; /* associated devid */ - struct block_device *bm_mdev; /* meta device itself */ - struct net *net; -}; - -enum exstate4 { - PNFS_BLOCK_READWRITE_DATA = 0, - PNFS_BLOCK_READ_DATA = 1, - PNFS_BLOCK_INVALID_DATA = 2, /* mapped, but data is invalid */ - PNFS_BLOCK_NONE_DATA = 3 /* unmapped, it's a hole */ -}; - -#define MY_MAX_TAGS (15) /* tag bitnums used must be less than this */ - -struct my_tree { - sector_t mtt_step_size; /* Internal sector alignment */ - struct list_head mtt_stub; /* Should be a radix tree */ -}; - -struct pnfs_inval_markings { - spinlock_t im_lock; - struct my_tree im_tree; /* Sectors that need LAYOUTCOMMIT */ - sector_t im_block_size; /* Server blocksize in sectors */ - struct list_head im_extents; /* Short extents for INVAL->RW conversion */ -}; - -struct pnfs_inval_tracking { - struct list_head it_link; - int it_sector; - int it_tags; -}; - -/* sector_t fields are all in 512-byte sectors */ -struct pnfs_block_extent { - struct kref be_refcnt; - struct list_head be_node; /* link into lseg list */ - struct nfs4_deviceid be_devid; /* FIXME: could use device cache instead */ - struct block_device *be_mdev; - sector_t be_f_offset; /* the starting offset in the file */ - sector_t be_length; /* the size of the extent */ - sector_t be_v_offset; /* the starting offset in the volume */ - enum exstate4 be_state; /* the state of this extent */ - struct pnfs_inval_markings *be_inval; /* tracks INVAL->RW transition */ -}; - -/* Shortened extent used by LAYOUTCOMMIT */ -struct pnfs_block_short_extent { - struct list_head bse_node; - struct nfs4_deviceid bse_devid; - struct block_device *bse_mdev; - sector_t bse_f_offset; /* the starting offset in the file */ - sector_t bse_length; /* the size of the extent */ -}; - -static inline void -BL_INIT_INVAL_MARKS(struct pnfs_inval_markings *marks, sector_t blocksize) -{ - spin_lock_init(&marks->im_lock); - INIT_LIST_HEAD(&marks->im_tree.mtt_stub); - INIT_LIST_HEAD(&marks->im_extents); - marks->im_block_size = blocksize; - marks->im_tree.mtt_step_size = min((sector_t)PAGE_CACHE_SECTORS, - blocksize); -} - -enum extentclass4 { - RW_EXTENT = 0, /* READWRTE and INVAL */ - RO_EXTENT = 1, /* READ and NONE */ - EXTENT_LISTS = 2, -}; - -static inline int bl_choose_list(enum exstate4 state) -{ - if (state == PNFS_BLOCK_READ_DATA || state == PNFS_BLOCK_NONE_DATA) - return RO_EXTENT; - else - return RW_EXTENT; -} - -struct pnfs_block_layout { - struct pnfs_layout_hdr bl_layout; - struct pnfs_inval_markings bl_inval; /* tracks INVAL->RW transition */ - spinlock_t bl_ext_lock; /* Protects list manipulation */ - struct list_head bl_extents[EXTENT_LISTS]; /* R and RW extents */ - struct list_head bl_commit; /* Needs layout commit */ - struct list_head bl_committing; /* Layout committing */ - unsigned int bl_count; /* entries in bl_commit */ - sector_t bl_blocksize; /* Server blocksize in sectors */ -}; - -#define BLK_ID(lo) ((struct block_mount_id *)(NFS_SERVER(lo->plh_inode)->pnfs_ld_data)) - -static inline struct pnfs_block_layout * -BLK_LO2EXT(struct pnfs_layout_hdr *lo) -{ - return container_of(lo, struct pnfs_block_layout, bl_layout); -} - -static inline struct pnfs_block_layout * -BLK_LSEG2EXT(struct pnfs_layout_segment *lseg) -{ - return BLK_LO2EXT(lseg->pls_layout); -} - -struct bl_pipe_msg { - struct rpc_pipe_msg msg; - wait_queue_head_t *bl_wq; -}; - -struct bl_msg_hdr { - u8 type; - u16 totallen; /* length of entire message, including hdr itself */ -}; - -#define BL_DEVICE_UMOUNT 0x0 /* Umount--delete devices */ -#define BL_DEVICE_MOUNT 0x1 /* Mount--create devices*/ -#define BL_DEVICE_REQUEST_INIT 0x0 /* Start request */ -#define BL_DEVICE_REQUEST_PROC 0x1 /* User level process succeeds */ -#define BL_DEVICE_REQUEST_ERR 0x2 /* User level process fails */ - -/* blocklayoutdev.c */ -ssize_t bl_pipe_downcall(struct file *, const char __user *, size_t); -void bl_pipe_destroy_msg(struct rpc_pipe_msg *); -struct block_device *nfs4_blkdev_get(dev_t dev); -int nfs4_blkdev_put(struct block_device *bdev); -struct pnfs_block_dev *nfs4_blk_decode_device(struct nfs_server *server, - struct pnfs_device *dev); -int nfs4_blk_process_layoutget(struct pnfs_layout_hdr *lo, - struct nfs4_layoutget_res *lgr, gfp_t gfp_flags); - -/* blocklayoutdm.c */ -void bl_free_block_dev(struct pnfs_block_dev *bdev); - -/* extents.c */ -struct pnfs_block_extent * -bl_find_get_extent(struct pnfs_block_layout *bl, sector_t isect, - struct pnfs_block_extent **cow_read); -int bl_mark_sectors_init(struct pnfs_inval_markings *marks, - sector_t offset, sector_t length); -void bl_put_extent(struct pnfs_block_extent *be); -struct pnfs_block_extent *bl_alloc_extent(void); -int bl_is_sector_init(struct pnfs_inval_markings *marks, sector_t isect); -int encode_pnfs_block_layoutupdate(struct pnfs_block_layout *bl, - struct xdr_stream *xdr, - const struct nfs4_layoutcommit_args *arg); -void clean_pnfs_block_layoutupdate(struct pnfs_block_layout *bl, - const struct nfs4_layoutcommit_args *arg, - int status); -int bl_add_merge_extent(struct pnfs_block_layout *bl, - struct pnfs_block_extent *new); -int bl_mark_for_commit(struct pnfs_block_extent *be, - sector_t offset, sector_t length, - struct pnfs_block_short_extent *new); -int bl_push_one_short_extent(struct pnfs_inval_markings *marks); -struct pnfs_block_short_extent * -bl_pop_one_short_extent(struct pnfs_inval_markings *marks); -void bl_free_short_extents(struct pnfs_inval_markings *marks, int num_to_free); - -#endif /* FS_NFS_NFS4BLOCKLAYOUT_H */ diff --git a/ANDROID_3.4.5/fs/nfs/blocklayout/blocklayoutdev.c b/ANDROID_3.4.5/fs/nfs/blocklayout/blocklayoutdev.c deleted file mode 100644 index a5c88a55..00000000 --- a/ANDROID_3.4.5/fs/nfs/blocklayout/blocklayoutdev.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * linux/fs/nfs/blocklayout/blocklayoutdev.c - * - * Device operations for the pnfs nfs4 file layout driver. - * - * Copyright (c) 2006 The Regents of the University of Michigan. - * All rights reserved. - * - * Andy Adamson <andros@citi.umich.edu> - * Fred Isaman <iisaman@umich.edu> - * - * permission is granted to use, copy, create derivative works and - * redistribute this software and such derivative works for any purpose, - * so long as the name of the university of michigan is not used in - * any advertising or publicity pertaining to the use or distribution - * of this software without specific, written prior authorization. if - * the above copyright notice or any other identification of the - * university of michigan is included in any copy of any portion of - * this software, then the disclaimer below must also be included. - * - * this software is provided as is, without representation from the - * university of michigan as to its fitness for any purpose, and without - * warranty by the university of michigan of any kind, either express - * or implied, including without limitation the implied warranties of - * merchantability and fitness for a particular purpose. the regents - * of the university of michigan shall not be liable for any damages, - * including special, indirect, incidental, or consequential damages, - * with respect to any claim arising out or in connection with the use - * of the software, even if it has been or is hereafter advised of the - * possibility of such damages. - */ -#include <linux/module.h> -#include <linux/buffer_head.h> /* __bread */ - -#include <linux/genhd.h> -#include <linux/blkdev.h> -#include <linux/hash.h> - -#include "blocklayout.h" - -#define NFSDBG_FACILITY NFSDBG_PNFS_LD - -static int decode_sector_number(__be32 **rp, sector_t *sp) -{ - uint64_t s; - - *rp = xdr_decode_hyper(*rp, &s); - if (s & 0x1ff) { - printk(KERN_WARNING "NFS: %s: sector not aligned\n", __func__); - return -1; - } - *sp = s >> SECTOR_SHIFT; - return 0; -} - -/* Open a block_device by device number. */ -struct block_device *nfs4_blkdev_get(dev_t dev) -{ - struct block_device *bd; - - dprintk("%s enter\n", __func__); - bd = blkdev_get_by_dev(dev, FMODE_READ, NULL); - if (IS_ERR(bd)) - goto fail; - return bd; -fail: - dprintk("%s failed to open device : %ld\n", - __func__, PTR_ERR(bd)); - return NULL; -} - -/* - * Release the block device - */ -int nfs4_blkdev_put(struct block_device *bdev) -{ - dprintk("%s for device %d:%d\n", __func__, MAJOR(bdev->bd_dev), - MINOR(bdev->bd_dev)); - return blkdev_put(bdev, FMODE_READ); -} - -ssize_t bl_pipe_downcall(struct file *filp, const char __user *src, - size_t mlen) -{ - struct nfs_net *nn = net_generic(filp->f_dentry->d_sb->s_fs_info, - nfs_net_id); - - if (mlen != sizeof (struct bl_dev_msg)) - return -EINVAL; - - if (copy_from_user(&nn->bl_mount_reply, src, mlen) != 0) - return -EFAULT; - - wake_up(&nn->bl_wq); - - return mlen; -} - -void bl_pipe_destroy_msg(struct rpc_pipe_msg *msg) -{ - struct bl_pipe_msg *bl_pipe_msg = container_of(msg, struct bl_pipe_msg, msg); - - if (msg->errno >= 0) - return; - wake_up(bl_pipe_msg->bl_wq); -} - -/* - * Decodes pnfs_block_deviceaddr4 which is XDR encoded in dev->dev_addr_buf. - */ -struct pnfs_block_dev * -nfs4_blk_decode_device(struct nfs_server *server, - struct pnfs_device *dev) -{ - struct pnfs_block_dev *rv; - struct block_device *bd = NULL; - struct bl_pipe_msg bl_pipe_msg; - struct rpc_pipe_msg *msg = &bl_pipe_msg.msg; - struct bl_msg_hdr bl_msg = { - .type = BL_DEVICE_MOUNT, - .totallen = dev->mincount, - }; - uint8_t *dataptr; - DECLARE_WAITQUEUE(wq, current); - int offset, len, i, rc; - struct net *net = server->nfs_client->net; - struct nfs_net *nn = net_generic(net, nfs_net_id); - struct bl_dev_msg *reply = &nn->bl_mount_reply; - - dprintk("%s CREATING PIPEFS MESSAGE\n", __func__); - dprintk("%s: deviceid: %s, mincount: %d\n", __func__, dev->dev_id.data, - dev->mincount); - - bl_pipe_msg.bl_wq = &nn->bl_wq; - memset(msg, 0, sizeof(*msg)); - msg->data = kzalloc(sizeof(bl_msg) + dev->mincount, GFP_NOFS); - if (!msg->data) { - rv = ERR_PTR(-ENOMEM); - goto out; - } - - memcpy(msg->data, &bl_msg, sizeof(bl_msg)); - dataptr = (uint8_t *) msg->data; - len = dev->mincount; - offset = sizeof(bl_msg); - for (i = 0; len > 0; i++) { - memcpy(&dataptr[offset], page_address(dev->pages[i]), - len < PAGE_CACHE_SIZE ? len : PAGE_CACHE_SIZE); - len -= PAGE_CACHE_SIZE; - offset += PAGE_CACHE_SIZE; - } - msg->len = sizeof(bl_msg) + dev->mincount; - - dprintk("%s CALLING USERSPACE DAEMON\n", __func__); - add_wait_queue(&nn->bl_wq, &wq); - rc = rpc_queue_upcall(nn->bl_device_pipe, msg); - if (rc < 0) { - remove_wait_queue(&nn->bl_wq, &wq); - rv = ERR_PTR(rc); - goto out; - } - - set_current_state(TASK_UNINTERRUPTIBLE); - schedule(); - __set_current_state(TASK_RUNNING); - remove_wait_queue(&nn->bl_wq, &wq); - - if (reply->status != BL_DEVICE_REQUEST_PROC) { - dprintk("%s failed to open device: %d\n", - __func__, reply->status); - rv = ERR_PTR(-EINVAL); - goto out; - } - - bd = nfs4_blkdev_get(MKDEV(reply->major, reply->minor)); - if (IS_ERR(bd)) { - rc = PTR_ERR(bd); - dprintk("%s failed to open device : %d\n", __func__, rc); - rv = ERR_PTR(rc); - goto out; - } - - rv = kzalloc(sizeof(*rv), GFP_NOFS); - if (!rv) { - rv = ERR_PTR(-ENOMEM); - goto out; - } - - rv->bm_mdev = bd; - memcpy(&rv->bm_mdevid, &dev->dev_id, sizeof(struct nfs4_deviceid)); - rv->net = net; - dprintk("%s Created device %s with bd_block_size %u\n", - __func__, - bd->bd_disk->disk_name, - bd->bd_block_size); - -out: - kfree(msg->data); - return rv; -} - -/* Map deviceid returned by the server to constructed block_device */ -static struct block_device *translate_devid(struct pnfs_layout_hdr *lo, - struct nfs4_deviceid *id) -{ - struct block_device *rv = NULL; - struct block_mount_id *mid; - struct pnfs_block_dev *dev; - - dprintk("%s enter, lo=%p, id=%p\n", __func__, lo, id); - mid = BLK_ID(lo); - spin_lock(&mid->bm_lock); - list_for_each_entry(dev, &mid->bm_devlist, bm_node) { - if (memcmp(id->data, dev->bm_mdevid.data, - NFS4_DEVICEID4_SIZE) == 0) { - rv = dev->bm_mdev; - goto out; - } - } - out: - spin_unlock(&mid->bm_lock); - dprintk("%s returning %p\n", __func__, rv); - return rv; -} - -/* Tracks info needed to ensure extents in layout obey constraints of spec */ -struct layout_verification { - u32 mode; /* R or RW */ - u64 start; /* Expected start of next non-COW extent */ - u64 inval; /* Start of INVAL coverage */ - u64 cowread; /* End of COW read coverage */ -}; - -/* Verify the extent meets the layout requirements of the pnfs-block draft, - * section 2.3.1. - */ -static int verify_extent(struct pnfs_block_extent *be, - struct layout_verification *lv) -{ - if (lv->mode == IOMODE_READ) { - if (be->be_state == PNFS_BLOCK_READWRITE_DATA || - be->be_state == PNFS_BLOCK_INVALID_DATA) - return -EIO; - if (be->be_f_offset != lv->start) - return -EIO; - lv->start += be->be_length; - return 0; - } - /* lv->mode == IOMODE_RW */ - if (be->be_state == PNFS_BLOCK_READWRITE_DATA) { - if (be->be_f_offset != lv->start) - return -EIO; - if (lv->cowread > lv->start) - return -EIO; - lv->start += be->be_length; - lv->inval = lv->start; - return 0; - } else if (be->be_state == PNFS_BLOCK_INVALID_DATA) { - if (be->be_f_offset != lv->start) - return -EIO; - lv->start += be->be_length; - return 0; - } else if (be->be_state == PNFS_BLOCK_READ_DATA) { - if (be->be_f_offset > lv->start) - return -EIO; - if (be->be_f_offset < lv->inval) - return -EIO; - if (be->be_f_offset < lv->cowread) - return -EIO; - /* It looks like you might want to min this with lv->start, - * but you really don't. - */ - lv->inval = lv->inval + be->be_length; - lv->cowread = be->be_f_offset + be->be_length; - return 0; - } else - return -EIO; -} - -/* XDR decode pnfs_block_layout4 structure */ -int -nfs4_blk_process_layoutget(struct pnfs_layout_hdr *lo, - struct nfs4_layoutget_res *lgr, gfp_t gfp_flags) -{ - struct pnfs_block_layout *bl = BLK_LO2EXT(lo); - int i, status = -EIO; - uint32_t count; - struct pnfs_block_extent *be = NULL, *save; - struct xdr_stream stream; - struct xdr_buf buf; - struct page *scratch; - __be32 *p; - struct layout_verification lv = { - .mode = lgr->range.iomode, - .start = lgr->range.offset >> SECTOR_SHIFT, - .inval = lgr->range.offset >> SECTOR_SHIFT, - .cowread = lgr->range.offset >> SECTOR_SHIFT, - }; - LIST_HEAD(extents); - - dprintk("---> %s\n", __func__); - - scratch = alloc_page(gfp_flags); - if (!scratch) - return -ENOMEM; - - xdr_init_decode_pages(&stream, &buf, lgr->layoutp->pages, lgr->layoutp->len); - xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE); - - p = xdr_inline_decode(&stream, 4); - if (unlikely(!p)) - goto out_err; - - count = be32_to_cpup(p++); - - dprintk("%s enter, number of extents %i\n", __func__, count); - p = xdr_inline_decode(&stream, (28 + NFS4_DEVICEID4_SIZE) * count); - if (unlikely(!p)) - goto out_err; - - /* Decode individual extents, putting them in temporary - * staging area until whole layout is decoded to make error - * recovery easier. - */ - for (i = 0; i < count; i++) { - be = bl_alloc_extent(); - if (!be) { - status = -ENOMEM; - goto out_err; - } - memcpy(&be->be_devid, p, NFS4_DEVICEID4_SIZE); - p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE); - be->be_mdev = translate_devid(lo, &be->be_devid); - if (!be->be_mdev) - goto out_err; - - /* The next three values are read in as bytes, - * but stored as 512-byte sector lengths - */ - if (decode_sector_number(&p, &be->be_f_offset) < 0) - goto out_err; - if (decode_sector_number(&p, &be->be_length) < 0) - goto out_err; - if (decode_sector_number(&p, &be->be_v_offset) < 0) - goto out_err; - be->be_state = be32_to_cpup(p++); - if (be->be_state == PNFS_BLOCK_INVALID_DATA) - be->be_inval = &bl->bl_inval; - if (verify_extent(be, &lv)) { - dprintk("%s verify failed\n", __func__); - goto out_err; - } - list_add_tail(&be->be_node, &extents); - } - if (lgr->range.offset + lgr->range.length != - lv.start << SECTOR_SHIFT) { - dprintk("%s Final length mismatch\n", __func__); - be = NULL; - goto out_err; - } - if (lv.start < lv.cowread) { - dprintk("%s Final uncovered COW extent\n", __func__); - be = NULL; - goto out_err; - } - /* Extents decoded properly, now try to merge them in to - * existing layout extents. - */ - spin_lock(&bl->bl_ext_lock); - list_for_each_entry_safe(be, save, &extents, be_node) { - list_del(&be->be_node); - status = bl_add_merge_extent(bl, be); - if (status) { - spin_unlock(&bl->bl_ext_lock); - /* This is a fairly catastrophic error, as the - * entire layout extent lists are now corrupted. - * We should have some way to distinguish this. - */ - be = NULL; - goto out_err; - } - } - spin_unlock(&bl->bl_ext_lock); - status = 0; - out: - __free_page(scratch); - dprintk("%s returns %i\n", __func__, status); - return status; - - out_err: - bl_put_extent(be); - while (!list_empty(&extents)) { - be = list_first_entry(&extents, struct pnfs_block_extent, - be_node); - list_del(&be->be_node); - bl_put_extent(be); - } - goto out; -} diff --git a/ANDROID_3.4.5/fs/nfs/blocklayout/blocklayoutdm.c b/ANDROID_3.4.5/fs/nfs/blocklayout/blocklayoutdm.c deleted file mode 100644 index 737d839b..00000000 --- a/ANDROID_3.4.5/fs/nfs/blocklayout/blocklayoutdm.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * linux/fs/nfs/blocklayout/blocklayoutdm.c - * - * Module for the NFSv4.1 pNFS block layout driver. - * - * Copyright (c) 2007 The Regents of the University of Michigan. - * All rights reserved. - * - * Fred Isaman <iisaman@umich.edu> - * Andy Adamson <andros@citi.umich.edu> - * - * permission is granted to use, copy, create derivative works and - * redistribute this software and such derivative works for any purpose, - * so long as the name of the university of michigan is not used in - * any advertising or publicity pertaining to the use or distribution - * of this software without specific, written prior authorization. if - * the above copyright notice or any other identification of the - * university of michigan is included in any copy of any portion of - * this software, then the disclaimer below must also be included. - * - * this software is provided as is, without representation from the - * university of michigan as to its fitness for any purpose, and without - * warranty by the university of michigan of any kind, either express - * or implied, including without limitation the implied warranties of - * merchantability and fitness for a particular purpose. the regents - * of the university of michigan shall not be liable for any damages, - * including special, indirect, incidental, or consequential damages, - * with respect to any claim arising out or in connection with the use - * of the software, even if it has been or is hereafter advised of the - * possibility of such damages. - */ - -#include <linux/genhd.h> /* gendisk - used in a dprintk*/ -#include <linux/sched.h> -#include <linux/hash.h> - -#include "blocklayout.h" - -#define NFSDBG_FACILITY NFSDBG_PNFS_LD - -static void dev_remove(struct net *net, dev_t dev) -{ - struct bl_pipe_msg bl_pipe_msg; - struct rpc_pipe_msg *msg = &bl_pipe_msg.msg; - struct bl_dev_msg bl_umount_request; - struct bl_msg_hdr bl_msg = { - .type = BL_DEVICE_UMOUNT, - .totallen = sizeof(bl_umount_request), - }; - uint8_t *dataptr; - DECLARE_WAITQUEUE(wq, current); - struct nfs_net *nn = net_generic(net, nfs_net_id); - - dprintk("Entering %s\n", __func__); - - bl_pipe_msg.bl_wq = &nn->bl_wq; - memset(msg, 0, sizeof(*msg)); - msg->data = kzalloc(1 + sizeof(bl_umount_request), GFP_NOFS); - if (!msg->data) - goto out; - - memset(&bl_umount_request, 0, sizeof(bl_umount_request)); - bl_umount_request.major = MAJOR(dev); - bl_umount_request.minor = MINOR(dev); - - memcpy(msg->data, &bl_msg, sizeof(bl_msg)); - dataptr = (uint8_t *) msg->data; - memcpy(&dataptr[sizeof(bl_msg)], &bl_umount_request, sizeof(bl_umount_request)); - msg->len = sizeof(bl_msg) + bl_msg.totallen; - - add_wait_queue(&nn->bl_wq, &wq); - if (rpc_queue_upcall(nn->bl_device_pipe, msg) < 0) { - remove_wait_queue(&nn->bl_wq, &wq); - goto out; - } - - set_current_state(TASK_UNINTERRUPTIBLE); - schedule(); - __set_current_state(TASK_RUNNING); - remove_wait_queue(&nn->bl_wq, &wq); - -out: - kfree(msg->data); -} - -/* - * Release meta device - */ -static void nfs4_blk_metadev_release(struct pnfs_block_dev *bdev) -{ - int rv; - - dprintk("%s Releasing\n", __func__); - rv = nfs4_blkdev_put(bdev->bm_mdev); - if (rv) - printk(KERN_ERR "NFS: %s nfs4_blkdev_put returns %d\n", - __func__, rv); - - dev_remove(bdev->net, bdev->bm_mdev->bd_dev); -} - -void bl_free_block_dev(struct pnfs_block_dev *bdev) -{ - if (bdev) { - if (bdev->bm_mdev) { - dprintk("%s Removing DM device: %d:%d\n", - __func__, - MAJOR(bdev->bm_mdev->bd_dev), - MINOR(bdev->bm_mdev->bd_dev)); - nfs4_blk_metadev_release(bdev); - } - kfree(bdev); - } -} diff --git a/ANDROID_3.4.5/fs/nfs/blocklayout/extents.c b/ANDROID_3.4.5/fs/nfs/blocklayout/extents.c deleted file mode 100644 index 1f9a6032..00000000 --- a/ANDROID_3.4.5/fs/nfs/blocklayout/extents.c +++ /dev/null @@ -1,909 +0,0 @@ -/* - * linux/fs/nfs/blocklayout/blocklayout.h - * - * Module for the NFSv4.1 pNFS block layout driver. - * - * Copyright (c) 2006 The Regents of the University of Michigan. - * All rights reserved. - * - * Andy Adamson <andros@citi.umich.edu> - * Fred Isaman <iisaman@umich.edu> - * - * permission is granted to use, copy, create derivative works and - * redistribute this software and such derivative works for any purpose, - * so long as the name of the university of michigan is not used in - * any advertising or publicity pertaining to the use or distribution - * of this software without specific, written prior authorization. if - * the above copyright notice or any other identification of the - * university of michigan is included in any copy of any portion of - * this software, then the disclaimer below must also be included. - * - * this software is provided as is, without representation from the - * university of michigan as to its fitness for any purpose, and without - * warranty by the university of michigan of any kind, either express - * or implied, including without limitation the implied warranties of - * merchantability and fitness for a particular purpose. the regents - * of the university of michigan shall not be liable for any damages, - * including special, indirect, incidental, or consequential damages, - * with respect to any claim arising out or in connection with the use - * of the software, even if it has been or is hereafter advised of the - * possibility of such damages. - */ - -#include "blocklayout.h" -#define NFSDBG_FACILITY NFSDBG_PNFS_LD - -/* Bit numbers */ -#define EXTENT_INITIALIZED 0 -#define EXTENT_WRITTEN 1 -#define EXTENT_IN_COMMIT 2 -#define INTERNAL_EXISTS MY_MAX_TAGS -#define INTERNAL_MASK ((1 << INTERNAL_EXISTS) - 1) - -/* Returns largest t<=s s.t. t%base==0 */ -static inline sector_t normalize(sector_t s, int base) -{ - sector_t tmp = s; /* Since do_div modifies its argument */ - return s - do_div(tmp, base); -} - -static inline sector_t normalize_up(sector_t s, int base) -{ - return normalize(s + base - 1, base); -} - -/* Complete stub using list while determine API wanted */ - -/* Returns tags, or negative */ -static int32_t _find_entry(struct my_tree *tree, u64 s) -{ - struct pnfs_inval_tracking *pos; - - dprintk("%s(%llu) enter\n", __func__, s); - list_for_each_entry_reverse(pos, &tree->mtt_stub, it_link) { - if (pos->it_sector > s) - continue; - else if (pos->it_sector == s) - return pos->it_tags & INTERNAL_MASK; - else - break; - } - return -ENOENT; -} - -static inline -int _has_tag(struct my_tree *tree, u64 s, int32_t tag) -{ - int32_t tags; - - dprintk("%s(%llu, %i) enter\n", __func__, s, tag); - s = normalize(s, tree->mtt_step_size); - tags = _find_entry(tree, s); - if ((tags < 0) || !(tags & (1 << tag))) - return 0; - else - return 1; -} - -/* Creates entry with tag, or if entry already exists, unions tag to it. - * If storage is not NULL, newly created entry will use it. - * Returns number of entries added, or negative on error. - */ -static int _add_entry(struct my_tree *tree, u64 s, int32_t tag, - struct pnfs_inval_tracking *storage) -{ - int found = 0; - struct pnfs_inval_tracking *pos; - - dprintk("%s(%llu, %i, %p) enter\n", __func__, s, tag, storage); - list_for_each_entry_reverse(pos, &tree->mtt_stub, it_link) { - if (pos->it_sector > s) - continue; - else if (pos->it_sector == s) { - found = 1; - break; - } else - break; - } - if (found) { - pos->it_tags |= (1 << tag); - return 0; - } else { - struct pnfs_inval_tracking *new; - new = storage; - new->it_sector = s; - new->it_tags = (1 << tag); - list_add(&new->it_link, &pos->it_link); - return 1; - } -} - -/* XXXX Really want option to not create */ -/* Over range, unions tag with existing entries, else creates entry with tag */ -static int _set_range(struct my_tree *tree, int32_t tag, u64 s, u64 length) -{ - u64 i; - - dprintk("%s(%i, %llu, %llu) enter\n", __func__, tag, s, length); - for (i = normalize(s, tree->mtt_step_size); i < s + length; - i += tree->mtt_step_size) - if (_add_entry(tree, i, tag, NULL)) - return -ENOMEM; - return 0; -} - -/* Ensure that future operations on given range of tree will not malloc */ -static int _preload_range(struct pnfs_inval_markings *marks, - u64 offset, u64 length) -{ - u64 start, end, s; - int count, i, used = 0, status = -ENOMEM; - struct pnfs_inval_tracking **storage; - struct my_tree *tree = &marks->im_tree; - - dprintk("%s(%llu, %llu) enter\n", __func__, offset, length); - start = normalize(offset, tree->mtt_step_size); - end = normalize_up(offset + length, tree->mtt_step_size); - count = (int)(end - start) / (int)tree->mtt_step_size; - - /* Pre-malloc what memory we might need */ - storage = kcalloc(count, sizeof(*storage), GFP_NOFS); - if (!storage) - return -ENOMEM; - for (i = 0; i < count; i++) { - storage[i] = kmalloc(sizeof(struct pnfs_inval_tracking), - GFP_NOFS); - if (!storage[i]) - goto out_cleanup; - } - - spin_lock_bh(&marks->im_lock); - for (s = start; s < end; s += tree->mtt_step_size) - used += _add_entry(tree, s, INTERNAL_EXISTS, storage[used]); - spin_unlock_bh(&marks->im_lock); - - status = 0; - - out_cleanup: - for (i = used; i < count; i++) { - if (!storage[i]) - break; - kfree(storage[i]); - } - kfree(storage); - return status; -} - -/* We are relying on page lock to serialize this */ -int bl_is_sector_init(struct pnfs_inval_markings *marks, sector_t isect) -{ - int rv; - - spin_lock_bh(&marks->im_lock); - rv = _has_tag(&marks->im_tree, isect, EXTENT_INITIALIZED); - spin_unlock_bh(&marks->im_lock); - return rv; -} - -/* Assume start, end already sector aligned */ -static int -_range_has_tag(struct my_tree *tree, u64 start, u64 end, int32_t tag) -{ - struct pnfs_inval_tracking *pos; - u64 expect = 0; - - dprintk("%s(%llu, %llu, %i) enter\n", __func__, start, end, tag); - list_for_each_entry_reverse(pos, &tree->mtt_stub, it_link) { - if (pos->it_sector >= end) - continue; - if (!expect) { - if ((pos->it_sector == end - tree->mtt_step_size) && - (pos->it_tags & (1 << tag))) { - expect = pos->it_sector - tree->mtt_step_size; - if (pos->it_sector < tree->mtt_step_size || expect < start) - return 1; - continue; - } else { - return 0; - } - } - if (pos->it_sector != expect || !(pos->it_tags & (1 << tag))) - return 0; - expect -= tree->mtt_step_size; - if (expect < start) - return 1; - } - return 0; -} - -static int is_range_written(struct pnfs_inval_markings *marks, - sector_t start, sector_t end) -{ - int rv; - - spin_lock_bh(&marks->im_lock); - rv = _range_has_tag(&marks->im_tree, start, end, EXTENT_WRITTEN); - spin_unlock_bh(&marks->im_lock); - return rv; -} - -/* Marks sectors in [offest, offset_length) as having been initialized. - * All lengths are step-aligned, where step is min(pagesize, blocksize). - * Currently assumes offset is page-aligned - */ -int bl_mark_sectors_init(struct pnfs_inval_markings *marks, - sector_t offset, sector_t length) -{ - sector_t start, end; - - dprintk("%s(offset=%llu,len=%llu) enter\n", - __func__, (u64)offset, (u64)length); - - start = normalize(offset, marks->im_block_size); - end = normalize_up(offset + length, marks->im_block_size); - if (_preload_range(marks, start, end - start)) - goto outerr; - - spin_lock_bh(&marks->im_lock); - if (_set_range(&marks->im_tree, EXTENT_INITIALIZED, offset, length)) - goto out_unlock; - spin_unlock_bh(&marks->im_lock); - - return 0; - -out_unlock: - spin_unlock_bh(&marks->im_lock); -outerr: - return -ENOMEM; -} - -/* Marks sectors in [offest, offset+length) as having been written to disk. - * All lengths should be block aligned. - */ -static int mark_written_sectors(struct pnfs_inval_markings *marks, - sector_t offset, sector_t length) -{ - int status; - - dprintk("%s(offset=%llu,len=%llu) enter\n", __func__, - (u64)offset, (u64)length); - spin_lock_bh(&marks->im_lock); - status = _set_range(&marks->im_tree, EXTENT_WRITTEN, offset, length); - spin_unlock_bh(&marks->im_lock); - return status; -} - -static void print_short_extent(struct pnfs_block_short_extent *be) -{ - dprintk("PRINT SHORT EXTENT extent %p\n", be); - if (be) { - dprintk(" be_f_offset %llu\n", (u64)be->bse_f_offset); - dprintk(" be_length %llu\n", (u64)be->bse_length); - } -} - -static void print_clist(struct list_head *list, unsigned int count) -{ - struct pnfs_block_short_extent *be; - unsigned int i = 0; - - ifdebug(FACILITY) { - printk(KERN_DEBUG "****************\n"); - printk(KERN_DEBUG "Extent list looks like:\n"); - list_for_each_entry(be, list, bse_node) { - i++; - print_short_extent(be); - } - if (i != count) - printk(KERN_DEBUG "\n\nExpected %u entries\n\n\n", count); - printk(KERN_DEBUG "****************\n"); - } -} - -/* Note: In theory, we should do more checking that devid's match between - * old and new, but if they don't, the lists are too corrupt to salvage anyway. - */ -/* Note this is very similar to bl_add_merge_extent */ -static void add_to_commitlist(struct pnfs_block_layout *bl, - struct pnfs_block_short_extent *new) -{ - struct list_head *clist = &bl->bl_commit; - struct pnfs_block_short_extent *old, *save; - sector_t end = new->bse_f_offset + new->bse_length; - - dprintk("%s enter\n", __func__); - print_short_extent(new); - print_clist(clist, bl->bl_count); - bl->bl_count++; - /* Scan for proper place to insert, extending new to the left - * as much as possible. - */ - list_for_each_entry_safe(old, save, clist, bse_node) { - if (new->bse_f_offset < old->bse_f_offset) - break; - if (end <= old->bse_f_offset + old->bse_length) { - /* Range is already in list */ - bl->bl_count--; - kfree(new); - return; - } else if (new->bse_f_offset <= - old->bse_f_offset + old->bse_length) { - /* new overlaps or abuts existing be */ - if (new->bse_mdev == old->bse_mdev) { - /* extend new to fully replace old */ - new->bse_length += new->bse_f_offset - - old->bse_f_offset; - new->bse_f_offset = old->bse_f_offset; - list_del(&old->bse_node); - bl->bl_count--; - kfree(old); - } - } - } - /* Note that if we never hit the above break, old will not point to a - * valid extent. However, in that case &old->bse_node==list. - */ - list_add_tail(&new->bse_node, &old->bse_node); - /* Scan forward for overlaps. If we find any, extend new and - * remove the overlapped extent. - */ - old = list_prepare_entry(new, clist, bse_node); - list_for_each_entry_safe_continue(old, save, clist, bse_node) { - if (end < old->bse_f_offset) - break; - /* new overlaps or abuts old */ - if (new->bse_mdev == old->bse_mdev) { - if (end < old->bse_f_offset + old->bse_length) { - /* extend new to fully cover old */ - end = old->bse_f_offset + old->bse_length; - new->bse_length = end - new->bse_f_offset; - } - list_del(&old->bse_node); - bl->bl_count--; - kfree(old); - } - } - dprintk("%s: after merging\n", __func__); - print_clist(clist, bl->bl_count); -} - -/* Note the range described by offset, length is guaranteed to be contained - * within be. - * new will be freed, either by this function or add_to_commitlist if they - * decide not to use it, or after LAYOUTCOMMIT uses it in the commitlist. - */ -int bl_mark_for_commit(struct pnfs_block_extent *be, - sector_t offset, sector_t length, - struct pnfs_block_short_extent *new) -{ - sector_t new_end, end = offset + length; - struct pnfs_block_layout *bl = container_of(be->be_inval, - struct pnfs_block_layout, - bl_inval); - - mark_written_sectors(be->be_inval, offset, length); - /* We want to add the range to commit list, but it must be - * block-normalized, and verified that the normalized range has - * been entirely written to disk. - */ - new->bse_f_offset = offset; - offset = normalize(offset, bl->bl_blocksize); - if (offset < new->bse_f_offset) { - if (is_range_written(be->be_inval, offset, new->bse_f_offset)) - new->bse_f_offset = offset; - else - new->bse_f_offset = offset + bl->bl_blocksize; - } - new_end = normalize_up(end, bl->bl_blocksize); - if (end < new_end) { - if (is_range_written(be->be_inval, end, new_end)) - end = new_end; - else - end = new_end - bl->bl_blocksize; - } - if (end <= new->bse_f_offset) { - kfree(new); - return 0; - } - new->bse_length = end - new->bse_f_offset; - new->bse_devid = be->be_devid; - new->bse_mdev = be->be_mdev; - - spin_lock(&bl->bl_ext_lock); - add_to_commitlist(bl, new); - spin_unlock(&bl->bl_ext_lock); - return 0; -} - -static void print_bl_extent(struct pnfs_block_extent *be) -{ - dprintk("PRINT EXTENT extent %p\n", be); - if (be) { - dprintk(" be_f_offset %llu\n", (u64)be->be_f_offset); - dprintk(" be_length %llu\n", (u64)be->be_length); - dprintk(" be_v_offset %llu\n", (u64)be->be_v_offset); - dprintk(" be_state %d\n", be->be_state); - } -} - -static void -destroy_extent(struct kref *kref) -{ - struct pnfs_block_extent *be; - - be = container_of(kref, struct pnfs_block_extent, be_refcnt); - dprintk("%s be=%p\n", __func__, be); - kfree(be); -} - -void -bl_put_extent(struct pnfs_block_extent *be) -{ - if (be) { - dprintk("%s enter %p (%i)\n", __func__, be, - atomic_read(&be->be_refcnt.refcount)); - kref_put(&be->be_refcnt, destroy_extent); - } -} - -struct pnfs_block_extent *bl_alloc_extent(void) -{ - struct pnfs_block_extent *be; - - be = kmalloc(sizeof(struct pnfs_block_extent), GFP_NOFS); - if (!be) - return NULL; - INIT_LIST_HEAD(&be->be_node); - kref_init(&be->be_refcnt); - be->be_inval = NULL; - return be; -} - -static void print_elist(struct list_head *list) -{ - struct pnfs_block_extent *be; - dprintk("****************\n"); - dprintk("Extent list looks like:\n"); - list_for_each_entry(be, list, be_node) { - print_bl_extent(be); - } - dprintk("****************\n"); -} - -static inline int -extents_consistent(struct pnfs_block_extent *old, struct pnfs_block_extent *new) -{ - /* Note this assumes new->be_f_offset >= old->be_f_offset */ - return (new->be_state == old->be_state) && - ((new->be_state == PNFS_BLOCK_NONE_DATA) || - ((new->be_v_offset - old->be_v_offset == - new->be_f_offset - old->be_f_offset) && - new->be_mdev == old->be_mdev)); -} - -/* Adds new to appropriate list in bl, modifying new and removing existing - * extents as appropriate to deal with overlaps. - * - * See bl_find_get_extent for list constraints. - * - * Refcount on new is already set. If end up not using it, or error out, - * need to put the reference. - * - * bl->bl_ext_lock is held by caller. - */ -int -bl_add_merge_extent(struct pnfs_block_layout *bl, - struct pnfs_block_extent *new) -{ - struct pnfs_block_extent *be, *tmp; - sector_t end = new->be_f_offset + new->be_length; - struct list_head *list; - - dprintk("%s enter with be=%p\n", __func__, new); - print_bl_extent(new); - list = &bl->bl_extents[bl_choose_list(new->be_state)]; - print_elist(list); - - /* Scan for proper place to insert, extending new to the left - * as much as possible. - */ - list_for_each_entry_safe_reverse(be, tmp, list, be_node) { - if (new->be_f_offset >= be->be_f_offset + be->be_length) - break; - if (new->be_f_offset >= be->be_f_offset) { - if (end <= be->be_f_offset + be->be_length) { - /* new is a subset of existing be*/ - if (extents_consistent(be, new)) { - dprintk("%s: new is subset, ignoring\n", - __func__); - bl_put_extent(new); - return 0; - } else { - goto out_err; - } - } else { - /* |<-- be -->| - * |<-- new -->| */ - if (extents_consistent(be, new)) { - /* extend new to fully replace be */ - new->be_length += new->be_f_offset - - be->be_f_offset; - new->be_f_offset = be->be_f_offset; - new->be_v_offset = be->be_v_offset; - dprintk("%s: removing %p\n", __func__, be); - list_del(&be->be_node); - bl_put_extent(be); - } else { - goto out_err; - } - } - } else if (end >= be->be_f_offset + be->be_length) { - /* new extent overlap existing be */ - if (extents_consistent(be, new)) { - /* extend new to fully replace be */ - dprintk("%s: removing %p\n", __func__, be); - list_del(&be->be_node); - bl_put_extent(be); - } else { - goto out_err; - } - } else if (end > be->be_f_offset) { - /* |<-- be -->| - *|<-- new -->| */ - if (extents_consistent(new, be)) { - /* extend new to fully replace be */ - new->be_length += be->be_f_offset + be->be_length - - new->be_f_offset - new->be_length; - dprintk("%s: removing %p\n", __func__, be); - list_del(&be->be_node); - bl_put_extent(be); - } else { - goto out_err; - } - } - } - /* Note that if we never hit the above break, be will not point to a - * valid extent. However, in that case &be->be_node==list. - */ - list_add(&new->be_node, &be->be_node); - dprintk("%s: inserting new\n", __func__); - print_elist(list); - /* FIXME - The per-list consistency checks have all been done, - * should now check cross-list consistency. - */ - return 0; - - out_err: - bl_put_extent(new); - return -EIO; -} - -/* Returns extent, or NULL. If a second READ extent exists, it is returned - * in cow_read, if given. - * - * The extents are kept in two seperate ordered lists, one for READ and NONE, - * one for READWRITE and INVALID. Within each list, we assume: - * 1. Extents are ordered by file offset. - * 2. For any given isect, there is at most one extents that matches. - */ -struct pnfs_block_extent * -bl_find_get_extent(struct pnfs_block_layout *bl, sector_t isect, - struct pnfs_block_extent **cow_read) -{ - struct pnfs_block_extent *be, *cow, *ret; - int i; - - dprintk("%s enter with isect %llu\n", __func__, (u64)isect); - cow = ret = NULL; - spin_lock(&bl->bl_ext_lock); - for (i = 0; i < EXTENT_LISTS; i++) { - list_for_each_entry_reverse(be, &bl->bl_extents[i], be_node) { - if (isect >= be->be_f_offset + be->be_length) - break; - if (isect >= be->be_f_offset) { - /* We have found an extent */ - dprintk("%s Get %p (%i)\n", __func__, be, - atomic_read(&be->be_refcnt.refcount)); - kref_get(&be->be_refcnt); - if (!ret) - ret = be; - else if (be->be_state != PNFS_BLOCK_READ_DATA) - bl_put_extent(be); - else - cow = be; - break; - } - } - if (ret && - (!cow_read || ret->be_state != PNFS_BLOCK_INVALID_DATA)) - break; - } - spin_unlock(&bl->bl_ext_lock); - if (cow_read) - *cow_read = cow; - print_bl_extent(ret); - return ret; -} - -/* Similar to bl_find_get_extent, but called with lock held, and ignores cow */ -static struct pnfs_block_extent * -bl_find_get_extent_locked(struct pnfs_block_layout *bl, sector_t isect) -{ - struct pnfs_block_extent *be, *ret = NULL; - int i; - - dprintk("%s enter with isect %llu\n", __func__, (u64)isect); - for (i = 0; i < EXTENT_LISTS; i++) { - if (ret) - break; - list_for_each_entry_reverse(be, &bl->bl_extents[i], be_node) { - if (isect >= be->be_f_offset + be->be_length) - break; - if (isect >= be->be_f_offset) { - /* We have found an extent */ - dprintk("%s Get %p (%i)\n", __func__, be, - atomic_read(&be->be_refcnt.refcount)); - kref_get(&be->be_refcnt); - ret = be; - break; - } - } - } - print_bl_extent(ret); - return ret; -} - -int -encode_pnfs_block_layoutupdate(struct pnfs_block_layout *bl, - struct xdr_stream *xdr, - const struct nfs4_layoutcommit_args *arg) -{ - struct pnfs_block_short_extent *lce, *save; - unsigned int count = 0; - __be32 *p, *xdr_start; - - dprintk("%s enter\n", __func__); - /* BUG - creation of bl_commit is buggy - need to wait for - * entire block to be marked WRITTEN before it can be added. - */ - spin_lock(&bl->bl_ext_lock); - /* Want to adjust for possible truncate */ - /* We now want to adjust argument range */ - - /* XDR encode the ranges found */ - xdr_start = xdr_reserve_space(xdr, 8); - if (!xdr_start) - goto out; - list_for_each_entry_safe(lce, save, &bl->bl_commit, bse_node) { - p = xdr_reserve_space(xdr, 7 * 4 + sizeof(lce->bse_devid.data)); - if (!p) - break; - p = xdr_encode_opaque_fixed(p, lce->bse_devid.data, NFS4_DEVICEID4_SIZE); - p = xdr_encode_hyper(p, lce->bse_f_offset << SECTOR_SHIFT); - p = xdr_encode_hyper(p, lce->bse_length << SECTOR_SHIFT); - p = xdr_encode_hyper(p, 0LL); - *p++ = cpu_to_be32(PNFS_BLOCK_READWRITE_DATA); - list_del(&lce->bse_node); - list_add_tail(&lce->bse_node, &bl->bl_committing); - bl->bl_count--; - count++; - } - xdr_start[0] = cpu_to_be32((xdr->p - xdr_start - 1) * 4); - xdr_start[1] = cpu_to_be32(count); -out: - spin_unlock(&bl->bl_ext_lock); - dprintk("%s found %i ranges\n", __func__, count); - return 0; -} - -/* Helper function to set_to_rw that initialize a new extent */ -static void -_prep_new_extent(struct pnfs_block_extent *new, - struct pnfs_block_extent *orig, - sector_t offset, sector_t length, int state) -{ - kref_init(&new->be_refcnt); - /* don't need to INIT_LIST_HEAD(&new->be_node) */ - memcpy(&new->be_devid, &orig->be_devid, sizeof(struct nfs4_deviceid)); - new->be_mdev = orig->be_mdev; - new->be_f_offset = offset; - new->be_length = length; - new->be_v_offset = orig->be_v_offset - orig->be_f_offset + offset; - new->be_state = state; - new->be_inval = orig->be_inval; -} - -/* Tries to merge be with extent in front of it in list. - * Frees storage if not used. - */ -static struct pnfs_block_extent * -_front_merge(struct pnfs_block_extent *be, struct list_head *head, - struct pnfs_block_extent *storage) -{ - struct pnfs_block_extent *prev; - - if (!storage) - goto no_merge; - if (&be->be_node == head || be->be_node.prev == head) - goto no_merge; - prev = list_entry(be->be_node.prev, struct pnfs_block_extent, be_node); - if ((prev->be_f_offset + prev->be_length != be->be_f_offset) || - !extents_consistent(prev, be)) - goto no_merge; - _prep_new_extent(storage, prev, prev->be_f_offset, - prev->be_length + be->be_length, prev->be_state); - list_replace(&prev->be_node, &storage->be_node); - bl_put_extent(prev); - list_del(&be->be_node); - bl_put_extent(be); - return storage; - - no_merge: - kfree(storage); - return be; -} - -static u64 -set_to_rw(struct pnfs_block_layout *bl, u64 offset, u64 length) -{ - u64 rv = offset + length; - struct pnfs_block_extent *be, *e1, *e2, *e3, *new, *old; - struct pnfs_block_extent *children[3]; - struct pnfs_block_extent *merge1 = NULL, *merge2 = NULL; - int i = 0, j; - - dprintk("%s(%llu, %llu)\n", __func__, offset, length); - /* Create storage for up to three new extents e1, e2, e3 */ - e1 = kmalloc(sizeof(*e1), GFP_ATOMIC); - e2 = kmalloc(sizeof(*e2), GFP_ATOMIC); - e3 = kmalloc(sizeof(*e3), GFP_ATOMIC); - /* BUG - we are ignoring any failure */ - if (!e1 || !e2 || !e3) - goto out_nosplit; - - spin_lock(&bl->bl_ext_lock); - be = bl_find_get_extent_locked(bl, offset); - rv = be->be_f_offset + be->be_length; - if (be->be_state != PNFS_BLOCK_INVALID_DATA) { - spin_unlock(&bl->bl_ext_lock); - goto out_nosplit; - } - /* Add e* to children, bumping e*'s krefs */ - if (be->be_f_offset != offset) { - _prep_new_extent(e1, be, be->be_f_offset, - offset - be->be_f_offset, - PNFS_BLOCK_INVALID_DATA); - children[i++] = e1; - print_bl_extent(e1); - } else - merge1 = e1; - _prep_new_extent(e2, be, offset, - min(length, be->be_f_offset + be->be_length - offset), - PNFS_BLOCK_READWRITE_DATA); - children[i++] = e2; - print_bl_extent(e2); - if (offset + length < be->be_f_offset + be->be_length) { - _prep_new_extent(e3, be, e2->be_f_offset + e2->be_length, - be->be_f_offset + be->be_length - - offset - length, - PNFS_BLOCK_INVALID_DATA); - children[i++] = e3; - print_bl_extent(e3); - } else - merge2 = e3; - - /* Remove be from list, and insert the e* */ - /* We don't get refs on e*, since this list is the base reference - * set when init'ed. - */ - if (i < 3) - children[i] = NULL; - new = children[0]; - list_replace(&be->be_node, &new->be_node); - bl_put_extent(be); - new = _front_merge(new, &bl->bl_extents[RW_EXTENT], merge1); - for (j = 1; j < i; j++) { - old = new; - new = children[j]; - list_add(&new->be_node, &old->be_node); - } - if (merge2) { - /* This is a HACK, should just create a _back_merge function */ - new = list_entry(new->be_node.next, - struct pnfs_block_extent, be_node); - new = _front_merge(new, &bl->bl_extents[RW_EXTENT], merge2); - } - spin_unlock(&bl->bl_ext_lock); - - /* Since we removed the base reference above, be is now scheduled for - * destruction. - */ - bl_put_extent(be); - dprintk("%s returns %llu after split\n", __func__, rv); - return rv; - - out_nosplit: - kfree(e1); - kfree(e2); - kfree(e3); - dprintk("%s returns %llu without splitting\n", __func__, rv); - return rv; -} - -void -clean_pnfs_block_layoutupdate(struct pnfs_block_layout *bl, - const struct nfs4_layoutcommit_args *arg, - int status) -{ - struct pnfs_block_short_extent *lce, *save; - - dprintk("%s status %d\n", __func__, status); - list_for_each_entry_safe(lce, save, &bl->bl_committing, bse_node) { - if (likely(!status)) { - u64 offset = lce->bse_f_offset; - u64 end = offset + lce->bse_length; - - do { - offset = set_to_rw(bl, offset, end - offset); - } while (offset < end); - list_del(&lce->bse_node); - - kfree(lce); - } else { - list_del(&lce->bse_node); - spin_lock(&bl->bl_ext_lock); - add_to_commitlist(bl, lce); - spin_unlock(&bl->bl_ext_lock); - } - } -} - -int bl_push_one_short_extent(struct pnfs_inval_markings *marks) -{ - struct pnfs_block_short_extent *new; - - new = kmalloc(sizeof(*new), GFP_NOFS); - if (unlikely(!new)) - return -ENOMEM; - - spin_lock_bh(&marks->im_lock); - list_add(&new->bse_node, &marks->im_extents); - spin_unlock_bh(&marks->im_lock); - - return 0; -} - -struct pnfs_block_short_extent * -bl_pop_one_short_extent(struct pnfs_inval_markings *marks) -{ - struct pnfs_block_short_extent *rv = NULL; - - spin_lock_bh(&marks->im_lock); - if (!list_empty(&marks->im_extents)) { - rv = list_entry((&marks->im_extents)->next, - struct pnfs_block_short_extent, bse_node); - list_del_init(&rv->bse_node); - } - spin_unlock_bh(&marks->im_lock); - - return rv; -} - -void bl_free_short_extents(struct pnfs_inval_markings *marks, int num_to_free) -{ - struct pnfs_block_short_extent *se = NULL, *tmp; - - if (num_to_free <= 0) - return; - - spin_lock(&marks->im_lock); - list_for_each_entry_safe(se, tmp, &marks->im_extents, bse_node) { - list_del(&se->bse_node); - kfree(se); - if (--num_to_free == 0) - break; - } - spin_unlock(&marks->im_lock); - - BUG_ON(num_to_free > 0); -} diff --git a/ANDROID_3.4.5/fs/nfs/cache_lib.c b/ANDROID_3.4.5/fs/nfs/cache_lib.c deleted file mode 100644 index dded2636..00000000 --- a/ANDROID_3.4.5/fs/nfs/cache_lib.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * linux/fs/nfs/cache_lib.c - * - * Helper routines for the NFS client caches - * - * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com> - */ -#include <linux/kmod.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/mount.h> -#include <linux/namei.h> -#include <linux/slab.h> -#include <linux/sunrpc/cache.h> -#include <linux/sunrpc/rpc_pipe_fs.h> -#include <net/net_namespace.h> - -#include "cache_lib.h" - -#define NFS_CACHE_UPCALL_PATHLEN 256 -#define NFS_CACHE_UPCALL_TIMEOUT 15 - -static char nfs_cache_getent_prog[NFS_CACHE_UPCALL_PATHLEN] = - "/sbin/nfs_cache_getent"; -static unsigned long nfs_cache_getent_timeout = NFS_CACHE_UPCALL_TIMEOUT; - -module_param_string(cache_getent, nfs_cache_getent_prog, - sizeof(nfs_cache_getent_prog), 0600); -MODULE_PARM_DESC(cache_getent, "Path to the client cache upcall program"); -module_param_named(cache_getent_timeout, nfs_cache_getent_timeout, ulong, 0600); -MODULE_PARM_DESC(cache_getent_timeout, "Timeout (in seconds) after which " - "the cache upcall is assumed to have failed"); - -int nfs_cache_upcall(struct cache_detail *cd, char *entry_name) -{ - static char *envp[] = { "HOME=/", - "TERM=linux", - "PATH=/sbin:/usr/sbin:/bin:/usr/bin", - NULL - }; - char *argv[] = { - nfs_cache_getent_prog, - cd->name, - entry_name, - NULL - }; - int ret = -EACCES; - - if (nfs_cache_getent_prog[0] == '\0') - goto out; - ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); - /* - * Disable the upcall mechanism if we're getting an ENOENT or - * EACCES error. The admin can re-enable it on the fly by using - * sysfs to set the 'cache_getent' parameter once the problem - * has been fixed. - */ - if (ret == -ENOENT || ret == -EACCES) - nfs_cache_getent_prog[0] = '\0'; -out: - return ret > 0 ? 0 : ret; -} - -/* - * Deferred request handling - */ -void nfs_cache_defer_req_put(struct nfs_cache_defer_req *dreq) -{ - if (atomic_dec_and_test(&dreq->count)) - kfree(dreq); -} - -static void nfs_dns_cache_revisit(struct cache_deferred_req *d, int toomany) -{ - struct nfs_cache_defer_req *dreq; - - dreq = container_of(d, struct nfs_cache_defer_req, deferred_req); - - complete_all(&dreq->completion); - nfs_cache_defer_req_put(dreq); -} - -static struct cache_deferred_req *nfs_dns_cache_defer(struct cache_req *req) -{ - struct nfs_cache_defer_req *dreq; - - dreq = container_of(req, struct nfs_cache_defer_req, req); - dreq->deferred_req.revisit = nfs_dns_cache_revisit; - atomic_inc(&dreq->count); - - return &dreq->deferred_req; -} - -struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void) -{ - struct nfs_cache_defer_req *dreq; - - dreq = kzalloc(sizeof(*dreq), GFP_KERNEL); - if (dreq) { - init_completion(&dreq->completion); - atomic_set(&dreq->count, 1); - dreq->req.defer = nfs_dns_cache_defer; - } - return dreq; -} - -int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq) -{ - if (wait_for_completion_timeout(&dreq->completion, - nfs_cache_getent_timeout * HZ) == 0) - return -ETIMEDOUT; - return 0; -} - -int nfs_cache_register_sb(struct super_block *sb, struct cache_detail *cd) -{ - int ret; - struct dentry *dir; - - dir = rpc_d_lookup_sb(sb, "cache"); - BUG_ON(dir == NULL); - ret = sunrpc_cache_register_pipefs(dir, cd->name, 0600, cd); - dput(dir); - return ret; -} - -int nfs_cache_register_net(struct net *net, struct cache_detail *cd) -{ - struct super_block *pipefs_sb; - int ret = 0; - - pipefs_sb = rpc_get_sb_net(net); - if (pipefs_sb) { - ret = nfs_cache_register_sb(pipefs_sb, cd); - rpc_put_sb_net(net); - } - return ret; -} - -void nfs_cache_unregister_sb(struct super_block *sb, struct cache_detail *cd) -{ - if (cd->u.pipefs.dir) - sunrpc_cache_unregister_pipefs(cd); -} - -void nfs_cache_unregister_net(struct net *net, struct cache_detail *cd) -{ - struct super_block *pipefs_sb; - - pipefs_sb = rpc_get_sb_net(net); - if (pipefs_sb) { - nfs_cache_unregister_sb(pipefs_sb, cd); - rpc_put_sb_net(net); - } -} - -void nfs_cache_init(struct cache_detail *cd) -{ - sunrpc_init_cache_detail(cd); -} - -void nfs_cache_destroy(struct cache_detail *cd) -{ - sunrpc_destroy_cache_detail(cd); -} diff --git a/ANDROID_3.4.5/fs/nfs/cache_lib.h b/ANDROID_3.4.5/fs/nfs/cache_lib.h deleted file mode 100644 index 317db95e..00000000 --- a/ANDROID_3.4.5/fs/nfs/cache_lib.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Helper routines for the NFS client caches - * - * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com> - */ - -#include <linux/completion.h> -#include <linux/sunrpc/cache.h> -#include <linux/atomic.h> - -/* - * Deferred request handling - */ -struct nfs_cache_defer_req { - struct cache_req req; - struct cache_deferred_req deferred_req; - struct completion completion; - atomic_t count; -}; - -extern int nfs_cache_upcall(struct cache_detail *cd, char *entry_name); -extern struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void); -extern void nfs_cache_defer_req_put(struct nfs_cache_defer_req *dreq); -extern int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq); - -extern void nfs_cache_init(struct cache_detail *cd); -extern void nfs_cache_destroy(struct cache_detail *cd); -extern int nfs_cache_register_net(struct net *net, struct cache_detail *cd); -extern void nfs_cache_unregister_net(struct net *net, struct cache_detail *cd); -extern int nfs_cache_register_sb(struct super_block *sb, - struct cache_detail *cd); -extern void nfs_cache_unregister_sb(struct super_block *sb, - struct cache_detail *cd); diff --git a/ANDROID_3.4.5/fs/nfs/callback.c b/ANDROID_3.4.5/fs/nfs/callback.c deleted file mode 100644 index 38a44c67..00000000 --- a/ANDROID_3.4.5/fs/nfs/callback.c +++ /dev/null @@ -1,412 +0,0 @@ -/* - * linux/fs/nfs/callback.c - * - * Copyright (C) 2004 Trond Myklebust - * - * NFSv4 callback handling - */ - -#include <linux/completion.h> -#include <linux/ip.h> -#include <linux/module.h> -#include <linux/sunrpc/svc.h> -#include <linux/sunrpc/svcsock.h> -#include <linux/nfs_fs.h> -#include <linux/mutex.h> -#include <linux/freezer.h> -#include <linux/kthread.h> -#include <linux/sunrpc/svcauth_gss.h> -#include <linux/sunrpc/bc_xprt.h> - -#include <net/inet_sock.h> - -#include "nfs4_fs.h" -#include "callback.h" -#include "internal.h" - -#define NFSDBG_FACILITY NFSDBG_CALLBACK - -struct nfs_callback_data { - unsigned int users; - struct svc_serv *serv; - struct svc_rqst *rqst; - struct task_struct *task; -}; - -static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1]; -static DEFINE_MUTEX(nfs_callback_mutex); -static struct svc_program nfs4_callback_program; - -unsigned int nfs_callback_set_tcpport; -unsigned short nfs_callback_tcpport; -unsigned short nfs_callback_tcpport6; -#define NFS_CALLBACK_MAXPORTNR (65535U) - -static int param_set_portnr(const char *val, const struct kernel_param *kp) -{ - unsigned long num; - int ret; - - if (!val) - return -EINVAL; - ret = strict_strtoul(val, 0, &num); - if (ret == -EINVAL || num > NFS_CALLBACK_MAXPORTNR) - return -EINVAL; - *((unsigned int *)kp->arg) = num; - return 0; -} -static struct kernel_param_ops param_ops_portnr = { - .set = param_set_portnr, - .get = param_get_uint, -}; -#define param_check_portnr(name, p) __param_check(name, p, unsigned int); - -module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644); - -/* - * This is the NFSv4 callback kernel thread. - */ -static int -nfs4_callback_svc(void *vrqstp) -{ - int err, preverr = 0; - struct svc_rqst *rqstp = vrqstp; - - set_freezable(); - - while (!kthread_should_stop()) { - /* - * Listen for a request on the socket - */ - err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT); - if (err == -EAGAIN || err == -EINTR) { - preverr = err; - continue; - } - if (err < 0) { - if (err != preverr) { - printk(KERN_WARNING "NFS: %s: unexpected error " - "from svc_recv (%d)\n", __func__, err); - preverr = err; - } - schedule_timeout_uninterruptible(HZ); - continue; - } - preverr = err; - svc_process(rqstp); - } - return 0; -} - -/* - * Prepare to bring up the NFSv4 callback service - */ -static struct svc_rqst * -nfs4_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) -{ - int ret; - - ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET, - nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); - if (ret <= 0) - goto out_err; - nfs_callback_tcpport = ret; - dprintk("NFS: Callback listener port = %u (af %u)\n", - nfs_callback_tcpport, PF_INET); - - ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET6, - nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); - if (ret > 0) { - nfs_callback_tcpport6 = ret; - dprintk("NFS: Callback listener port = %u (af %u)\n", - nfs_callback_tcpport6, PF_INET6); - } else if (ret == -EAFNOSUPPORT) - ret = 0; - else - goto out_err; - - return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE); - -out_err: - if (ret == 0) - ret = -ENOMEM; - return ERR_PTR(ret); -} - -#if defined(CONFIG_NFS_V4_1) -/* - * The callback service for NFSv4.1 callbacks - */ -static int -nfs41_callback_svc(void *vrqstp) -{ - struct svc_rqst *rqstp = vrqstp; - struct svc_serv *serv = rqstp->rq_server; - struct rpc_rqst *req; - int error; - DEFINE_WAIT(wq); - - set_freezable(); - - while (!kthread_should_stop()) { - prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE); - spin_lock_bh(&serv->sv_cb_lock); - if (!list_empty(&serv->sv_cb_list)) { - req = list_first_entry(&serv->sv_cb_list, - struct rpc_rqst, rq_bc_list); - list_del(&req->rq_bc_list); - spin_unlock_bh(&serv->sv_cb_lock); - dprintk("Invoking bc_svc_process()\n"); - error = bc_svc_process(serv, req, rqstp); - dprintk("bc_svc_process() returned w/ error code= %d\n", - error); - } else { - spin_unlock_bh(&serv->sv_cb_lock); - schedule(); - } - finish_wait(&serv->sv_cb_waitq, &wq); - } - return 0; -} - -/* - * Bring up the NFSv4.1 callback service - */ -static struct svc_rqst * -nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) -{ - struct svc_rqst *rqstp; - int ret; - - /* - * Create an svc_sock for the back channel service that shares the - * fore channel connection. - * Returns the input port (0) and sets the svc_serv bc_xprt on success - */ - ret = svc_create_xprt(serv, "tcp-bc", &init_net, PF_INET, 0, - SVC_SOCK_ANONYMOUS); - if (ret < 0) { - rqstp = ERR_PTR(ret); - goto out; - } - - /* - * Save the svc_serv in the transport so that it can - * be referenced when the session backchannel is initialized - */ - xprt->bc_serv = serv; - - INIT_LIST_HEAD(&serv->sv_cb_list); - spin_lock_init(&serv->sv_cb_lock); - init_waitqueue_head(&serv->sv_cb_waitq); - rqstp = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE); - if (IS_ERR(rqstp)) { - svc_xprt_put(serv->sv_bc_xprt); - serv->sv_bc_xprt = NULL; - } -out: - dprintk("--> %s return %ld\n", __func__, - IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0); - return rqstp; -} - -static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, - struct svc_serv *serv, struct rpc_xprt *xprt, - struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) -{ - if (minorversion) { - *rqstpp = nfs41_callback_up(serv, xprt); - *callback_svc = nfs41_callback_svc; - } - return minorversion; -} - -static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, - struct nfs_callback_data *cb_info) -{ - if (minorversion) - xprt->bc_serv = cb_info->serv; -} -#else -static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, - struct svc_serv *serv, struct rpc_xprt *xprt, - struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) -{ - return 0; -} - -static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, - struct nfs_callback_data *cb_info) -{ -} -#endif /* CONFIG_NFS_V4_1 */ - -/* - * Bring up the callback thread if it is not already up. - */ -int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) -{ - struct svc_serv *serv = NULL; - struct svc_rqst *rqstp; - int (*callback_svc)(void *vrqstp); - struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; - char svc_name[12]; - int ret = 0; - int minorversion_setup; - struct net *net = &init_net; - - mutex_lock(&nfs_callback_mutex); - if (cb_info->users++ || cb_info->task != NULL) { - nfs_callback_bc_serv(minorversion, xprt, cb_info); - goto out; - } - serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); - if (!serv) { - ret = -ENOMEM; - goto out_err; - } - - ret = svc_bind(serv, net); - if (ret < 0) { - printk(KERN_WARNING "NFS: bind callback service failed\n"); - goto out_err; - } - - minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion, - serv, xprt, &rqstp, &callback_svc); - if (!minorversion_setup) { - /* v4.0 callback setup */ - rqstp = nfs4_callback_up(serv, xprt); - callback_svc = nfs4_callback_svc; - } - - if (IS_ERR(rqstp)) { - ret = PTR_ERR(rqstp); - goto out_err; - } - - svc_sock_update_bufs(serv); - - sprintf(svc_name, "nfsv4.%u-svc", minorversion); - cb_info->serv = serv; - cb_info->rqst = rqstp; - cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name); - if (IS_ERR(cb_info->task)) { - ret = PTR_ERR(cb_info->task); - svc_exit_thread(cb_info->rqst); - cb_info->rqst = NULL; - cb_info->task = NULL; - goto out_err; - } -out: - /* - * svc_create creates the svc_serv with sv_nrthreads == 1, and then - * svc_prepare_thread increments that. So we need to call svc_destroy - * on both success and failure so that the refcount is 1 when the - * thread exits. - */ - if (serv) - svc_destroy(serv); - mutex_unlock(&nfs_callback_mutex); - return ret; -out_err: - dprintk("NFS: Couldn't create callback socket or server thread; " - "err = %d\n", ret); - cb_info->users--; - if (serv) - svc_shutdown_net(serv, net); - goto out; -} - -/* - * Kill the callback thread if it's no longer being used. - */ -void nfs_callback_down(int minorversion) -{ - struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; - - mutex_lock(&nfs_callback_mutex); - cb_info->users--; - if (cb_info->users == 0 && cb_info->task != NULL) { - kthread_stop(cb_info->task); - svc_shutdown_net(cb_info->serv, &init_net); - svc_exit_thread(cb_info->rqst); - cb_info->serv = NULL; - cb_info->rqst = NULL; - cb_info->task = NULL; - } - mutex_unlock(&nfs_callback_mutex); -} - -/* Boolean check of RPC_AUTH_GSS principal */ -int -check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp) -{ - char *p = svc_gss_principal(rqstp); - - if (rqstp->rq_authop->flavour != RPC_AUTH_GSS) - return 1; - - /* No RPC_AUTH_GSS on NFSv4.1 back channel yet */ - if (clp->cl_minorversion != 0) - return 0; - /* - * It might just be a normal user principal, in which case - * userspace won't bother to tell us the name at all. - */ - if (p == NULL) - return 0; - - /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */ - - if (memcmp(p, "nfs@", 4) != 0) - return 0; - p += 4; - if (strcmp(p, clp->cl_hostname) != 0) - return 0; - return 1; -} - -/* - * pg_authenticate method for nfsv4 callback threads. - * - * The authflavor has been negotiated, so an incorrect flavor is a server - * bug. Drop packets with incorrect authflavor. - * - * All other checking done after NFS decoding where the nfs_client can be - * found in nfs4_callback_compound - */ -static int nfs_callback_authenticate(struct svc_rqst *rqstp) -{ - switch (rqstp->rq_authop->flavour) { - case RPC_AUTH_NULL: - if (rqstp->rq_proc != CB_NULL) - return SVC_DROP; - break; - case RPC_AUTH_GSS: - /* No RPC_AUTH_GSS support yet in NFSv4.1 */ - if (svc_is_backchannel(rqstp)) - return SVC_DROP; - } - return SVC_OK; -} - -/* - * Define NFS4 callback program - */ -static struct svc_version *nfs4_callback_version[] = { - [1] = &nfs4_callback_version1, - [4] = &nfs4_callback_version4, -}; - -static struct svc_stat nfs4_callback_stats; - -static struct svc_program nfs4_callback_program = { - .pg_prog = NFS4_CALLBACK, /* RPC service number */ - .pg_nvers = ARRAY_SIZE(nfs4_callback_version), /* Number of entries */ - .pg_vers = nfs4_callback_version, /* version table */ - .pg_name = "NFSv4 callback", /* service name */ - .pg_class = "nfs", /* authentication class */ - .pg_stats = &nfs4_callback_stats, - .pg_authenticate = nfs_callback_authenticate, -}; diff --git a/ANDROID_3.4.5/fs/nfs/callback.h b/ANDROID_3.4.5/fs/nfs/callback.h deleted file mode 100644 index a5527c90..00000000 --- a/ANDROID_3.4.5/fs/nfs/callback.h +++ /dev/null @@ -1,214 +0,0 @@ -/* - * linux/fs/nfs/callback.h - * - * Copyright (C) 2004 Trond Myklebust - * - * NFSv4 callback definitions - */ -#ifndef __LINUX_FS_NFS_CALLBACK_H -#define __LINUX_FS_NFS_CALLBACK_H -#include <linux/sunrpc/svc.h> - -#define NFS4_CALLBACK 0x40000000 -#define NFS4_CALLBACK_XDRSIZE 2048 -#define NFS4_CALLBACK_BUFSIZE (1024 + NFS4_CALLBACK_XDRSIZE) - -enum nfs4_callback_procnum { - CB_NULL = 0, - CB_COMPOUND = 1, -}; - -enum nfs4_callback_opnum { - OP_CB_GETATTR = 3, - OP_CB_RECALL = 4, -/* Callback operations new to NFSv4.1 */ - OP_CB_LAYOUTRECALL = 5, - OP_CB_NOTIFY = 6, - OP_CB_PUSH_DELEG = 7, - OP_CB_RECALL_ANY = 8, - OP_CB_RECALLABLE_OBJ_AVAIL = 9, - OP_CB_RECALL_SLOT = 10, - OP_CB_SEQUENCE = 11, - OP_CB_WANTS_CANCELLED = 12, - OP_CB_NOTIFY_LOCK = 13, - OP_CB_NOTIFY_DEVICEID = 14, - OP_CB_ILLEGAL = 10044, -}; - -struct cb_process_state { - __be32 drc_status; - struct nfs_client *clp; - u32 slotid; - struct net *net; -}; - -struct cb_compound_hdr_arg { - unsigned int taglen; - const char *tag; - unsigned int minorversion; - unsigned int cb_ident; /* v4.0 callback identifier */ - unsigned nops; -}; - -struct cb_compound_hdr_res { - __be32 *status; - unsigned int taglen; - const char *tag; - __be32 *nops; -}; - -struct cb_getattrargs { - struct sockaddr *addr; - struct nfs_fh fh; - uint32_t bitmap[2]; -}; - -struct cb_getattrres { - __be32 status; - uint32_t bitmap[2]; - uint64_t size; - uint64_t change_attr; - struct timespec ctime; - struct timespec mtime; -}; - -struct cb_recallargs { - struct sockaddr *addr; - struct nfs_fh fh; - nfs4_stateid stateid; - uint32_t truncate; -}; - -#if defined(CONFIG_NFS_V4_1) - -struct referring_call { - uint32_t rc_sequenceid; - uint32_t rc_slotid; -}; - -struct referring_call_list { - struct nfs4_sessionid rcl_sessionid; - uint32_t rcl_nrefcalls; - struct referring_call *rcl_refcalls; -}; - -struct cb_sequenceargs { - struct sockaddr *csa_addr; - struct nfs4_sessionid csa_sessionid; - uint32_t csa_sequenceid; - uint32_t csa_slotid; - uint32_t csa_highestslotid; - uint32_t csa_cachethis; - uint32_t csa_nrclists; - struct referring_call_list *csa_rclists; -}; - -struct cb_sequenceres { - __be32 csr_status; - struct nfs4_sessionid csr_sessionid; - uint32_t csr_sequenceid; - uint32_t csr_slotid; - uint32_t csr_highestslotid; - uint32_t csr_target_highestslotid; -}; - -extern __be32 nfs4_callback_sequence(struct cb_sequenceargs *args, - struct cb_sequenceres *res, - struct cb_process_state *cps); - -extern int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation, - const nfs4_stateid *stateid); - -#define RCA4_TYPE_MASK_RDATA_DLG 0 -#define RCA4_TYPE_MASK_WDATA_DLG 1 -#define RCA4_TYPE_MASK_DIR_DLG 2 -#define RCA4_TYPE_MASK_FILE_LAYOUT 3 -#define RCA4_TYPE_MASK_BLK_LAYOUT 4 -#define RCA4_TYPE_MASK_OBJ_LAYOUT_MIN 8 -#define RCA4_TYPE_MASK_OBJ_LAYOUT_MAX 9 -#define RCA4_TYPE_MASK_OTHER_LAYOUT_MIN 12 -#define RCA4_TYPE_MASK_OTHER_LAYOUT_MAX 15 -#define RCA4_TYPE_MASK_ALL 0xf31f - -struct cb_recallanyargs { - struct sockaddr *craa_addr; - uint32_t craa_objs_to_keep; - uint32_t craa_type_mask; -}; - -extern __be32 nfs4_callback_recallany(struct cb_recallanyargs *args, - void *dummy, - struct cb_process_state *cps); - -struct cb_recallslotargs { - struct sockaddr *crsa_addr; - uint32_t crsa_target_max_slots; -}; -extern __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, - void *dummy, - struct cb_process_state *cps); - -struct cb_layoutrecallargs { - struct sockaddr *cbl_addr; - uint32_t cbl_recall_type; - uint32_t cbl_layout_type; - uint32_t cbl_layoutchanged; - union { - struct { - struct nfs_fh cbl_fh; - struct pnfs_layout_range cbl_range; - nfs4_stateid cbl_stateid; - }; - struct nfs_fsid cbl_fsid; - }; -}; - -extern __be32 nfs4_callback_layoutrecall( - struct cb_layoutrecallargs *args, - void *dummy, struct cb_process_state *cps); - -extern void nfs4_check_drain_bc_complete(struct nfs4_session *ses); - -struct cb_devicenotifyitem { - uint32_t cbd_notify_type; - uint32_t cbd_layout_type; - struct nfs4_deviceid cbd_dev_id; - uint32_t cbd_immediate; -}; - -struct cb_devicenotifyargs { - int ndevs; - struct cb_devicenotifyitem *devs; -}; - -extern __be32 nfs4_callback_devicenotify( - struct cb_devicenotifyargs *args, - void *dummy, struct cb_process_state *cps); - -#endif /* CONFIG_NFS_V4_1 */ -extern int check_gss_callback_principal(struct nfs_client *, struct svc_rqst *); -extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, - struct cb_getattrres *res, - struct cb_process_state *cps); -extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy, - struct cb_process_state *cps); -#ifdef CONFIG_NFS_V4 -extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt); -extern void nfs_callback_down(int minorversion); -extern int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation, - const nfs4_stateid *stateid); -extern int nfs4_set_callback_sessionid(struct nfs_client *clp); -#endif /* CONFIG_NFS_V4 */ -/* - * nfs41: Callbacks are expected to not cause substantial latency, - * so we limit their concurrency to 1 by setting up the maximum number - * of slots for the backchannel. - */ -#define NFS41_BC_MIN_CALLBACKS 1 -#define NFS41_BC_MAX_CALLBACKS 1 - -extern unsigned int nfs_callback_set_tcpport; -extern unsigned short nfs_callback_tcpport; -extern unsigned short nfs_callback_tcpport6; - -#endif /* __LINUX_FS_NFS_CALLBACK_H */ diff --git a/ANDROID_3.4.5/fs/nfs/callback_proc.c b/ANDROID_3.4.5/fs/nfs/callback_proc.c deleted file mode 100644 index 1b5d809a..00000000 --- a/ANDROID_3.4.5/fs/nfs/callback_proc.c +++ /dev/null @@ -1,569 +0,0 @@ -/* - * linux/fs/nfs/callback_proc.c - * - * Copyright (C) 2004 Trond Myklebust - * - * NFSv4 callback procedures - */ -#include <linux/nfs4.h> -#include <linux/nfs_fs.h> -#include <linux/slab.h> -#include <linux/rcupdate.h> -#include "nfs4_fs.h" -#include "callback.h" -#include "delegation.h" -#include "internal.h" -#include "pnfs.h" - -#ifdef NFS_DEBUG -#define NFSDBG_FACILITY NFSDBG_CALLBACK -#endif - -__be32 nfs4_callback_getattr(struct cb_getattrargs *args, - struct cb_getattrres *res, - struct cb_process_state *cps) -{ - struct nfs_delegation *delegation; - struct nfs_inode *nfsi; - struct inode *inode; - - res->status = htonl(NFS4ERR_OP_NOT_IN_SESSION); - if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */ - goto out; - - res->bitmap[0] = res->bitmap[1] = 0; - res->status = htonl(NFS4ERR_BADHANDLE); - - dprintk_rcu("NFS: GETATTR callback request from %s\n", - rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR)); - - inode = nfs_delegation_find_inode(cps->clp, &args->fh); - if (inode == NULL) - goto out; - nfsi = NFS_I(inode); - rcu_read_lock(); - delegation = rcu_dereference(nfsi->delegation); - if (delegation == NULL || (delegation->type & FMODE_WRITE) == 0) - goto out_iput; - res->size = i_size_read(inode); - res->change_attr = delegation->change_attr; - if (nfsi->npages != 0) - res->change_attr++; - res->ctime = inode->i_ctime; - res->mtime = inode->i_mtime; - res->bitmap[0] = (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE) & - args->bitmap[0]; - res->bitmap[1] = (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY) & - args->bitmap[1]; - res->status = 0; -out_iput: - rcu_read_unlock(); - iput(inode); -out: - dprintk("%s: exit with status = %d\n", __func__, ntohl(res->status)); - return res->status; -} - -__be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy, - struct cb_process_state *cps) -{ - struct inode *inode; - __be32 res; - - res = htonl(NFS4ERR_OP_NOT_IN_SESSION); - if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */ - goto out; - - dprintk_rcu("NFS: RECALL callback request from %s\n", - rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR)); - - res = htonl(NFS4ERR_BADHANDLE); - inode = nfs_delegation_find_inode(cps->clp, &args->fh); - if (inode == NULL) - goto out; - /* Set up a helper thread to actually return the delegation */ - switch (nfs_async_inode_return_delegation(inode, &args->stateid)) { - case 0: - res = 0; - break; - case -ENOENT: - res = htonl(NFS4ERR_BAD_STATEID); - break; - default: - res = htonl(NFS4ERR_RESOURCE); - } - iput(inode); -out: - dprintk("%s: exit with status = %d\n", __func__, ntohl(res)); - return res; -} - -#if defined(CONFIG_NFS_V4_1) - -/* - * Lookup a layout by filehandle. - * - * Note: gets a refcount on the layout hdr and on its respective inode. - * Caller must put the layout hdr and the inode. - * - * TODO: keep track of all layouts (and delegations) in a hash table - * hashed by filehandle. - */ -static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp, struct nfs_fh *fh) -{ - struct nfs_server *server; - struct inode *ino; - struct pnfs_layout_hdr *lo; - - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { - list_for_each_entry(lo, &server->layouts, plh_layouts) { - if (nfs_compare_fh(fh, &NFS_I(lo->plh_inode)->fh)) - continue; - ino = igrab(lo->plh_inode); - if (!ino) - continue; - get_layout_hdr(lo); - return lo; - } - } - - return NULL; -} - -static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp, struct nfs_fh *fh) -{ - struct pnfs_layout_hdr *lo; - - spin_lock(&clp->cl_lock); - rcu_read_lock(); - lo = get_layout_by_fh_locked(clp, fh); - rcu_read_unlock(); - spin_unlock(&clp->cl_lock); - - return lo; -} - -static u32 initiate_file_draining(struct nfs_client *clp, - struct cb_layoutrecallargs *args) -{ - struct inode *ino; - struct pnfs_layout_hdr *lo; - u32 rv = NFS4ERR_NOMATCHING_LAYOUT; - LIST_HEAD(free_me_list); - - lo = get_layout_by_fh(clp, &args->cbl_fh); - if (!lo) - return NFS4ERR_NOMATCHING_LAYOUT; - - ino = lo->plh_inode; - spin_lock(&ino->i_lock); - if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags) || - mark_matching_lsegs_invalid(lo, &free_me_list, - &args->cbl_range)) - rv = NFS4ERR_DELAY; - else - rv = NFS4ERR_NOMATCHING_LAYOUT; - pnfs_set_layout_stateid(lo, &args->cbl_stateid, true); - spin_unlock(&ino->i_lock); - pnfs_free_lseg_list(&free_me_list); - put_layout_hdr(lo); - iput(ino); - return rv; -} - -static u32 initiate_bulk_draining(struct nfs_client *clp, - struct cb_layoutrecallargs *args) -{ - struct nfs_server *server; - struct pnfs_layout_hdr *lo; - struct inode *ino; - u32 rv = NFS4ERR_NOMATCHING_LAYOUT; - struct pnfs_layout_hdr *tmp; - LIST_HEAD(recall_list); - LIST_HEAD(free_me_list); - struct pnfs_layout_range range = { - .iomode = IOMODE_ANY, - .offset = 0, - .length = NFS4_MAX_UINT64, - }; - - spin_lock(&clp->cl_lock); - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { - if ((args->cbl_recall_type == RETURN_FSID) && - memcmp(&server->fsid, &args->cbl_fsid, - sizeof(struct nfs_fsid))) - continue; - - list_for_each_entry(lo, &server->layouts, plh_layouts) { - if (!igrab(lo->plh_inode)) - continue; - get_layout_hdr(lo); - BUG_ON(!list_empty(&lo->plh_bulk_recall)); - list_add(&lo->plh_bulk_recall, &recall_list); - } - } - rcu_read_unlock(); - spin_unlock(&clp->cl_lock); - - list_for_each_entry_safe(lo, tmp, - &recall_list, plh_bulk_recall) { - ino = lo->plh_inode; - spin_lock(&ino->i_lock); - set_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags); - if (mark_matching_lsegs_invalid(lo, &free_me_list, &range)) - rv = NFS4ERR_DELAY; - list_del_init(&lo->plh_bulk_recall); - spin_unlock(&ino->i_lock); - pnfs_free_lseg_list(&free_me_list); - put_layout_hdr(lo); - iput(ino); - } - return rv; -} - -static u32 do_callback_layoutrecall(struct nfs_client *clp, - struct cb_layoutrecallargs *args) -{ - u32 res; - - dprintk("%s enter, type=%i\n", __func__, args->cbl_recall_type); - if (args->cbl_recall_type == RETURN_FILE) - res = initiate_file_draining(clp, args); - else - res = initiate_bulk_draining(clp, args); - dprintk("%s returning %i\n", __func__, res); - return res; - -} - -__be32 nfs4_callback_layoutrecall(struct cb_layoutrecallargs *args, - void *dummy, struct cb_process_state *cps) -{ - u32 res; - - dprintk("%s: -->\n", __func__); - - if (cps->clp) - res = do_callback_layoutrecall(cps->clp, args); - else - res = NFS4ERR_OP_NOT_IN_SESSION; - - dprintk("%s: exit with status = %d\n", __func__, res); - return cpu_to_be32(res); -} - -static void pnfs_recall_all_layouts(struct nfs_client *clp) -{ - struct cb_layoutrecallargs args; - - /* Pretend we got a CB_LAYOUTRECALL(ALL) */ - memset(&args, 0, sizeof(args)); - args.cbl_recall_type = RETURN_ALL; - /* FIXME we ignore errors, what should we do? */ - do_callback_layoutrecall(clp, &args); -} - -__be32 nfs4_callback_devicenotify(struct cb_devicenotifyargs *args, - void *dummy, struct cb_process_state *cps) -{ - int i; - __be32 res = 0; - struct nfs_client *clp = cps->clp; - struct nfs_server *server = NULL; - - dprintk("%s: -->\n", __func__); - - if (!clp) { - res = cpu_to_be32(NFS4ERR_OP_NOT_IN_SESSION); - goto out; - } - - for (i = 0; i < args->ndevs; i++) { - struct cb_devicenotifyitem *dev = &args->devs[i]; - - if (!server || - server->pnfs_curr_ld->id != dev->cbd_layout_type) { - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) - if (server->pnfs_curr_ld && - server->pnfs_curr_ld->id == dev->cbd_layout_type) { - rcu_read_unlock(); - goto found; - } - rcu_read_unlock(); - dprintk("%s: layout type %u not found\n", - __func__, dev->cbd_layout_type); - continue; - } - - found: - if (dev->cbd_notify_type == NOTIFY_DEVICEID4_CHANGE) - dprintk("%s: NOTIFY_DEVICEID4_CHANGE not supported, " - "deleting instead\n", __func__); - nfs4_delete_deviceid(server->pnfs_curr_ld, clp, &dev->cbd_dev_id); - } - -out: - kfree(args->devs); - dprintk("%s: exit with status = %u\n", - __func__, be32_to_cpu(res)); - return res; -} - -/* - * Validate the sequenceID sent by the server. - * Return success if the sequenceID is one more than what we last saw on - * this slot, accounting for wraparound. Increments the slot's sequence. - * - * We don't yet implement a duplicate request cache, instead we set the - * back channel ca_maxresponsesize_cached to zero. This is OK for now - * since we only currently implement idempotent callbacks anyway. - * - * We have a single slot backchannel at this time, so we don't bother - * checking the used_slots bit array on the table. The lower layer guarantees - * a single outstanding callback request at a time. - */ -static __be32 -validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args) -{ - struct nfs4_slot *slot; - - dprintk("%s enter. slotid %d seqid %d\n", - __func__, args->csa_slotid, args->csa_sequenceid); - - if (args->csa_slotid >= NFS41_BC_MAX_CALLBACKS) - return htonl(NFS4ERR_BADSLOT); - - slot = tbl->slots + args->csa_slotid; - dprintk("%s slot table seqid: %d\n", __func__, slot->seq_nr); - - /* Normal */ - if (likely(args->csa_sequenceid == slot->seq_nr + 1)) { - slot->seq_nr++; - goto out_ok; - } - - /* Replay */ - if (args->csa_sequenceid == slot->seq_nr) { - dprintk("%s seqid %d is a replay\n", - __func__, args->csa_sequenceid); - /* Signal process_op to set this error on next op */ - if (args->csa_cachethis == 0) - return htonl(NFS4ERR_RETRY_UNCACHED_REP); - - /* The ca_maxresponsesize_cached is 0 with no DRC */ - else if (args->csa_cachethis == 1) - return htonl(NFS4ERR_REP_TOO_BIG_TO_CACHE); - } - - /* Wraparound */ - if (args->csa_sequenceid == 1 && (slot->seq_nr + 1) == 0) { - slot->seq_nr = 1; - goto out_ok; - } - - /* Misordered request */ - return htonl(NFS4ERR_SEQ_MISORDERED); -out_ok: - tbl->highest_used_slotid = args->csa_slotid; - return htonl(NFS4_OK); -} - -/* - * For each referring call triple, check the session's slot table for - * a match. If the slot is in use and the sequence numbers match, the - * client is still waiting for a response to the original request. - */ -static bool referring_call_exists(struct nfs_client *clp, - uint32_t nrclists, - struct referring_call_list *rclists) -{ - bool status = 0; - int i, j; - struct nfs4_session *session; - struct nfs4_slot_table *tbl; - struct referring_call_list *rclist; - struct referring_call *ref; - - /* - * XXX When client trunking is implemented, this becomes - * a session lookup from within the loop - */ - session = clp->cl_session; - tbl = &session->fc_slot_table; - - for (i = 0; i < nrclists; i++) { - rclist = &rclists[i]; - if (memcmp(session->sess_id.data, - rclist->rcl_sessionid.data, - NFS4_MAX_SESSIONID_LEN) != 0) - continue; - - for (j = 0; j < rclist->rcl_nrefcalls; j++) { - ref = &rclist->rcl_refcalls[j]; - - dprintk("%s: sessionid %x:%x:%x:%x sequenceid %u " - "slotid %u\n", __func__, - ((u32 *)&rclist->rcl_sessionid.data)[0], - ((u32 *)&rclist->rcl_sessionid.data)[1], - ((u32 *)&rclist->rcl_sessionid.data)[2], - ((u32 *)&rclist->rcl_sessionid.data)[3], - ref->rc_sequenceid, ref->rc_slotid); - - spin_lock(&tbl->slot_tbl_lock); - status = (test_bit(ref->rc_slotid, tbl->used_slots) && - tbl->slots[ref->rc_slotid].seq_nr == - ref->rc_sequenceid); - spin_unlock(&tbl->slot_tbl_lock); - if (status) - goto out; - } - } - -out: - return status; -} - -__be32 nfs4_callback_sequence(struct cb_sequenceargs *args, - struct cb_sequenceres *res, - struct cb_process_state *cps) -{ - struct nfs4_slot_table *tbl; - struct nfs_client *clp; - int i; - __be32 status = htonl(NFS4ERR_BADSESSION); - - clp = nfs4_find_client_sessionid(cps->net, args->csa_addr, &args->csa_sessionid); - if (clp == NULL) - goto out; - - tbl = &clp->cl_session->bc_slot_table; - - spin_lock(&tbl->slot_tbl_lock); - /* state manager is resetting the session */ - if (test_bit(NFS4_SESSION_DRAINING, &clp->cl_session->session_state)) { - spin_unlock(&tbl->slot_tbl_lock); - status = htonl(NFS4ERR_DELAY); - /* Return NFS4ERR_BADSESSION if we're draining the session - * in order to reset it. - */ - if (test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) - status = htonl(NFS4ERR_BADSESSION); - goto out; - } - - status = validate_seqid(&clp->cl_session->bc_slot_table, args); - spin_unlock(&tbl->slot_tbl_lock); - if (status) - goto out; - - cps->slotid = args->csa_slotid; - - /* - * Check for pending referring calls. If a match is found, a - * related callback was received before the response to the original - * call. - */ - if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) { - status = htonl(NFS4ERR_DELAY); - goto out; - } - - memcpy(&res->csr_sessionid, &args->csa_sessionid, - sizeof(res->csr_sessionid)); - res->csr_sequenceid = args->csa_sequenceid; - res->csr_slotid = args->csa_slotid; - res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; - res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; - -out: - cps->clp = clp; /* put in nfs4_callback_compound */ - for (i = 0; i < args->csa_nrclists; i++) - kfree(args->csa_rclists[i].rcl_refcalls); - kfree(args->csa_rclists); - - if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) { - cps->drc_status = status; - status = 0; - } else - res->csr_status = status; - - dprintk("%s: exit with status = %d res->csr_status %d\n", __func__, - ntohl(status), ntohl(res->csr_status)); - return status; -} - -static bool -validate_bitmap_values(unsigned long mask) -{ - return (mask & ~RCA4_TYPE_MASK_ALL) == 0; -} - -__be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy, - struct cb_process_state *cps) -{ - __be32 status; - fmode_t flags = 0; - - status = cpu_to_be32(NFS4ERR_OP_NOT_IN_SESSION); - if (!cps->clp) /* set in cb_sequence */ - goto out; - - dprintk_rcu("NFS: RECALL_ANY callback request from %s\n", - rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR)); - - status = cpu_to_be32(NFS4ERR_INVAL); - if (!validate_bitmap_values(args->craa_type_mask)) - goto out; - - status = cpu_to_be32(NFS4_OK); - if (test_bit(RCA4_TYPE_MASK_RDATA_DLG, (const unsigned long *) - &args->craa_type_mask)) - flags = FMODE_READ; - if (test_bit(RCA4_TYPE_MASK_WDATA_DLG, (const unsigned long *) - &args->craa_type_mask)) - flags |= FMODE_WRITE; - if (test_bit(RCA4_TYPE_MASK_FILE_LAYOUT, (const unsigned long *) - &args->craa_type_mask)) - pnfs_recall_all_layouts(cps->clp); - if (flags) - nfs_expire_all_delegation_types(cps->clp, flags); -out: - dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); - return status; -} - -/* Reduce the fore channel's max_slots to the target value */ -__be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy, - struct cb_process_state *cps) -{ - struct nfs4_slot_table *fc_tbl; - __be32 status; - - status = htonl(NFS4ERR_OP_NOT_IN_SESSION); - if (!cps->clp) /* set in cb_sequence */ - goto out; - - dprintk_rcu("NFS: CB_RECALL_SLOT request from %s target max slots %d\n", - rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR), - args->crsa_target_max_slots); - - fc_tbl = &cps->clp->cl_session->fc_slot_table; - - status = htonl(NFS4ERR_BAD_HIGH_SLOT); - if (args->crsa_target_max_slots > fc_tbl->max_slots || - args->crsa_target_max_slots < 1) - goto out; - - status = htonl(NFS4_OK); - if (args->crsa_target_max_slots == fc_tbl->max_slots) - goto out; - - fc_tbl->target_max_slots = args->crsa_target_max_slots; - nfs41_handle_recall_slot(cps->clp); -out: - dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); - return status; -} -#endif /* CONFIG_NFS_V4_1 */ diff --git a/ANDROID_3.4.5/fs/nfs/callback_xdr.c b/ANDROID_3.4.5/fs/nfs/callback_xdr.c deleted file mode 100644 index 95bfc243..00000000 --- a/ANDROID_3.4.5/fs/nfs/callback_xdr.c +++ /dev/null @@ -1,998 +0,0 @@ -/* - * linux/fs/nfs/callback_xdr.c - * - * Copyright (C) 2004 Trond Myklebust - * - * NFSv4 callback encode/decode procedures - */ -#include <linux/kernel.h> -#include <linux/sunrpc/svc.h> -#include <linux/nfs4.h> -#include <linux/nfs_fs.h> -#include <linux/ratelimit.h> -#include <linux/printk.h> -#include <linux/slab.h> -#include <linux/sunrpc/bc_xprt.h> -#include "nfs4_fs.h" -#include "callback.h" -#include "internal.h" - -#define CB_OP_TAGLEN_MAXSZ (512) -#define CB_OP_HDR_RES_MAXSZ (2 + CB_OP_TAGLEN_MAXSZ) -#define CB_OP_GETATTR_BITMAP_MAXSZ (4) -#define CB_OP_GETATTR_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \ - CB_OP_GETATTR_BITMAP_MAXSZ + \ - 2 + 2 + 3 + 3) -#define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) - -#if defined(CONFIG_NFS_V4_1) -#define CB_OP_LAYOUTRECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) -#define CB_OP_DEVICENOTIFY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) -#define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \ - 4 + 1 + 3) -#define CB_OP_RECALLANY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) -#define CB_OP_RECALLSLOT_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) -#endif /* CONFIG_NFS_V4_1 */ - -#define NFSDBG_FACILITY NFSDBG_CALLBACK - -/* Internal error code */ -#define NFS4ERR_RESOURCE_HDR 11050 - -typedef __be32 (*callback_process_op_t)(void *, void *, - struct cb_process_state *); -typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *); -typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *); - - -struct callback_op { - callback_process_op_t process_op; - callback_decode_arg_t decode_args; - callback_encode_res_t encode_res; - long res_maxsize; -}; - -static struct callback_op callback_ops[]; - -static __be32 nfs4_callback_null(struct svc_rqst *rqstp, void *argp, void *resp) -{ - return htonl(NFS4_OK); -} - -static int nfs4_decode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy) -{ - return xdr_argsize_check(rqstp, p); -} - -static int nfs4_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy) -{ - return xdr_ressize_check(rqstp, p); -} - -static __be32 *read_buf(struct xdr_stream *xdr, int nbytes) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, nbytes); - if (unlikely(p == NULL)) - printk(KERN_WARNING "NFS: NFSv4 callback reply buffer overflowed!\n"); - return p; -} - -static __be32 decode_string(struct xdr_stream *xdr, unsigned int *len, const char **str) -{ - __be32 *p; - - p = read_buf(xdr, 4); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - *len = ntohl(*p); - - if (*len != 0) { - p = read_buf(xdr, *len); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - *str = (const char *)p; - } else - *str = NULL; - - return 0; -} - -static __be32 decode_fh(struct xdr_stream *xdr, struct nfs_fh *fh) -{ - __be32 *p; - - p = read_buf(xdr, 4); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - fh->size = ntohl(*p); - if (fh->size > NFS4_FHSIZE) - return htonl(NFS4ERR_BADHANDLE); - p = read_buf(xdr, fh->size); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - memcpy(&fh->data[0], p, fh->size); - memset(&fh->data[fh->size], 0, sizeof(fh->data) - fh->size); - return 0; -} - -static __be32 decode_bitmap(struct xdr_stream *xdr, uint32_t *bitmap) -{ - __be32 *p; - unsigned int attrlen; - - p = read_buf(xdr, 4); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - attrlen = ntohl(*p); - p = read_buf(xdr, attrlen << 2); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - if (likely(attrlen > 0)) - bitmap[0] = ntohl(*p++); - if (attrlen > 1) - bitmap[1] = ntohl(*p); - return 0; -} - -static __be32 decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid) -{ - __be32 *p; - - p = read_buf(xdr, NFS4_STATEID_SIZE); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - memcpy(stateid, p, NFS4_STATEID_SIZE); - return 0; -} - -static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound_hdr_arg *hdr) -{ - __be32 *p; - __be32 status; - - status = decode_string(xdr, &hdr->taglen, &hdr->tag); - if (unlikely(status != 0)) - return status; - /* We do not like overly long tags! */ - if (hdr->taglen > CB_OP_TAGLEN_MAXSZ - 12) { - printk("NFS: NFSv4 CALLBACK %s: client sent tag of length %u\n", - __func__, hdr->taglen); - return htonl(NFS4ERR_RESOURCE); - } - p = read_buf(xdr, 12); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - hdr->minorversion = ntohl(*p++); - /* Check minor version is zero or one. */ - if (hdr->minorversion <= 1) { - hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 */ - } else { - pr_warn_ratelimited("NFS: %s: NFSv4 server callback with " - "illegal minor version %u!\n", - __func__, hdr->minorversion); - return htonl(NFS4ERR_MINOR_VERS_MISMATCH); - } - hdr->nops = ntohl(*p); - dprintk("%s: minorversion %d nops %d\n", __func__, - hdr->minorversion, hdr->nops); - return 0; -} - -static __be32 decode_op_hdr(struct xdr_stream *xdr, unsigned int *op) -{ - __be32 *p; - p = read_buf(xdr, 4); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE_HDR); - *op = ntohl(*p); - return 0; -} - -static __be32 decode_getattr_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_getattrargs *args) -{ - __be32 status; - - status = decode_fh(xdr, &args->fh); - if (unlikely(status != 0)) - goto out; - args->addr = svc_addr(rqstp); - status = decode_bitmap(xdr, args->bitmap); -out: - dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); - return status; -} - -static __be32 decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_recallargs *args) -{ - __be32 *p; - __be32 status; - - args->addr = svc_addr(rqstp); - status = decode_stateid(xdr, &args->stateid); - if (unlikely(status != 0)) - goto out; - p = read_buf(xdr, 4); - if (unlikely(p == NULL)) { - status = htonl(NFS4ERR_RESOURCE); - goto out; - } - args->truncate = ntohl(*p); - status = decode_fh(xdr, &args->fh); -out: - dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); - return status; -} - -#if defined(CONFIG_NFS_V4_1) - -static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp, - struct xdr_stream *xdr, - struct cb_layoutrecallargs *args) -{ - __be32 *p; - __be32 status = 0; - uint32_t iomode; - - args->cbl_addr = svc_addr(rqstp); - p = read_buf(xdr, 4 * sizeof(uint32_t)); - if (unlikely(p == NULL)) { - status = htonl(NFS4ERR_BADXDR); - goto out; - } - - args->cbl_layout_type = ntohl(*p++); - /* Depite the spec's xdr, iomode really belongs in the FILE switch, - * as it is unusable and ignored with the other types. - */ - iomode = ntohl(*p++); - args->cbl_layoutchanged = ntohl(*p++); - args->cbl_recall_type = ntohl(*p++); - - if (args->cbl_recall_type == RETURN_FILE) { - args->cbl_range.iomode = iomode; - status = decode_fh(xdr, &args->cbl_fh); - if (unlikely(status != 0)) - goto out; - - p = read_buf(xdr, 2 * sizeof(uint64_t)); - if (unlikely(p == NULL)) { - status = htonl(NFS4ERR_BADXDR); - goto out; - } - p = xdr_decode_hyper(p, &args->cbl_range.offset); - p = xdr_decode_hyper(p, &args->cbl_range.length); - status = decode_stateid(xdr, &args->cbl_stateid); - if (unlikely(status != 0)) - goto out; - } else if (args->cbl_recall_type == RETURN_FSID) { - p = read_buf(xdr, 2 * sizeof(uint64_t)); - if (unlikely(p == NULL)) { - status = htonl(NFS4ERR_BADXDR); - goto out; - } - p = xdr_decode_hyper(p, &args->cbl_fsid.major); - p = xdr_decode_hyper(p, &args->cbl_fsid.minor); - } else if (args->cbl_recall_type != RETURN_ALL) { - status = htonl(NFS4ERR_BADXDR); - goto out; - } - dprintk("%s: ltype 0x%x iomode %d changed %d recall_type %d\n", - __func__, - args->cbl_layout_type, iomode, - args->cbl_layoutchanged, args->cbl_recall_type); -out: - dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); - return status; -} - -static -__be32 decode_devicenotify_args(struct svc_rqst *rqstp, - struct xdr_stream *xdr, - struct cb_devicenotifyargs *args) -{ - __be32 *p; - __be32 status = 0; - u32 tmp; - int n, i; - args->ndevs = 0; - - /* Num of device notifications */ - p = read_buf(xdr, sizeof(uint32_t)); - if (unlikely(p == NULL)) { - status = htonl(NFS4ERR_BADXDR); - goto out; - } - n = ntohl(*p++); - if (n <= 0) - goto out; - if (n > ULONG_MAX / sizeof(*args->devs)) { - status = htonl(NFS4ERR_BADXDR); - goto out; - } - - args->devs = kmalloc(n * sizeof(*args->devs), GFP_KERNEL); - if (!args->devs) { - status = htonl(NFS4ERR_DELAY); - goto out; - } - - /* Decode each dev notification */ - for (i = 0; i < n; i++) { - struct cb_devicenotifyitem *dev = &args->devs[i]; - - p = read_buf(xdr, (4 * sizeof(uint32_t)) + NFS4_DEVICEID4_SIZE); - if (unlikely(p == NULL)) { - status = htonl(NFS4ERR_BADXDR); - goto err; - } - - tmp = ntohl(*p++); /* bitmap size */ - if (tmp != 1) { - status = htonl(NFS4ERR_INVAL); - goto err; - } - dev->cbd_notify_type = ntohl(*p++); - if (dev->cbd_notify_type != NOTIFY_DEVICEID4_CHANGE && - dev->cbd_notify_type != NOTIFY_DEVICEID4_DELETE) { - status = htonl(NFS4ERR_INVAL); - goto err; - } - - tmp = ntohl(*p++); /* opaque size */ - if (((dev->cbd_notify_type == NOTIFY_DEVICEID4_CHANGE) && - (tmp != NFS4_DEVICEID4_SIZE + 8)) || - ((dev->cbd_notify_type == NOTIFY_DEVICEID4_DELETE) && - (tmp != NFS4_DEVICEID4_SIZE + 4))) { - status = htonl(NFS4ERR_INVAL); - goto err; - } - dev->cbd_layout_type = ntohl(*p++); - memcpy(dev->cbd_dev_id.data, p, NFS4_DEVICEID4_SIZE); - p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE); - - if (dev->cbd_layout_type == NOTIFY_DEVICEID4_CHANGE) { - p = read_buf(xdr, sizeof(uint32_t)); - if (unlikely(p == NULL)) { - status = htonl(NFS4ERR_BADXDR); - goto err; - } - dev->cbd_immediate = ntohl(*p++); - } else { - dev->cbd_immediate = 0; - } - - args->ndevs++; - - dprintk("%s: type %d layout 0x%x immediate %d\n", - __func__, dev->cbd_notify_type, dev->cbd_layout_type, - dev->cbd_immediate); - } -out: - dprintk("%s: status %d ndevs %d\n", - __func__, ntohl(status), args->ndevs); - return status; -err: - kfree(args->devs); - goto out; -} - -static __be32 decode_sessionid(struct xdr_stream *xdr, - struct nfs4_sessionid *sid) -{ - __be32 *p; - int len = NFS4_MAX_SESSIONID_LEN; - - p = read_buf(xdr, len); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - - memcpy(sid->data, p, len); - return 0; -} - -static __be32 decode_rc_list(struct xdr_stream *xdr, - struct referring_call_list *rc_list) -{ - __be32 *p; - int i; - __be32 status; - - status = decode_sessionid(xdr, &rc_list->rcl_sessionid); - if (status) - goto out; - - status = htonl(NFS4ERR_RESOURCE); - p = read_buf(xdr, sizeof(uint32_t)); - if (unlikely(p == NULL)) - goto out; - - rc_list->rcl_nrefcalls = ntohl(*p++); - if (rc_list->rcl_nrefcalls) { - p = read_buf(xdr, - rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t)); - if (unlikely(p == NULL)) - goto out; - rc_list->rcl_refcalls = kmalloc(rc_list->rcl_nrefcalls * - sizeof(*rc_list->rcl_refcalls), - GFP_KERNEL); - if (unlikely(rc_list->rcl_refcalls == NULL)) - goto out; - for (i = 0; i < rc_list->rcl_nrefcalls; i++) { - rc_list->rcl_refcalls[i].rc_sequenceid = ntohl(*p++); - rc_list->rcl_refcalls[i].rc_slotid = ntohl(*p++); - } - } - status = 0; - -out: - return status; -} - -static __be32 decode_cb_sequence_args(struct svc_rqst *rqstp, - struct xdr_stream *xdr, - struct cb_sequenceargs *args) -{ - __be32 *p; - int i; - __be32 status; - - status = decode_sessionid(xdr, &args->csa_sessionid); - if (status) - goto out; - - status = htonl(NFS4ERR_RESOURCE); - p = read_buf(xdr, 5 * sizeof(uint32_t)); - if (unlikely(p == NULL)) - goto out; - - args->csa_addr = svc_addr(rqstp); - args->csa_sequenceid = ntohl(*p++); - args->csa_slotid = ntohl(*p++); - args->csa_highestslotid = ntohl(*p++); - args->csa_cachethis = ntohl(*p++); - args->csa_nrclists = ntohl(*p++); - args->csa_rclists = NULL; - if (args->csa_nrclists) { - args->csa_rclists = kmalloc(args->csa_nrclists * - sizeof(*args->csa_rclists), - GFP_KERNEL); - if (unlikely(args->csa_rclists == NULL)) - goto out; - - for (i = 0; i < args->csa_nrclists; i++) { - status = decode_rc_list(xdr, &args->csa_rclists[i]); - if (status) - goto out_free; - } - } - status = 0; - - dprintk("%s: sessionid %x:%x:%x:%x sequenceid %u slotid %u " - "highestslotid %u cachethis %d nrclists %u\n", - __func__, - ((u32 *)&args->csa_sessionid)[0], - ((u32 *)&args->csa_sessionid)[1], - ((u32 *)&args->csa_sessionid)[2], - ((u32 *)&args->csa_sessionid)[3], - args->csa_sequenceid, args->csa_slotid, - args->csa_highestslotid, args->csa_cachethis, - args->csa_nrclists); -out: - dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); - return status; - -out_free: - for (i = 0; i < args->csa_nrclists; i++) - kfree(args->csa_rclists[i].rcl_refcalls); - kfree(args->csa_rclists); - goto out; -} - -static __be32 decode_recallany_args(struct svc_rqst *rqstp, - struct xdr_stream *xdr, - struct cb_recallanyargs *args) -{ - uint32_t bitmap[2]; - __be32 *p, status; - - args->craa_addr = svc_addr(rqstp); - p = read_buf(xdr, 4); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_BADXDR); - args->craa_objs_to_keep = ntohl(*p++); - status = decode_bitmap(xdr, bitmap); - if (unlikely(status)) - return status; - args->craa_type_mask = bitmap[0]; - - return 0; -} - -static __be32 decode_recallslot_args(struct svc_rqst *rqstp, - struct xdr_stream *xdr, - struct cb_recallslotargs *args) -{ - __be32 *p; - - args->crsa_addr = svc_addr(rqstp); - p = read_buf(xdr, 4); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_BADXDR); - args->crsa_target_max_slots = ntohl(*p++); - return 0; -} - -#endif /* CONFIG_NFS_V4_1 */ - -static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str) -{ - __be32 *p; - - p = xdr_reserve_space(xdr, 4 + len); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - xdr_encode_opaque(p, str, len); - return 0; -} - -#define CB_SUPPORTED_ATTR0 (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE) -#define CB_SUPPORTED_ATTR1 (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY) -static __be32 encode_attr_bitmap(struct xdr_stream *xdr, const uint32_t *bitmap, __be32 **savep) -{ - __be32 bm[2]; - __be32 *p; - - bm[0] = htonl(bitmap[0] & CB_SUPPORTED_ATTR0); - bm[1] = htonl(bitmap[1] & CB_SUPPORTED_ATTR1); - if (bm[1] != 0) { - p = xdr_reserve_space(xdr, 16); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - *p++ = htonl(2); - *p++ = bm[0]; - *p++ = bm[1]; - } else if (bm[0] != 0) { - p = xdr_reserve_space(xdr, 12); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - *p++ = htonl(1); - *p++ = bm[0]; - } else { - p = xdr_reserve_space(xdr, 8); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - *p++ = htonl(0); - } - *savep = p; - return 0; -} - -static __be32 encode_attr_change(struct xdr_stream *xdr, const uint32_t *bitmap, uint64_t change) -{ - __be32 *p; - - if (!(bitmap[0] & FATTR4_WORD0_CHANGE)) - return 0; - p = xdr_reserve_space(xdr, 8); - if (unlikely(!p)) - return htonl(NFS4ERR_RESOURCE); - p = xdr_encode_hyper(p, change); - return 0; -} - -static __be32 encode_attr_size(struct xdr_stream *xdr, const uint32_t *bitmap, uint64_t size) -{ - __be32 *p; - - if (!(bitmap[0] & FATTR4_WORD0_SIZE)) - return 0; - p = xdr_reserve_space(xdr, 8); - if (unlikely(!p)) - return htonl(NFS4ERR_RESOURCE); - p = xdr_encode_hyper(p, size); - return 0; -} - -static __be32 encode_attr_time(struct xdr_stream *xdr, const struct timespec *time) -{ - __be32 *p; - - p = xdr_reserve_space(xdr, 12); - if (unlikely(!p)) - return htonl(NFS4ERR_RESOURCE); - p = xdr_encode_hyper(p, time->tv_sec); - *p = htonl(time->tv_nsec); - return 0; -} - -static __be32 encode_attr_ctime(struct xdr_stream *xdr, const uint32_t *bitmap, const struct timespec *time) -{ - if (!(bitmap[1] & FATTR4_WORD1_TIME_METADATA)) - return 0; - return encode_attr_time(xdr,time); -} - -static __be32 encode_attr_mtime(struct xdr_stream *xdr, const uint32_t *bitmap, const struct timespec *time) -{ - if (!(bitmap[1] & FATTR4_WORD1_TIME_MODIFY)) - return 0; - return encode_attr_time(xdr,time); -} - -static __be32 encode_compound_hdr_res(struct xdr_stream *xdr, struct cb_compound_hdr_res *hdr) -{ - __be32 status; - - hdr->status = xdr_reserve_space(xdr, 4); - if (unlikely(hdr->status == NULL)) - return htonl(NFS4ERR_RESOURCE); - status = encode_string(xdr, hdr->taglen, hdr->tag); - if (unlikely(status != 0)) - return status; - hdr->nops = xdr_reserve_space(xdr, 4); - if (unlikely(hdr->nops == NULL)) - return htonl(NFS4ERR_RESOURCE); - return 0; -} - -static __be32 encode_op_hdr(struct xdr_stream *xdr, uint32_t op, __be32 res) -{ - __be32 *p; - - p = xdr_reserve_space(xdr, 8); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE_HDR); - *p++ = htonl(op); - *p = res; - return 0; -} - -static __be32 encode_getattr_res(struct svc_rqst *rqstp, struct xdr_stream *xdr, const struct cb_getattrres *res) -{ - __be32 *savep = NULL; - __be32 status = res->status; - - if (unlikely(status != 0)) - goto out; - status = encode_attr_bitmap(xdr, res->bitmap, &savep); - if (unlikely(status != 0)) - goto out; - status = encode_attr_change(xdr, res->bitmap, res->change_attr); - if (unlikely(status != 0)) - goto out; - status = encode_attr_size(xdr, res->bitmap, res->size); - if (unlikely(status != 0)) - goto out; - status = encode_attr_ctime(xdr, res->bitmap, &res->ctime); - if (unlikely(status != 0)) - goto out; - status = encode_attr_mtime(xdr, res->bitmap, &res->mtime); - *savep = htonl((unsigned int)((char *)xdr->p - (char *)(savep+1))); -out: - dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); - return status; -} - -#if defined(CONFIG_NFS_V4_1) - -static __be32 encode_sessionid(struct xdr_stream *xdr, - const struct nfs4_sessionid *sid) -{ - __be32 *p; - int len = NFS4_MAX_SESSIONID_LEN; - - p = xdr_reserve_space(xdr, len); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - - memcpy(p, sid, len); - return 0; -} - -static __be32 encode_cb_sequence_res(struct svc_rqst *rqstp, - struct xdr_stream *xdr, - const struct cb_sequenceres *res) -{ - __be32 *p; - unsigned status = res->csr_status; - - if (unlikely(status != 0)) - goto out; - - encode_sessionid(xdr, &res->csr_sessionid); - - p = xdr_reserve_space(xdr, 4 * sizeof(uint32_t)); - if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE); - - *p++ = htonl(res->csr_sequenceid); - *p++ = htonl(res->csr_slotid); - *p++ = htonl(res->csr_highestslotid); - *p++ = htonl(res->csr_target_highestslotid); -out: - dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); - return status; -} - -static __be32 -preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) -{ - if (op_nr == OP_CB_SEQUENCE) { - if (nop != 0) - return htonl(NFS4ERR_SEQUENCE_POS); - } else { - if (nop == 0) - return htonl(NFS4ERR_OP_NOT_IN_SESSION); - } - - switch (op_nr) { - case OP_CB_GETATTR: - case OP_CB_RECALL: - case OP_CB_SEQUENCE: - case OP_CB_RECALL_ANY: - case OP_CB_RECALL_SLOT: - case OP_CB_LAYOUTRECALL: - case OP_CB_NOTIFY_DEVICEID: - *op = &callback_ops[op_nr]; - break; - - case OP_CB_NOTIFY: - case OP_CB_PUSH_DELEG: - case OP_CB_RECALLABLE_OBJ_AVAIL: - case OP_CB_WANTS_CANCELLED: - case OP_CB_NOTIFY_LOCK: - return htonl(NFS4ERR_NOTSUPP); - - default: - return htonl(NFS4ERR_OP_ILLEGAL); - } - - return htonl(NFS_OK); -} - -static void nfs4_callback_free_slot(struct nfs4_session *session) -{ - struct nfs4_slot_table *tbl = &session->bc_slot_table; - - spin_lock(&tbl->slot_tbl_lock); - /* - * Let the state manager know callback processing done. - * A single slot, so highest used slotid is either 0 or -1 - */ - tbl->highest_used_slotid = NFS4_NO_SLOT; - nfs4_check_drain_bc_complete(session); - spin_unlock(&tbl->slot_tbl_lock); -} - -static void nfs4_cb_free_slot(struct cb_process_state *cps) -{ - if (cps->slotid != NFS4_NO_SLOT) - nfs4_callback_free_slot(cps->clp->cl_session); -} - -#else /* CONFIG_NFS_V4_1 */ - -static __be32 -preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) -{ - return htonl(NFS4ERR_MINOR_VERS_MISMATCH); -} - -static void nfs4_cb_free_slot(struct cb_process_state *cps) -{ -} -#endif /* CONFIG_NFS_V4_1 */ - -static __be32 -preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op) -{ - switch (op_nr) { - case OP_CB_GETATTR: - case OP_CB_RECALL: - *op = &callback_ops[op_nr]; - break; - default: - return htonl(NFS4ERR_OP_ILLEGAL); - } - - return htonl(NFS_OK); -} - -static __be32 process_op(uint32_t minorversion, int nop, - struct svc_rqst *rqstp, - struct xdr_stream *xdr_in, void *argp, - struct xdr_stream *xdr_out, void *resp, - struct cb_process_state *cps) -{ - struct callback_op *op = &callback_ops[0]; - unsigned int op_nr; - __be32 status; - long maxlen; - __be32 res; - - dprintk("%s: start\n", __func__); - status = decode_op_hdr(xdr_in, &op_nr); - if (unlikely(status)) - return status; - - dprintk("%s: minorversion=%d nop=%d op_nr=%u\n", - __func__, minorversion, nop, op_nr); - - status = minorversion ? preprocess_nfs41_op(nop, op_nr, &op) : - preprocess_nfs4_op(op_nr, &op); - if (status == htonl(NFS4ERR_OP_ILLEGAL)) - op_nr = OP_CB_ILLEGAL; - if (status) - goto encode_hdr; - - if (cps->drc_status) { - status = cps->drc_status; - goto encode_hdr; - } - - maxlen = xdr_out->end - xdr_out->p; - if (maxlen > 0 && maxlen < PAGE_SIZE) { - status = op->decode_args(rqstp, xdr_in, argp); - if (likely(status == 0)) - status = op->process_op(argp, resp, cps); - } else - status = htonl(NFS4ERR_RESOURCE); - -encode_hdr: - res = encode_op_hdr(xdr_out, op_nr, status); - if (unlikely(res)) - return res; - if (op->encode_res != NULL && status == 0) - status = op->encode_res(rqstp, xdr_out, resp); - dprintk("%s: done, status = %d\n", __func__, ntohl(status)); - return status; -} - -/* - * Decode, process and encode a COMPOUND - */ -static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *resp) -{ - struct cb_compound_hdr_arg hdr_arg = { 0 }; - struct cb_compound_hdr_res hdr_res = { NULL }; - struct xdr_stream xdr_in, xdr_out; - __be32 *p, status; - struct cb_process_state cps = { - .drc_status = 0, - .clp = NULL, - .slotid = NFS4_NO_SLOT, - .net = rqstp->rq_xprt->xpt_net, - }; - unsigned int nops = 0; - - dprintk("%s: start\n", __func__); - - xdr_init_decode(&xdr_in, &rqstp->rq_arg, rqstp->rq_arg.head[0].iov_base); - - p = (__be32*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len); - xdr_init_encode(&xdr_out, &rqstp->rq_res, p); - - status = decode_compound_hdr_arg(&xdr_in, &hdr_arg); - if (status == __constant_htonl(NFS4ERR_RESOURCE)) - return rpc_garbage_args; - - if (hdr_arg.minorversion == 0) { - cps.clp = nfs4_find_client_ident(rqstp->rq_xprt->xpt_net, hdr_arg.cb_ident); - if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp)) - return rpc_drop_reply; - } - - hdr_res.taglen = hdr_arg.taglen; - hdr_res.tag = hdr_arg.tag; - if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0) - return rpc_system_err; - - while (status == 0 && nops != hdr_arg.nops) { - status = process_op(hdr_arg.minorversion, nops, rqstp, - &xdr_in, argp, &xdr_out, resp, &cps); - nops++; - } - - /* Buffer overflow in decode_ops_hdr or encode_ops_hdr. Return - * resource error in cb_compound status without returning op */ - if (unlikely(status == htonl(NFS4ERR_RESOURCE_HDR))) { - status = htonl(NFS4ERR_RESOURCE); - nops--; - } - - *hdr_res.status = status; - *hdr_res.nops = htonl(nops); - nfs4_cb_free_slot(&cps); - nfs_put_client(cps.clp); - dprintk("%s: done, status = %u\n", __func__, ntohl(status)); - return rpc_success; -} - -/* - * Define NFS4 callback COMPOUND ops. - */ -static struct callback_op callback_ops[] = { - [0] = { - .res_maxsize = CB_OP_HDR_RES_MAXSZ, - }, - [OP_CB_GETATTR] = { - .process_op = (callback_process_op_t)nfs4_callback_getattr, - .decode_args = (callback_decode_arg_t)decode_getattr_args, - .encode_res = (callback_encode_res_t)encode_getattr_res, - .res_maxsize = CB_OP_GETATTR_RES_MAXSZ, - }, - [OP_CB_RECALL] = { - .process_op = (callback_process_op_t)nfs4_callback_recall, - .decode_args = (callback_decode_arg_t)decode_recall_args, - .res_maxsize = CB_OP_RECALL_RES_MAXSZ, - }, -#if defined(CONFIG_NFS_V4_1) - [OP_CB_LAYOUTRECALL] = { - .process_op = (callback_process_op_t)nfs4_callback_layoutrecall, - .decode_args = - (callback_decode_arg_t)decode_layoutrecall_args, - .res_maxsize = CB_OP_LAYOUTRECALL_RES_MAXSZ, - }, - [OP_CB_NOTIFY_DEVICEID] = { - .process_op = (callback_process_op_t)nfs4_callback_devicenotify, - .decode_args = - (callback_decode_arg_t)decode_devicenotify_args, - .res_maxsize = CB_OP_DEVICENOTIFY_RES_MAXSZ, - }, - [OP_CB_SEQUENCE] = { - .process_op = (callback_process_op_t)nfs4_callback_sequence, - .decode_args = (callback_decode_arg_t)decode_cb_sequence_args, - .encode_res = (callback_encode_res_t)encode_cb_sequence_res, - .res_maxsize = CB_OP_SEQUENCE_RES_MAXSZ, - }, - [OP_CB_RECALL_ANY] = { - .process_op = (callback_process_op_t)nfs4_callback_recallany, - .decode_args = (callback_decode_arg_t)decode_recallany_args, - .res_maxsize = CB_OP_RECALLANY_RES_MAXSZ, - }, - [OP_CB_RECALL_SLOT] = { - .process_op = (callback_process_op_t)nfs4_callback_recallslot, - .decode_args = (callback_decode_arg_t)decode_recallslot_args, - .res_maxsize = CB_OP_RECALLSLOT_RES_MAXSZ, - }, -#endif /* CONFIG_NFS_V4_1 */ -}; - -/* - * Define NFS4 callback procedures - */ -static struct svc_procedure nfs4_callback_procedures1[] = { - [CB_NULL] = { - .pc_func = nfs4_callback_null, - .pc_decode = (kxdrproc_t)nfs4_decode_void, - .pc_encode = (kxdrproc_t)nfs4_encode_void, - .pc_xdrressize = 1, - }, - [CB_COMPOUND] = { - .pc_func = nfs4_callback_compound, - .pc_encode = (kxdrproc_t)nfs4_encode_void, - .pc_argsize = 256, - .pc_ressize = 256, - .pc_xdrressize = NFS4_CALLBACK_BUFSIZE, - } -}; - -struct svc_version nfs4_callback_version1 = { - .vs_vers = 1, - .vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1), - .vs_proc = nfs4_callback_procedures1, - .vs_xdrsize = NFS4_CALLBACK_XDRSIZE, - .vs_dispatch = NULL, - .vs_hidden = 1, -}; - -struct svc_version nfs4_callback_version4 = { - .vs_vers = 4, - .vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1), - .vs_proc = nfs4_callback_procedures1, - .vs_xdrsize = NFS4_CALLBACK_XDRSIZE, - .vs_dispatch = NULL, - .vs_hidden = 1, -}; diff --git a/ANDROID_3.4.5/fs/nfs/client.c b/ANDROID_3.4.5/fs/nfs/client.c deleted file mode 100644 index 60f7e4ec..00000000 --- a/ANDROID_3.4.5/fs/nfs/client.c +++ /dev/null @@ -1,2078 +0,0 @@ -/* client.c: NFS client sharing and management code - * - * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/sched.h> -#include <linux/time.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/string.h> -#include <linux/stat.h> -#include <linux/errno.h> -#include <linux/unistd.h> -#include <linux/sunrpc/clnt.h> -#include <linux/sunrpc/stats.h> -#include <linux/sunrpc/metrics.h> -#include <linux/sunrpc/xprtsock.h> -#include <linux/sunrpc/xprtrdma.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_mount.h> -#include <linux/nfs4_mount.h> -#include <linux/lockd/bind.h> -#include <linux/seq_file.h> -#include <linux/mount.h> -#include <linux/nfs_idmap.h> -#include <linux/vfs.h> -#include <linux/inet.h> -#include <linux/in6.h> -#include <linux/slab.h> -#include <linux/idr.h> -#include <net/ipv6.h> -#include <linux/nfs_xdr.h> -#include <linux/sunrpc/bc_xprt.h> -#include <linux/nsproxy.h> -#include <linux/pid_namespace.h> - - -#include "nfs4_fs.h" -#include "callback.h" -#include "delegation.h" -#include "iostat.h" -#include "internal.h" -#include "fscache.h" -#include "pnfs.h" -#include "netns.h" - -#define NFSDBG_FACILITY NFSDBG_CLIENT - -static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); -#ifdef CONFIG_NFS_V4 - -/* - * Get a unique NFSv4.0 callback identifier which will be used - * by the V4.0 callback service to lookup the nfs_client struct - */ -static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion) -{ - int ret = 0; - struct nfs_net *nn = net_generic(clp->net, nfs_net_id); - - if (clp->rpc_ops->version != 4 || minorversion != 0) - return ret; -retry: - if (!idr_pre_get(&nn->cb_ident_idr, GFP_KERNEL)) - return -ENOMEM; - spin_lock(&nn->nfs_client_lock); - ret = idr_get_new(&nn->cb_ident_idr, clp, &clp->cl_cb_ident); - spin_unlock(&nn->nfs_client_lock); - if (ret == -EAGAIN) - goto retry; - return ret; -} -#endif /* CONFIG_NFS_V4 */ - -/* - * Turn off NFSv4 uid/gid mapping when using AUTH_SYS - */ -static bool nfs4_disable_idmapping = true; - -/* - * RPC cruft for NFS - */ -static const struct rpc_version *nfs_version[5] = { - [2] = &nfs_version2, -#ifdef CONFIG_NFS_V3 - [3] = &nfs_version3, -#endif -#ifdef CONFIG_NFS_V4 - [4] = &nfs_version4, -#endif -}; - -const struct rpc_program nfs_program = { - .name = "nfs", - .number = NFS_PROGRAM, - .nrvers = ARRAY_SIZE(nfs_version), - .version = nfs_version, - .stats = &nfs_rpcstat, - .pipe_dir_name = NFS_PIPE_DIRNAME, -}; - -struct rpc_stat nfs_rpcstat = { - .program = &nfs_program -}; - - -#ifdef CONFIG_NFS_V3_ACL -static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; -static const struct rpc_version *nfsacl_version[] = { - [3] = &nfsacl_version3, -}; - -const struct rpc_program nfsacl_program = { - .name = "nfsacl", - .number = NFS_ACL_PROGRAM, - .nrvers = ARRAY_SIZE(nfsacl_version), - .version = nfsacl_version, - .stats = &nfsacl_rpcstat, -}; -#endif /* CONFIG_NFS_V3_ACL */ - -struct nfs_client_initdata { - const char *hostname; - const struct sockaddr *addr; - size_t addrlen; - const struct nfs_rpc_ops *rpc_ops; - int proto; - u32 minorversion; - struct net *net; -}; - -/* - * Allocate a shared client record - * - * Since these are allocated/deallocated very rarely, we don't - * bother putting them in a slab cache... - */ -static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) -{ - struct nfs_client *clp; - struct rpc_cred *cred; - int err = -ENOMEM; - - if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) - goto error_0; - - clp->rpc_ops = cl_init->rpc_ops; - - atomic_set(&clp->cl_count, 1); - clp->cl_cons_state = NFS_CS_INITING; - - memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen); - clp->cl_addrlen = cl_init->addrlen; - - if (cl_init->hostname) { - err = -ENOMEM; - clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); - if (!clp->cl_hostname) - goto error_cleanup; - } - - INIT_LIST_HEAD(&clp->cl_superblocks); - clp->cl_rpcclient = ERR_PTR(-EINVAL); - - clp->cl_proto = cl_init->proto; - clp->net = get_net(cl_init->net); - -#ifdef CONFIG_NFS_V4 - err = nfs_get_cb_ident_idr(clp, cl_init->minorversion); - if (err) - goto error_cleanup; - - spin_lock_init(&clp->cl_lock); - INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); - rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); - clp->cl_boot_time = CURRENT_TIME; - clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; - clp->cl_minorversion = cl_init->minorversion; - clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion]; -#endif - cred = rpc_lookup_machine_cred("*"); - if (!IS_ERR(cred)) - clp->cl_machine_cred = cred; - nfs_fscache_get_client_cookie(clp); - - return clp; - -error_cleanup: - kfree(clp); -error_0: - return ERR_PTR(err); -} - -#ifdef CONFIG_NFS_V4 -#ifdef CONFIG_NFS_V4_1 -static void nfs4_shutdown_session(struct nfs_client *clp) -{ - if (nfs4_has_session(clp)) { - nfs4_deviceid_purge_client(clp); - nfs4_destroy_session(clp->cl_session); - } - -} -#else /* CONFIG_NFS_V4_1 */ -static void nfs4_shutdown_session(struct nfs_client *clp) -{ -} -#endif /* CONFIG_NFS_V4_1 */ - -/* - * Destroy the NFS4 callback service - */ -static void nfs4_destroy_callback(struct nfs_client *clp) -{ - if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) - nfs_callback_down(clp->cl_mvops->minor_version); -} - -static void nfs4_shutdown_client(struct nfs_client *clp) -{ - if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) - nfs4_kill_renewd(clp); - nfs4_shutdown_session(clp); - nfs4_destroy_callback(clp); - if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) - nfs_idmap_delete(clp); - - rpc_destroy_wait_queue(&clp->cl_rpcwaitq); -} - -/* idr_remove_all is not needed as all id's are removed by nfs_put_client */ -void nfs_cleanup_cb_ident_idr(struct net *net) -{ - struct nfs_net *nn = net_generic(net, nfs_net_id); - - idr_destroy(&nn->cb_ident_idr); -} - -/* nfs_client_lock held */ -static void nfs_cb_idr_remove_locked(struct nfs_client *clp) -{ - struct nfs_net *nn = net_generic(clp->net, nfs_net_id); - - if (clp->cl_cb_ident) - idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident); -} - -static void pnfs_init_server(struct nfs_server *server) -{ - rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC"); -} - -static void nfs4_destroy_server(struct nfs_server *server) -{ - nfs4_purge_state_owners(server); -} - -#else -static void nfs4_shutdown_client(struct nfs_client *clp) -{ -} - -void nfs_cleanup_cb_ident_idr(struct net *net) -{ -} - -static void nfs_cb_idr_remove_locked(struct nfs_client *clp) -{ -} - -static void pnfs_init_server(struct nfs_server *server) -{ -} - -#endif /* CONFIG_NFS_V4 */ - -/* - * Destroy a shared client record - */ -static void nfs_free_client(struct nfs_client *clp) -{ - dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); - - nfs4_shutdown_client(clp); - - nfs_fscache_release_client_cookie(clp); - - /* -EIO all pending I/O */ - if (!IS_ERR(clp->cl_rpcclient)) - rpc_shutdown_client(clp->cl_rpcclient); - - if (clp->cl_machine_cred != NULL) - put_rpccred(clp->cl_machine_cred); - - put_net(clp->net); - kfree(clp->cl_hostname); - kfree(clp->server_scope); - kfree(clp->impl_id); - kfree(clp); - - dprintk("<-- nfs_free_client()\n"); -} - -/* - * Release a reference to a shared client record - */ -void nfs_put_client(struct nfs_client *clp) -{ - struct nfs_net *nn; - - if (!clp) - return; - - dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count)); - nn = net_generic(clp->net, nfs_net_id); - - if (atomic_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) { - list_del(&clp->cl_share_link); - nfs_cb_idr_remove_locked(clp); - spin_unlock(&nn->nfs_client_lock); - - BUG_ON(!list_empty(&clp->cl_superblocks)); - - nfs_free_client(clp); - } -} -EXPORT_SYMBOL_GPL(nfs_put_client); - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -/* - * Test if two ip6 socket addresses refer to the same socket by - * comparing relevant fields. The padding bytes specifically, are not - * compared. sin6_flowinfo is not compared because it only affects QoS - * and sin6_scope_id is only compared if the address is "link local" - * because "link local" addresses need only be unique to a specific - * link. Conversely, ordinary unicast addresses might have different - * sin6_scope_id. - * - * The caller should ensure both socket addresses are AF_INET6. - */ -static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, - const struct sockaddr *sa2) -{ - const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; - const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; - - if (!ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr)) - return 0; - else if (ipv6_addr_type(&sin1->sin6_addr) & IPV6_ADDR_LINKLOCAL) - return sin1->sin6_scope_id == sin2->sin6_scope_id; - - return 1; -} -#else /* !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) */ -static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, - const struct sockaddr *sa2) -{ - return 0; -} -#endif - -/* - * Test if two ip4 socket addresses refer to the same socket, by - * comparing relevant fields. The padding bytes specifically, are - * not compared. - * - * The caller should ensure both socket addresses are AF_INET. - */ -static int nfs_sockaddr_match_ipaddr4(const struct sockaddr *sa1, - const struct sockaddr *sa2) -{ - const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; - const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; - - return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; -} - -static int nfs_sockaddr_cmp_ip6(const struct sockaddr *sa1, - const struct sockaddr *sa2) -{ - const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; - const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; - - return nfs_sockaddr_match_ipaddr6(sa1, sa2) && - (sin1->sin6_port == sin2->sin6_port); -} - -static int nfs_sockaddr_cmp_ip4(const struct sockaddr *sa1, - const struct sockaddr *sa2) -{ - const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; - const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; - - return nfs_sockaddr_match_ipaddr4(sa1, sa2) && - (sin1->sin_port == sin2->sin_port); -} - -#if defined(CONFIG_NFS_V4_1) -/* - * Test if two socket addresses represent the same actual socket, - * by comparing (only) relevant fields, excluding the port number. - */ -static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, - const struct sockaddr *sa2) -{ - if (sa1->sa_family != sa2->sa_family) - return 0; - - switch (sa1->sa_family) { - case AF_INET: - return nfs_sockaddr_match_ipaddr4(sa1, sa2); - case AF_INET6: - return nfs_sockaddr_match_ipaddr6(sa1, sa2); - } - return 0; -} -#endif /* CONFIG_NFS_V4_1 */ - -/* - * Test if two socket addresses represent the same actual socket, - * by comparing (only) relevant fields, including the port number. - */ -static int nfs_sockaddr_cmp(const struct sockaddr *sa1, - const struct sockaddr *sa2) -{ - if (sa1->sa_family != sa2->sa_family) - return 0; - - switch (sa1->sa_family) { - case AF_INET: - return nfs_sockaddr_cmp_ip4(sa1, sa2); - case AF_INET6: - return nfs_sockaddr_cmp_ip6(sa1, sa2); - } - return 0; -} - -#if defined(CONFIG_NFS_V4_1) -/* Common match routine for v4.0 and v4.1 callback services */ -static bool nfs4_cb_match_client(const struct sockaddr *addr, - struct nfs_client *clp, u32 minorversion) -{ - struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; - - /* Don't match clients that failed to initialise */ - if (!(clp->cl_cons_state == NFS_CS_READY || - clp->cl_cons_state == NFS_CS_SESSION_INITING)) - return false; - - /* Match the version and minorversion */ - if (clp->rpc_ops->version != 4 || - clp->cl_minorversion != minorversion) - return false; - - /* Match only the IP address, not the port number */ - if (!nfs_sockaddr_match_ipaddr(addr, clap)) - return false; - - return true; -} -#endif /* CONFIG_NFS_V4_1 */ - -/* - * Find an nfs_client on the list that matches the initialisation data - * that is supplied. - */ -static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) -{ - struct nfs_client *clp; - const struct sockaddr *sap = data->addr; - struct nfs_net *nn = net_generic(data->net, nfs_net_id); - - list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { - const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; - /* Don't match clients that failed to initialise properly */ - if (clp->cl_cons_state < 0) - continue; - - /* Different NFS versions cannot share the same nfs_client */ - if (clp->rpc_ops != data->rpc_ops) - continue; - - if (clp->cl_proto != data->proto) - continue; - /* Match nfsv4 minorversion */ - if (clp->cl_minorversion != data->minorversion) - continue; - /* Match the full socket address */ - if (!nfs_sockaddr_cmp(sap, clap)) - continue; - - atomic_inc(&clp->cl_count); - return clp; - } - return NULL; -} - -/* - * Look up a client by IP address and protocol version - * - creates a new record if one doesn't yet exist - */ -static struct nfs_client * -nfs_get_client(const struct nfs_client_initdata *cl_init, - const struct rpc_timeout *timeparms, - const char *ip_addr, - rpc_authflavor_t authflavour, - int noresvport) -{ - struct nfs_client *clp, *new = NULL; - int error; - struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id); - - dprintk("--> nfs_get_client(%s,v%u)\n", - cl_init->hostname ?: "", cl_init->rpc_ops->version); - - /* see if the client already exists */ - do { - spin_lock(&nn->nfs_client_lock); - - clp = nfs_match_client(cl_init); - if (clp) - goto found_client; - if (new) - goto install_client; - - spin_unlock(&nn->nfs_client_lock); - - new = nfs_alloc_client(cl_init); - } while (!IS_ERR(new)); - - dprintk("--> nfs_get_client() = %ld [failed]\n", PTR_ERR(new)); - return new; - - /* install a new client and return with it unready */ -install_client: - clp = new; - list_add(&clp->cl_share_link, &nn->nfs_client_list); - spin_unlock(&nn->nfs_client_lock); - - error = cl_init->rpc_ops->init_client(clp, timeparms, ip_addr, - authflavour, noresvport); - if (error < 0) { - nfs_put_client(clp); - return ERR_PTR(error); - } - dprintk("--> nfs_get_client() = %p [new]\n", clp); - return clp; - - /* found an existing client - * - make sure it's ready before returning - */ -found_client: - spin_unlock(&nn->nfs_client_lock); - - if (new) - nfs_free_client(new); - - error = wait_event_killable(nfs_client_active_wq, - clp->cl_cons_state < NFS_CS_INITING); - if (error < 0) { - nfs_put_client(clp); - return ERR_PTR(-ERESTARTSYS); - } - - if (clp->cl_cons_state < NFS_CS_READY) { - error = clp->cl_cons_state; - nfs_put_client(clp); - return ERR_PTR(error); - } - - BUG_ON(clp->cl_cons_state != NFS_CS_READY); - - dprintk("--> nfs_get_client() = %p [share]\n", clp); - return clp; -} - -/* - * Mark a server as ready or failed - */ -void nfs_mark_client_ready(struct nfs_client *clp, int state) -{ - clp->cl_cons_state = state; - wake_up_all(&nfs_client_active_wq); -} - -/* - * With sessions, the client is not marked ready until after a - * successful EXCHANGE_ID and CREATE_SESSION. - * - * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate - * other versions of NFS can be tried. - */ -int nfs4_check_client_ready(struct nfs_client *clp) -{ - if (!nfs4_has_session(clp)) - return 0; - if (clp->cl_cons_state < NFS_CS_READY) - return -EPROTONOSUPPORT; - return 0; -} - -/* - * Initialise the timeout values for a connection - */ -static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, - unsigned int timeo, unsigned int retrans) -{ - to->to_initval = timeo * HZ / 10; - to->to_retries = retrans; - - switch (proto) { - case XPRT_TRANSPORT_TCP: - case XPRT_TRANSPORT_RDMA: - if (to->to_retries == 0) - to->to_retries = NFS_DEF_TCP_RETRANS; - if (to->to_initval == 0) - to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10; - if (to->to_initval > NFS_MAX_TCP_TIMEOUT) - to->to_initval = NFS_MAX_TCP_TIMEOUT; - to->to_increment = to->to_initval; - to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); - if (to->to_maxval > NFS_MAX_TCP_TIMEOUT) - to->to_maxval = NFS_MAX_TCP_TIMEOUT; - if (to->to_maxval < to->to_initval) - to->to_maxval = to->to_initval; - to->to_exponential = 0; - break; - case XPRT_TRANSPORT_UDP: - if (to->to_retries == 0) - to->to_retries = NFS_DEF_UDP_RETRANS; - if (!to->to_initval) - to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10; - if (to->to_initval > NFS_MAX_UDP_TIMEOUT) - to->to_initval = NFS_MAX_UDP_TIMEOUT; - to->to_maxval = NFS_MAX_UDP_TIMEOUT; - to->to_exponential = 1; - break; - default: - BUG(); - } -} - -/* - * Create an RPC client handle - */ -static int nfs_create_rpc_client(struct nfs_client *clp, - const struct rpc_timeout *timeparms, - rpc_authflavor_t flavor, - int discrtry, int noresvport) -{ - struct rpc_clnt *clnt = NULL; - struct rpc_create_args args = { - .net = clp->net, - .protocol = clp->cl_proto, - .address = (struct sockaddr *)&clp->cl_addr, - .addrsize = clp->cl_addrlen, - .timeout = timeparms, - .servername = clp->cl_hostname, - .program = &nfs_program, - .version = clp->rpc_ops->version, - .authflavor = flavor, - }; - - if (discrtry) - args.flags |= RPC_CLNT_CREATE_DISCRTRY; - if (noresvport) - args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; - - if (!IS_ERR(clp->cl_rpcclient)) - return 0; - - clnt = rpc_create(&args); - if (IS_ERR(clnt)) { - dprintk("%s: cannot create RPC client. Error = %ld\n", - __func__, PTR_ERR(clnt)); - return PTR_ERR(clnt); - } - - clp->cl_rpcclient = clnt; - return 0; -} - -/* - * Version 2 or 3 client destruction - */ -static void nfs_destroy_server(struct nfs_server *server) -{ - if (!(server->flags & NFS_MOUNT_LOCAL_FLOCK) || - !(server->flags & NFS_MOUNT_LOCAL_FCNTL)) - nlmclnt_done(server->nlm_host); -} - -/* - * Version 2 or 3 lockd setup - */ -static int nfs_start_lockd(struct nfs_server *server) -{ - struct nlm_host *host; - struct nfs_client *clp = server->nfs_client; - struct nlmclnt_initdata nlm_init = { - .hostname = clp->cl_hostname, - .address = (struct sockaddr *)&clp->cl_addr, - .addrlen = clp->cl_addrlen, - .nfs_version = clp->rpc_ops->version, - .noresvport = server->flags & NFS_MOUNT_NORESVPORT ? - 1 : 0, - .net = clp->net, - }; - - if (nlm_init.nfs_version > 3) - return 0; - if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) && - (server->flags & NFS_MOUNT_LOCAL_FCNTL)) - return 0; - - switch (clp->cl_proto) { - default: - nlm_init.protocol = IPPROTO_TCP; - break; - case XPRT_TRANSPORT_UDP: - nlm_init.protocol = IPPROTO_UDP; - } - - host = nlmclnt_init(&nlm_init); - if (IS_ERR(host)) - return PTR_ERR(host); - - server->nlm_host = host; - server->destroy = nfs_destroy_server; - return 0; -} - -/* - * Initialise an NFSv3 ACL client connection - */ -#ifdef CONFIG_NFS_V3_ACL -static void nfs_init_server_aclclient(struct nfs_server *server) -{ - if (server->nfs_client->rpc_ops->version != 3) - goto out_noacl; - if (server->flags & NFS_MOUNT_NOACL) - goto out_noacl; - - server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); - if (IS_ERR(server->client_acl)) - goto out_noacl; - - /* No errors! Assume that Sun nfsacls are supported */ - server->caps |= NFS_CAP_ACLS; - return; - -out_noacl: - server->caps &= ~NFS_CAP_ACLS; -} -#else -static inline void nfs_init_server_aclclient(struct nfs_server *server) -{ - server->flags &= ~NFS_MOUNT_NOACL; - server->caps &= ~NFS_CAP_ACLS; -} -#endif - -/* - * Create a general RPC client - */ -static int nfs_init_server_rpcclient(struct nfs_server *server, - const struct rpc_timeout *timeo, - rpc_authflavor_t pseudoflavour) -{ - struct nfs_client *clp = server->nfs_client; - - server->client = rpc_clone_client(clp->cl_rpcclient); - if (IS_ERR(server->client)) { - dprintk("%s: couldn't create rpc_client!\n", __func__); - return PTR_ERR(server->client); - } - - memcpy(&server->client->cl_timeout_default, - timeo, - sizeof(server->client->cl_timeout_default)); - server->client->cl_timeout = &server->client->cl_timeout_default; - - if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) { - struct rpc_auth *auth; - - auth = rpcauth_create(pseudoflavour, server->client); - if (IS_ERR(auth)) { - dprintk("%s: couldn't create credcache!\n", __func__); - return PTR_ERR(auth); - } - } - server->client->cl_softrtry = 0; - if (server->flags & NFS_MOUNT_SOFT) - server->client->cl_softrtry = 1; - - return 0; -} - -/* - * Initialise an NFS2 or NFS3 client - */ -int nfs_init_client(struct nfs_client *clp, const struct rpc_timeout *timeparms, - const char *ip_addr, rpc_authflavor_t authflavour, - int noresvport) -{ - int error; - - if (clp->cl_cons_state == NFS_CS_READY) { - /* the client is already initialised */ - dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp); - return 0; - } - - /* - * Create a client RPC handle for doing FSSTAT with UNIX auth only - * - RFC 2623, sec 2.3.2 - */ - error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX, - 0, noresvport); - if (error < 0) - goto error; - nfs_mark_client_ready(clp, NFS_CS_READY); - return 0; - -error: - nfs_mark_client_ready(clp, error); - dprintk("<-- nfs_init_client() = xerror %d\n", error); - return error; -} - -/* - * Create a version 2 or 3 client - */ -static int nfs_init_server(struct nfs_server *server, - const struct nfs_parsed_mount_data *data) -{ - struct nfs_client_initdata cl_init = { - .hostname = data->nfs_server.hostname, - .addr = (const struct sockaddr *)&data->nfs_server.address, - .addrlen = data->nfs_server.addrlen, - .rpc_ops = &nfs_v2_clientops, - .proto = data->nfs_server.protocol, - .net = data->net, - }; - struct rpc_timeout timeparms; - struct nfs_client *clp; - int error; - - dprintk("--> nfs_init_server()\n"); - -#ifdef CONFIG_NFS_V3 - if (data->version == 3) - cl_init.rpc_ops = &nfs_v3_clientops; -#endif - - nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, - data->timeo, data->retrans); - - /* Allocate or find a client reference we can use */ - clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX, - data->flags & NFS_MOUNT_NORESVPORT); - if (IS_ERR(clp)) { - dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp)); - return PTR_ERR(clp); - } - - server->nfs_client = clp; - - /* Initialise the client representation from the mount data */ - server->flags = data->flags; - server->options = data->options; - server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID| - NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP| - NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME; - - if (data->rsize) - server->rsize = nfs_block_size(data->rsize, NULL); - if (data->wsize) - server->wsize = nfs_block_size(data->wsize, NULL); - - server->acregmin = data->acregmin * HZ; - server->acregmax = data->acregmax * HZ; - server->acdirmin = data->acdirmin * HZ; - server->acdirmax = data->acdirmax * HZ; - - /* Start lockd here, before we might error out */ - error = nfs_start_lockd(server); - if (error < 0) - goto error; - - server->port = data->nfs_server.port; - - error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); - if (error < 0) - goto error; - - /* Preserve the values of mount_server-related mount options */ - if (data->mount_server.addrlen) { - memcpy(&server->mountd_address, &data->mount_server.address, - data->mount_server.addrlen); - server->mountd_addrlen = data->mount_server.addrlen; - } - server->mountd_version = data->mount_server.version; - server->mountd_port = data->mount_server.port; - server->mountd_protocol = data->mount_server.protocol; - - server->namelen = data->namlen; - /* Create a client RPC handle for the NFSv3 ACL management interface */ - nfs_init_server_aclclient(server); - dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp); - return 0; - -error: - server->nfs_client = NULL; - nfs_put_client(clp); - dprintk("<-- nfs_init_server() = xerror %d\n", error); - return error; -} - -/* - * Load up the server record from information gained in an fsinfo record - */ -static void nfs_server_set_fsinfo(struct nfs_server *server, - struct nfs_fh *mntfh, - struct nfs_fsinfo *fsinfo) -{ - unsigned long max_rpc_payload; - - /* Work out a lot of parameters */ - if (server->rsize == 0) - server->rsize = nfs_block_size(fsinfo->rtpref, NULL); - if (server->wsize == 0) - server->wsize = nfs_block_size(fsinfo->wtpref, NULL); - - if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) - server->rsize = nfs_block_size(fsinfo->rtmax, NULL); - if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) - server->wsize = nfs_block_size(fsinfo->wtmax, NULL); - - max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); - if (server->rsize > max_rpc_payload) - server->rsize = max_rpc_payload; - if (server->rsize > NFS_MAX_FILE_IO_SIZE) - server->rsize = NFS_MAX_FILE_IO_SIZE; - server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - - server->backing_dev_info.name = "nfs"; - server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; - - if (server->wsize > max_rpc_payload) - server->wsize = max_rpc_payload; - if (server->wsize > NFS_MAX_FILE_IO_SIZE) - server->wsize = NFS_MAX_FILE_IO_SIZE; - server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - server->pnfs_blksize = fsinfo->blksize; - set_pnfs_layoutdriver(server, mntfh, fsinfo->layouttype); - - server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); - - server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); - if (server->dtsize > PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES) - server->dtsize = PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES; - if (server->dtsize > server->rsize) - server->dtsize = server->rsize; - - if (server->flags & NFS_MOUNT_NOAC) { - server->acregmin = server->acregmax = 0; - server->acdirmin = server->acdirmax = 0; - } - - server->maxfilesize = fsinfo->maxfilesize; - - server->time_delta = fsinfo->time_delta; - - /* We're airborne Set socket buffersize */ - rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); -} - -/* - * Probe filesystem information, including the FSID on v2/v3 - */ -static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) -{ - struct nfs_fsinfo fsinfo; - struct nfs_client *clp = server->nfs_client; - int error; - - dprintk("--> nfs_probe_fsinfo()\n"); - - if (clp->rpc_ops->set_capabilities != NULL) { - error = clp->rpc_ops->set_capabilities(server, mntfh); - if (error < 0) - goto out_error; - } - - fsinfo.fattr = fattr; - fsinfo.layouttype = 0; - error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); - if (error < 0) - goto out_error; - - nfs_server_set_fsinfo(server, mntfh, &fsinfo); - - /* Get some general file system info */ - if (server->namelen == 0) { - struct nfs_pathconf pathinfo; - - pathinfo.fattr = fattr; - nfs_fattr_init(fattr); - - if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) - server->namelen = pathinfo.max_namelen; - } - - dprintk("<-- nfs_probe_fsinfo() = 0\n"); - return 0; - -out_error: - dprintk("nfs_probe_fsinfo: error = %d\n", -error); - return error; -} - -/* - * Copy useful information when duplicating a server record - */ -static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) -{ - target->flags = source->flags; - target->rsize = source->rsize; - target->wsize = source->wsize; - target->acregmin = source->acregmin; - target->acregmax = source->acregmax; - target->acdirmin = source->acdirmin; - target->acdirmax = source->acdirmax; - target->caps = source->caps; - target->options = source->options; -} - -static void nfs_server_insert_lists(struct nfs_server *server) -{ - struct nfs_client *clp = server->nfs_client; - struct nfs_net *nn = net_generic(clp->net, nfs_net_id); - - spin_lock(&nn->nfs_client_lock); - list_add_tail_rcu(&server->client_link, &clp->cl_superblocks); - list_add_tail(&server->master_link, &nn->nfs_volume_list); - clear_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); - spin_unlock(&nn->nfs_client_lock); - -} - -static void nfs_server_remove_lists(struct nfs_server *server) -{ - struct nfs_client *clp = server->nfs_client; - struct nfs_net *nn; - - if (clp == NULL) - return; - nn = net_generic(clp->net, nfs_net_id); - spin_lock(&nn->nfs_client_lock); - list_del_rcu(&server->client_link); - if (list_empty(&clp->cl_superblocks)) - set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); - list_del(&server->master_link); - spin_unlock(&nn->nfs_client_lock); - - synchronize_rcu(); -} - -/* - * Allocate and initialise a server record - */ -static struct nfs_server *nfs_alloc_server(void) -{ - struct nfs_server *server; - - server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); - if (!server) - return NULL; - - server->client = server->client_acl = ERR_PTR(-EINVAL); - - /* Zero out the NFS state stuff */ - INIT_LIST_HEAD(&server->client_link); - INIT_LIST_HEAD(&server->master_link); - INIT_LIST_HEAD(&server->delegations); - INIT_LIST_HEAD(&server->layouts); - INIT_LIST_HEAD(&server->state_owners_lru); - - atomic_set(&server->active, 0); - - server->io_stats = nfs_alloc_iostats(); - if (!server->io_stats) { - kfree(server); - return NULL; - } - - if (bdi_init(&server->backing_dev_info)) { - nfs_free_iostats(server->io_stats); - kfree(server); - return NULL; - } - - ida_init(&server->openowner_id); - ida_init(&server->lockowner_id); - pnfs_init_server(server); - - return server; -} - -/* - * Free up a server record - */ -void nfs_free_server(struct nfs_server *server) -{ - dprintk("--> nfs_free_server()\n"); - - nfs_server_remove_lists(server); - unset_pnfs_layoutdriver(server); - - if (server->destroy != NULL) - server->destroy(server); - - if (!IS_ERR(server->client_acl)) - rpc_shutdown_client(server->client_acl); - if (!IS_ERR(server->client)) - rpc_shutdown_client(server->client); - - nfs_put_client(server->nfs_client); - - ida_destroy(&server->lockowner_id); - ida_destroy(&server->openowner_id); - nfs_free_iostats(server->io_stats); - bdi_destroy(&server->backing_dev_info); - kfree(server); - nfs_release_automount_timer(); - dprintk("<-- nfs_free_server()\n"); -} - -/* - * Create a version 2 or 3 volume record - * - keyed on server and FSID - */ -struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, - struct nfs_fh *mntfh) -{ - struct nfs_server *server; - struct nfs_fattr *fattr; - int error; - - server = nfs_alloc_server(); - if (!server) - return ERR_PTR(-ENOMEM); - - error = -ENOMEM; - fattr = nfs_alloc_fattr(); - if (fattr == NULL) - goto error; - - /* Get a client representation */ - error = nfs_init_server(server, data); - if (error < 0) - goto error; - - BUG_ON(!server->nfs_client); - BUG_ON(!server->nfs_client->rpc_ops); - BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); - - /* Probe the root fh to retrieve its FSID */ - error = nfs_probe_fsinfo(server, mntfh, fattr); - if (error < 0) - goto error; - if (server->nfs_client->rpc_ops->version == 3) { - if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) - server->namelen = NFS3_MAXNAMLEN; - if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) - server->caps |= NFS_CAP_READDIRPLUS; - } else { - if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) - server->namelen = NFS2_MAXNAMLEN; - } - - if (!(fattr->valid & NFS_ATTR_FATTR)) { - error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); - if (error < 0) { - dprintk("nfs_create_server: getattr error = %d\n", -error); - goto error; - } - } - memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid)); - - dprintk("Server FSID: %llx:%llx\n", - (unsigned long long) server->fsid.major, - (unsigned long long) server->fsid.minor); - - nfs_server_insert_lists(server); - server->mount_time = jiffies; - nfs_free_fattr(fattr); - return server; - -error: - nfs_free_fattr(fattr); - nfs_free_server(server); - return ERR_PTR(error); -} - -#ifdef CONFIG_NFS_V4 -/* - * NFSv4.0 callback thread helper - * - * Find a client by callback identifier - */ -struct nfs_client * -nfs4_find_client_ident(struct net *net, int cb_ident) -{ - struct nfs_client *clp; - struct nfs_net *nn = net_generic(net, nfs_net_id); - - spin_lock(&nn->nfs_client_lock); - clp = idr_find(&nn->cb_ident_idr, cb_ident); - if (clp) - atomic_inc(&clp->cl_count); - spin_unlock(&nn->nfs_client_lock); - return clp; -} - -#if defined(CONFIG_NFS_V4_1) -/* - * NFSv4.1 callback thread helper - * For CB_COMPOUND calls, find a client by IP address, protocol version, - * minorversion, and sessionID - * - * Returns NULL if no such client - */ -struct nfs_client * -nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, - struct nfs4_sessionid *sid) -{ - struct nfs_client *clp; - struct nfs_net *nn = net_generic(net, nfs_net_id); - - spin_lock(&nn->nfs_client_lock); - list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { - if (nfs4_cb_match_client(addr, clp, 1) == false) - continue; - - if (!nfs4_has_session(clp)) - continue; - - /* Match sessionid*/ - if (memcmp(clp->cl_session->sess_id.data, - sid->data, NFS4_MAX_SESSIONID_LEN) != 0) - continue; - - atomic_inc(&clp->cl_count); - spin_unlock(&nn->nfs_client_lock); - return clp; - } - spin_unlock(&nn->nfs_client_lock); - return NULL; -} - -#else /* CONFIG_NFS_V4_1 */ - -struct nfs_client * -nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, - struct nfs4_sessionid *sid) -{ - return NULL; -} -#endif /* CONFIG_NFS_V4_1 */ - -/* - * Initialize the NFS4 callback service - */ -static int nfs4_init_callback(struct nfs_client *clp) -{ - int error; - - if (clp->rpc_ops->version == 4) { - struct rpc_xprt *xprt; - - xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt); - - if (nfs4_has_session(clp)) { - error = xprt_setup_backchannel(xprt, - NFS41_BC_MIN_CALLBACKS); - if (error < 0) - return error; - } - - error = nfs_callback_up(clp->cl_mvops->minor_version, xprt); - if (error < 0) { - dprintk("%s: failed to start callback. Error = %d\n", - __func__, error); - return error; - } - __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); - } - return 0; -} - -/* - * Initialize the minor version specific parts of an NFS4 client record - */ -static int nfs4_init_client_minor_version(struct nfs_client *clp) -{ -#if defined(CONFIG_NFS_V4_1) - if (clp->cl_mvops->minor_version) { - struct nfs4_session *session = NULL; - /* - * Create the session and mark it expired. - * When a SEQUENCE operation encounters the expired session - * it will do session recovery to initialize it. - */ - session = nfs4_alloc_session(clp); - if (!session) - return -ENOMEM; - - clp->cl_session = session; - /* - * The create session reply races with the server back - * channel probe. Mark the client NFS_CS_SESSION_INITING - * so that the client back channel can find the - * nfs_client struct - */ - clp->cl_cons_state = NFS_CS_SESSION_INITING; - } -#endif /* CONFIG_NFS_V4_1 */ - - return nfs4_init_callback(clp); -} - -/* - * Initialise an NFS4 client record - */ -int nfs4_init_client(struct nfs_client *clp, - const struct rpc_timeout *timeparms, - const char *ip_addr, - rpc_authflavor_t authflavour, - int noresvport) -{ - char buf[INET6_ADDRSTRLEN + 1]; - int error; - - if (clp->cl_cons_state == NFS_CS_READY) { - /* the client is initialised already */ - dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp); - return 0; - } - - /* Check NFS protocol revision and initialize RPC op vector */ - clp->rpc_ops = &nfs_v4_clientops; - - error = nfs_create_rpc_client(clp, timeparms, authflavour, - 1, noresvport); - if (error < 0) - goto error; - - /* If no clientaddr= option was specified, find a usable cb address */ - if (ip_addr == NULL) { - struct sockaddr_storage cb_addr; - struct sockaddr *sap = (struct sockaddr *)&cb_addr; - - error = rpc_localaddr(clp->cl_rpcclient, sap, sizeof(cb_addr)); - if (error < 0) - goto error; - error = rpc_ntop(sap, buf, sizeof(buf)); - if (error < 0) - goto error; - ip_addr = (const char *)buf; - } - strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr)); - - error = nfs_idmap_new(clp); - if (error < 0) { - dprintk("%s: failed to create idmapper. Error = %d\n", - __func__, error); - goto error; - } - __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); - - error = nfs4_init_client_minor_version(clp); - if (error < 0) - goto error; - - if (!nfs4_has_session(clp)) - nfs_mark_client_ready(clp, NFS_CS_READY); - return 0; - -error: - nfs_mark_client_ready(clp, error); - dprintk("<-- nfs4_init_client() = xerror %d\n", error); - return error; -} - -/* - * Set up an NFS4 client - */ -static int nfs4_set_client(struct nfs_server *server, - const char *hostname, - const struct sockaddr *addr, - const size_t addrlen, - const char *ip_addr, - rpc_authflavor_t authflavour, - int proto, const struct rpc_timeout *timeparms, - u32 minorversion, struct net *net) -{ - struct nfs_client_initdata cl_init = { - .hostname = hostname, - .addr = addr, - .addrlen = addrlen, - .rpc_ops = &nfs_v4_clientops, - .proto = proto, - .minorversion = minorversion, - .net = net, - }; - struct nfs_client *clp; - int error; - - dprintk("--> nfs4_set_client()\n"); - - /* Allocate or find a client reference we can use */ - clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour, - server->flags & NFS_MOUNT_NORESVPORT); - if (IS_ERR(clp)) { - error = PTR_ERR(clp); - goto error; - } - - /* - * Query for the lease time on clientid setup or renewal - * - * Note that this will be set on nfs_clients that were created - * only for the DS role and did not set this bit, but now will - * serve a dual role. - */ - set_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state); - - server->nfs_client = clp; - dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp); - return 0; -error: - dprintk("<-- nfs4_set_client() = xerror %d\n", error); - return error; -} - -/* - * Set up a pNFS Data Server client. - * - * Return any existing nfs_client that matches server address,port,version - * and minorversion. - * - * For a new nfs_client, use a soft mount (default), a low retrans and a - * low timeout interval so that if a connection is lost, we retry through - * the MDS. - */ -struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp, - const struct sockaddr *ds_addr, - int ds_addrlen, int ds_proto) -{ - struct nfs_client_initdata cl_init = { - .addr = ds_addr, - .addrlen = ds_addrlen, - .rpc_ops = &nfs_v4_clientops, - .proto = ds_proto, - .minorversion = mds_clp->cl_minorversion, - .net = mds_clp->net, - }; - struct rpc_timeout ds_timeout = { - .to_initval = 15 * HZ, - .to_maxval = 15 * HZ, - .to_retries = 1, - .to_exponential = 1, - }; - struct nfs_client *clp; - - /* - * Set an authflavor equual to the MDS value. Use the MDS nfs_client - * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS - * (section 13.1 RFC 5661). - */ - clp = nfs_get_client(&cl_init, &ds_timeout, mds_clp->cl_ipaddr, - mds_clp->cl_rpcclient->cl_auth->au_flavor, 0); - - dprintk("<-- %s %p\n", __func__, clp); - return clp; -} -EXPORT_SYMBOL_GPL(nfs4_set_ds_client); - -/* - * Session has been established, and the client marked ready. - * Set the mount rsize and wsize with negotiated fore channel - * attributes which will be bound checked in nfs_server_set_fsinfo. - */ -static void nfs4_session_set_rwsize(struct nfs_server *server) -{ -#ifdef CONFIG_NFS_V4_1 - struct nfs4_session *sess; - u32 server_resp_sz; - u32 server_rqst_sz; - - if (!nfs4_has_session(server->nfs_client)) - return; - sess = server->nfs_client->cl_session; - server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead; - server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead; - - if (server->rsize > server_resp_sz) - server->rsize = server_resp_sz; - if (server->wsize > server_rqst_sz) - server->wsize = server_rqst_sz; -#endif /* CONFIG_NFS_V4_1 */ -} - -static int nfs4_server_common_setup(struct nfs_server *server, - struct nfs_fh *mntfh) -{ - struct nfs_fattr *fattr; - int error; - - BUG_ON(!server->nfs_client); - BUG_ON(!server->nfs_client->rpc_ops); - BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); - - /* data servers support only a subset of NFSv4.1 */ - if (is_ds_only_client(server->nfs_client)) - return -EPROTONOSUPPORT; - - fattr = nfs_alloc_fattr(); - if (fattr == NULL) - return -ENOMEM; - - /* We must ensure the session is initialised first */ - error = nfs4_init_session(server); - if (error < 0) - goto out; - - /* Probe the root fh to retrieve its FSID and filehandle */ - error = nfs4_get_rootfh(server, mntfh); - if (error < 0) - goto out; - - dprintk("Server FSID: %llx:%llx\n", - (unsigned long long) server->fsid.major, - (unsigned long long) server->fsid.minor); - dprintk("Mount FH: %d\n", mntfh->size); - - nfs4_session_set_rwsize(server); - - error = nfs_probe_fsinfo(server, mntfh, fattr); - if (error < 0) - goto out; - - if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) - server->namelen = NFS4_MAXNAMLEN; - - nfs_server_insert_lists(server); - server->mount_time = jiffies; - server->destroy = nfs4_destroy_server; -out: - nfs_free_fattr(fattr); - return error; -} - -/* - * Create a version 4 volume record - */ -static int nfs4_init_server(struct nfs_server *server, - const struct nfs_parsed_mount_data *data) -{ - struct rpc_timeout timeparms; - int error; - - dprintk("--> nfs4_init_server()\n"); - - nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, - data->timeo, data->retrans); - - /* Initialise the client representation from the mount data */ - server->flags = data->flags; - server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK; - if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) - server->caps |= NFS_CAP_READDIRPLUS; - server->options = data->options; - - /* Get a client record */ - error = nfs4_set_client(server, - data->nfs_server.hostname, - (const struct sockaddr *)&data->nfs_server.address, - data->nfs_server.addrlen, - data->client_address, - data->auth_flavors[0], - data->nfs_server.protocol, - &timeparms, - data->minorversion, - data->net); - if (error < 0) - goto error; - - /* - * Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower - * authentication. - */ - if (nfs4_disable_idmapping && data->auth_flavors[0] == RPC_AUTH_UNIX) - server->caps |= NFS_CAP_UIDGID_NOMAP; - - if (data->rsize) - server->rsize = nfs_block_size(data->rsize, NULL); - if (data->wsize) - server->wsize = nfs_block_size(data->wsize, NULL); - - server->acregmin = data->acregmin * HZ; - server->acregmax = data->acregmax * HZ; - server->acdirmin = data->acdirmin * HZ; - server->acdirmax = data->acdirmax * HZ; - - server->port = data->nfs_server.port; - - error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); - -error: - /* Done */ - dprintk("<-- nfs4_init_server() = %d\n", error); - return error; -} - -/* - * Create a version 4 volume record - * - keyed on server and FSID - */ -struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, - struct nfs_fh *mntfh) -{ - struct nfs_server *server; - int error; - - dprintk("--> nfs4_create_server()\n"); - - server = nfs_alloc_server(); - if (!server) - return ERR_PTR(-ENOMEM); - - /* set up the general RPC client */ - error = nfs4_init_server(server, data); - if (error < 0) - goto error; - - error = nfs4_server_common_setup(server, mntfh); - if (error < 0) - goto error; - - dprintk("<-- nfs4_create_server() = %p\n", server); - return server; - -error: - nfs_free_server(server); - dprintk("<-- nfs4_create_server() = error %d\n", error); - return ERR_PTR(error); -} - -/* - * Create an NFS4 referral server record - */ -struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, - struct nfs_fh *mntfh) -{ - struct nfs_client *parent_client; - struct nfs_server *server, *parent_server; - int error; - - dprintk("--> nfs4_create_referral_server()\n"); - - server = nfs_alloc_server(); - if (!server) - return ERR_PTR(-ENOMEM); - - parent_server = NFS_SB(data->sb); - parent_client = parent_server->nfs_client; - - /* Initialise the client representation from the parent server */ - nfs_server_copy_userdata(server, parent_server); - server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR; - - /* Get a client representation. - * Note: NFSv4 always uses TCP, */ - error = nfs4_set_client(server, data->hostname, - data->addr, - data->addrlen, - parent_client->cl_ipaddr, - data->authflavor, - rpc_protocol(parent_server->client), - parent_server->client->cl_timeout, - parent_client->cl_mvops->minor_version, - parent_client->net); - if (error < 0) - goto error; - - error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor); - if (error < 0) - goto error; - - error = nfs4_server_common_setup(server, mntfh); - if (error < 0) - goto error; - - dprintk("<-- nfs_create_referral_server() = %p\n", server); - return server; - -error: - nfs_free_server(server); - dprintk("<-- nfs4_create_referral_server() = error %d\n", error); - return ERR_PTR(error); -} - -#endif /* CONFIG_NFS_V4 */ - -/* - * Clone an NFS2, NFS3 or NFS4 server record - */ -struct nfs_server *nfs_clone_server(struct nfs_server *source, - struct nfs_fh *fh, - struct nfs_fattr *fattr, - rpc_authflavor_t flavor) -{ - struct nfs_server *server; - struct nfs_fattr *fattr_fsinfo; - int error; - - dprintk("--> nfs_clone_server(,%llx:%llx,)\n", - (unsigned long long) fattr->fsid.major, - (unsigned long long) fattr->fsid.minor); - - server = nfs_alloc_server(); - if (!server) - return ERR_PTR(-ENOMEM); - - error = -ENOMEM; - fattr_fsinfo = nfs_alloc_fattr(); - if (fattr_fsinfo == NULL) - goto out_free_server; - - /* Copy data from the source */ - server->nfs_client = source->nfs_client; - server->destroy = source->destroy; - atomic_inc(&server->nfs_client->cl_count); - nfs_server_copy_userdata(server, source); - - server->fsid = fattr->fsid; - - error = nfs_init_server_rpcclient(server, - source->client->cl_timeout, - flavor); - if (error < 0) - goto out_free_server; - if (!IS_ERR(source->client_acl)) - nfs_init_server_aclclient(server); - - /* probe the filesystem info for this server filesystem */ - error = nfs_probe_fsinfo(server, fh, fattr_fsinfo); - if (error < 0) - goto out_free_server; - - if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) - server->namelen = NFS4_MAXNAMLEN; - - dprintk("Cloned FSID: %llx:%llx\n", - (unsigned long long) server->fsid.major, - (unsigned long long) server->fsid.minor); - - error = nfs_start_lockd(server); - if (error < 0) - goto out_free_server; - - nfs_server_insert_lists(server); - server->mount_time = jiffies; - - nfs_free_fattr(fattr_fsinfo); - dprintk("<-- nfs_clone_server() = %p\n", server); - return server; - -out_free_server: - nfs_free_fattr(fattr_fsinfo); - nfs_free_server(server); - dprintk("<-- nfs_clone_server() = error %d\n", error); - return ERR_PTR(error); -} - -void nfs_clients_init(struct net *net) -{ - struct nfs_net *nn = net_generic(net, nfs_net_id); - - INIT_LIST_HEAD(&nn->nfs_client_list); - INIT_LIST_HEAD(&nn->nfs_volume_list); -#ifdef CONFIG_NFS_V4 - idr_init(&nn->cb_ident_idr); -#endif - spin_lock_init(&nn->nfs_client_lock); -} - -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry *proc_fs_nfs; - -static int nfs_server_list_open(struct inode *inode, struct file *file); -static void *nfs_server_list_start(struct seq_file *p, loff_t *pos); -static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); -static void nfs_server_list_stop(struct seq_file *p, void *v); -static int nfs_server_list_show(struct seq_file *m, void *v); - -static const struct seq_operations nfs_server_list_ops = { - .start = nfs_server_list_start, - .next = nfs_server_list_next, - .stop = nfs_server_list_stop, - .show = nfs_server_list_show, -}; - -static const struct file_operations nfs_server_list_fops = { - .open = nfs_server_list_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, - .owner = THIS_MODULE, -}; - -static int nfs_volume_list_open(struct inode *inode, struct file *file); -static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos); -static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); -static void nfs_volume_list_stop(struct seq_file *p, void *v); -static int nfs_volume_list_show(struct seq_file *m, void *v); - -static const struct seq_operations nfs_volume_list_ops = { - .start = nfs_volume_list_start, - .next = nfs_volume_list_next, - .stop = nfs_volume_list_stop, - .show = nfs_volume_list_show, -}; - -static const struct file_operations nfs_volume_list_fops = { - .open = nfs_volume_list_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, - .owner = THIS_MODULE, -}; - -/* - * open "/proc/fs/nfsfs/servers" which provides a summary of servers with which - * we're dealing - */ -static int nfs_server_list_open(struct inode *inode, struct file *file) -{ - struct seq_file *m; - int ret; - struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info; - struct net *net = pid_ns->child_reaper->nsproxy->net_ns; - - ret = seq_open(file, &nfs_server_list_ops); - if (ret < 0) - return ret; - - m = file->private_data; - m->private = net; - - return 0; -} - -/* - * set up the iterator to start reading from the server list and return the first item - */ -static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) -{ - struct nfs_net *nn = net_generic(m->private, nfs_net_id); - - /* lock the list against modification */ - spin_lock(&nn->nfs_client_lock); - return seq_list_start_head(&nn->nfs_client_list, *_pos); -} - -/* - * move to next server - */ -static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) -{ - struct nfs_net *nn = net_generic(p->private, nfs_net_id); - - return seq_list_next(v, &nn->nfs_client_list, pos); -} - -/* - * clean up after reading from the transports list - */ -static void nfs_server_list_stop(struct seq_file *p, void *v) -{ - struct nfs_net *nn = net_generic(p->private, nfs_net_id); - - spin_unlock(&nn->nfs_client_lock); -} - -/* - * display a header line followed by a load of call lines - */ -static int nfs_server_list_show(struct seq_file *m, void *v) -{ - struct nfs_client *clp; - struct nfs_net *nn = net_generic(m->private, nfs_net_id); - - /* display header on line 1 */ - if (v == &nn->nfs_client_list) { - seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); - return 0; - } - - /* display one transport per line on subsequent lines */ - clp = list_entry(v, struct nfs_client, cl_share_link); - - /* Check if the client is initialized */ - if (clp->cl_cons_state != NFS_CS_READY) - return 0; - - rcu_read_lock(); - seq_printf(m, "v%u %s %s %3d %s\n", - clp->rpc_ops->version, - rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), - rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), - atomic_read(&clp->cl_count), - clp->cl_hostname); - rcu_read_unlock(); - - return 0; -} - -/* - * open "/proc/fs/nfsfs/volumes" which provides a summary of extant volumes - */ -static int nfs_volume_list_open(struct inode *inode, struct file *file) -{ - struct seq_file *m; - int ret; - struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info; - struct net *net = pid_ns->child_reaper->nsproxy->net_ns; - - ret = seq_open(file, &nfs_volume_list_ops); - if (ret < 0) - return ret; - - m = file->private_data; - m->private = net; - - return 0; -} - -/* - * set up the iterator to start reading from the volume list and return the first item - */ -static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) -{ - struct nfs_net *nn = net_generic(m->private, nfs_net_id); - - /* lock the list against modification */ - spin_lock(&nn->nfs_client_lock); - return seq_list_start_head(&nn->nfs_volume_list, *_pos); -} - -/* - * move to next volume - */ -static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) -{ - struct nfs_net *nn = net_generic(p->private, nfs_net_id); - - return seq_list_next(v, &nn->nfs_volume_list, pos); -} - -/* - * clean up after reading from the transports list - */ -static void nfs_volume_list_stop(struct seq_file *p, void *v) -{ - struct nfs_net *nn = net_generic(p->private, nfs_net_id); - - spin_unlock(&nn->nfs_client_lock); -} - -/* - * display a header line followed by a load of call lines - */ -static int nfs_volume_list_show(struct seq_file *m, void *v) -{ - struct nfs_server *server; - struct nfs_client *clp; - char dev[8], fsid[17]; - struct nfs_net *nn = net_generic(m->private, nfs_net_id); - - /* display header on line 1 */ - if (v == &nn->nfs_volume_list) { - seq_puts(m, "NV SERVER PORT DEV FSID FSC\n"); - return 0; - } - /* display one transport per line on subsequent lines */ - server = list_entry(v, struct nfs_server, master_link); - clp = server->nfs_client; - - snprintf(dev, 8, "%u:%u", - MAJOR(server->s_dev), MINOR(server->s_dev)); - - snprintf(fsid, 17, "%llx:%llx", - (unsigned long long) server->fsid.major, - (unsigned long long) server->fsid.minor); - - rcu_read_lock(); - seq_printf(m, "v%u %s %s %-7s %-17s %s\n", - clp->rpc_ops->version, - rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), - rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), - dev, - fsid, - nfs_server_fscache_state(server)); - rcu_read_unlock(); - - return 0; -} - -/* - * initialise the /proc/fs/nfsfs/ directory - */ -int __init nfs_fs_proc_init(void) -{ - struct proc_dir_entry *p; - - proc_fs_nfs = proc_mkdir("fs/nfsfs", NULL); - if (!proc_fs_nfs) - goto error_0; - - /* a file of servers with which we're dealing */ - p = proc_create("servers", S_IFREG|S_IRUGO, - proc_fs_nfs, &nfs_server_list_fops); - if (!p) - goto error_1; - - /* a file of volumes that we have mounted */ - p = proc_create("volumes", S_IFREG|S_IRUGO, - proc_fs_nfs, &nfs_volume_list_fops); - if (!p) - goto error_2; - return 0; - -error_2: - remove_proc_entry("servers", proc_fs_nfs); -error_1: - remove_proc_entry("fs/nfsfs", NULL); -error_0: - return -ENOMEM; -} - -/* - * clean up the /proc/fs/nfsfs/ directory - */ -void nfs_fs_proc_exit(void) -{ - remove_proc_entry("volumes", proc_fs_nfs); - remove_proc_entry("servers", proc_fs_nfs); - remove_proc_entry("fs/nfsfs", NULL); -} - -#endif /* CONFIG_PROC_FS */ - -module_param(nfs4_disable_idmapping, bool, 0644); -MODULE_PARM_DESC(nfs4_disable_idmapping, - "Turn off NFSv4 idmapping when using 'sec=sys'"); diff --git a/ANDROID_3.4.5/fs/nfs/delegation.c b/ANDROID_3.4.5/fs/nfs/delegation.c deleted file mode 100644 index 89af1d26..00000000 --- a/ANDROID_3.4.5/fs/nfs/delegation.c +++ /dev/null @@ -1,705 +0,0 @@ -/* - * linux/fs/nfs/delegation.c - * - * Copyright (C) 2004 Trond Myklebust - * - * NFS file delegation management - * - */ -#include <linux/completion.h> -#include <linux/kthread.h> -#include <linux/module.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/spinlock.h> - -#include <linux/nfs4.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_xdr.h> - -#include "nfs4_fs.h" -#include "delegation.h" -#include "internal.h" - -static void nfs_free_delegation(struct nfs_delegation *delegation) -{ - if (delegation->cred) { - put_rpccred(delegation->cred); - delegation->cred = NULL; - } - kfree_rcu(delegation, rcu); -} - -/** - * nfs_mark_delegation_referenced - set delegation's REFERENCED flag - * @delegation: delegation to process - * - */ -void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) -{ - set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags); -} - -/** - * nfs_have_delegation - check if inode has a delegation - * @inode: inode to check - * @flags: delegation types to check for - * - * Returns one if inode has the indicated delegation, otherwise zero. - */ -int nfs_have_delegation(struct inode *inode, fmode_t flags) -{ - struct nfs_delegation *delegation; - int ret = 0; - - flags &= FMODE_READ|FMODE_WRITE; - rcu_read_lock(); - delegation = rcu_dereference(NFS_I(inode)->delegation); - if (delegation != NULL && (delegation->type & flags) == flags) { - nfs_mark_delegation_referenced(delegation); - ret = 1; - } - rcu_read_unlock(); - return ret; -} - -static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state) -{ - struct inode *inode = state->inode; - struct file_lock *fl; - int status = 0; - - if (inode->i_flock == NULL) - goto out; - - /* Protect inode->i_flock using the file locks lock */ - lock_flocks(); - for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { - if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) - continue; - if (nfs_file_open_context(fl->fl_file) != ctx) - continue; - unlock_flocks(); - status = nfs4_lock_delegation_recall(state, fl); - if (status < 0) - goto out; - lock_flocks(); - } - unlock_flocks(); -out: - return status; -} - -static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid) -{ - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs_open_context *ctx; - struct nfs4_state *state; - int err; - -again: - spin_lock(&inode->i_lock); - list_for_each_entry(ctx, &nfsi->open_files, list) { - state = ctx->state; - if (state == NULL) - continue; - if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) - continue; - if (!nfs4_stateid_match(&state->stateid, stateid)) - continue; - get_nfs_open_context(ctx); - spin_unlock(&inode->i_lock); - err = nfs4_open_delegation_recall(ctx, state, stateid); - if (err >= 0) - err = nfs_delegation_claim_locks(ctx, state); - put_nfs_open_context(ctx); - if (err != 0) - return err; - goto again; - } - spin_unlock(&inode->i_lock); - return 0; -} - -/** - * nfs_inode_reclaim_delegation - process a delegation reclaim request - * @inode: inode to process - * @cred: credential to use for request - * @res: new delegation state from server - * - */ -void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, - struct nfs_openres *res) -{ - struct nfs_delegation *delegation; - struct rpc_cred *oldcred = NULL; - - rcu_read_lock(); - delegation = rcu_dereference(NFS_I(inode)->delegation); - if (delegation != NULL) { - spin_lock(&delegation->lock); - if (delegation->inode != NULL) { - nfs4_stateid_copy(&delegation->stateid, &res->delegation); - delegation->type = res->delegation_type; - delegation->maxsize = res->maxsize; - oldcred = delegation->cred; - delegation->cred = get_rpccred(cred); - clear_bit(NFS_DELEGATION_NEED_RECLAIM, - &delegation->flags); - NFS_I(inode)->delegation_state = delegation->type; - spin_unlock(&delegation->lock); - put_rpccred(oldcred); - rcu_read_unlock(); - } else { - /* We appear to have raced with a delegation return. */ - spin_unlock(&delegation->lock); - rcu_read_unlock(); - nfs_inode_set_delegation(inode, cred, res); - } - } else { - rcu_read_unlock(); - } -} - -static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) -{ - int res = 0; - - res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); - nfs_free_delegation(delegation); - return res; -} - -static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) -{ - struct inode *inode = NULL; - - spin_lock(&delegation->lock); - if (delegation->inode != NULL) - inode = igrab(delegation->inode); - spin_unlock(&delegation->lock); - return inode; -} - -static struct nfs_delegation * -nfs_detach_delegation_locked(struct nfs_inode *nfsi, - struct nfs_server *server) -{ - struct nfs_delegation *delegation = - rcu_dereference_protected(nfsi->delegation, - lockdep_is_held(&server->nfs_client->cl_lock)); - - if (delegation == NULL) - goto nomatch; - - spin_lock(&delegation->lock); - list_del_rcu(&delegation->super_list); - delegation->inode = NULL; - nfsi->delegation_state = 0; - rcu_assign_pointer(nfsi->delegation, NULL); - spin_unlock(&delegation->lock); - return delegation; -nomatch: - return NULL; -} - -static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, - struct nfs_server *server) -{ - struct nfs_client *clp = server->nfs_client; - struct nfs_delegation *delegation; - - spin_lock(&clp->cl_lock); - delegation = nfs_detach_delegation_locked(nfsi, server); - spin_unlock(&clp->cl_lock); - return delegation; -} - -/** - * nfs_inode_set_delegation - set up a delegation on an inode - * @inode: inode to which delegation applies - * @cred: cred to use for subsequent delegation processing - * @res: new delegation state from server - * - * Returns zero on success, or a negative errno value. - */ -int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct nfs_client *clp = server->nfs_client; - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs_delegation *delegation, *old_delegation; - struct nfs_delegation *freeme = NULL; - int status = 0; - - delegation = kmalloc(sizeof(*delegation), GFP_NOFS); - if (delegation == NULL) - return -ENOMEM; - nfs4_stateid_copy(&delegation->stateid, &res->delegation); - delegation->type = res->delegation_type; - delegation->maxsize = res->maxsize; - delegation->change_attr = inode->i_version; - delegation->cred = get_rpccred(cred); - delegation->inode = inode; - delegation->flags = 1<<NFS_DELEGATION_REFERENCED; - spin_lock_init(&delegation->lock); - - spin_lock(&clp->cl_lock); - old_delegation = rcu_dereference_protected(nfsi->delegation, - lockdep_is_held(&clp->cl_lock)); - if (old_delegation != NULL) { - if (nfs4_stateid_match(&delegation->stateid, - &old_delegation->stateid) && - delegation->type == old_delegation->type) { - goto out; - } - /* - * Deal with broken servers that hand out two - * delegations for the same file. - * Allow for upgrades to a WRITE delegation, but - * nothing else. - */ - dfprintk(FILE, "%s: server %s handed out " - "a duplicate delegation!\n", - __func__, clp->cl_hostname); - if (delegation->type == old_delegation->type || - !(delegation->type & FMODE_WRITE)) { - freeme = delegation; - delegation = NULL; - goto out; - } - freeme = nfs_detach_delegation_locked(nfsi, server); - } - list_add_rcu(&delegation->super_list, &server->delegations); - nfsi->delegation_state = delegation->type; - rcu_assign_pointer(nfsi->delegation, delegation); - delegation = NULL; - - /* Ensure we revalidate the attributes and page cache! */ - spin_lock(&inode->i_lock); - nfsi->cache_validity |= NFS_INO_REVAL_FORCED; - spin_unlock(&inode->i_lock); - -out: - spin_unlock(&clp->cl_lock); - if (delegation != NULL) - nfs_free_delegation(delegation); - if (freeme != NULL) - nfs_do_return_delegation(inode, freeme, 0); - return status; -} - -/* - * Basic procedure for returning a delegation to the server - */ -static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) -{ - struct nfs_inode *nfsi = NFS_I(inode); - int err; - - /* - * Guard against new delegated open/lock/unlock calls and against - * state recovery - */ - down_write(&nfsi->rwsem); - err = nfs_delegation_claim_opens(inode, &delegation->stateid); - up_write(&nfsi->rwsem); - if (err) - goto out; - - err = nfs_do_return_delegation(inode, delegation, issync); -out: - return err; -} - -/** - * nfs_client_return_marked_delegations - return previously marked delegations - * @clp: nfs_client to process - * - * Returns zero on success, or a negative errno value. - */ -int nfs_client_return_marked_delegations(struct nfs_client *clp) -{ - struct nfs_delegation *delegation; - struct nfs_server *server; - struct inode *inode; - int err = 0; - -restart: - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { - list_for_each_entry_rcu(delegation, &server->delegations, - super_list) { - if (!test_and_clear_bit(NFS_DELEGATION_RETURN, - &delegation->flags)) - continue; - inode = nfs_delegation_grab_inode(delegation); - if (inode == NULL) - continue; - delegation = nfs_detach_delegation(NFS_I(inode), - server); - rcu_read_unlock(); - - if (delegation != NULL) { - filemap_flush(inode->i_mapping); - err = __nfs_inode_return_delegation(inode, - delegation, 0); - } - iput(inode); - if (!err) - goto restart; - set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); - return err; - } - } - rcu_read_unlock(); - return 0; -} - -/** - * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens - * @inode: inode to process - * - * Does not protect against delegation reclaims, therefore really only safe - * to be called from nfs4_clear_inode(). - */ -void nfs_inode_return_delegation_noreclaim(struct inode *inode) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs_delegation *delegation; - - if (rcu_access_pointer(nfsi->delegation) != NULL) { - delegation = nfs_detach_delegation(nfsi, server); - if (delegation != NULL) - nfs_do_return_delegation(inode, delegation, 0); - } -} - -/** - * nfs_inode_return_delegation - synchronously return a delegation - * @inode: inode to process - * - * Returns zero on success, or a negative errno value. - */ -int nfs_inode_return_delegation(struct inode *inode) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs_delegation *delegation; - int err = 0; - - if (rcu_access_pointer(nfsi->delegation) != NULL) { - delegation = nfs_detach_delegation(nfsi, server); - if (delegation != NULL) { - nfs_wb_all(inode); - err = __nfs_inode_return_delegation(inode, delegation, 1); - } - } - return err; -} - -static void nfs_mark_return_delegation(struct nfs_server *server, - struct nfs_delegation *delegation) -{ - set_bit(NFS_DELEGATION_RETURN, &delegation->flags); - set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); -} - -/** - * nfs_super_return_all_delegations - return delegations for one superblock - * @sb: sb to process - * - */ -void nfs_super_return_all_delegations(struct super_block *sb) -{ - struct nfs_server *server = NFS_SB(sb); - struct nfs_client *clp = server->nfs_client; - struct nfs_delegation *delegation; - - if (clp == NULL) - return; - - rcu_read_lock(); - list_for_each_entry_rcu(delegation, &server->delegations, super_list) { - spin_lock(&delegation->lock); - set_bit(NFS_DELEGATION_RETURN, &delegation->flags); - spin_unlock(&delegation->lock); - } - rcu_read_unlock(); - - if (nfs_client_return_marked_delegations(clp) != 0) - nfs4_schedule_state_manager(clp); -} - -static void nfs_mark_return_all_delegation_types(struct nfs_server *server, - fmode_t flags) -{ - struct nfs_delegation *delegation; - - list_for_each_entry_rcu(delegation, &server->delegations, super_list) { - if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) - continue; - if (delegation->type & flags) - nfs_mark_return_delegation(server, delegation); - } -} - -static void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, - fmode_t flags) -{ - struct nfs_server *server; - - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) - nfs_mark_return_all_delegation_types(server, flags); - rcu_read_unlock(); -} - -static void nfs_delegation_run_state_manager(struct nfs_client *clp) -{ - if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) - nfs4_schedule_state_manager(clp); -} - -void nfs_remove_bad_delegation(struct inode *inode) -{ - struct nfs_delegation *delegation; - - delegation = nfs_detach_delegation(NFS_I(inode), NFS_SERVER(inode)); - if (delegation) { - nfs_inode_find_state_and_recover(inode, &delegation->stateid); - nfs_free_delegation(delegation); - } -} -EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); - -/** - * nfs_expire_all_delegation_types - * @clp: client to process - * @flags: delegation types to expire - * - */ -void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags) -{ - nfs_client_mark_return_all_delegation_types(clp, flags); - nfs_delegation_run_state_manager(clp); -} - -/** - * nfs_expire_all_delegations - * @clp: client to process - * - */ -void nfs_expire_all_delegations(struct nfs_client *clp) -{ - nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE); -} - -static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) -{ - struct nfs_delegation *delegation; - - list_for_each_entry_rcu(delegation, &server->delegations, super_list) { - if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) - continue; - nfs_mark_return_delegation(server, delegation); - } -} - -/** - * nfs_expire_unreferenced_delegations - Eliminate unused delegations - * @clp: nfs_client to process - * - */ -void nfs_expire_unreferenced_delegations(struct nfs_client *clp) -{ - struct nfs_server *server; - - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) - nfs_mark_return_unreferenced_delegations(server); - rcu_read_unlock(); - - nfs_delegation_run_state_manager(clp); -} - -/** - * nfs_async_inode_return_delegation - asynchronously return a delegation - * @inode: inode to process - * @stateid: state ID information - * - * Returns zero on success, or a negative errno value. - */ -int nfs_async_inode_return_delegation(struct inode *inode, - const nfs4_stateid *stateid) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct nfs_client *clp = server->nfs_client; - struct nfs_delegation *delegation; - - rcu_read_lock(); - delegation = rcu_dereference(NFS_I(inode)->delegation); - - if (!clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) { - rcu_read_unlock(); - return -ENOENT; - } - nfs_mark_return_delegation(server, delegation); - rcu_read_unlock(); - - nfs_delegation_run_state_manager(clp); - return 0; -} - -static struct inode * -nfs_delegation_find_inode_server(struct nfs_server *server, - const struct nfs_fh *fhandle) -{ - struct nfs_delegation *delegation; - struct inode *res = NULL; - - list_for_each_entry_rcu(delegation, &server->delegations, super_list) { - spin_lock(&delegation->lock); - if (delegation->inode != NULL && - nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { - res = igrab(delegation->inode); - } - spin_unlock(&delegation->lock); - if (res != NULL) - break; - } - return res; -} - -/** - * nfs_delegation_find_inode - retrieve the inode associated with a delegation - * @clp: client state handle - * @fhandle: filehandle from a delegation recall - * - * Returns pointer to inode matching "fhandle," or NULL if a matching inode - * cannot be found. - */ -struct inode *nfs_delegation_find_inode(struct nfs_client *clp, - const struct nfs_fh *fhandle) -{ - struct nfs_server *server; - struct inode *res = NULL; - - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { - res = nfs_delegation_find_inode_server(server, fhandle); - if (res != NULL) - break; - } - rcu_read_unlock(); - return res; -} - -static void nfs_delegation_mark_reclaim_server(struct nfs_server *server) -{ - struct nfs_delegation *delegation; - - list_for_each_entry_rcu(delegation, &server->delegations, super_list) - set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); -} - -/** - * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed - * @clp: nfs_client to process - * - */ -void nfs_delegation_mark_reclaim(struct nfs_client *clp) -{ - struct nfs_server *server; - - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) - nfs_delegation_mark_reclaim_server(server); - rcu_read_unlock(); -} - -/** - * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done - * @clp: nfs_client to process - * - */ -void nfs_delegation_reap_unclaimed(struct nfs_client *clp) -{ - struct nfs_delegation *delegation; - struct nfs_server *server; - struct inode *inode; - -restart: - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { - list_for_each_entry_rcu(delegation, &server->delegations, - super_list) { - if (test_bit(NFS_DELEGATION_NEED_RECLAIM, - &delegation->flags) == 0) - continue; - inode = nfs_delegation_grab_inode(delegation); - if (inode == NULL) - continue; - delegation = nfs_detach_delegation(NFS_I(inode), - server); - rcu_read_unlock(); - - if (delegation != NULL) - nfs_free_delegation(delegation); - iput(inode); - goto restart; - } - } - rcu_read_unlock(); -} - -/** - * nfs_delegations_present - check for existence of delegations - * @clp: client state handle - * - * Returns one if there are any nfs_delegation structures attached - * to this nfs_client. - */ -int nfs_delegations_present(struct nfs_client *clp) -{ - struct nfs_server *server; - int ret = 0; - - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) - if (!list_empty(&server->delegations)) { - ret = 1; - break; - } - rcu_read_unlock(); - return ret; -} - -/** - * nfs4_copy_delegation_stateid - Copy inode's state ID information - * @dst: stateid data structure to fill in - * @inode: inode to check - * @flags: delegation type requirement - * - * Returns "true" and fills in "dst->data" * if inode had a delegation, - * otherwise "false" is returned. - */ -bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, - fmode_t flags) -{ - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs_delegation *delegation; - bool ret; - - flags &= FMODE_READ|FMODE_WRITE; - rcu_read_lock(); - delegation = rcu_dereference(nfsi->delegation); - ret = (delegation != NULL && (delegation->type & flags) == flags); - if (ret) { - nfs4_stateid_copy(dst, &delegation->stateid); - nfs_mark_delegation_referenced(delegation); - } - rcu_read_unlock(); - return ret; -} diff --git a/ANDROID_3.4.5/fs/nfs/delegation.h b/ANDROID_3.4.5/fs/nfs/delegation.h deleted file mode 100644 index cd6a7a8d..00000000 --- a/ANDROID_3.4.5/fs/nfs/delegation.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * linux/fs/nfs/delegation.h - * - * Copyright (c) Trond Myklebust - * - * Definitions pertaining to NFS delegated files - */ -#ifndef FS_NFS_DELEGATION_H -#define FS_NFS_DELEGATION_H - -#if defined(CONFIG_NFS_V4) -/* - * NFSv4 delegation - */ -struct nfs_delegation { - struct list_head super_list; - struct rpc_cred *cred; - struct inode *inode; - nfs4_stateid stateid; - fmode_t type; - loff_t maxsize; - __u64 change_attr; - unsigned long flags; - spinlock_t lock; - struct rcu_head rcu; -}; - -enum { - NFS_DELEGATION_NEED_RECLAIM = 0, - NFS_DELEGATION_RETURN, - NFS_DELEGATION_REFERENCED, -}; - -int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); -void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); -int nfs_inode_return_delegation(struct inode *inode); -int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); -void nfs_inode_return_delegation_noreclaim(struct inode *inode); - -struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle); -void nfs_super_return_all_delegations(struct super_block *sb); -void nfs_expire_all_delegations(struct nfs_client *clp); -void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags); -void nfs_expire_unreferenced_delegations(struct nfs_client *clp); -int nfs_client_return_marked_delegations(struct nfs_client *clp); -int nfs_delegations_present(struct nfs_client *clp); -void nfs_remove_bad_delegation(struct inode *inode); - -void nfs_delegation_mark_reclaim(struct nfs_client *clp); -void nfs_delegation_reap_unclaimed(struct nfs_client *clp); - -/* NFSv4 delegation-related procedures */ -int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync); -int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid); -int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl); -bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, fmode_t flags); - -void nfs_mark_delegation_referenced(struct nfs_delegation *delegation); -int nfs_have_delegation(struct inode *inode, fmode_t flags); - -#else -static inline int nfs_have_delegation(struct inode *inode, fmode_t flags) -{ - return 0; -} - -static inline int nfs_inode_return_delegation(struct inode *inode) -{ - return 0; -} -#endif - -static inline int nfs_have_delegated_attributes(struct inode *inode) -{ - return nfs_have_delegation(inode, FMODE_READ) && - !(NFS_I(inode)->cache_validity & NFS_INO_REVAL_FORCED); -} - -#endif diff --git a/ANDROID_3.4.5/fs/nfs/dir.c b/ANDROID_3.4.5/fs/nfs/dir.c deleted file mode 100644 index 8789210c..00000000 --- a/ANDROID_3.4.5/fs/nfs/dir.c +++ /dev/null @@ -1,2362 +0,0 @@ -/* - * linux/fs/nfs/dir.c - * - * Copyright (C) 1992 Rick Sladkey - * - * nfs directory handling functions - * - * 10 Apr 1996 Added silly rename for unlink --okir - * 28 Sep 1996 Improved directory cache --okir - * 23 Aug 1997 Claus Heine claus@momo.math.rwth-aachen.de - * Re-implemented silly rename for unlink, newly implemented - * silly rename for nfs_rename() following the suggestions - * of Olaf Kirch (okir) found in this file. - * Following Linus comments on my original hack, this version - * depends only on the dcache stuff and doesn't touch the inode - * layer (iput() and friends). - * 6 Jun 1999 Cache readdir lookups in the page cache. -DaveM - */ - -#include <linux/time.h> -#include <linux/errno.h> -#include <linux/stat.h> -#include <linux/fcntl.h> -#include <linux/string.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/sunrpc/clnt.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_mount.h> -#include <linux/pagemap.h> -#include <linux/pagevec.h> -#include <linux/namei.h> -#include <linux/mount.h> -#include <linux/sched.h> -#include <linux/kmemleak.h> -#include <linux/xattr.h> - -#include "delegation.h" -#include "iostat.h" -#include "internal.h" -#include "fscache.h" - -/* #define NFS_DEBUG_VERBOSE 1 */ - -static int nfs_opendir(struct inode *, struct file *); -static int nfs_closedir(struct inode *, struct file *); -static int nfs_readdir(struct file *, void *, filldir_t); -static struct dentry *nfs_lookup(struct inode *, struct dentry *, struct nameidata *); -static int nfs_create(struct inode *, struct dentry *, umode_t, struct nameidata *); -static int nfs_mkdir(struct inode *, struct dentry *, umode_t); -static int nfs_rmdir(struct inode *, struct dentry *); -static int nfs_unlink(struct inode *, struct dentry *); -static int nfs_symlink(struct inode *, struct dentry *, const char *); -static int nfs_link(struct dentry *, struct inode *, struct dentry *); -static int nfs_mknod(struct inode *, struct dentry *, umode_t, dev_t); -static int nfs_rename(struct inode *, struct dentry *, - struct inode *, struct dentry *); -static int nfs_fsync_dir(struct file *, loff_t, loff_t, int); -static loff_t nfs_llseek_dir(struct file *, loff_t, int); -static void nfs_readdir_clear_array(struct page*); - -const struct file_operations nfs_dir_operations = { - .llseek = nfs_llseek_dir, - .read = generic_read_dir, - .readdir = nfs_readdir, - .open = nfs_opendir, - .release = nfs_closedir, - .fsync = nfs_fsync_dir, -}; - -const struct inode_operations nfs_dir_inode_operations = { - .create = nfs_create, - .lookup = nfs_lookup, - .link = nfs_link, - .unlink = nfs_unlink, - .symlink = nfs_symlink, - .mkdir = nfs_mkdir, - .rmdir = nfs_rmdir, - .mknod = nfs_mknod, - .rename = nfs_rename, - .permission = nfs_permission, - .getattr = nfs_getattr, - .setattr = nfs_setattr, -}; - -const struct address_space_operations nfs_dir_aops = { - .freepage = nfs_readdir_clear_array, -}; - -#ifdef CONFIG_NFS_V3 -const struct inode_operations nfs3_dir_inode_operations = { - .create = nfs_create, - .lookup = nfs_lookup, - .link = nfs_link, - .unlink = nfs_unlink, - .symlink = nfs_symlink, - .mkdir = nfs_mkdir, - .rmdir = nfs_rmdir, - .mknod = nfs_mknod, - .rename = nfs_rename, - .permission = nfs_permission, - .getattr = nfs_getattr, - .setattr = nfs_setattr, - .listxattr = nfs3_listxattr, - .getxattr = nfs3_getxattr, - .setxattr = nfs3_setxattr, - .removexattr = nfs3_removexattr, -}; -#endif /* CONFIG_NFS_V3 */ - -#ifdef CONFIG_NFS_V4 - -static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *); -static int nfs_open_create(struct inode *dir, struct dentry *dentry, umode_t mode, struct nameidata *nd); -const struct inode_operations nfs4_dir_inode_operations = { - .create = nfs_open_create, - .lookup = nfs_atomic_lookup, - .link = nfs_link, - .unlink = nfs_unlink, - .symlink = nfs_symlink, - .mkdir = nfs_mkdir, - .rmdir = nfs_rmdir, - .mknod = nfs_mknod, - .rename = nfs_rename, - .permission = nfs_permission, - .getattr = nfs_getattr, - .setattr = nfs_setattr, - .getxattr = generic_getxattr, - .setxattr = generic_setxattr, - .listxattr = generic_listxattr, - .removexattr = generic_removexattr, -}; - -#endif /* CONFIG_NFS_V4 */ - -static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred) -{ - struct nfs_open_dir_context *ctx; - ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); - if (ctx != NULL) { - ctx->duped = 0; - ctx->attr_gencount = NFS_I(dir)->attr_gencount; - ctx->dir_cookie = 0; - ctx->dup_cookie = 0; - ctx->cred = get_rpccred(cred); - return ctx; - } - return ERR_PTR(-ENOMEM); -} - -static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx) -{ - put_rpccred(ctx->cred); - kfree(ctx); -} - -/* - * Open file - */ -static int -nfs_opendir(struct inode *inode, struct file *filp) -{ - int res = 0; - struct nfs_open_dir_context *ctx; - struct rpc_cred *cred; - - dfprintk(FILE, "NFS: open dir(%s/%s)\n", - filp->f_path.dentry->d_parent->d_name.name, - filp->f_path.dentry->d_name.name); - - nfs_inc_stats(inode, NFSIOS_VFSOPEN); - - cred = rpc_lookup_cred(); - if (IS_ERR(cred)) - return PTR_ERR(cred); - ctx = alloc_nfs_open_dir_context(inode, cred); - if (IS_ERR(ctx)) { - res = PTR_ERR(ctx); - goto out; - } - filp->private_data = ctx; - if (filp->f_path.dentry == filp->f_path.mnt->mnt_root) { - /* This is a mountpoint, so d_revalidate will never - * have been called, so we need to refresh the - * inode (for close-open consistency) ourselves. - */ - __nfs_revalidate_inode(NFS_SERVER(inode), inode); - } -out: - put_rpccred(cred); - return res; -} - -static int -nfs_closedir(struct inode *inode, struct file *filp) -{ - put_nfs_open_dir_context(filp->private_data); - return 0; -} - -struct nfs_cache_array_entry { - u64 cookie; - u64 ino; - struct qstr string; - unsigned char d_type; -}; - -struct nfs_cache_array { - int size; - int eof_index; - u64 last_cookie; - struct nfs_cache_array_entry array[0]; -}; - -typedef int (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, int); -typedef struct { - struct file *file; - struct page *page; - unsigned long page_index; - u64 *dir_cookie; - u64 last_cookie; - loff_t current_index; - decode_dirent_t decode; - - unsigned long timestamp; - unsigned long gencount; - unsigned int cache_entry_index; - unsigned int plus:1; - unsigned int eof:1; -} nfs_readdir_descriptor_t; - -/* - * The caller is responsible for calling nfs_readdir_release_array(page) - */ -static -struct nfs_cache_array *nfs_readdir_get_array(struct page *page) -{ - void *ptr; - if (page == NULL) - return ERR_PTR(-EIO); - ptr = kmap(page); - if (ptr == NULL) - return ERR_PTR(-ENOMEM); - return ptr; -} - -static -void nfs_readdir_release_array(struct page *page) -{ - kunmap(page); -} - -/* - * we are freeing strings created by nfs_add_to_readdir_array() - */ -static -void nfs_readdir_clear_array(struct page *page) -{ - struct nfs_cache_array *array; - int i; - - array = kmap_atomic(page); - for (i = 0; i < array->size; i++) - kfree(array->array[i].string.name); - kunmap_atomic(array); -} - -/* - * the caller is responsible for freeing qstr.name - * when called by nfs_readdir_add_to_array, the strings will be freed in - * nfs_clear_readdir_array() - */ -static -int nfs_readdir_make_qstr(struct qstr *string, const char *name, unsigned int len) -{ - string->len = len; - string->name = kmemdup(name, len, GFP_KERNEL); - if (string->name == NULL) - return -ENOMEM; - /* - * Avoid a kmemleak false positive. The pointer to the name is stored - * in a page cache page which kmemleak does not scan. - */ - kmemleak_not_leak(string->name); - string->hash = full_name_hash(name, len); - return 0; -} - -static -int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page) -{ - struct nfs_cache_array *array = nfs_readdir_get_array(page); - struct nfs_cache_array_entry *cache_entry; - int ret; - - if (IS_ERR(array)) - return PTR_ERR(array); - - cache_entry = &array->array[array->size]; - - /* Check that this entry lies within the page bounds */ - ret = -ENOSPC; - if ((char *)&cache_entry[1] - (char *)page_address(page) > PAGE_SIZE) - goto out; - - cache_entry->cookie = entry->prev_cookie; - cache_entry->ino = entry->ino; - cache_entry->d_type = entry->d_type; - ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len); - if (ret) - goto out; - array->last_cookie = entry->cookie; - array->size++; - if (entry->eof != 0) - array->eof_index = array->size; -out: - nfs_readdir_release_array(page); - return ret; -} - -static -int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc) -{ - loff_t diff = desc->file->f_pos - desc->current_index; - unsigned int index; - - if (diff < 0) - goto out_eof; - if (diff >= array->size) { - if (array->eof_index >= 0) - goto out_eof; - return -EAGAIN; - } - - index = (unsigned int)diff; - *desc->dir_cookie = array->array[index].cookie; - desc->cache_entry_index = index; - return 0; -out_eof: - desc->eof = 1; - return -EBADCOOKIE; -} - -static -int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc) -{ - int i; - loff_t new_pos; - int status = -EAGAIN; - - for (i = 0; i < array->size; i++) { - if (array->array[i].cookie == *desc->dir_cookie) { - struct nfs_inode *nfsi = NFS_I(desc->file->f_path.dentry->d_inode); - struct nfs_open_dir_context *ctx = desc->file->private_data; - - new_pos = desc->current_index + i; - if (ctx->attr_gencount != nfsi->attr_gencount - || (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))) { - ctx->duped = 0; - ctx->attr_gencount = nfsi->attr_gencount; - } else if (new_pos < desc->file->f_pos) { - if (ctx->duped > 0 - && ctx->dup_cookie == *desc->dir_cookie) { - if (printk_ratelimit()) { - pr_notice("NFS: directory %s/%s contains a readdir loop." - "Please contact your server vendor. " - "The file: %s has duplicate cookie %llu\n", - desc->file->f_dentry->d_parent->d_name.name, - desc->file->f_dentry->d_name.name, - array->array[i].string.name, - *desc->dir_cookie); - } - status = -ELOOP; - goto out; - } - ctx->dup_cookie = *desc->dir_cookie; - ctx->duped = -1; - } - desc->file->f_pos = new_pos; - desc->cache_entry_index = i; - return 0; - } - } - if (array->eof_index >= 0) { - status = -EBADCOOKIE; - if (*desc->dir_cookie == array->last_cookie) - desc->eof = 1; - } -out: - return status; -} - -static -int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc) -{ - struct nfs_cache_array *array; - int status; - - array = nfs_readdir_get_array(desc->page); - if (IS_ERR(array)) { - status = PTR_ERR(array); - goto out; - } - - if (*desc->dir_cookie == 0) - status = nfs_readdir_search_for_pos(array, desc); - else - status = nfs_readdir_search_for_cookie(array, desc); - - if (status == -EAGAIN) { - desc->last_cookie = array->last_cookie; - desc->current_index += array->size; - desc->page_index++; - } - nfs_readdir_release_array(desc->page); -out: - return status; -} - -/* Fill a page with xdr information before transferring to the cache page */ -static -int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc, - struct nfs_entry *entry, struct file *file, struct inode *inode) -{ - struct nfs_open_dir_context *ctx = file->private_data; - struct rpc_cred *cred = ctx->cred; - unsigned long timestamp, gencount; - int error; - - again: - timestamp = jiffies; - gencount = nfs_inc_attr_generation_counter(); - error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, entry->cookie, pages, - NFS_SERVER(inode)->dtsize, desc->plus); - if (error < 0) { - /* We requested READDIRPLUS, but the server doesn't grok it */ - if (error == -ENOTSUPP && desc->plus) { - NFS_SERVER(inode)->caps &= ~NFS_CAP_READDIRPLUS; - clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); - desc->plus = 0; - goto again; - } - goto error; - } - desc->timestamp = timestamp; - desc->gencount = gencount; -error: - return error; -} - -static int xdr_decode(nfs_readdir_descriptor_t *desc, - struct nfs_entry *entry, struct xdr_stream *xdr) -{ - int error; - - error = desc->decode(xdr, entry, desc->plus); - if (error) - return error; - entry->fattr->time_start = desc->timestamp; - entry->fattr->gencount = desc->gencount; - return 0; -} - -static -int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry) -{ - if (dentry->d_inode == NULL) - goto different; - if (nfs_compare_fh(entry->fh, NFS_FH(dentry->d_inode)) != 0) - goto different; - return 1; -different: - return 0; -} - -static -void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) -{ - struct qstr filename = { - .len = entry->len, - .name = entry->name, - }; - struct dentry *dentry; - struct dentry *alias; - struct inode *dir = parent->d_inode; - struct inode *inode; - - if (filename.name[0] == '.') { - if (filename.len == 1) - return; - if (filename.len == 2 && filename.name[1] == '.') - return; - } - filename.hash = full_name_hash(filename.name, filename.len); - - dentry = d_lookup(parent, &filename); - if (dentry != NULL) { - if (nfs_same_file(dentry, entry)) { - nfs_refresh_inode(dentry->d_inode, entry->fattr); - goto out; - } else { - d_drop(dentry); - dput(dentry); - } - } - - dentry = d_alloc(parent, &filename); - if (dentry == NULL) - return; - - inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr); - if (IS_ERR(inode)) - goto out; - - alias = d_materialise_unique(dentry, inode); - if (IS_ERR(alias)) - goto out; - else if (alias) { - nfs_set_verifier(alias, nfs_save_change_attribute(dir)); - dput(alias); - } else - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); - -out: - dput(dentry); -} - -/* Perform conversion from xdr to cache array */ -static -int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, - struct page **xdr_pages, struct page *page, unsigned int buflen) -{ - struct xdr_stream stream; - struct xdr_buf buf; - struct page *scratch; - struct nfs_cache_array *array; - unsigned int count = 0; - int status; - - scratch = alloc_page(GFP_KERNEL); - if (scratch == NULL) - return -ENOMEM; - - xdr_init_decode_pages(&stream, &buf, xdr_pages, buflen); - xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE); - - do { - status = xdr_decode(desc, entry, &stream); - if (status != 0) { - if (status == -EAGAIN) - status = 0; - break; - } - - count++; - - if (desc->plus != 0) - nfs_prime_dcache(desc->file->f_path.dentry, entry); - - status = nfs_readdir_add_to_array(entry, page); - if (status != 0) - break; - } while (!entry->eof); - - if (count == 0 || (status == -EBADCOOKIE && entry->eof != 0)) { - array = nfs_readdir_get_array(page); - if (!IS_ERR(array)) { - array->eof_index = array->size; - status = 0; - nfs_readdir_release_array(page); - } else - status = PTR_ERR(array); - } - - put_page(scratch); - return status; -} - -static -void nfs_readdir_free_pagearray(struct page **pages, unsigned int npages) -{ - unsigned int i; - for (i = 0; i < npages; i++) - put_page(pages[i]); -} - -static -void nfs_readdir_free_large_page(void *ptr, struct page **pages, - unsigned int npages) -{ - nfs_readdir_free_pagearray(pages, npages); -} - -/* - * nfs_readdir_large_page will allocate pages that must be freed with a call - * to nfs_readdir_free_large_page - */ -static -int nfs_readdir_large_page(struct page **pages, unsigned int npages) -{ - unsigned int i; - - for (i = 0; i < npages; i++) { - struct page *page = alloc_page(GFP_KERNEL); - if (page == NULL) - goto out_freepages; - pages[i] = page; - } - return 0; - -out_freepages: - nfs_readdir_free_pagearray(pages, i); - return -ENOMEM; -} - -static -int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, struct inode *inode) -{ - struct page *pages[NFS_MAX_READDIR_PAGES]; - void *pages_ptr = NULL; - struct nfs_entry entry; - struct file *file = desc->file; - struct nfs_cache_array *array; - int status = -ENOMEM; - unsigned int array_size = ARRAY_SIZE(pages); - - entry.prev_cookie = 0; - entry.cookie = desc->last_cookie; - entry.eof = 0; - entry.fh = nfs_alloc_fhandle(); - entry.fattr = nfs_alloc_fattr(); - entry.server = NFS_SERVER(inode); - if (entry.fh == NULL || entry.fattr == NULL) - goto out; - - array = nfs_readdir_get_array(page); - if (IS_ERR(array)) { - status = PTR_ERR(array); - goto out; - } - memset(array, 0, sizeof(struct nfs_cache_array)); - array->eof_index = -1; - - status = nfs_readdir_large_page(pages, array_size); - if (status < 0) - goto out_release_array; - do { - unsigned int pglen; - status = nfs_readdir_xdr_filler(pages, desc, &entry, file, inode); - - if (status < 0) - break; - pglen = status; - status = nfs_readdir_page_filler(desc, &entry, pages, page, pglen); - if (status < 0) { - if (status == -ENOSPC) - status = 0; - break; - } - } while (array->eof_index < 0); - - nfs_readdir_free_large_page(pages_ptr, pages, array_size); -out_release_array: - nfs_readdir_release_array(page); -out: - nfs_free_fattr(entry.fattr); - nfs_free_fhandle(entry.fh); - return status; -} - -/* - * Now we cache directories properly, by converting xdr information - * to an array that can be used for lookups later. This results in - * fewer cache pages, since we can store more information on each page. - * We only need to convert from xdr once so future lookups are much simpler - */ -static -int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) -{ - struct inode *inode = desc->file->f_path.dentry->d_inode; - int ret; - - ret = nfs_readdir_xdr_to_array(desc, page, inode); - if (ret < 0) - goto error; - SetPageUptodate(page); - - if (invalidate_inode_pages2_range(inode->i_mapping, page->index + 1, -1) < 0) { - /* Should never happen */ - nfs_zap_mapping(inode, inode->i_mapping); - } - unlock_page(page); - return 0; - error: - unlock_page(page); - return ret; -} - -static -void cache_page_release(nfs_readdir_descriptor_t *desc) -{ - if (!desc->page->mapping) - nfs_readdir_clear_array(desc->page); - page_cache_release(desc->page); - desc->page = NULL; -} - -static -struct page *get_cache_page(nfs_readdir_descriptor_t *desc) -{ - return read_cache_page(desc->file->f_path.dentry->d_inode->i_mapping, - desc->page_index, (filler_t *)nfs_readdir_filler, desc); -} - -/* - * Returns 0 if desc->dir_cookie was found on page desc->page_index - */ -static -int find_cache_page(nfs_readdir_descriptor_t *desc) -{ - int res; - - desc->page = get_cache_page(desc); - if (IS_ERR(desc->page)) - return PTR_ERR(desc->page); - - res = nfs_readdir_search_array(desc); - if (res != 0) - cache_page_release(desc); - return res; -} - -/* Search for desc->dir_cookie from the beginning of the page cache */ -static inline -int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) -{ - int res; - - if (desc->page_index == 0) { - desc->current_index = 0; - desc->last_cookie = 0; - } - do { - res = find_cache_page(desc); - } while (res == -EAGAIN); - return res; -} - -/* - * Once we've found the start of the dirent within a page: fill 'er up... - */ -static -int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, - filldir_t filldir) -{ - struct file *file = desc->file; - int i = 0; - int res = 0; - struct nfs_cache_array *array = NULL; - struct nfs_open_dir_context *ctx = file->private_data; - - array = nfs_readdir_get_array(desc->page); - if (IS_ERR(array)) { - res = PTR_ERR(array); - goto out; - } - - for (i = desc->cache_entry_index; i < array->size; i++) { - struct nfs_cache_array_entry *ent; - - ent = &array->array[i]; - if (filldir(dirent, ent->string.name, ent->string.len, - file->f_pos, nfs_compat_user_ino64(ent->ino), - ent->d_type) < 0) { - desc->eof = 1; - break; - } - file->f_pos++; - if (i < (array->size-1)) - *desc->dir_cookie = array->array[i+1].cookie; - else - *desc->dir_cookie = array->last_cookie; - if (ctx->duped != 0) - ctx->duped = 1; - } - if (array->eof_index >= 0) - desc->eof = 1; - - nfs_readdir_release_array(desc->page); -out: - cache_page_release(desc); - dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", - (unsigned long long)*desc->dir_cookie, res); - return res; -} - -/* - * If we cannot find a cookie in our cache, we suspect that this is - * because it points to a deleted file, so we ask the server to return - * whatever it thinks is the next entry. We then feed this to filldir. - * If all goes well, we should then be able to find our way round the - * cache on the next call to readdir_search_pagecache(); - * - * NOTE: we cannot add the anonymous page to the pagecache because - * the data it contains might not be page aligned. Besides, - * we should already have a complete representation of the - * directory in the page cache by the time we get here. - */ -static inline -int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, - filldir_t filldir) -{ - struct page *page = NULL; - int status; - struct inode *inode = desc->file->f_path.dentry->d_inode; - struct nfs_open_dir_context *ctx = desc->file->private_data; - - dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n", - (unsigned long long)*desc->dir_cookie); - - page = alloc_page(GFP_HIGHUSER); - if (!page) { - status = -ENOMEM; - goto out; - } - - desc->page_index = 0; - desc->last_cookie = *desc->dir_cookie; - desc->page = page; - ctx->duped = 0; - - status = nfs_readdir_xdr_to_array(desc, page, inode); - if (status < 0) - goto out_release; - - status = nfs_do_filldir(desc, dirent, filldir); - - out: - dfprintk(DIRCACHE, "NFS: %s: returns %d\n", - __func__, status); - return status; - out_release: - cache_page_release(desc); - goto out; -} - -/* The file offset position represents the dirent entry number. A - last cookie cache takes care of the common case of reading the - whole directory. - */ -static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) -{ - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = dentry->d_inode; - nfs_readdir_descriptor_t my_desc, - *desc = &my_desc; - struct nfs_open_dir_context *dir_ctx = filp->private_data; - int res; - - dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - (long long)filp->f_pos); - nfs_inc_stats(inode, NFSIOS_VFSGETDENTS); - - /* - * filp->f_pos points to the dirent entry number. - * *desc->dir_cookie has the cookie for the next entry. We have - * to either find the entry with the appropriate number or - * revalidate the cookie. - */ - memset(desc, 0, sizeof(*desc)); - - desc->file = filp; - desc->dir_cookie = &dir_ctx->dir_cookie; - desc->decode = NFS_PROTO(inode)->decode_dirent; - desc->plus = NFS_USE_READDIRPLUS(inode); - - nfs_block_sillyrename(dentry); - res = nfs_revalidate_mapping(inode, filp->f_mapping); - if (res < 0) - goto out; - - do { - res = readdir_search_pagecache(desc); - - if (res == -EBADCOOKIE) { - res = 0; - /* This means either end of directory */ - if (*desc->dir_cookie && desc->eof == 0) { - /* Or that the server has 'lost' a cookie */ - res = uncached_readdir(desc, dirent, filldir); - if (res == 0) - continue; - } - break; - } - if (res == -ETOOSMALL && desc->plus) { - clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); - nfs_zap_caches(inode); - desc->page_index = 0; - desc->plus = 0; - desc->eof = 0; - continue; - } - if (res < 0) - break; - - res = nfs_do_filldir(desc, dirent, filldir); - if (res < 0) - break; - } while (!desc->eof); -out: - nfs_unblock_sillyrename(dentry); - if (res > 0) - res = 0; - dfprintk(FILE, "NFS: readdir(%s/%s) returns %d\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - res); - return res; -} - -static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin) -{ - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = dentry->d_inode; - struct nfs_open_dir_context *dir_ctx = filp->private_data; - - dfprintk(FILE, "NFS: llseek dir(%s/%s, %lld, %d)\n", - dentry->d_parent->d_name.name, - dentry->d_name.name, - offset, origin); - - mutex_lock(&inode->i_mutex); - switch (origin) { - case 1: - offset += filp->f_pos; - case 0: - if (offset >= 0) - break; - default: - offset = -EINVAL; - goto out; - } - if (offset != filp->f_pos) { - filp->f_pos = offset; - dir_ctx->dir_cookie = 0; - dir_ctx->duped = 0; - } -out: - mutex_unlock(&inode->i_mutex); - return offset; -} - -/* - * All directory operations under NFS are synchronous, so fsync() - * is a dummy operation. - */ -static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end, - int datasync) -{ - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = dentry->d_inode; - - dfprintk(FILE, "NFS: fsync dir(%s/%s) datasync %d\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - datasync); - - mutex_lock(&inode->i_mutex); - nfs_inc_stats(dentry->d_inode, NFSIOS_VFSFSYNC); - mutex_unlock(&inode->i_mutex); - return 0; -} - -/** - * nfs_force_lookup_revalidate - Mark the directory as having changed - * @dir - pointer to directory inode - * - * This forces the revalidation code in nfs_lookup_revalidate() to do a - * full lookup on all child dentries of 'dir' whenever a change occurs - * on the server that might have invalidated our dcache. - * - * The caller should be holding dir->i_lock - */ -void nfs_force_lookup_revalidate(struct inode *dir) -{ - NFS_I(dir)->cache_change_attribute++; -} - -/* - * A check for whether or not the parent directory has changed. - * In the case it has, we assume that the dentries are untrustworthy - * and may need to be looked up again. - */ -static int nfs_check_verifier(struct inode *dir, struct dentry *dentry) -{ - if (IS_ROOT(dentry)) - return 1; - if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONE) - return 0; - if (!nfs_verify_change_attribute(dir, dentry->d_time)) - return 0; - /* Revalidate nfsi->cache_change_attribute before we declare a match */ - if (nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0) - return 0; - if (!nfs_verify_change_attribute(dir, dentry->d_time)) - return 0; - return 1; -} - -/* - * Return the intent data that applies to this particular path component - * - * Note that the current set of intents only apply to the very last - * component of the path and none of them is set before that last - * component. - */ -static inline unsigned int nfs_lookup_check_intent(struct nameidata *nd, - unsigned int mask) -{ - return nd->flags & mask; -} - -/* - * Use intent information to check whether or not we're going to do - * an O_EXCL create using this path component. - */ -static int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd) -{ - if (NFS_PROTO(dir)->version == 2) - return 0; - return nd && nfs_lookup_check_intent(nd, LOOKUP_EXCL); -} - -/* - * Inode and filehandle revalidation for lookups. - * - * We force revalidation in the cases where the VFS sets LOOKUP_REVAL, - * or if the intent information indicates that we're about to open this - * particular file and the "nocto" mount flag is not set. - * - */ -static inline -int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd) -{ - struct nfs_server *server = NFS_SERVER(inode); - - if (IS_AUTOMOUNT(inode)) - return 0; - if (nd != NULL) { - /* VFS wants an on-the-wire revalidation */ - if (nd->flags & LOOKUP_REVAL) - goto out_force; - /* This is an open(2) */ - if (nfs_lookup_check_intent(nd, LOOKUP_OPEN) != 0 && - !(server->flags & NFS_MOUNT_NOCTO) && - (S_ISREG(inode->i_mode) || - S_ISDIR(inode->i_mode))) - goto out_force; - return 0; - } - return nfs_revalidate_inode(server, inode); -out_force: - return __nfs_revalidate_inode(server, inode); -} - -/* - * We judge how long we want to trust negative - * dentries by looking at the parent inode mtime. - * - * If parent mtime has changed, we revalidate, else we wait for a - * period corresponding to the parent's attribute cache timeout value. - */ -static inline -int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry, - struct nameidata *nd) -{ - /* Don't revalidate a negative dentry if we're creating a new file */ - if (nd != NULL && nfs_lookup_check_intent(nd, LOOKUP_CREATE) != 0) - return 0; - if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG) - return 1; - return !nfs_check_verifier(dir, dentry); -} - -/* - * This is called every time the dcache has a lookup hit, - * and we should check whether we can really trust that - * lookup. - * - * NOTE! The hit can be a negative hit too, don't assume - * we have an inode! - * - * If the parent directory is seen to have changed, we throw out the - * cached dentry and do a new lookup. - */ -static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd) -{ - struct inode *dir; - struct inode *inode; - struct dentry *parent; - struct nfs_fh *fhandle = NULL; - struct nfs_fattr *fattr = NULL; - int error; - - if (nd->flags & LOOKUP_RCU) - return -ECHILD; - - parent = dget_parent(dentry); - dir = parent->d_inode; - nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE); - inode = dentry->d_inode; - - if (!inode) { - if (nfs_neg_need_reval(dir, dentry, nd)) - goto out_bad; - goto out_valid; - } - - if (is_bad_inode(inode)) { - dfprintk(LOOKUPCACHE, "%s: %s/%s has dud inode\n", - __func__, dentry->d_parent->d_name.name, - dentry->d_name.name); - goto out_bad; - } - - if (nfs_have_delegation(inode, FMODE_READ)) - goto out_set_verifier; - - /* Force a full look up iff the parent directory has changed */ - if (!nfs_is_exclusive_create(dir, nd) && nfs_check_verifier(dir, dentry)) { - if (nfs_lookup_verify_inode(inode, nd)) - goto out_zap_parent; - goto out_valid; - } - - if (NFS_STALE(inode)) - goto out_bad; - - error = -ENOMEM; - fhandle = nfs_alloc_fhandle(); - fattr = nfs_alloc_fattr(); - if (fhandle == NULL || fattr == NULL) - goto out_error; - - error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr); - if (error) - goto out_bad; - if (nfs_compare_fh(NFS_FH(inode), fhandle)) - goto out_bad; - if ((error = nfs_refresh_inode(inode, fattr)) != 0) - goto out_bad; - - nfs_free_fattr(fattr); - nfs_free_fhandle(fhandle); -out_set_verifier: - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); - out_valid: - dput(parent); - dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n", - __func__, dentry->d_parent->d_name.name, - dentry->d_name.name); - return 1; -out_zap_parent: - nfs_zap_caches(dir); - out_bad: - nfs_mark_for_revalidate(dir); - if (inode && S_ISDIR(inode->i_mode)) { - /* Purge readdir caches. */ - nfs_zap_caches(inode); - /* If we have submounts, don't unhash ! */ - if (have_submounts(dentry)) - goto out_valid; - if (dentry->d_flags & DCACHE_DISCONNECTED) - goto out_valid; - shrink_dcache_parent(dentry); - } - d_drop(dentry); - nfs_free_fattr(fattr); - nfs_free_fhandle(fhandle); - dput(parent); - dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is invalid\n", - __func__, dentry->d_parent->d_name.name, - dentry->d_name.name); - return 0; -out_error: - nfs_free_fattr(fattr); - nfs_free_fhandle(fhandle); - dput(parent); - dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) lookup returned error %d\n", - __func__, dentry->d_parent->d_name.name, - dentry->d_name.name, error); - return error; -} - -/* - * This is called from dput() when d_count is going to 0. - */ -static int nfs_dentry_delete(const struct dentry *dentry) -{ - dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - dentry->d_flags); - - /* Unhash any dentry with a stale inode */ - if (dentry->d_inode != NULL && NFS_STALE(dentry->d_inode)) - return 1; - - if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { - /* Unhash it, so that ->d_iput() would be called */ - return 1; - } - if (!(dentry->d_sb->s_flags & MS_ACTIVE)) { - /* Unhash it, so that ancestors of killed async unlink - * files will be cleaned up during umount */ - return 1; - } - return 0; - -} - -static void nfs_drop_nlink(struct inode *inode) -{ - spin_lock(&inode->i_lock); - if (inode->i_nlink > 0) - drop_nlink(inode); - spin_unlock(&inode->i_lock); -} - -/* - * Called when the dentry loses inode. - * We use it to clean up silly-renamed files. - */ -static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) -{ - if (S_ISDIR(inode->i_mode)) - /* drop any readdir cache as it could easily be old */ - NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA; - - if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { - drop_nlink(inode); - nfs_complete_unlink(dentry, inode); - } - iput(inode); -} - -static void nfs_d_release(struct dentry *dentry) -{ - /* free cached devname value, if it survived that far */ - if (unlikely(dentry->d_fsdata)) { - if (dentry->d_flags & DCACHE_NFSFS_RENAMED) - WARN_ON(1); - else - kfree(dentry->d_fsdata); - } -} - -const struct dentry_operations nfs_dentry_operations = { - .d_revalidate = nfs_lookup_revalidate, - .d_delete = nfs_dentry_delete, - .d_iput = nfs_dentry_iput, - .d_automount = nfs_d_automount, - .d_release = nfs_d_release, -}; - -static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) -{ - struct dentry *res; - struct dentry *parent; - struct inode *inode = NULL; - struct nfs_fh *fhandle = NULL; - struct nfs_fattr *fattr = NULL; - int error; - - dfprintk(VFS, "NFS: lookup(%s/%s)\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - nfs_inc_stats(dir, NFSIOS_VFSLOOKUP); - - res = ERR_PTR(-ENAMETOOLONG); - if (dentry->d_name.len > NFS_SERVER(dir)->namelen) - goto out; - - /* - * If we're doing an exclusive create, optimize away the lookup - * but don't hash the dentry. - */ - if (nfs_is_exclusive_create(dir, nd)) { - d_instantiate(dentry, NULL); - res = NULL; - goto out; - } - - res = ERR_PTR(-ENOMEM); - fhandle = nfs_alloc_fhandle(); - fattr = nfs_alloc_fattr(); - if (fhandle == NULL || fattr == NULL) - goto out; - - parent = dentry->d_parent; - /* Protect against concurrent sillydeletes */ - nfs_block_sillyrename(parent); - error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr); - if (error == -ENOENT) - goto no_entry; - if (error < 0) { - res = ERR_PTR(error); - goto out_unblock_sillyrename; - } - inode = nfs_fhget(dentry->d_sb, fhandle, fattr); - res = ERR_CAST(inode); - if (IS_ERR(res)) - goto out_unblock_sillyrename; - -no_entry: - res = d_materialise_unique(dentry, inode); - if (res != NULL) { - if (IS_ERR(res)) - goto out_unblock_sillyrename; - dentry = res; - } - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); -out_unblock_sillyrename: - nfs_unblock_sillyrename(parent); -out: - nfs_free_fattr(fattr); - nfs_free_fhandle(fhandle); - return res; -} - -#ifdef CONFIG_NFS_V4 -static int nfs_open_revalidate(struct dentry *, struct nameidata *); - -const struct dentry_operations nfs4_dentry_operations = { - .d_revalidate = nfs_open_revalidate, - .d_delete = nfs_dentry_delete, - .d_iput = nfs_dentry_iput, - .d_automount = nfs_d_automount, - .d_release = nfs_d_release, -}; - -/* - * Use intent information to determine whether we need to substitute - * the NFSv4-style stateful OPEN for the LOOKUP call - */ -static int is_atomic_open(struct nameidata *nd) -{ - if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_OPEN) == 0) - return 0; - /* NFS does not (yet) have a stateful open for directories */ - if (nd->flags & LOOKUP_DIRECTORY) - return 0; - /* Are we trying to write to a read only partition? */ - if (__mnt_is_readonly(nd->path.mnt) && - (nd->intent.open.flags & (O_CREAT|O_TRUNC|O_ACCMODE))) - return 0; - return 1; -} - -static fmode_t flags_to_mode(int flags) -{ - fmode_t res = (__force fmode_t)flags & FMODE_EXEC; - if ((flags & O_ACCMODE) != O_WRONLY) - res |= FMODE_READ; - if ((flags & O_ACCMODE) != O_RDONLY) - res |= FMODE_WRITE; - return res; -} - -static struct nfs_open_context *create_nfs_open_context(struct dentry *dentry, int open_flags) -{ - return alloc_nfs_open_context(dentry, flags_to_mode(open_flags)); -} - -static int do_open(struct inode *inode, struct file *filp) -{ - nfs_fscache_set_inode_cookie(inode, filp); - return 0; -} - -static int nfs_intent_set_file(struct nameidata *nd, struct nfs_open_context *ctx) -{ - struct file *filp; - int ret = 0; - - /* If the open_intent is for execute, we have an extra check to make */ - if (ctx->mode & FMODE_EXEC) { - ret = nfs_may_open(ctx->dentry->d_inode, - ctx->cred, - nd->intent.open.flags); - if (ret < 0) - goto out; - } - filp = lookup_instantiate_filp(nd, ctx->dentry, do_open); - if (IS_ERR(filp)) - ret = PTR_ERR(filp); - else - nfs_file_set_open_context(filp, ctx); -out: - put_nfs_open_context(ctx); - return ret; -} - -static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) -{ - struct nfs_open_context *ctx; - struct iattr attr; - struct dentry *res = NULL; - struct inode *inode; - int open_flags; - int err; - - dfprintk(VFS, "NFS: atomic_lookup(%s/%ld), %s\n", - dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); - - /* Check that we are indeed trying to open this file */ - if (!is_atomic_open(nd)) - goto no_open; - - if (dentry->d_name.len > NFS_SERVER(dir)->namelen) { - res = ERR_PTR(-ENAMETOOLONG); - goto out; - } - - /* Let vfs_create() deal with O_EXCL. Instantiate, but don't hash - * the dentry. */ - if (nd->flags & LOOKUP_EXCL) { - d_instantiate(dentry, NULL); - goto out; - } - - open_flags = nd->intent.open.flags; - attr.ia_valid = ATTR_OPEN; - - ctx = create_nfs_open_context(dentry, open_flags); - res = ERR_CAST(ctx); - if (IS_ERR(ctx)) - goto out; - - if (nd->flags & LOOKUP_CREATE) { - attr.ia_mode = nd->intent.open.create_mode; - attr.ia_valid |= ATTR_MODE; - attr.ia_mode &= ~current_umask(); - } else - open_flags &= ~(O_EXCL | O_CREAT); - - if (open_flags & O_TRUNC) { - attr.ia_valid |= ATTR_SIZE; - attr.ia_size = 0; - } - - /* Open the file on the server */ - nfs_block_sillyrename(dentry->d_parent); - inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr); - if (IS_ERR(inode)) { - nfs_unblock_sillyrename(dentry->d_parent); - put_nfs_open_context(ctx); - switch (PTR_ERR(inode)) { - /* Make a negative dentry */ - case -ENOENT: - d_add(dentry, NULL); - res = NULL; - goto out; - /* This turned out not to be a regular file */ - case -EISDIR: - case -ENOTDIR: - goto no_open; - case -ELOOP: - if (!(nd->intent.open.flags & O_NOFOLLOW)) - goto no_open; - /* case -EINVAL: */ - default: - res = ERR_CAST(inode); - goto out; - } - } - res = d_add_unique(dentry, inode); - nfs_unblock_sillyrename(dentry->d_parent); - if (res != NULL) { - dput(ctx->dentry); - ctx->dentry = dget(res); - dentry = res; - } - err = nfs_intent_set_file(nd, ctx); - if (err < 0) { - if (res != NULL) - dput(res); - return ERR_PTR(err); - } -out: - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); - return res; -no_open: - return nfs_lookup(dir, dentry, nd); -} - -static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) -{ - struct dentry *parent = NULL; - struct inode *inode; - struct inode *dir; - struct nfs_open_context *ctx; - struct iattr attr; - int openflags, ret = 0; - - if (nd->flags & LOOKUP_RCU) - return -ECHILD; - - inode = dentry->d_inode; - if (!is_atomic_open(nd) || d_mountpoint(dentry)) - goto no_open; - - parent = dget_parent(dentry); - dir = parent->d_inode; - - /* We can't create new files in nfs_open_revalidate(), so we - * optimize away revalidation of negative dentries. - */ - if (inode == NULL) { - if (!nfs_neg_need_reval(dir, dentry, nd)) - ret = 1; - goto out; - } - - /* NFS only supports OPEN on regular files */ - if (!S_ISREG(inode->i_mode)) - goto no_open_dput; - openflags = nd->intent.open.flags; - /* We cannot do exclusive creation on a positive dentry */ - if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) - goto no_open_dput; - /* We can't create new files here */ - openflags &= ~(O_CREAT|O_EXCL); - - ctx = create_nfs_open_context(dentry, openflags); - ret = PTR_ERR(ctx); - if (IS_ERR(ctx)) - goto out; - - attr.ia_valid = ATTR_OPEN; - if (openflags & O_TRUNC) { - attr.ia_valid |= ATTR_SIZE; - attr.ia_size = 0; - nfs_wb_all(inode); - } - - /* - * Note: we're not holding inode->i_mutex and so may be racing with - * operations that change the directory. We therefore save the - * change attribute *before* we do the RPC call. - */ - inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr); - if (IS_ERR(inode)) { - ret = PTR_ERR(inode); - switch (ret) { - case -EPERM: - case -EACCES: - case -EDQUOT: - case -ENOSPC: - case -EROFS: - goto out_put_ctx; - default: - goto out_drop; - } - } - iput(inode); - if (inode != dentry->d_inode) - goto out_drop; - - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); - ret = nfs_intent_set_file(nd, ctx); - if (ret >= 0) - ret = 1; -out: - dput(parent); - return ret; -out_drop: - d_drop(dentry); - ret = 0; -out_put_ctx: - put_nfs_open_context(ctx); - goto out; - -no_open_dput: - dput(parent); -no_open: - return nfs_lookup_revalidate(dentry, nd); -} - -static int nfs_open_create(struct inode *dir, struct dentry *dentry, - umode_t mode, struct nameidata *nd) -{ - struct nfs_open_context *ctx = NULL; - struct iattr attr; - int error; - int open_flags = O_CREAT|O_EXCL; - - dfprintk(VFS, "NFS: create(%s/%ld), %s\n", - dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); - - attr.ia_mode = mode; - attr.ia_valid = ATTR_MODE; - - if (nd) - open_flags = nd->intent.open.flags; - - ctx = create_nfs_open_context(dentry, open_flags); - error = PTR_ERR(ctx); - if (IS_ERR(ctx)) - goto out_err_drop; - - error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, ctx); - if (error != 0) - goto out_put_ctx; - if (nd) { - error = nfs_intent_set_file(nd, ctx); - if (error < 0) - goto out_err; - } else { - put_nfs_open_context(ctx); - } - return 0; -out_put_ctx: - put_nfs_open_context(ctx); -out_err_drop: - d_drop(dentry); -out_err: - return error; -} - -#endif /* CONFIG_NFSV4 */ - -/* - * Code common to create, mkdir, and mknod. - */ -int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) -{ - struct dentry *parent = dget_parent(dentry); - struct inode *dir = parent->d_inode; - struct inode *inode; - int error = -EACCES; - - d_drop(dentry); - - /* We may have been initialized further down */ - if (dentry->d_inode) - goto out; - if (fhandle->size == 0) { - error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr); - if (error) - goto out_error; - } - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); - if (!(fattr->valid & NFS_ATTR_FATTR)) { - struct nfs_server *server = NFS_SB(dentry->d_sb); - error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr); - if (error < 0) - goto out_error; - } - inode = nfs_fhget(dentry->d_sb, fhandle, fattr); - error = PTR_ERR(inode); - if (IS_ERR(inode)) - goto out_error; - d_add(dentry, inode); -out: - dput(parent); - return 0; -out_error: - nfs_mark_for_revalidate(dir); - dput(parent); - return error; -} - -/* - * Following a failed create operation, we drop the dentry rather - * than retain a negative dentry. This avoids a problem in the event - * 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, - umode_t mode, struct nameidata *nd) -{ - struct iattr attr; - int error; - int open_flags = O_CREAT|O_EXCL; - - dfprintk(VFS, "NFS: create(%s/%ld), %s\n", - dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); - - attr.ia_mode = mode; - attr.ia_valid = ATTR_MODE; - - if (nd) - open_flags = nd->intent.open.flags; - - error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, NULL); - if (error != 0) - goto out_err; - return 0; -out_err: - d_drop(dentry); - return error; -} - -/* - * See comments for nfs_proc_create regarding failed operations. - */ -static int -nfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev) -{ - struct iattr attr; - int status; - - dfprintk(VFS, "NFS: mknod(%s/%ld), %s\n", - dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); - - if (!new_valid_dev(rdev)) - return -EINVAL; - - attr.ia_mode = mode; - attr.ia_valid = ATTR_MODE; - - status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev); - if (status != 0) - goto out_err; - return 0; -out_err: - d_drop(dentry); - return status; -} - -/* - * See comments for nfs_proc_create regarding failed operations. - */ -static int nfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -{ - struct iattr attr; - int error; - - dfprintk(VFS, "NFS: mkdir(%s/%ld), %s\n", - dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); - - attr.ia_valid = ATTR_MODE; - attr.ia_mode = mode | S_IFDIR; - - error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr); - if (error != 0) - goto out_err; - return 0; -out_err: - d_drop(dentry); - return error; -} - -static void nfs_dentry_handle_enoent(struct dentry *dentry) -{ - if (dentry->d_inode != NULL && !d_unhashed(dentry)) - d_delete(dentry); -} - -static int nfs_rmdir(struct inode *dir, struct dentry *dentry) -{ - int error; - - dfprintk(VFS, "NFS: rmdir(%s/%ld), %s\n", - dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); - - error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name); - /* Ensure the VFS deletes this inode */ - if (error == 0 && dentry->d_inode != NULL) - clear_nlink(dentry->d_inode); - else if (error == -ENOENT) - nfs_dentry_handle_enoent(dentry); - - return error; -} - -/* - * Remove a file after making sure there are no pending writes, - * and after checking that the file has only one user. - * - * We invalidate the attribute cache and free the inode prior to the operation - * to avoid possible races if the server reuses the inode. - */ -static int nfs_safe_remove(struct dentry *dentry) -{ - struct inode *dir = dentry->d_parent->d_inode; - struct inode *inode = dentry->d_inode; - int error = -EBUSY; - - dfprintk(VFS, "NFS: safe_remove(%s/%s)\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - - /* If the dentry was sillyrenamed, we simply call d_delete() */ - if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { - error = 0; - goto out; - } - - if (inode != NULL) { - nfs_inode_return_delegation(inode); - error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); - /* The VFS may want to delete this inode */ - if (error == 0) - nfs_drop_nlink(inode); - nfs_mark_for_revalidate(inode); - } else - error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); - if (error == -ENOENT) - nfs_dentry_handle_enoent(dentry); -out: - return error; -} - -/* We do silly rename. In case sillyrename() returns -EBUSY, the inode - * belongs to an active ".nfs..." file and we return -EBUSY. - * - * If sillyrename() returns 0, we do nothing, otherwise we unlink. - */ -static int nfs_unlink(struct inode *dir, struct dentry *dentry) -{ - int error; - int need_rehash = 0; - - dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id, - dir->i_ino, dentry->d_name.name); - - spin_lock(&dentry->d_lock); - if (dentry->d_count > 1) { - spin_unlock(&dentry->d_lock); - /* Start asynchronous writeout of the inode */ - write_inode_now(dentry->d_inode, 0); - error = nfs_sillyrename(dir, dentry); - return error; - } - if (!d_unhashed(dentry)) { - __d_drop(dentry); - need_rehash = 1; - } - spin_unlock(&dentry->d_lock); - error = nfs_safe_remove(dentry); - if (!error || error == -ENOENT) { - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); - } else if (need_rehash) - d_rehash(dentry); - return error; -} - -/* - * To create a symbolic link, most file systems instantiate a new inode, - * add a page to it containing the path, then write it out to the disk - * using prepare_write/commit_write. - * - * Unfortunately the NFS client can't create the in-core inode first - * because it needs a file handle to create an in-core inode (see - * fs/nfs/inode.c:nfs_fhget). We only have a file handle *after* the - * symlink request has completed on the server. - * - * So instead we allocate a raw page, copy the symname into it, then do - * the SYMLINK request with the page as the buffer. If it succeeds, we - * now have a new file handle and can instantiate an in-core NFS inode - * and move the raw page into its mapping. - */ -static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) -{ - struct pagevec lru_pvec; - struct page *page; - char *kaddr; - struct iattr attr; - unsigned int pathlen = strlen(symname); - int error; - - dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id, - dir->i_ino, dentry->d_name.name, symname); - - if (pathlen > PAGE_SIZE) - return -ENAMETOOLONG; - - attr.ia_mode = S_IFLNK | S_IRWXUGO; - attr.ia_valid = ATTR_MODE; - - page = alloc_page(GFP_HIGHUSER); - if (!page) - return -ENOMEM; - - kaddr = kmap_atomic(page); - memcpy(kaddr, symname, pathlen); - if (pathlen < PAGE_SIZE) - memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen); - kunmap_atomic(kaddr); - - error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr); - if (error != 0) { - dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d\n", - dir->i_sb->s_id, dir->i_ino, - dentry->d_name.name, symname, error); - d_drop(dentry); - __free_page(page); - return error; - } - - /* - * No big deal if we can't add this page to the page cache here. - * READLINK will get the missing page from the server if needed. - */ - pagevec_init(&lru_pvec, 0); - if (!add_to_page_cache(page, dentry->d_inode->i_mapping, 0, - GFP_KERNEL)) { - pagevec_add(&lru_pvec, page); - pagevec_lru_add_file(&lru_pvec); - SetPageUptodate(page); - unlock_page(page); - } else - __free_page(page); - - return 0; -} - -static int -nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) -{ - struct inode *inode = old_dentry->d_inode; - int error; - - dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n", - old_dentry->d_parent->d_name.name, old_dentry->d_name.name, - dentry->d_parent->d_name.name, dentry->d_name.name); - - nfs_inode_return_delegation(inode); - - d_drop(dentry); - error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name); - if (error == 0) { - ihold(inode); - d_add(dentry, inode); - } - return error; -} - -/* - * RENAME - * FIXME: Some nfsds, like the Linux user space nfsd, may generate a - * different file handle for the same inode after a rename (e.g. when - * moving to a different directory). A fail-safe method to do so would - * be to look up old_dir/old_name, create a link to new_dir/new_name and - * rename the old file using the sillyrename stuff. This way, the original - * file in old_dir will go away when the last process iput()s the inode. - * - * FIXED. - * - * It actually works quite well. One needs to have the possibility for - * at least one ".nfs..." file in each directory the file ever gets - * moved or linked to which happens automagically with the new - * implementation that only depends on the dcache stuff instead of - * using the inode layer - * - * Unfortunately, things are a little more complicated than indicated - * above. For a cross-directory move, we want to make sure we can get - * rid of the old inode after the operation. This means there must be - * no pending writes (if it's a file), and the use count must be 1. - * If these conditions are met, we can drop the dentries before doing - * the rename. - */ -static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - struct inode *old_inode = old_dentry->d_inode; - struct inode *new_inode = new_dentry->d_inode; - struct dentry *dentry = NULL, *rehash = NULL; - int error = -EBUSY; - - dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n", - old_dentry->d_parent->d_name.name, old_dentry->d_name.name, - new_dentry->d_parent->d_name.name, new_dentry->d_name.name, - new_dentry->d_count); - - /* - * For non-directories, check whether the target is busy and if so, - * make a copy of the dentry and then do a silly-rename. If the - * silly-rename succeeds, the copied dentry is hashed and becomes - * the new target. - */ - if (new_inode && !S_ISDIR(new_inode->i_mode)) { - /* - * To prevent any new references to the target during the - * rename, we unhash the dentry in advance. - */ - if (!d_unhashed(new_dentry)) { - d_drop(new_dentry); - rehash = new_dentry; - } - - if (new_dentry->d_count > 2) { - int err; - - /* copy the target dentry's name */ - dentry = d_alloc(new_dentry->d_parent, - &new_dentry->d_name); - if (!dentry) - goto out; - - /* silly-rename the existing target ... */ - err = nfs_sillyrename(new_dir, new_dentry); - if (err) - goto out; - - new_dentry = dentry; - rehash = NULL; - new_inode = NULL; - } - } - - nfs_inode_return_delegation(old_inode); - if (new_inode != NULL) - nfs_inode_return_delegation(new_inode); - - error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name, - new_dir, &new_dentry->d_name); - nfs_mark_for_revalidate(old_inode); -out: - if (rehash) - d_rehash(rehash); - if (!error) { - if (new_inode != NULL) - nfs_drop_nlink(new_inode); - d_move(old_dentry, new_dentry); - nfs_set_verifier(new_dentry, - nfs_save_change_attribute(new_dir)); - } else if (error == -ENOENT) - nfs_dentry_handle_enoent(old_dentry); - - /* new dentry created? */ - if (dentry) - dput(dentry); - return error; -} - -static DEFINE_SPINLOCK(nfs_access_lru_lock); -static LIST_HEAD(nfs_access_lru_list); -static atomic_long_t nfs_access_nr_entries; - -static void nfs_access_free_entry(struct nfs_access_entry *entry) -{ - put_rpccred(entry->cred); - kfree(entry); - smp_mb__before_atomic_dec(); - atomic_long_dec(&nfs_access_nr_entries); - smp_mb__after_atomic_dec(); -} - -static void nfs_access_free_list(struct list_head *head) -{ - struct nfs_access_entry *cache; - - while (!list_empty(head)) { - cache = list_entry(head->next, struct nfs_access_entry, lru); - list_del(&cache->lru); - nfs_access_free_entry(cache); - } -} - -int nfs_access_cache_shrinker(struct shrinker *shrink, - struct shrink_control *sc) -{ - LIST_HEAD(head); - struct nfs_inode *nfsi, *next; - struct nfs_access_entry *cache; - int nr_to_scan = sc->nr_to_scan; - gfp_t gfp_mask = sc->gfp_mask; - - if ((gfp_mask & GFP_KERNEL) != GFP_KERNEL) - return (nr_to_scan == 0) ? 0 : -1; - - spin_lock(&nfs_access_lru_lock); - list_for_each_entry_safe(nfsi, next, &nfs_access_lru_list, access_cache_inode_lru) { - struct inode *inode; - - if (nr_to_scan-- == 0) - break; - inode = &nfsi->vfs_inode; - spin_lock(&inode->i_lock); - if (list_empty(&nfsi->access_cache_entry_lru)) - goto remove_lru_entry; - cache = list_entry(nfsi->access_cache_entry_lru.next, - struct nfs_access_entry, lru); - list_move(&cache->lru, &head); - rb_erase(&cache->rb_node, &nfsi->access_cache); - if (!list_empty(&nfsi->access_cache_entry_lru)) - list_move_tail(&nfsi->access_cache_inode_lru, - &nfs_access_lru_list); - else { -remove_lru_entry: - list_del_init(&nfsi->access_cache_inode_lru); - smp_mb__before_clear_bit(); - clear_bit(NFS_INO_ACL_LRU_SET, &nfsi->flags); - smp_mb__after_clear_bit(); - } - spin_unlock(&inode->i_lock); - } - spin_unlock(&nfs_access_lru_lock); - nfs_access_free_list(&head); - return (atomic_long_read(&nfs_access_nr_entries) / 100) * sysctl_vfs_cache_pressure; -} - -static void __nfs_access_zap_cache(struct nfs_inode *nfsi, struct list_head *head) -{ - struct rb_root *root_node = &nfsi->access_cache; - struct rb_node *n; - struct nfs_access_entry *entry; - - /* Unhook entries from the cache */ - while ((n = rb_first(root_node)) != NULL) { - entry = rb_entry(n, struct nfs_access_entry, rb_node); - rb_erase(n, root_node); - list_move(&entry->lru, head); - } - nfsi->cache_validity &= ~NFS_INO_INVALID_ACCESS; -} - -void nfs_access_zap_cache(struct inode *inode) -{ - LIST_HEAD(head); - - if (test_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags) == 0) - return; - /* Remove from global LRU init */ - spin_lock(&nfs_access_lru_lock); - if (test_and_clear_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags)) - list_del_init(&NFS_I(inode)->access_cache_inode_lru); - - spin_lock(&inode->i_lock); - __nfs_access_zap_cache(NFS_I(inode), &head); - spin_unlock(&inode->i_lock); - spin_unlock(&nfs_access_lru_lock); - nfs_access_free_list(&head); -} - -static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, struct rpc_cred *cred) -{ - struct rb_node *n = NFS_I(inode)->access_cache.rb_node; - struct nfs_access_entry *entry; - - while (n != NULL) { - entry = rb_entry(n, struct nfs_access_entry, rb_node); - - if (cred < entry->cred) - n = n->rb_left; - else if (cred > entry->cred) - n = n->rb_right; - else - return entry; - } - return NULL; -} - -static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res) -{ - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs_access_entry *cache; - int err = -ENOENT; - - spin_lock(&inode->i_lock); - if (nfsi->cache_validity & NFS_INO_INVALID_ACCESS) - goto out_zap; - cache = nfs_access_search_rbtree(inode, cred); - if (cache == NULL) - goto out; - if (!nfs_have_delegated_attributes(inode) && - !time_in_range_open(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo)) - goto out_stale; - res->jiffies = cache->jiffies; - res->cred = cache->cred; - res->mask = cache->mask; - list_move_tail(&cache->lru, &nfsi->access_cache_entry_lru); - err = 0; -out: - spin_unlock(&inode->i_lock); - return err; -out_stale: - rb_erase(&cache->rb_node, &nfsi->access_cache); - list_del(&cache->lru); - spin_unlock(&inode->i_lock); - nfs_access_free_entry(cache); - return -ENOENT; -out_zap: - spin_unlock(&inode->i_lock); - nfs_access_zap_cache(inode); - return -ENOENT; -} - -static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry *set) -{ - struct nfs_inode *nfsi = NFS_I(inode); - struct rb_root *root_node = &nfsi->access_cache; - struct rb_node **p = &root_node->rb_node; - struct rb_node *parent = NULL; - struct nfs_access_entry *entry; - - spin_lock(&inode->i_lock); - while (*p != NULL) { - parent = *p; - entry = rb_entry(parent, struct nfs_access_entry, rb_node); - - if (set->cred < entry->cred) - p = &parent->rb_left; - else if (set->cred > entry->cred) - p = &parent->rb_right; - else - goto found; - } - rb_link_node(&set->rb_node, parent, p); - rb_insert_color(&set->rb_node, root_node); - list_add_tail(&set->lru, &nfsi->access_cache_entry_lru); - spin_unlock(&inode->i_lock); - return; -found: - rb_replace_node(parent, &set->rb_node, root_node); - list_add_tail(&set->lru, &nfsi->access_cache_entry_lru); - list_del(&entry->lru); - spin_unlock(&inode->i_lock); - nfs_access_free_entry(entry); -} - -static void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set) -{ - struct nfs_access_entry *cache = kmalloc(sizeof(*cache), GFP_KERNEL); - if (cache == NULL) - return; - RB_CLEAR_NODE(&cache->rb_node); - cache->jiffies = set->jiffies; - cache->cred = get_rpccred(set->cred); - cache->mask = set->mask; - - nfs_access_add_rbtree(inode, cache); - - /* Update accounting */ - smp_mb__before_atomic_inc(); - atomic_long_inc(&nfs_access_nr_entries); - smp_mb__after_atomic_inc(); - - /* Add inode to global LRU list */ - if (!test_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags)) { - spin_lock(&nfs_access_lru_lock); - if (!test_and_set_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags)) - list_add_tail(&NFS_I(inode)->access_cache_inode_lru, - &nfs_access_lru_list); - spin_unlock(&nfs_access_lru_lock); - } -} - -static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask) -{ - struct nfs_access_entry cache; - int status; - - status = nfs_access_get_cached(inode, cred, &cache); - if (status == 0) - goto out; - - /* Be clever: ask server to check for all possible rights */ - cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ; - cache.cred = cred; - cache.jiffies = jiffies; - status = NFS_PROTO(inode)->access(inode, &cache); - if (status != 0) { - if (status == -ESTALE) { - nfs_zap_caches(inode); - if (!S_ISDIR(inode->i_mode)) - set_bit(NFS_INO_STALE, &NFS_I(inode)->flags); - } - return status; - } - nfs_access_add_cache(inode, &cache); -out: - if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) - return 0; - return -EACCES; -} - -static int nfs_open_permission_mask(int openflags) -{ - int mask = 0; - - if ((openflags & O_ACCMODE) != O_WRONLY) - mask |= MAY_READ; - if ((openflags & O_ACCMODE) != O_RDONLY) - mask |= MAY_WRITE; - if (openflags & __FMODE_EXEC) - mask |= MAY_EXEC; - return mask; -} - -int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags) -{ - return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags)); -} - -int nfs_permission(struct inode *inode, int mask) -{ - struct rpc_cred *cred; - int res = 0; - - if (mask & MAY_NOT_BLOCK) - return -ECHILD; - - nfs_inc_stats(inode, NFSIOS_VFSACCESS); - - if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) - goto out; - /* Is this sys_access() ? */ - if (mask & (MAY_ACCESS | MAY_CHDIR)) - goto force_lookup; - - switch (inode->i_mode & S_IFMT) { - case S_IFLNK: - goto out; - case S_IFREG: - /* NFSv4 has atomic_open... */ - if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN) - && (mask & MAY_OPEN) - && !(mask & MAY_EXEC)) - goto out; - break; - case S_IFDIR: - /* - * Optimize away all write operations, since the server - * will check permissions when we perform the op. - */ - if ((mask & MAY_WRITE) && !(mask & MAY_READ)) - goto out; - } - -force_lookup: - if (!NFS_PROTO(inode)->access) - goto out_notsup; - - cred = rpc_lookup_cred(); - if (!IS_ERR(cred)) { - res = nfs_do_access(inode, cred, mask); - put_rpccred(cred); - } else - res = PTR_ERR(cred); -out: - if (!res && (mask & MAY_EXEC) && !execute_ok(inode)) - res = -EACCES; - - dfprintk(VFS, "NFS: permission(%s/%ld), mask=0x%x, res=%d\n", - inode->i_sb->s_id, inode->i_ino, mask, res); - return res; -out_notsup: - res = nfs_revalidate_inode(NFS_SERVER(inode), inode); - if (res == 0) - res = generic_permission(inode, mask); - goto out; -} - -/* - * Local variables: - * version-control: t - * kept-new-versions: 5 - * End: - */ diff --git a/ANDROID_3.4.5/fs/nfs/direct.c b/ANDROID_3.4.5/fs/nfs/direct.c deleted file mode 100644 index 481be7f7..00000000 --- a/ANDROID_3.4.5/fs/nfs/direct.c +++ /dev/null @@ -1,1032 +0,0 @@ -/* - * linux/fs/nfs/direct.c - * - * Copyright (C) 2003 by Chuck Lever <cel@netapp.com> - * - * High-performance uncached I/O for the Linux NFS client - * - * There are important applications whose performance or correctness - * depends on uncached access to file data. Database clusters - * (multiple copies of the same instance running on separate hosts) - * implement their own cache coherency protocol that subsumes file - * system cache protocols. Applications that process datasets - * considerably larger than the client's memory do not always benefit - * from a local cache. A streaming video server, for instance, has no - * need to cache the contents of a file. - * - * When an application requests uncached I/O, all read and write requests - * are made directly to the server; data stored or fetched via these - * requests is not cached in the Linux page cache. The client does not - * correct unaligned requests from applications. All requested bytes are - * held on permanent storage before a direct write system call returns to - * an application. - * - * Solaris implements an uncached I/O facility called directio() that - * is used for backups and sequential I/O to very large files. Solaris - * also supports uncaching whole NFS partitions with "-o forcedirectio," - * an undocumented mount option. - * - * Designed by Jeff Kimmel, Chuck Lever, and Trond Myklebust, with - * help from Andrew Morton. - * - * 18 Dec 2001 Initial implementation for 2.4 --cel - * 08 Jul 2002 Version for 2.4.19, with bug fixes --trondmy - * 08 Jun 2003 Port to 2.5 APIs --cel - * 31 Mar 2004 Handle direct I/O without VFS support --cel - * 15 Sep 2004 Parallel async reads --cel - * 04 May 2005 support O_DIRECT with aio --cel - * - */ - -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/file.h> -#include <linux/pagemap.h> -#include <linux/kref.h> -#include <linux/slab.h> -#include <linux/task_io_accounting_ops.h> - -#include <linux/nfs_fs.h> -#include <linux/nfs_page.h> -#include <linux/sunrpc/clnt.h> - -#include <asm/uaccess.h> -#include <linux/atomic.h> - -#include "internal.h" -#include "iostat.h" - -#define NFSDBG_FACILITY NFSDBG_VFS - -static struct kmem_cache *nfs_direct_cachep; - -/* - * This represents a set of asynchronous requests that we're waiting on - */ -struct nfs_direct_req { - struct kref kref; /* release manager */ - - /* I/O parameters */ - struct nfs_open_context *ctx; /* file open context info */ - struct nfs_lock_context *l_ctx; /* Lock context info */ - struct kiocb * iocb; /* controlling i/o request */ - struct inode * inode; /* target file of i/o */ - - /* completion state */ - atomic_t io_count; /* i/os we're waiting for */ - spinlock_t lock; /* protect completion state */ - ssize_t count, /* bytes actually processed */ - error; /* any reported error */ - struct completion completion; /* wait for i/o completion */ - - /* commit state */ - struct list_head rewrite_list; /* saved nfs_write_data structs */ - struct nfs_write_data * commit_data; /* special write_data for commits */ - int flags; -#define NFS_ODIRECT_DO_COMMIT (1) /* an unstable reply was received */ -#define NFS_ODIRECT_RESCHED_WRITES (2) /* write verification failed */ - struct nfs_writeverf verf; /* unstable write verifier */ -}; - -static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode); -static const struct rpc_call_ops nfs_write_direct_ops; - -static inline void get_dreq(struct nfs_direct_req *dreq) -{ - atomic_inc(&dreq->io_count); -} - -static inline int put_dreq(struct nfs_direct_req *dreq) -{ - return atomic_dec_and_test(&dreq->io_count); -} - -/** - * nfs_direct_IO - NFS address space operation for direct I/O - * @rw: direction (read or write) - * @iocb: target I/O control block - * @iov: array of vectors that define I/O buffer - * @pos: offset in file to begin the operation - * @nr_segs: size of iovec array - * - * The presence of this routine in the address space ops vector means - * the NFS client supports direct I/O. However, we shunt off direct - * read and write requests before the VFS gets them, so this method - * should never be called. - */ -ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t pos, unsigned long nr_segs) -{ - dprintk("NFS: nfs_direct_IO (%s) off/no(%Ld/%lu) EINVAL\n", - iocb->ki_filp->f_path.dentry->d_name.name, - (long long) pos, nr_segs); - - return -EINVAL; -} - -static void nfs_direct_dirty_pages(struct page **pages, unsigned int pgbase, size_t count) -{ - unsigned int npages; - unsigned int i; - - if (count == 0) - return; - pages += (pgbase >> PAGE_SHIFT); - npages = (count + (pgbase & ~PAGE_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT; - for (i = 0; i < npages; i++) { - struct page *page = pages[i]; - if (!PageCompound(page)) - set_page_dirty(page); - } -} - -static void nfs_direct_release_pages(struct page **pages, unsigned int npages) -{ - unsigned int i; - for (i = 0; i < npages; i++) - page_cache_release(pages[i]); -} - -static inline struct nfs_direct_req *nfs_direct_req_alloc(void) -{ - struct nfs_direct_req *dreq; - - dreq = kmem_cache_alloc(nfs_direct_cachep, GFP_KERNEL); - if (!dreq) - return NULL; - - kref_init(&dreq->kref); - kref_get(&dreq->kref); - init_completion(&dreq->completion); - INIT_LIST_HEAD(&dreq->rewrite_list); - dreq->iocb = NULL; - dreq->ctx = NULL; - dreq->l_ctx = NULL; - spin_lock_init(&dreq->lock); - atomic_set(&dreq->io_count, 0); - dreq->count = 0; - dreq->error = 0; - dreq->flags = 0; - - return dreq; -} - -static void nfs_direct_req_free(struct kref *kref) -{ - struct nfs_direct_req *dreq = container_of(kref, struct nfs_direct_req, kref); - - if (dreq->l_ctx != NULL) - nfs_put_lock_context(dreq->l_ctx); - if (dreq->ctx != NULL) - put_nfs_open_context(dreq->ctx); - kmem_cache_free(nfs_direct_cachep, dreq); -} - -static void nfs_direct_req_release(struct nfs_direct_req *dreq) -{ - kref_put(&dreq->kref, nfs_direct_req_free); -} - -/* - * Collects and returns the final error value/byte-count. - */ -static ssize_t nfs_direct_wait(struct nfs_direct_req *dreq) -{ - ssize_t result = -EIOCBQUEUED; - - /* Async requests don't wait here */ - if (dreq->iocb) - goto out; - - result = wait_for_completion_killable(&dreq->completion); - - if (!result) - result = dreq->error; - if (!result) - result = dreq->count; - -out: - return (ssize_t) result; -} - -/* - * Synchronous I/O uses a stack-allocated iocb. Thus we can't trust - * the iocb is still valid here if this is a synchronous request. - */ -static void nfs_direct_complete(struct nfs_direct_req *dreq) -{ - if (dreq->iocb) { - long res = (long) dreq->error; - if (!res) - res = (long) dreq->count; - aio_complete(dreq->iocb, res, 0); - } - complete_all(&dreq->completion); - - nfs_direct_req_release(dreq); -} - -/* - * We must hold a reference to all the pages in this direct read request - * until the RPCs complete. This could be long *after* we are woken up in - * nfs_direct_wait (for instance, if someone hits ^C on a slow server). - */ -static void nfs_direct_read_result(struct rpc_task *task, void *calldata) -{ - struct nfs_read_data *data = calldata; - - nfs_readpage_result(task, data); -} - -static void nfs_direct_read_release(void *calldata) -{ - - struct nfs_read_data *data = calldata; - struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req; - int status = data->task.tk_status; - - spin_lock(&dreq->lock); - if (unlikely(status < 0)) { - dreq->error = status; - spin_unlock(&dreq->lock); - } else { - dreq->count += data->res.count; - spin_unlock(&dreq->lock); - nfs_direct_dirty_pages(data->pagevec, - data->args.pgbase, - data->res.count); - } - nfs_direct_release_pages(data->pagevec, data->npages); - - if (put_dreq(dreq)) - nfs_direct_complete(dreq); - nfs_readdata_free(data); -} - -static const struct rpc_call_ops nfs_read_direct_ops = { - .rpc_call_prepare = nfs_read_prepare, - .rpc_call_done = nfs_direct_read_result, - .rpc_release = nfs_direct_read_release, -}; - -/* - * For each rsize'd chunk of the user's buffer, dispatch an NFS READ - * operation. If nfs_readdata_alloc() or get_user_pages() fails, - * bail and stop sending more reads. Read length accounting is - * handled automatically by nfs_direct_read_result(). Otherwise, if - * no requests have been sent, just return an error. - */ -static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq, - const struct iovec *iov, - loff_t pos) -{ - struct nfs_open_context *ctx = dreq->ctx; - struct inode *inode = ctx->dentry->d_inode; - unsigned long user_addr = (unsigned long)iov->iov_base; - size_t count = iov->iov_len; - size_t rsize = NFS_SERVER(inode)->rsize; - struct rpc_task *task; - struct rpc_message msg = { - .rpc_cred = ctx->cred, - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = NFS_CLIENT(inode), - .rpc_message = &msg, - .callback_ops = &nfs_read_direct_ops, - .workqueue = nfsiod_workqueue, - .flags = RPC_TASK_ASYNC, - }; - unsigned int pgbase; - int result; - ssize_t started = 0; - - do { - struct nfs_read_data *data; - size_t bytes; - - pgbase = user_addr & ~PAGE_MASK; - bytes = min(rsize,count); - - result = -ENOMEM; - data = nfs_readdata_alloc(nfs_page_array_len(pgbase, bytes)); - if (unlikely(!data)) - break; - - down_read(¤t->mm->mmap_sem); - result = get_user_pages(current, current->mm, user_addr, - data->npages, 1, 0, data->pagevec, NULL); - up_read(¤t->mm->mmap_sem); - if (result < 0) { - nfs_readdata_free(data); - break; - } - if ((unsigned)result < data->npages) { - bytes = result * PAGE_SIZE; - if (bytes <= pgbase) { - nfs_direct_release_pages(data->pagevec, result); - nfs_readdata_free(data); - break; - } - bytes -= pgbase; - data->npages = result; - } - - get_dreq(dreq); - - data->req = (struct nfs_page *) dreq; - data->inode = inode; - data->cred = msg.rpc_cred; - data->args.fh = NFS_FH(inode); - data->args.context = ctx; - data->args.lock_context = dreq->l_ctx; - data->args.offset = pos; - data->args.pgbase = pgbase; - data->args.pages = data->pagevec; - data->args.count = bytes; - data->res.fattr = &data->fattr; - data->res.eof = 0; - data->res.count = bytes; - nfs_fattr_init(&data->fattr); - msg.rpc_argp = &data->args; - msg.rpc_resp = &data->res; - - task_setup_data.task = &data->task; - task_setup_data.callback_data = data; - NFS_PROTO(inode)->read_setup(data, &msg); - - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) - break; - rpc_put_task(task); - - dprintk("NFS: %5u initiated direct read call " - "(req %s/%Ld, %zu bytes @ offset %Lu)\n", - data->task.tk_pid, - inode->i_sb->s_id, - (long long)NFS_FILEID(inode), - bytes, - (unsigned long long)data->args.offset); - - started += bytes; - user_addr += bytes; - pos += bytes; - /* FIXME: Remove this unnecessary math from final patch */ - pgbase += bytes; - pgbase &= ~PAGE_MASK; - BUG_ON(pgbase != (user_addr & ~PAGE_MASK)); - - count -= bytes; - } while (count != 0); - - if (started) - return started; - return result < 0 ? (ssize_t) result : -EFAULT; -} - -static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, - const struct iovec *iov, - unsigned long nr_segs, - loff_t pos) -{ - ssize_t result = -EINVAL; - size_t requested_bytes = 0; - unsigned long seg; - - get_dreq(dreq); - - for (seg = 0; seg < nr_segs; seg++) { - const struct iovec *vec = &iov[seg]; - result = nfs_direct_read_schedule_segment(dreq, vec, pos); - if (result < 0) - break; - requested_bytes += result; - if ((size_t)result < vec->iov_len) - break; - pos += vec->iov_len; - } - - /* - * If no bytes were started, return the error, and let the - * generic layer handle the completion. - */ - if (requested_bytes == 0) { - nfs_direct_req_release(dreq); - return result < 0 ? result : -EIO; - } - - if (put_dreq(dreq)) - nfs_direct_complete(dreq); - return 0; -} - -static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - ssize_t result = -ENOMEM; - struct inode *inode = iocb->ki_filp->f_mapping->host; - struct nfs_direct_req *dreq; - - dreq = nfs_direct_req_alloc(); - if (dreq == NULL) - goto out; - - dreq->inode = inode; - dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); - dreq->l_ctx = nfs_get_lock_context(dreq->ctx); - if (dreq->l_ctx == NULL) - goto out_release; - if (!is_sync_kiocb(iocb)) - dreq->iocb = iocb; - - result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos); - if (!result) - result = nfs_direct_wait(dreq); -out_release: - nfs_direct_req_release(dreq); -out: - return result; -} - -static void nfs_direct_free_writedata(struct nfs_direct_req *dreq) -{ - while (!list_empty(&dreq->rewrite_list)) { - struct nfs_write_data *data = list_entry(dreq->rewrite_list.next, struct nfs_write_data, pages); - list_del(&data->pages); - nfs_direct_release_pages(data->pagevec, data->npages); - nfs_writedata_free(data); - } -} - -#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) -static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) -{ - struct inode *inode = dreq->inode; - struct list_head *p; - struct nfs_write_data *data; - struct rpc_task *task; - struct rpc_message msg = { - .rpc_cred = dreq->ctx->cred, - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = NFS_CLIENT(inode), - .rpc_message = &msg, - .callback_ops = &nfs_write_direct_ops, - .workqueue = nfsiod_workqueue, - .flags = RPC_TASK_ASYNC, - }; - - dreq->count = 0; - get_dreq(dreq); - - list_for_each(p, &dreq->rewrite_list) { - data = list_entry(p, struct nfs_write_data, pages); - - get_dreq(dreq); - - /* Use stable writes */ - data->args.stable = NFS_FILE_SYNC; - - /* - * Reset data->res. - */ - nfs_fattr_init(&data->fattr); - data->res.count = data->args.count; - memset(&data->verf, 0, sizeof(data->verf)); - - /* - * Reuse data->task; data->args should not have changed - * since the original request was sent. - */ - task_setup_data.task = &data->task; - task_setup_data.callback_data = data; - msg.rpc_argp = &data->args; - msg.rpc_resp = &data->res; - NFS_PROTO(inode)->write_setup(data, &msg); - - /* - * We're called via an RPC callback, so BKL is already held. - */ - task = rpc_run_task(&task_setup_data); - if (!IS_ERR(task)) - rpc_put_task(task); - - dprintk("NFS: %5u rescheduled direct write call (req %s/%Ld, %u bytes @ offset %Lu)\n", - data->task.tk_pid, - inode->i_sb->s_id, - (long long)NFS_FILEID(inode), - data->args.count, - (unsigned long long)data->args.offset); - } - - if (put_dreq(dreq)) - nfs_direct_write_complete(dreq, inode); -} - -static void nfs_direct_commit_result(struct rpc_task *task, void *calldata) -{ - struct nfs_write_data *data = calldata; - - /* Call the NFS version-specific code */ - NFS_PROTO(data->inode)->commit_done(task, data); -} - -static void nfs_direct_commit_release(void *calldata) -{ - struct nfs_write_data *data = calldata; - struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req; - int status = data->task.tk_status; - - if (status < 0) { - dprintk("NFS: %5u commit failed with error %d.\n", - data->task.tk_pid, status); - dreq->flags = NFS_ODIRECT_RESCHED_WRITES; - } else if (memcmp(&dreq->verf, &data->verf, sizeof(data->verf))) { - dprintk("NFS: %5u commit verify failed\n", data->task.tk_pid); - dreq->flags = NFS_ODIRECT_RESCHED_WRITES; - } - - dprintk("NFS: %5u commit returned %d\n", data->task.tk_pid, status); - nfs_direct_write_complete(dreq, data->inode); - nfs_commit_free(data); -} - -static const struct rpc_call_ops nfs_commit_direct_ops = { - .rpc_call_prepare = nfs_write_prepare, - .rpc_call_done = nfs_direct_commit_result, - .rpc_release = nfs_direct_commit_release, -}; - -static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq) -{ - struct nfs_write_data *data = dreq->commit_data; - struct rpc_task *task; - struct rpc_message msg = { - .rpc_argp = &data->args, - .rpc_resp = &data->res, - .rpc_cred = dreq->ctx->cred, - }; - struct rpc_task_setup task_setup_data = { - .task = &data->task, - .rpc_client = NFS_CLIENT(dreq->inode), - .rpc_message = &msg, - .callback_ops = &nfs_commit_direct_ops, - .callback_data = data, - .workqueue = nfsiod_workqueue, - .flags = RPC_TASK_ASYNC, - }; - - data->inode = dreq->inode; - data->cred = msg.rpc_cred; - - data->args.fh = NFS_FH(data->inode); - data->args.offset = 0; - data->args.count = 0; - data->args.context = dreq->ctx; - data->args.lock_context = dreq->l_ctx; - data->res.count = 0; - data->res.fattr = &data->fattr; - data->res.verf = &data->verf; - nfs_fattr_init(&data->fattr); - - NFS_PROTO(data->inode)->commit_setup(data, &msg); - - /* Note: task.tk_ops->rpc_release will free dreq->commit_data */ - dreq->commit_data = NULL; - - dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid); - - task = rpc_run_task(&task_setup_data); - if (!IS_ERR(task)) - rpc_put_task(task); -} - -static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode) -{ - int flags = dreq->flags; - - dreq->flags = 0; - switch (flags) { - case NFS_ODIRECT_DO_COMMIT: - nfs_direct_commit_schedule(dreq); - break; - case NFS_ODIRECT_RESCHED_WRITES: - nfs_direct_write_reschedule(dreq); - break; - default: - if (dreq->commit_data != NULL) - nfs_commit_free(dreq->commit_data); - nfs_direct_free_writedata(dreq); - nfs_zap_mapping(inode, inode->i_mapping); - nfs_direct_complete(dreq); - } -} - -static void nfs_alloc_commit_data(struct nfs_direct_req *dreq) -{ - dreq->commit_data = nfs_commitdata_alloc(); - if (dreq->commit_data != NULL) - dreq->commit_data->req = (struct nfs_page *) dreq; -} -#else -static inline void nfs_alloc_commit_data(struct nfs_direct_req *dreq) -{ - dreq->commit_data = NULL; -} - -static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode) -{ - nfs_direct_free_writedata(dreq); - nfs_zap_mapping(inode, inode->i_mapping); - nfs_direct_complete(dreq); -} -#endif - -static void nfs_direct_write_result(struct rpc_task *task, void *calldata) -{ - struct nfs_write_data *data = calldata; - - nfs_writeback_done(task, data); -} - -/* - * NB: Return the value of the first error return code. Subsequent - * errors after the first one are ignored. - */ -static void nfs_direct_write_release(void *calldata) -{ - struct nfs_write_data *data = calldata; - struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req; - int status = data->task.tk_status; - - spin_lock(&dreq->lock); - - if (unlikely(status < 0)) { - /* An error has occurred, so we should not commit */ - dreq->flags = 0; - dreq->error = status; - } - if (unlikely(dreq->error != 0)) - goto out_unlock; - - dreq->count += data->res.count; - - if (data->res.verf->committed != NFS_FILE_SYNC) { - switch (dreq->flags) { - case 0: - memcpy(&dreq->verf, &data->verf, sizeof(dreq->verf)); - dreq->flags = NFS_ODIRECT_DO_COMMIT; - break; - case NFS_ODIRECT_DO_COMMIT: - if (memcmp(&dreq->verf, &data->verf, sizeof(dreq->verf))) { - dprintk("NFS: %5u write verify failed\n", data->task.tk_pid); - dreq->flags = NFS_ODIRECT_RESCHED_WRITES; - } - } - } -out_unlock: - spin_unlock(&dreq->lock); - - if (put_dreq(dreq)) - nfs_direct_write_complete(dreq, data->inode); -} - -static const struct rpc_call_ops nfs_write_direct_ops = { - .rpc_call_prepare = nfs_write_prepare, - .rpc_call_done = nfs_direct_write_result, - .rpc_release = nfs_direct_write_release, -}; - -/* - * For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE - * operation. If nfs_writedata_alloc() or get_user_pages() fails, - * bail and stop sending more writes. Write length accounting is - * handled automatically by nfs_direct_write_result(). Otherwise, if - * no requests have been sent, just return an error. - */ -static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq, - const struct iovec *iov, - loff_t pos, int sync) -{ - struct nfs_open_context *ctx = dreq->ctx; - struct inode *inode = ctx->dentry->d_inode; - unsigned long user_addr = (unsigned long)iov->iov_base; - size_t count = iov->iov_len; - struct rpc_task *task; - struct rpc_message msg = { - .rpc_cred = ctx->cred, - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = NFS_CLIENT(inode), - .rpc_message = &msg, - .callback_ops = &nfs_write_direct_ops, - .workqueue = nfsiod_workqueue, - .flags = RPC_TASK_ASYNC, - }; - size_t wsize = NFS_SERVER(inode)->wsize; - unsigned int pgbase; - int result; - ssize_t started = 0; - - do { - struct nfs_write_data *data; - size_t bytes; - - pgbase = user_addr & ~PAGE_MASK; - bytes = min(wsize,count); - - result = -ENOMEM; - data = nfs_writedata_alloc(nfs_page_array_len(pgbase, bytes)); - if (unlikely(!data)) - break; - - down_read(¤t->mm->mmap_sem); - result = get_user_pages(current, current->mm, user_addr, - data->npages, 0, 0, data->pagevec, NULL); - up_read(¤t->mm->mmap_sem); - if (result < 0) { - nfs_writedata_free(data); - break; - } - if ((unsigned)result < data->npages) { - bytes = result * PAGE_SIZE; - if (bytes <= pgbase) { - nfs_direct_release_pages(data->pagevec, result); - nfs_writedata_free(data); - break; - } - bytes -= pgbase; - data->npages = result; - } - - get_dreq(dreq); - - list_move_tail(&data->pages, &dreq->rewrite_list); - - data->req = (struct nfs_page *) dreq; - data->inode = inode; - data->cred = msg.rpc_cred; - data->args.fh = NFS_FH(inode); - data->args.context = ctx; - data->args.lock_context = dreq->l_ctx; - data->args.offset = pos; - data->args.pgbase = pgbase; - data->args.pages = data->pagevec; - data->args.count = bytes; - data->args.stable = sync; - data->res.fattr = &data->fattr; - data->res.count = bytes; - data->res.verf = &data->verf; - nfs_fattr_init(&data->fattr); - - task_setup_data.task = &data->task; - task_setup_data.callback_data = data; - msg.rpc_argp = &data->args; - msg.rpc_resp = &data->res; - NFS_PROTO(inode)->write_setup(data, &msg); - - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) - break; - rpc_put_task(task); - - dprintk("NFS: %5u initiated direct write call " - "(req %s/%Ld, %zu bytes @ offset %Lu)\n", - data->task.tk_pid, - inode->i_sb->s_id, - (long long)NFS_FILEID(inode), - bytes, - (unsigned long long)data->args.offset); - - started += bytes; - user_addr += bytes; - pos += bytes; - - /* FIXME: Remove this useless math from the final patch */ - pgbase += bytes; - pgbase &= ~PAGE_MASK; - BUG_ON(pgbase != (user_addr & ~PAGE_MASK)); - - count -= bytes; - } while (count != 0); - - if (started) - return started; - return result < 0 ? (ssize_t) result : -EFAULT; -} - -static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, - const struct iovec *iov, - unsigned long nr_segs, - loff_t pos, int sync) -{ - ssize_t result = 0; - size_t requested_bytes = 0; - unsigned long seg; - - get_dreq(dreq); - - for (seg = 0; seg < nr_segs; seg++) { - const struct iovec *vec = &iov[seg]; - result = nfs_direct_write_schedule_segment(dreq, vec, - pos, sync); - if (result < 0) - break; - requested_bytes += result; - if ((size_t)result < vec->iov_len) - break; - pos += vec->iov_len; - } - - /* - * If no bytes were started, return the error, and let the - * generic layer handle the completion. - */ - if (requested_bytes == 0) { - nfs_direct_req_release(dreq); - return result < 0 ? result : -EIO; - } - - if (put_dreq(dreq)) - nfs_direct_write_complete(dreq, dreq->inode); - return 0; -} - -static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos, - size_t count) -{ - ssize_t result = -ENOMEM; - struct inode *inode = iocb->ki_filp->f_mapping->host; - struct nfs_direct_req *dreq; - size_t wsize = NFS_SERVER(inode)->wsize; - int sync = NFS_UNSTABLE; - - dreq = nfs_direct_req_alloc(); - if (!dreq) - goto out; - nfs_alloc_commit_data(dreq); - - if (dreq->commit_data == NULL || count <= wsize) - sync = NFS_FILE_SYNC; - - dreq->inode = inode; - dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); - dreq->l_ctx = nfs_get_lock_context(dreq->ctx); - if (dreq->l_ctx == NULL) - goto out_release; - if (!is_sync_kiocb(iocb)) - dreq->iocb = iocb; - - result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync); - if (!result) - result = nfs_direct_wait(dreq); -out_release: - nfs_direct_req_release(dreq); -out: - return result; -} - -/** - * nfs_file_direct_read - file direct read operation for NFS files - * @iocb: target I/O control block - * @iov: vector of user buffers into which to read data - * @nr_segs: size of iov vector - * @pos: byte offset in file where reading starts - * - * We use this function for direct reads instead of calling - * generic_file_aio_read() in order to avoid gfar's check to see if - * the request starts before the end of the file. For that check - * to work, we must generate a GETATTR before each direct read, and - * even then there is a window between the GETATTR and the subsequent - * READ where the file size could change. Our preference is simply - * to do all reads the application wants, and the server will take - * care of managing the end of file boundary. - * - * This function also eliminates unnecessarily updating the file's - * atime locally, as the NFS server sets the file's atime, and this - * client must read the updated atime from the server back into its - * cache. - */ -ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - ssize_t retval = -EINVAL; - struct file *file = iocb->ki_filp; - struct address_space *mapping = file->f_mapping; - size_t count; - - count = iov_length(iov, nr_segs); - nfs_add_stats(mapping->host, NFSIOS_DIRECTREADBYTES, count); - - dfprintk(FILE, "NFS: direct read(%s/%s, %zd@%Ld)\n", - file->f_path.dentry->d_parent->d_name.name, - file->f_path.dentry->d_name.name, - count, (long long) pos); - - retval = 0; - if (!count) - goto out; - - retval = nfs_sync_mapping(mapping); - if (retval) - goto out; - - task_io_account_read(count); - - retval = nfs_direct_read(iocb, iov, nr_segs, pos); - if (retval > 0) - iocb->ki_pos = pos + retval; - -out: - return retval; -} - -/** - * nfs_file_direct_write - file direct write operation for NFS files - * @iocb: target I/O control block - * @iov: vector of user buffers from which to write data - * @nr_segs: size of iov vector - * @pos: byte offset in file where writing starts - * - * We use this function for direct writes instead of calling - * generic_file_aio_write() in order to avoid taking the inode - * semaphore and updating the i_size. The NFS server will set - * the new i_size and this client must read the updated size - * back into its cache. We let the server do generic write - * parameter checking and report problems. - * - * We eliminate local atime updates, see direct read above. - * - * We avoid unnecessary page cache invalidations for normal cached - * readers of this file. - * - * Note that O_APPEND is not supported for NFS direct writes, as there - * is no atomic O_APPEND write facility in the NFS protocol. - */ -ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - ssize_t retval = -EINVAL; - struct file *file = iocb->ki_filp; - struct address_space *mapping = file->f_mapping; - size_t count; - - count = iov_length(iov, nr_segs); - nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, count); - - dfprintk(FILE, "NFS: direct write(%s/%s, %zd@%Ld)\n", - file->f_path.dentry->d_parent->d_name.name, - file->f_path.dentry->d_name.name, - count, (long long) pos); - - retval = generic_write_checks(file, &pos, &count, 0); - if (retval) - goto out; - - retval = -EINVAL; - if ((ssize_t) count < 0) - goto out; - retval = 0; - if (!count) - goto out; - - retval = nfs_sync_mapping(mapping); - if (retval) - goto out; - - task_io_account_write(count); - - retval = nfs_direct_write(iocb, iov, nr_segs, pos, count); - - if (retval > 0) - iocb->ki_pos = pos + retval; - -out: - return retval; -} - -/** - * nfs_init_directcache - create a slab cache for nfs_direct_req structures - * - */ -int __init nfs_init_directcache(void) -{ - nfs_direct_cachep = kmem_cache_create("nfs_direct_cache", - sizeof(struct nfs_direct_req), - 0, (SLAB_RECLAIM_ACCOUNT| - SLAB_MEM_SPREAD), - NULL); - if (nfs_direct_cachep == NULL) - return -ENOMEM; - - return 0; -} - -/** - * nfs_destroy_directcache - destroy the slab cache for nfs_direct_req structures - * - */ -void nfs_destroy_directcache(void) -{ - kmem_cache_destroy(nfs_direct_cachep); -} diff --git a/ANDROID_3.4.5/fs/nfs/dns_resolve.c b/ANDROID_3.4.5/fs/nfs/dns_resolve.c deleted file mode 100644 index b3924b8a..00000000 --- a/ANDROID_3.4.5/fs/nfs/dns_resolve.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * linux/fs/nfs/dns_resolve.c - * - * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com> - * - * Resolves DNS hostnames into valid ip addresses - */ - -#ifdef CONFIG_NFS_USE_KERNEL_DNS - -#include <linux/sunrpc/clnt.h> -#include <linux/dns_resolver.h> -#include "dns_resolve.h" - -ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen, - struct sockaddr *sa, size_t salen) -{ - ssize_t ret; - char *ip_addr = NULL; - int ip_len; - - ip_len = dns_query(NULL, name, namelen, NULL, &ip_addr, NULL); - if (ip_len > 0) - ret = rpc_pton(net, ip_addr, ip_len, sa, salen); - else - ret = -ESRCH; - kfree(ip_addr); - return ret; -} - -#else - -#include <linux/hash.h> -#include <linux/string.h> -#include <linux/kmod.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/socket.h> -#include <linux/seq_file.h> -#include <linux/inet.h> -#include <linux/sunrpc/clnt.h> -#include <linux/sunrpc/cache.h> -#include <linux/sunrpc/svcauth.h> -#include <linux/sunrpc/rpc_pipe_fs.h> - -#include "dns_resolve.h" -#include "cache_lib.h" -#include "netns.h" - -#define NFS_DNS_HASHBITS 4 -#define NFS_DNS_HASHTBL_SIZE (1 << NFS_DNS_HASHBITS) - -struct nfs_dns_ent { - struct cache_head h; - - char *hostname; - size_t namelen; - - struct sockaddr_storage addr; - size_t addrlen; -}; - - -static void nfs_dns_ent_update(struct cache_head *cnew, - struct cache_head *ckey) -{ - struct nfs_dns_ent *new; - struct nfs_dns_ent *key; - - new = container_of(cnew, struct nfs_dns_ent, h); - key = container_of(ckey, struct nfs_dns_ent, h); - - memcpy(&new->addr, &key->addr, key->addrlen); - new->addrlen = key->addrlen; -} - -static void nfs_dns_ent_init(struct cache_head *cnew, - struct cache_head *ckey) -{ - struct nfs_dns_ent *new; - struct nfs_dns_ent *key; - - new = container_of(cnew, struct nfs_dns_ent, h); - key = container_of(ckey, struct nfs_dns_ent, h); - - kfree(new->hostname); - new->hostname = kstrndup(key->hostname, key->namelen, GFP_KERNEL); - if (new->hostname) { - new->namelen = key->namelen; - nfs_dns_ent_update(cnew, ckey); - } else { - new->namelen = 0; - new->addrlen = 0; - } -} - -static void nfs_dns_ent_put(struct kref *ref) -{ - struct nfs_dns_ent *item; - - item = container_of(ref, struct nfs_dns_ent, h.ref); - kfree(item->hostname); - kfree(item); -} - -static struct cache_head *nfs_dns_ent_alloc(void) -{ - struct nfs_dns_ent *item = kmalloc(sizeof(*item), GFP_KERNEL); - - if (item != NULL) { - item->hostname = NULL; - item->namelen = 0; - item->addrlen = 0; - return &item->h; - } - return NULL; -}; - -static unsigned int nfs_dns_hash(const struct nfs_dns_ent *key) -{ - return hash_str(key->hostname, NFS_DNS_HASHBITS); -} - -static void nfs_dns_request(struct cache_detail *cd, - struct cache_head *ch, - char **bpp, int *blen) -{ - struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h); - - qword_add(bpp, blen, key->hostname); - (*bpp)[-1] = '\n'; -} - -static int nfs_dns_upcall(struct cache_detail *cd, - struct cache_head *ch) -{ - struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h); - int ret; - - ret = nfs_cache_upcall(cd, key->hostname); - if (ret) - ret = sunrpc_cache_pipe_upcall(cd, ch, nfs_dns_request); - return ret; -} - -static int nfs_dns_match(struct cache_head *ca, - struct cache_head *cb) -{ - struct nfs_dns_ent *a; - struct nfs_dns_ent *b; - - a = container_of(ca, struct nfs_dns_ent, h); - b = container_of(cb, struct nfs_dns_ent, h); - - if (a->namelen == 0 || a->namelen != b->namelen) - return 0; - return memcmp(a->hostname, b->hostname, a->namelen) == 0; -} - -static int nfs_dns_show(struct seq_file *m, struct cache_detail *cd, - struct cache_head *h) -{ - struct nfs_dns_ent *item; - long ttl; - - if (h == NULL) { - seq_puts(m, "# ip address hostname ttl\n"); - return 0; - } - item = container_of(h, struct nfs_dns_ent, h); - ttl = item->h.expiry_time - seconds_since_boot(); - if (ttl < 0) - ttl = 0; - - if (!test_bit(CACHE_NEGATIVE, &h->flags)) { - char buf[INET6_ADDRSTRLEN+IPV6_SCOPE_ID_LEN+1]; - - rpc_ntop((struct sockaddr *)&item->addr, buf, sizeof(buf)); - seq_printf(m, "%15s ", buf); - } else - seq_puts(m, "<none> "); - seq_printf(m, "%15s %ld\n", item->hostname, ttl); - return 0; -} - -static struct nfs_dns_ent *nfs_dns_lookup(struct cache_detail *cd, - struct nfs_dns_ent *key) -{ - struct cache_head *ch; - - ch = sunrpc_cache_lookup(cd, - &key->h, - nfs_dns_hash(key)); - if (!ch) - return NULL; - return container_of(ch, struct nfs_dns_ent, h); -} - -static struct nfs_dns_ent *nfs_dns_update(struct cache_detail *cd, - struct nfs_dns_ent *new, - struct nfs_dns_ent *key) -{ - struct cache_head *ch; - - ch = sunrpc_cache_update(cd, - &new->h, &key->h, - nfs_dns_hash(key)); - if (!ch) - return NULL; - return container_of(ch, struct nfs_dns_ent, h); -} - -static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen) -{ - char buf1[NFS_DNS_HOSTNAME_MAXLEN+1]; - struct nfs_dns_ent key, *item; - unsigned long ttl; - ssize_t len; - int ret = -EINVAL; - - if (buf[buflen-1] != '\n') - goto out; - buf[buflen-1] = '\0'; - - len = qword_get(&buf, buf1, sizeof(buf1)); - if (len <= 0) - goto out; - key.addrlen = rpc_pton(cd->net, buf1, len, - (struct sockaddr *)&key.addr, - sizeof(key.addr)); - - len = qword_get(&buf, buf1, sizeof(buf1)); - if (len <= 0) - goto out; - - key.hostname = buf1; - key.namelen = len; - memset(&key.h, 0, sizeof(key.h)); - - ttl = get_expiry(&buf); - if (ttl == 0) - goto out; - key.h.expiry_time = ttl + seconds_since_boot(); - - ret = -ENOMEM; - item = nfs_dns_lookup(cd, &key); - if (item == NULL) - goto out; - - if (key.addrlen == 0) - set_bit(CACHE_NEGATIVE, &key.h.flags); - - item = nfs_dns_update(cd, &key, item); - if (item == NULL) - goto out; - - ret = 0; - cache_put(&item->h, cd); -out: - return ret; -} - -static int do_cache_lookup(struct cache_detail *cd, - struct nfs_dns_ent *key, - struct nfs_dns_ent **item, - struct nfs_cache_defer_req *dreq) -{ - int ret = -ENOMEM; - - *item = nfs_dns_lookup(cd, key); - if (*item) { - ret = cache_check(cd, &(*item)->h, &dreq->req); - if (ret) - *item = NULL; - } - return ret; -} - -static int do_cache_lookup_nowait(struct cache_detail *cd, - struct nfs_dns_ent *key, - struct nfs_dns_ent **item) -{ - int ret = -ENOMEM; - - *item = nfs_dns_lookup(cd, key); - if (!*item) - goto out_err; - ret = -ETIMEDOUT; - if (!test_bit(CACHE_VALID, &(*item)->h.flags) - || (*item)->h.expiry_time < seconds_since_boot() - || cd->flush_time > (*item)->h.last_refresh) - goto out_put; - ret = -ENOENT; - if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags)) - goto out_put; - return 0; -out_put: - cache_put(&(*item)->h, cd); -out_err: - *item = NULL; - return ret; -} - -static int do_cache_lookup_wait(struct cache_detail *cd, - struct nfs_dns_ent *key, - struct nfs_dns_ent **item) -{ - struct nfs_cache_defer_req *dreq; - int ret = -ENOMEM; - - dreq = nfs_cache_defer_req_alloc(); - if (!dreq) - goto out; - ret = do_cache_lookup(cd, key, item, dreq); - if (ret == -EAGAIN) { - ret = nfs_cache_wait_for_upcall(dreq); - if (!ret) - ret = do_cache_lookup_nowait(cd, key, item); - } - nfs_cache_defer_req_put(dreq); -out: - return ret; -} - -ssize_t nfs_dns_resolve_name(struct net *net, char *name, - size_t namelen, struct sockaddr *sa, size_t salen) -{ - struct nfs_dns_ent key = { - .hostname = name, - .namelen = namelen, - }; - struct nfs_dns_ent *item = NULL; - ssize_t ret; - struct nfs_net *nn = net_generic(net, nfs_net_id); - - ret = do_cache_lookup_wait(nn->nfs_dns_resolve, &key, &item); - if (ret == 0) { - if (salen >= item->addrlen) { - memcpy(sa, &item->addr, item->addrlen); - ret = item->addrlen; - } else - ret = -EOVERFLOW; - cache_put(&item->h, nn->nfs_dns_resolve); - } else if (ret == -ENOENT) - ret = -ESRCH; - return ret; -} - -int nfs_dns_resolver_cache_init(struct net *net) -{ - int err = -ENOMEM; - struct nfs_net *nn = net_generic(net, nfs_net_id); - struct cache_detail *cd; - struct cache_head **tbl; - - cd = kzalloc(sizeof(struct cache_detail), GFP_KERNEL); - if (cd == NULL) - goto err_cd; - - tbl = kzalloc(NFS_DNS_HASHTBL_SIZE * sizeof(struct cache_head *), - GFP_KERNEL); - if (tbl == NULL) - goto err_tbl; - - cd->owner = THIS_MODULE, - cd->hash_size = NFS_DNS_HASHTBL_SIZE, - cd->hash_table = tbl, - cd->name = "dns_resolve", - cd->cache_put = nfs_dns_ent_put, - cd->cache_upcall = nfs_dns_upcall, - cd->cache_parse = nfs_dns_parse, - cd->cache_show = nfs_dns_show, - cd->match = nfs_dns_match, - cd->init = nfs_dns_ent_init, - cd->update = nfs_dns_ent_update, - cd->alloc = nfs_dns_ent_alloc, - - nfs_cache_init(cd); - err = nfs_cache_register_net(net, cd); - if (err) - goto err_reg; - nn->nfs_dns_resolve = cd; - return 0; - -err_reg: - nfs_cache_destroy(cd); - kfree(cd->hash_table); -err_tbl: - kfree(cd); -err_cd: - return err; -} - -void nfs_dns_resolver_cache_destroy(struct net *net) -{ - struct nfs_net *nn = net_generic(net, nfs_net_id); - struct cache_detail *cd = nn->nfs_dns_resolve; - - nfs_cache_unregister_net(net, cd); - nfs_cache_destroy(cd); - kfree(cd->hash_table); - kfree(cd); -} - -static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event, - void *ptr) -{ - struct super_block *sb = ptr; - struct net *net = sb->s_fs_info; - struct nfs_net *nn = net_generic(net, nfs_net_id); - struct cache_detail *cd = nn->nfs_dns_resolve; - int ret = 0; - - if (cd == NULL) - return 0; - - if (!try_module_get(THIS_MODULE)) - return 0; - - switch (event) { - case RPC_PIPEFS_MOUNT: - ret = nfs_cache_register_sb(sb, cd); - break; - case RPC_PIPEFS_UMOUNT: - nfs_cache_unregister_sb(sb, cd); - break; - default: - ret = -ENOTSUPP; - break; - } - module_put(THIS_MODULE); - return ret; -} - -static struct notifier_block nfs_dns_resolver_block = { - .notifier_call = rpc_pipefs_event, -}; - -int nfs_dns_resolver_init(void) -{ - return rpc_pipefs_notifier_register(&nfs_dns_resolver_block); -} - -void nfs_dns_resolver_destroy(void) -{ - rpc_pipefs_notifier_unregister(&nfs_dns_resolver_block); -} -#endif diff --git a/ANDROID_3.4.5/fs/nfs/dns_resolve.h b/ANDROID_3.4.5/fs/nfs/dns_resolve.h deleted file mode 100644 index 2e4f596d..00000000 --- a/ANDROID_3.4.5/fs/nfs/dns_resolve.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Resolve DNS hostnames into valid ip addresses - */ -#ifndef __LINUX_FS_NFS_DNS_RESOLVE_H -#define __LINUX_FS_NFS_DNS_RESOLVE_H - -#define NFS_DNS_HOSTNAME_MAXLEN (128) - - -#ifdef CONFIG_NFS_USE_KERNEL_DNS -static inline int nfs_dns_resolver_init(void) -{ - return 0; -} - -static inline void nfs_dns_resolver_destroy(void) -{} - -static inline int nfs_dns_resolver_cache_init(struct net *net) -{ - return 0; -} - -static inline void nfs_dns_resolver_cache_destroy(struct net *net) -{} -#else -extern int nfs_dns_resolver_init(void); -extern void nfs_dns_resolver_destroy(void); -extern int nfs_dns_resolver_cache_init(struct net *net); -extern void nfs_dns_resolver_cache_destroy(struct net *net); -#endif - -extern ssize_t nfs_dns_resolve_name(struct net *net, char *name, - size_t namelen, struct sockaddr *sa, size_t salen); - -#endif diff --git a/ANDROID_3.4.5/fs/nfs/file.c b/ANDROID_3.4.5/fs/nfs/file.c deleted file mode 100644 index aa9b709f..00000000 --- a/ANDROID_3.4.5/fs/nfs/file.c +++ /dev/null @@ -1,900 +0,0 @@ -/* - * linux/fs/nfs/file.c - * - * Copyright (C) 1992 Rick Sladkey - * - * Changes Copyright (C) 1994 by Florian La Roche - * - Do not copy data too often around in the kernel. - * - In nfs_file_read the return value of kmalloc wasn't checked. - * - Put in a better version of read look-ahead buffering. Original idea - * and implementation by Wai S Kok elekokws@ee.nus.sg. - * - * Expire cache on write to a file by Wai S Kok (Oct 1994). - * - * Total rewrite of read side for new NFS buffer cache.. Linus. - * - * nfs regular file handling functions - */ - -#include <linux/time.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/fcntl.h> -#include <linux/stat.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_mount.h> -#include <linux/mm.h> -#include <linux/pagemap.h> -#include <linux/aio.h> -#include <linux/gfp.h> -#include <linux/swap.h> - -#include <asm/uaccess.h> - -#include "delegation.h" -#include "internal.h" -#include "iostat.h" -#include "fscache.h" -#include "pnfs.h" - -#define NFSDBG_FACILITY NFSDBG_FILE - -static const struct vm_operations_struct nfs_file_vm_ops; - -const struct inode_operations nfs_file_inode_operations = { - .permission = nfs_permission, - .getattr = nfs_getattr, - .setattr = nfs_setattr, -}; - -#ifdef CONFIG_NFS_V3 -const struct inode_operations nfs3_file_inode_operations = { - .permission = nfs_permission, - .getattr = nfs_getattr, - .setattr = nfs_setattr, - .listxattr = nfs3_listxattr, - .getxattr = nfs3_getxattr, - .setxattr = nfs3_setxattr, - .removexattr = nfs3_removexattr, -}; -#endif /* CONFIG_NFS_v3 */ - -/* Hack for future NFS swap support */ -#ifndef IS_SWAPFILE -# define IS_SWAPFILE(inode) (0) -#endif - -static int nfs_check_flags(int flags) -{ - if ((flags & (O_APPEND | O_DIRECT)) == (O_APPEND | O_DIRECT)) - return -EINVAL; - - return 0; -} - -/* - * Open file - */ -static int -nfs_file_open(struct inode *inode, struct file *filp) -{ - int res; - - dprintk("NFS: open file(%s/%s)\n", - filp->f_path.dentry->d_parent->d_name.name, - filp->f_path.dentry->d_name.name); - - nfs_inc_stats(inode, NFSIOS_VFSOPEN); - res = nfs_check_flags(filp->f_flags); - if (res) - return res; - - res = nfs_open(inode, filp); - return res; -} - -static int -nfs_file_release(struct inode *inode, struct file *filp) -{ - dprintk("NFS: release(%s/%s)\n", - filp->f_path.dentry->d_parent->d_name.name, - filp->f_path.dentry->d_name.name); - - nfs_inc_stats(inode, NFSIOS_VFSRELEASE); - return nfs_release(inode, filp); -} - -/** - * nfs_revalidate_size - Revalidate the file size - * @inode - pointer to inode struct - * @file - pointer to struct file - * - * Revalidates the file length. This is basically a wrapper around - * nfs_revalidate_inode() that takes into account the fact that we may - * have cached writes (in which case we don't care about the server's - * idea of what the file length is), or O_DIRECT (in which case we - * shouldn't trust the cache). - */ -static int nfs_revalidate_file_size(struct inode *inode, struct file *filp) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct nfs_inode *nfsi = NFS_I(inode); - - if (nfs_have_delegated_attributes(inode)) - goto out_noreval; - - if (filp->f_flags & O_DIRECT) - goto force_reval; - if (nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) - goto force_reval; - if (nfs_attribute_timeout(inode)) - goto force_reval; -out_noreval: - return 0; -force_reval: - return __nfs_revalidate_inode(server, inode); -} - -static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin) -{ - dprintk("NFS: llseek file(%s/%s, %lld, %d)\n", - filp->f_path.dentry->d_parent->d_name.name, - filp->f_path.dentry->d_name.name, - offset, origin); - - /* - * origin == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate - * the cached file length - */ - if (origin != SEEK_SET && origin != SEEK_CUR) { - struct inode *inode = filp->f_mapping->host; - - int retval = nfs_revalidate_file_size(inode, filp); - if (retval < 0) - return (loff_t)retval; - } - - return generic_file_llseek(filp, offset, origin); -} - -/* - * Flush all dirty pages, and check for write errors. - */ -static int -nfs_file_flush(struct file *file, fl_owner_t id) -{ - struct dentry *dentry = file->f_path.dentry; - struct inode *inode = dentry->d_inode; - - dprintk("NFS: flush(%s/%s)\n", - dentry->d_parent->d_name.name, - dentry->d_name.name); - - nfs_inc_stats(inode, NFSIOS_VFSFLUSH); - if ((file->f_mode & FMODE_WRITE) == 0) - return 0; - - /* Flush writes to the server and return any errors */ - return vfs_fsync(file, 0); -} - -static ssize_t -nfs_file_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - struct dentry * dentry = iocb->ki_filp->f_path.dentry; - struct inode * inode = dentry->d_inode; - ssize_t result; - - if (iocb->ki_filp->f_flags & O_DIRECT) - return nfs_file_direct_read(iocb, iov, nr_segs, pos); - - dprintk("NFS: read(%s/%s, %lu@%lu)\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - (unsigned long) iov_length(iov, nr_segs), (unsigned long) pos); - - result = nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping); - if (!result) { - result = generic_file_aio_read(iocb, iov, nr_segs, pos); - if (result > 0) - nfs_add_stats(inode, NFSIOS_NORMALREADBYTES, result); - } - return result; -} - -static ssize_t -nfs_file_splice_read(struct file *filp, loff_t *ppos, - struct pipe_inode_info *pipe, size_t count, - unsigned int flags) -{ - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = dentry->d_inode; - ssize_t res; - - dprintk("NFS: splice_read(%s/%s, %lu@%Lu)\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - (unsigned long) count, (unsigned long long) *ppos); - - res = nfs_revalidate_mapping(inode, filp->f_mapping); - if (!res) { - res = generic_file_splice_read(filp, ppos, pipe, count, flags); - if (res > 0) - nfs_add_stats(inode, NFSIOS_NORMALREADBYTES, res); - } - return res; -} - -static int -nfs_file_mmap(struct file * file, struct vm_area_struct * vma) -{ - struct dentry *dentry = file->f_path.dentry; - struct inode *inode = dentry->d_inode; - int status; - - dprintk("NFS: mmap(%s/%s)\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - - /* Note: generic_file_mmap() returns ENOSYS on nommu systems - * so we call that before revalidating the mapping - */ - status = generic_file_mmap(file, vma); - if (!status) { - vma->vm_ops = &nfs_file_vm_ops; - status = nfs_revalidate_mapping(inode, file->f_mapping); - } - return status; -} - -/* - * Flush any dirty pages for this process, and check for write errors. - * The return status from this call provides a reliable indication of - * whether any write errors occurred for this process. - * - * Notice that it clears the NFS_CONTEXT_ERROR_WRITE before synching to - * disk, but it retrieves and clears ctx->error after synching, despite - * the two being set at the same time in nfs_context_set_write_error(). - * This is because the former is used to notify the _next_ call to - * nfs_file_write() that a write error occurred, and hence cause it to - * fall back to doing a synchronous write. - */ -static int -nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) -{ - struct dentry *dentry = file->f_path.dentry; - struct nfs_open_context *ctx = nfs_file_open_context(file); - struct inode *inode = dentry->d_inode; - int have_error, status; - int ret = 0; - - dprintk("NFS: fsync file(%s/%s) datasync %d\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - datasync); - - ret = filemap_write_and_wait_range(inode->i_mapping, start, end); - mutex_lock(&inode->i_mutex); - - nfs_inc_stats(inode, NFSIOS_VFSFSYNC); - have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); - status = nfs_commit_inode(inode, FLUSH_SYNC); - if (status >= 0 && ret < 0) - status = ret; - have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); - if (have_error) - ret = xchg(&ctx->error, 0); - if (!ret && status < 0) - ret = status; - if (!ret && !datasync) - /* application has asked for meta-data sync */ - ret = pnfs_layoutcommit_inode(inode, true); - mutex_unlock(&inode->i_mutex); - return ret; -} - -/* - * Decide whether a read/modify/write cycle may be more efficient - * then a modify/write/read cycle when writing to a page in the - * page cache. - * - * The modify/write/read cycle may occur if a page is read before - * being completely filled by the writer. In this situation, the - * page must be completely written to stable storage on the server - * before it can be refilled by reading in the page from the server. - * This can lead to expensive, small, FILE_SYNC mode writes being - * done. - * - * It may be more efficient to read the page first if the file is - * open for reading in addition to writing, the page is not marked - * as Uptodate, it is not dirty or waiting to be committed, - * indicating that it was previously allocated and then modified, - * that there were valid bytes of data in that range of the file, - * and that the new data won't completely replace the old data in - * that range of the file. - */ -static int nfs_want_read_modify_write(struct file *file, struct page *page, - loff_t pos, unsigned len) -{ - unsigned int pglen = nfs_page_length(page); - unsigned int offset = pos & (PAGE_CACHE_SIZE - 1); - unsigned int end = offset + len; - - if ((file->f_mode & FMODE_READ) && /* open for read? */ - !PageUptodate(page) && /* Uptodate? */ - !PagePrivate(page) && /* i/o request already? */ - pglen && /* valid bytes of file? */ - (end < pglen || offset)) /* replace all valid bytes? */ - return 1; - return 0; -} - -/* - * This does the "real" work of the write. We must allocate and lock the - * page to be sent back to the generic routine, which then copies the - * data from user space. - * - * If the writer ends up delaying the write, the writer needs to - * increment the page use counts until he is done with the page. - */ -static int nfs_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) -{ - int ret; - pgoff_t index = pos >> PAGE_CACHE_SHIFT; - struct page *page; - int once_thru = 0; - - dfprintk(PAGECACHE, "NFS: write_begin(%s/%s(%ld), %u@%lld)\n", - file->f_path.dentry->d_parent->d_name.name, - file->f_path.dentry->d_name.name, - mapping->host->i_ino, len, (long long) pos); - -start: - /* - * Prevent starvation issues if someone is doing a consistency - * sync-to-disk - */ - ret = wait_on_bit(&NFS_I(mapping->host)->flags, NFS_INO_FLUSHING, - nfs_wait_bit_killable, TASK_KILLABLE); - if (ret) - return ret; - - page = grab_cache_page_write_begin(mapping, index, flags); - if (!page) - return -ENOMEM; - *pagep = page; - - ret = nfs_flush_incompatible(file, page); - if (ret) { - unlock_page(page); - page_cache_release(page); - } else if (!once_thru && - nfs_want_read_modify_write(file, page, pos, len)) { - once_thru = 1; - ret = nfs_readpage(file, page); - page_cache_release(page); - if (!ret) - goto start; - } - return ret; -} - -static int nfs_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned copied, - struct page *page, void *fsdata) -{ - unsigned offset = pos & (PAGE_CACHE_SIZE - 1); - int status; - - dfprintk(PAGECACHE, "NFS: write_end(%s/%s(%ld), %u@%lld)\n", - file->f_path.dentry->d_parent->d_name.name, - file->f_path.dentry->d_name.name, - mapping->host->i_ino, len, (long long) pos); - - /* - * Zero any uninitialised parts of the page, and then mark the page - * as up to date if it turns out that we're extending the file. - */ - if (!PageUptodate(page)) { - unsigned pglen = nfs_page_length(page); - unsigned end = offset + len; - - if (pglen == 0) { - zero_user_segments(page, 0, offset, - end, PAGE_CACHE_SIZE); - SetPageUptodate(page); - } else if (end >= pglen) { - zero_user_segment(page, end, PAGE_CACHE_SIZE); - if (offset == 0) - SetPageUptodate(page); - } else - zero_user_segment(page, pglen, PAGE_CACHE_SIZE); - } - - status = nfs_updatepage(file, page, offset, copied); - - unlock_page(page); - page_cache_release(page); - - if (status < 0) - return status; - return copied; -} - -/* - * Partially or wholly invalidate a page - * - Release the private state associated with a page if undergoing complete - * page invalidation - * - Called if either PG_private or PG_fscache is set on the page - * - Caller holds page lock - */ -static void nfs_invalidate_page(struct page *page, unsigned long offset) -{ - dfprintk(PAGECACHE, "NFS: invalidate_page(%p, %lu)\n", page, offset); - - if (offset != 0) - return; - /* Cancel any unstarted writes on this page */ - nfs_wb_page_cancel(page->mapping->host, page); - - nfs_fscache_invalidate_page(page, page->mapping->host); -} - -/* - * Attempt to release the private state associated with a page - * - Called if either PG_private or PG_fscache is set on the page - * - Caller holds page lock - * - Return true (may release page) or false (may not) - */ -static int nfs_release_page(struct page *page, gfp_t gfp) -{ - struct address_space *mapping = page->mapping; - - dfprintk(PAGECACHE, "NFS: release_page(%p)\n", page); - - /* Only do I/O if gfp is a superset of GFP_KERNEL */ - if (mapping && (gfp & GFP_KERNEL) == GFP_KERNEL) { - int how = FLUSH_SYNC; - - /* Don't let kswapd deadlock waiting for OOM RPC calls */ - if (current_is_kswapd()) - how = 0; - nfs_commit_inode(mapping->host, how); - } - /* If PagePrivate() is set, then the page is not freeable */ - if (PagePrivate(page)) - return 0; - return nfs_fscache_release_page(page, gfp); -} - -/* - * Attempt to clear the private state associated with a page when an error - * occurs that requires the cached contents of an inode to be written back or - * destroyed - * - Called if either PG_private or fscache is set on the page - * - Caller holds page lock - * - Return 0 if successful, -error otherwise - */ -static int nfs_launder_page(struct page *page) -{ - struct inode *inode = page->mapping->host; - struct nfs_inode *nfsi = NFS_I(inode); - - dfprintk(PAGECACHE, "NFS: launder_page(%ld, %llu)\n", - inode->i_ino, (long long)page_offset(page)); - - nfs_fscache_wait_on_page_write(nfsi, page); - return nfs_wb_page(inode, page); -} - -const struct address_space_operations nfs_file_aops = { - .readpage = nfs_readpage, - .readpages = nfs_readpages, - .set_page_dirty = __set_page_dirty_nobuffers, - .writepage = nfs_writepage, - .writepages = nfs_writepages, - .write_begin = nfs_write_begin, - .write_end = nfs_write_end, - .invalidatepage = nfs_invalidate_page, - .releasepage = nfs_release_page, - .direct_IO = nfs_direct_IO, - .migratepage = nfs_migrate_page, - .launder_page = nfs_launder_page, - .error_remove_page = generic_error_remove_page, -}; - -/* - * Notification that a PTE pointing to an NFS page is about to be made - * writable, implying that someone is about to modify the page through a - * shared-writable mapping - */ -static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct page *page = vmf->page; - struct file *filp = vma->vm_file; - struct dentry *dentry = filp->f_path.dentry; - unsigned pagelen; - int ret = VM_FAULT_NOPAGE; - struct address_space *mapping; - - dfprintk(PAGECACHE, "NFS: vm_page_mkwrite(%s/%s(%ld), offset %lld)\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - filp->f_mapping->host->i_ino, - (long long)page_offset(page)); - - /* make sure the cache has finished storing the page */ - nfs_fscache_wait_on_page_write(NFS_I(dentry->d_inode), page); - - lock_page(page); - mapping = page->mapping; - if (mapping != dentry->d_inode->i_mapping) - goto out_unlock; - - wait_on_page_writeback(page); - - pagelen = nfs_page_length(page); - if (pagelen == 0) - goto out_unlock; - - ret = VM_FAULT_LOCKED; - if (nfs_flush_incompatible(filp, page) == 0 && - nfs_updatepage(filp, page, 0, pagelen) == 0) - goto out; - - ret = VM_FAULT_SIGBUS; -out_unlock: - unlock_page(page); -out: - return ret; -} - -static const struct vm_operations_struct nfs_file_vm_ops = { - .fault = filemap_fault, - .page_mkwrite = nfs_vm_page_mkwrite, -}; - -static int nfs_need_sync_write(struct file *filp, struct inode *inode) -{ - struct nfs_open_context *ctx; - - if (IS_SYNC(inode) || (filp->f_flags & O_DSYNC)) - return 1; - ctx = nfs_file_open_context(filp); - if (test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags)) - return 1; - return 0; -} - -static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - struct dentry * dentry = iocb->ki_filp->f_path.dentry; - struct inode * inode = dentry->d_inode; - unsigned long written = 0; - ssize_t result; - size_t count = iov_length(iov, nr_segs); - - if (iocb->ki_filp->f_flags & O_DIRECT) - return nfs_file_direct_write(iocb, iov, nr_segs, pos); - - dprintk("NFS: write(%s/%s, %lu@%Ld)\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - (unsigned long) count, (long long) pos); - - result = -EBUSY; - if (IS_SWAPFILE(inode)) - goto out_swapfile; - /* - * O_APPEND implies that we must revalidate the file length. - */ - if (iocb->ki_filp->f_flags & O_APPEND) { - result = nfs_revalidate_file_size(inode, iocb->ki_filp); - if (result) - goto out; - } - - result = count; - if (!count) - goto out; - - result = generic_file_aio_write(iocb, iov, nr_segs, pos); - if (result > 0) - written = result; - - /* Return error values for O_DSYNC and IS_SYNC() */ - if (result >= 0 && nfs_need_sync_write(iocb->ki_filp, inode)) { - int err = vfs_fsync(iocb->ki_filp, 0); - if (err < 0) - result = err; - } - if (result > 0) - nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written); -out: - return result; - -out_swapfile: - printk(KERN_INFO "NFS: attempt to write to active swap file!\n"); - goto out; -} - -static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe, - struct file *filp, loff_t *ppos, - size_t count, unsigned int flags) -{ - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = dentry->d_inode; - unsigned long written = 0; - ssize_t ret; - - dprintk("NFS splice_write(%s/%s, %lu@%llu)\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - (unsigned long) count, (unsigned long long) *ppos); - - /* - * The combination of splice and an O_APPEND destination is disallowed. - */ - - ret = generic_file_splice_write(pipe, filp, ppos, count, flags); - if (ret > 0) - written = ret; - - if (ret >= 0 && nfs_need_sync_write(filp, inode)) { - int err = vfs_fsync(filp, 0); - if (err < 0) - ret = err; - } - if (ret > 0) - nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written); - return ret; -} - -static int -do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) -{ - struct inode *inode = filp->f_mapping->host; - int status = 0; - unsigned int saved_type = fl->fl_type; - - /* Try local locking first */ - posix_test_lock(filp, fl); - if (fl->fl_type != F_UNLCK) { - /* found a conflict */ - goto out; - } - fl->fl_type = saved_type; - - if (nfs_have_delegation(inode, FMODE_READ)) - goto out_noconflict; - - if (is_local) - goto out_noconflict; - - status = NFS_PROTO(inode)->lock(filp, cmd, fl); -out: - return status; -out_noconflict: - fl->fl_type = F_UNLCK; - goto out; -} - -static int do_vfs_lock(struct file *file, struct file_lock *fl) -{ - int res = 0; - switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) { - case FL_POSIX: - res = posix_lock_file_wait(file, fl); - break; - case FL_FLOCK: - res = flock_lock_file_wait(file, fl); - break; - default: - BUG(); - } - return res; -} - -static int -do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) -{ - struct inode *inode = filp->f_mapping->host; - int status; - - /* - * Flush all pending writes before doing anything - * with locks.. - */ - nfs_sync_mapping(filp->f_mapping); - - /* NOTE: special case - * If we're signalled while cleaning up locks on process exit, we - * still need to complete the unlock. - */ - /* - * Use local locking if mounted with "-onolock" or with appropriate - * "-olocal_lock=" - */ - if (!is_local) - status = NFS_PROTO(inode)->lock(filp, cmd, fl); - else - status = do_vfs_lock(filp, fl); - return status; -} - -static int -is_time_granular(struct timespec *ts) { - return ((ts->tv_sec == 0) && (ts->tv_nsec <= 1000)); -} - -static int -do_setlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) -{ - struct inode *inode = filp->f_mapping->host; - int status; - - /* - * Flush all pending writes before doing anything - * with locks.. - */ - status = nfs_sync_mapping(filp->f_mapping); - if (status != 0) - goto out; - - /* - * Use local locking if mounted with "-onolock" or with appropriate - * "-olocal_lock=" - */ - if (!is_local) - status = NFS_PROTO(inode)->lock(filp, cmd, fl); - else - status = do_vfs_lock(filp, fl); - if (status < 0) - goto out; - - /* - * Revalidate the cache if the server has time stamps granular - * enough to detect subsecond changes. Otherwise, clear the - * cache to prevent missing any changes. - * - * This makes locking act as a cache coherency point. - */ - nfs_sync_mapping(filp->f_mapping); - if (!nfs_have_delegation(inode, FMODE_READ)) { - if (is_time_granular(&NFS_SERVER(inode)->time_delta)) - __nfs_revalidate_inode(NFS_SERVER(inode), inode); - else - nfs_zap_caches(inode); - } -out: - return status; -} - -/* - * Lock a (portion of) a file - */ -static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) -{ - struct inode *inode = filp->f_mapping->host; - int ret = -ENOLCK; - int is_local = 0; - - dprintk("NFS: lock(%s/%s, t=%x, fl=%x, r=%lld:%lld)\n", - filp->f_path.dentry->d_parent->d_name.name, - filp->f_path.dentry->d_name.name, - fl->fl_type, fl->fl_flags, - (long long)fl->fl_start, (long long)fl->fl_end); - - nfs_inc_stats(inode, NFSIOS_VFSLOCK); - - /* No mandatory locks over NFS */ - if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) - goto out_err; - - if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FCNTL) - is_local = 1; - - if (NFS_PROTO(inode)->lock_check_bounds != NULL) { - ret = NFS_PROTO(inode)->lock_check_bounds(fl); - if (ret < 0) - goto out_err; - } - - if (IS_GETLK(cmd)) - ret = do_getlk(filp, cmd, fl, is_local); - else if (fl->fl_type == F_UNLCK) - ret = do_unlk(filp, cmd, fl, is_local); - else - ret = do_setlk(filp, cmd, fl, is_local); -out_err: - return ret; -} - -/* - * Lock a (portion of) a file - */ -static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) -{ - struct inode *inode = filp->f_mapping->host; - int is_local = 0; - - dprintk("NFS: flock(%s/%s, t=%x, fl=%x)\n", - filp->f_path.dentry->d_parent->d_name.name, - filp->f_path.dentry->d_name.name, - fl->fl_type, fl->fl_flags); - - if (!(fl->fl_flags & FL_FLOCK)) - return -ENOLCK; - - if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FLOCK) - is_local = 1; - - /* We're simulating flock() locks using posix locks on the server */ - fl->fl_owner = (fl_owner_t)filp; - fl->fl_start = 0; - fl->fl_end = OFFSET_MAX; - - if (fl->fl_type == F_UNLCK) - return do_unlk(filp, cmd, fl, is_local); - return do_setlk(filp, cmd, fl, is_local); -} - -/* - * There is no protocol support for leases, so we have no way to implement - * them correctly in the face of opens by other clients. - */ -static int nfs_setlease(struct file *file, long arg, struct file_lock **fl) -{ - dprintk("NFS: setlease(%s/%s, arg=%ld)\n", - file->f_path.dentry->d_parent->d_name.name, - file->f_path.dentry->d_name.name, arg); - return -EINVAL; -} - -const struct file_operations nfs_file_operations = { - .llseek = nfs_file_llseek, - .read = do_sync_read, - .write = do_sync_write, - .aio_read = nfs_file_read, - .aio_write = nfs_file_write, - .mmap = nfs_file_mmap, - .open = nfs_file_open, - .flush = nfs_file_flush, - .release = nfs_file_release, - .fsync = nfs_file_fsync, - .lock = nfs_lock, - .flock = nfs_flock, - .splice_read = nfs_file_splice_read, - .splice_write = nfs_file_splice_write, - .check_flags = nfs_check_flags, - .setlease = nfs_setlease, -}; - -#ifdef CONFIG_NFS_V4 -static int -nfs4_file_open(struct inode *inode, struct file *filp) -{ - /* - * NFSv4 opens are handled in d_lookup and d_revalidate. If we get to - * this point, then something is very wrong - */ - dprintk("NFS: %s called! inode=%p filp=%p\n", __func__, inode, filp); - return -ENOTDIR; -} - -const struct file_operations nfs4_file_operations = { - .llseek = nfs_file_llseek, - .read = do_sync_read, - .write = do_sync_write, - .aio_read = nfs_file_read, - .aio_write = nfs_file_write, - .mmap = nfs_file_mmap, - .open = nfs4_file_open, - .flush = nfs_file_flush, - .release = nfs_file_release, - .fsync = nfs_file_fsync, - .lock = nfs_lock, - .flock = nfs_flock, - .splice_read = nfs_file_splice_read, - .splice_write = nfs_file_splice_write, - .check_flags = nfs_check_flags, - .setlease = nfs_setlease, -}; -#endif /* CONFIG_NFS_V4 */ diff --git a/ANDROID_3.4.5/fs/nfs/fscache-index.c b/ANDROID_3.4.5/fs/nfs/fscache-index.c deleted file mode 100644 index 7cf2c469..00000000 --- a/ANDROID_3.4.5/fs/nfs/fscache-index.c +++ /dev/null @@ -1,337 +0,0 @@ -/* NFS FS-Cache index structure definition - * - * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public Licence - * as published by the Free Software Foundation; either version - * 2 of the Licence, or (at your option) any later version. - */ - -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_fs_sb.h> -#include <linux/in6.h> - -#include "internal.h" -#include "fscache.h" - -#define NFSDBG_FACILITY NFSDBG_FSCACHE - -/* - * Define the NFS filesystem for FS-Cache. Upon registration FS-Cache sticks - * the cookie for the top-level index object for NFS into here. The top-level - * index can than have other cache objects inserted into it. - */ -struct fscache_netfs nfs_fscache_netfs = { - .name = "nfs", - .version = 0, -}; - -/* - * Register NFS for caching - */ -int nfs_fscache_register(void) -{ - return fscache_register_netfs(&nfs_fscache_netfs); -} - -/* - * Unregister NFS for caching - */ -void nfs_fscache_unregister(void) -{ - fscache_unregister_netfs(&nfs_fscache_netfs); -} - -/* - * Layout of the key for an NFS server cache object. - */ -struct nfs_server_key { - uint16_t nfsversion; /* NFS protocol version */ - uint16_t family; /* address family */ - uint16_t port; /* IP port */ - union { - struct in_addr ipv4_addr; /* IPv4 address */ - struct in6_addr ipv6_addr; /* IPv6 address */ - } addr[0]; -}; - -/* - * Generate a key to describe a server in the main NFS index - * - We return the length of the key, or 0 if we can't generate one - */ -static uint16_t nfs_server_get_key(const void *cookie_netfs_data, - void *buffer, uint16_t bufmax) -{ - const struct nfs_client *clp = cookie_netfs_data; - const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &clp->cl_addr; - const struct sockaddr_in *sin = (struct sockaddr_in *) &clp->cl_addr; - struct nfs_server_key *key = buffer; - uint16_t len = sizeof(struct nfs_server_key); - - key->nfsversion = clp->rpc_ops->version; - key->family = clp->cl_addr.ss_family; - - memset(key, 0, len); - - switch (clp->cl_addr.ss_family) { - case AF_INET: - key->port = sin->sin_port; - key->addr[0].ipv4_addr = sin->sin_addr; - len += sizeof(key->addr[0].ipv4_addr); - break; - - case AF_INET6: - key->port = sin6->sin6_port; - key->addr[0].ipv6_addr = sin6->sin6_addr; - len += sizeof(key->addr[0].ipv6_addr); - break; - - default: - printk(KERN_WARNING "NFS: Unknown network family '%d'\n", - clp->cl_addr.ss_family); - len = 0; - break; - } - - return len; -} - -/* - * Define the server object for FS-Cache. This is used to describe a server - * object to fscache_acquire_cookie(). It is keyed by the NFS protocol and - * server address parameters. - */ -const struct fscache_cookie_def nfs_fscache_server_index_def = { - .name = "NFS.server", - .type = FSCACHE_COOKIE_TYPE_INDEX, - .get_key = nfs_server_get_key, -}; - -/* - * Generate a key to describe a superblock key in the main NFS index - */ -static uint16_t nfs_super_get_key(const void *cookie_netfs_data, - void *buffer, uint16_t bufmax) -{ - const struct nfs_fscache_key *key; - const struct nfs_server *nfss = cookie_netfs_data; - uint16_t len; - - key = nfss->fscache_key; - len = sizeof(key->key) + key->key.uniq_len; - if (len > bufmax) { - len = 0; - } else { - memcpy(buffer, &key->key, sizeof(key->key)); - memcpy(buffer + sizeof(key->key), - key->key.uniquifier, key->key.uniq_len); - } - - return len; -} - -/* - * Define the superblock object for FS-Cache. This is used to describe a - * superblock object to fscache_acquire_cookie(). It is keyed by all the NFS - * parameters that might cause a separate superblock. - */ -const struct fscache_cookie_def nfs_fscache_super_index_def = { - .name = "NFS.super", - .type = FSCACHE_COOKIE_TYPE_INDEX, - .get_key = nfs_super_get_key, -}; - -/* - * Definition of the auxiliary data attached to NFS inode storage objects - * within the cache. - * - * The contents of this struct are recorded in the on-disk local cache in the - * auxiliary data attached to the data storage object backing an inode. This - * permits coherency to be managed when a new inode binds to an already extant - * cache object. - */ -struct nfs_fscache_inode_auxdata { - struct timespec mtime; - struct timespec ctime; - loff_t size; - u64 change_attr; -}; - -/* - * Generate a key to describe an NFS inode in an NFS server's index - */ -static uint16_t nfs_fscache_inode_get_key(const void *cookie_netfs_data, - void *buffer, uint16_t bufmax) -{ - const struct nfs_inode *nfsi = cookie_netfs_data; - uint16_t nsize; - - /* use the inode's NFS filehandle as the key */ - nsize = nfsi->fh.size; - memcpy(buffer, nfsi->fh.data, nsize); - return nsize; -} - -/* - * Get certain file attributes from the netfs data - * - This function can be absent for an index - * - Not permitted to return an error - * - The netfs data from the cookie being used as the source is presented - */ -static void nfs_fscache_inode_get_attr(const void *cookie_netfs_data, - uint64_t *size) -{ - const struct nfs_inode *nfsi = cookie_netfs_data; - - *size = nfsi->vfs_inode.i_size; -} - -/* - * Get the auxiliary data from netfs data - * - This function can be absent if the index carries no state data - * - Should store the auxiliary data in the buffer - * - Should return the amount of amount stored - * - Not permitted to return an error - * - The netfs data from the cookie being used as the source is presented - */ -static uint16_t nfs_fscache_inode_get_aux(const void *cookie_netfs_data, - void *buffer, uint16_t bufmax) -{ - struct nfs_fscache_inode_auxdata auxdata; - const struct nfs_inode *nfsi = cookie_netfs_data; - - memset(&auxdata, 0, sizeof(auxdata)); - auxdata.size = nfsi->vfs_inode.i_size; - auxdata.mtime = nfsi->vfs_inode.i_mtime; - auxdata.ctime = nfsi->vfs_inode.i_ctime; - - if (NFS_SERVER(&nfsi->vfs_inode)->nfs_client->rpc_ops->version == 4) - auxdata.change_attr = nfsi->vfs_inode.i_version; - - if (bufmax > sizeof(auxdata)) - bufmax = sizeof(auxdata); - - memcpy(buffer, &auxdata, bufmax); - return bufmax; -} - -/* - * Consult the netfs about the state of an object - * - This function can be absent if the index carries no state data - * - The netfs data from the cookie being used as the target is - * presented, as is the auxiliary data - */ -static -enum fscache_checkaux nfs_fscache_inode_check_aux(void *cookie_netfs_data, - const void *data, - uint16_t datalen) -{ - struct nfs_fscache_inode_auxdata auxdata; - struct nfs_inode *nfsi = cookie_netfs_data; - - if (datalen != sizeof(auxdata)) - return FSCACHE_CHECKAUX_OBSOLETE; - - memset(&auxdata, 0, sizeof(auxdata)); - auxdata.size = nfsi->vfs_inode.i_size; - auxdata.mtime = nfsi->vfs_inode.i_mtime; - auxdata.ctime = nfsi->vfs_inode.i_ctime; - - if (NFS_SERVER(&nfsi->vfs_inode)->nfs_client->rpc_ops->version == 4) - auxdata.change_attr = nfsi->vfs_inode.i_version; - - if (memcmp(data, &auxdata, datalen) != 0) - return FSCACHE_CHECKAUX_OBSOLETE; - - return FSCACHE_CHECKAUX_OKAY; -} - -/* - * Indication from FS-Cache that the cookie is no longer cached - * - This function is called when the backing store currently caching a cookie - * is removed - * - The netfs should use this to clean up any markers indicating cached pages - * - This is mandatory for any object that may have data - */ -static void nfs_fscache_inode_now_uncached(void *cookie_netfs_data) -{ - struct nfs_inode *nfsi = cookie_netfs_data; - struct pagevec pvec; - pgoff_t first; - int loop, nr_pages; - - pagevec_init(&pvec, 0); - first = 0; - - dprintk("NFS: nfs_inode_now_uncached: nfs_inode 0x%p\n", nfsi); - - for (;;) { - /* grab a bunch of pages to unmark */ - nr_pages = pagevec_lookup(&pvec, - nfsi->vfs_inode.i_mapping, - first, - PAGEVEC_SIZE - pagevec_count(&pvec)); - if (!nr_pages) - break; - - for (loop = 0; loop < nr_pages; loop++) - ClearPageFsCache(pvec.pages[loop]); - - first = pvec.pages[nr_pages - 1]->index + 1; - - pvec.nr = nr_pages; - pagevec_release(&pvec); - cond_resched(); - } -} - -/* - * Get an extra reference on a read context. - * - This function can be absent if the completion function doesn't require a - * context. - * - The read context is passed back to NFS in the event that a data read on the - * cache fails with EIO - in which case the server must be contacted to - * retrieve the data, which requires the read context for security. - */ -static void nfs_fh_get_context(void *cookie_netfs_data, void *context) -{ - get_nfs_open_context(context); -} - -/* - * Release an extra reference on a read context. - * - This function can be absent if the completion function doesn't require a - * context. - */ -static void nfs_fh_put_context(void *cookie_netfs_data, void *context) -{ - if (context) - put_nfs_open_context(context); -} - -/* - * Define the inode object for FS-Cache. This is used to describe an inode - * object to fscache_acquire_cookie(). It is keyed by the NFS file handle for - * an inode. - * - * Coherency is managed by comparing the copies of i_size, i_mtime and i_ctime - * held in the cache auxiliary data for the data storage object with those in - * the inode struct in memory. - */ -const struct fscache_cookie_def nfs_fscache_inode_object_def = { - .name = "NFS.fh", - .type = FSCACHE_COOKIE_TYPE_DATAFILE, - .get_key = nfs_fscache_inode_get_key, - .get_attr = nfs_fscache_inode_get_attr, - .get_aux = nfs_fscache_inode_get_aux, - .check_aux = nfs_fscache_inode_check_aux, - .now_uncached = nfs_fscache_inode_now_uncached, - .get_context = nfs_fh_get_context, - .put_context = nfs_fh_put_context, -}; diff --git a/ANDROID_3.4.5/fs/nfs/fscache.c b/ANDROID_3.4.5/fs/nfs/fscache.c deleted file mode 100644 index ae65c16b..00000000 --- a/ANDROID_3.4.5/fs/nfs/fscache.c +++ /dev/null @@ -1,535 +0,0 @@ -/* NFS filesystem cache interface - * - * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public Licence - * as published by the Free Software Foundation; either version - * 2 of the Licence, or (at your option) any later version. - */ - -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_fs_sb.h> -#include <linux/in6.h> -#include <linux/seq_file.h> -#include <linux/slab.h> - -#include "internal.h" -#include "iostat.h" -#include "fscache.h" - -#define NFSDBG_FACILITY NFSDBG_FSCACHE - -static struct rb_root nfs_fscache_keys = RB_ROOT; -static DEFINE_SPINLOCK(nfs_fscache_keys_lock); - -/* - * Get the per-client index cookie for an NFS client if the appropriate mount - * flag was set - * - We always try and get an index cookie for the client, but get filehandle - * cookies on a per-superblock basis, depending on the mount flags - */ -void nfs_fscache_get_client_cookie(struct nfs_client *clp) -{ - /* create a cache index for looking up filehandles */ - clp->fscache = fscache_acquire_cookie(nfs_fscache_netfs.primary_index, - &nfs_fscache_server_index_def, - clp); - dfprintk(FSCACHE, "NFS: get client cookie (0x%p/0x%p)\n", - clp, clp->fscache); -} - -/* - * Dispose of a per-client cookie - */ -void nfs_fscache_release_client_cookie(struct nfs_client *clp) -{ - dfprintk(FSCACHE, "NFS: releasing client cookie (0x%p/0x%p)\n", - clp, clp->fscache); - - fscache_relinquish_cookie(clp->fscache, 0); - clp->fscache = NULL; -} - -/* - * Get the cache cookie for an NFS superblock. We have to handle - * uniquification here because the cache doesn't do it for us. - * - * The default uniquifier is just an empty string, but it may be overridden - * either by the 'fsc=xxx' option to mount, or by inheriting it from the parent - * superblock across an automount point of some nature. - */ -void nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, - struct nfs_clone_mount *mntdata) -{ - struct nfs_fscache_key *key, *xkey; - struct nfs_server *nfss = NFS_SB(sb); - struct rb_node **p, *parent; - int diff, ulen; - - if (uniq) { - ulen = strlen(uniq); - } else if (mntdata) { - struct nfs_server *mnt_s = NFS_SB(mntdata->sb); - if (mnt_s->fscache_key) { - uniq = mnt_s->fscache_key->key.uniquifier; - ulen = mnt_s->fscache_key->key.uniq_len; - } - } - - if (!uniq) { - uniq = ""; - ulen = 1; - } - - key = kzalloc(sizeof(*key) + ulen, GFP_KERNEL); - if (!key) - return; - - key->nfs_client = nfss->nfs_client; - key->key.super.s_flags = sb->s_flags & NFS_MS_MASK; - key->key.nfs_server.flags = nfss->flags; - key->key.nfs_server.rsize = nfss->rsize; - key->key.nfs_server.wsize = nfss->wsize; - key->key.nfs_server.acregmin = nfss->acregmin; - key->key.nfs_server.acregmax = nfss->acregmax; - key->key.nfs_server.acdirmin = nfss->acdirmin; - key->key.nfs_server.acdirmax = nfss->acdirmax; - key->key.nfs_server.fsid = nfss->fsid; - key->key.rpc_auth.au_flavor = nfss->client->cl_auth->au_flavor; - - key->key.uniq_len = ulen; - memcpy(key->key.uniquifier, uniq, ulen); - - spin_lock(&nfs_fscache_keys_lock); - p = &nfs_fscache_keys.rb_node; - parent = NULL; - while (*p) { - parent = *p; - xkey = rb_entry(parent, struct nfs_fscache_key, node); - - if (key->nfs_client < xkey->nfs_client) - goto go_left; - if (key->nfs_client > xkey->nfs_client) - goto go_right; - - diff = memcmp(&key->key, &xkey->key, sizeof(key->key)); - if (diff < 0) - goto go_left; - if (diff > 0) - goto go_right; - - if (key->key.uniq_len == 0) - goto non_unique; - diff = memcmp(key->key.uniquifier, - xkey->key.uniquifier, - key->key.uniq_len); - if (diff < 0) - goto go_left; - if (diff > 0) - goto go_right; - goto non_unique; - - go_left: - p = &(*p)->rb_left; - continue; - go_right: - p = &(*p)->rb_right; - } - - rb_link_node(&key->node, parent, p); - rb_insert_color(&key->node, &nfs_fscache_keys); - spin_unlock(&nfs_fscache_keys_lock); - nfss->fscache_key = key; - - /* create a cache index for looking up filehandles */ - nfss->fscache = fscache_acquire_cookie(nfss->nfs_client->fscache, - &nfs_fscache_super_index_def, - nfss); - dfprintk(FSCACHE, "NFS: get superblock cookie (0x%p/0x%p)\n", - nfss, nfss->fscache); - return; - -non_unique: - spin_unlock(&nfs_fscache_keys_lock); - kfree(key); - nfss->fscache_key = NULL; - nfss->fscache = NULL; - printk(KERN_WARNING "NFS:" - " Cache request denied due to non-unique superblock keys\n"); -} - -/* - * release a per-superblock cookie - */ -void nfs_fscache_release_super_cookie(struct super_block *sb) -{ - struct nfs_server *nfss = NFS_SB(sb); - - dfprintk(FSCACHE, "NFS: releasing superblock cookie (0x%p/0x%p)\n", - nfss, nfss->fscache); - - fscache_relinquish_cookie(nfss->fscache, 0); - nfss->fscache = NULL; - - if (nfss->fscache_key) { - spin_lock(&nfs_fscache_keys_lock); - rb_erase(&nfss->fscache_key->node, &nfs_fscache_keys); - spin_unlock(&nfs_fscache_keys_lock); - kfree(nfss->fscache_key); - nfss->fscache_key = NULL; - } -} - -/* - * Initialise the per-inode cache cookie pointer for an NFS inode. - */ -void nfs_fscache_init_inode_cookie(struct inode *inode) -{ - NFS_I(inode)->fscache = NULL; - if (S_ISREG(inode->i_mode)) - set_bit(NFS_INO_FSCACHE, &NFS_I(inode)->flags); -} - -/* - * Get the per-inode cache cookie for an NFS inode. - */ -static void nfs_fscache_enable_inode_cookie(struct inode *inode) -{ - struct super_block *sb = inode->i_sb; - struct nfs_inode *nfsi = NFS_I(inode); - - if (nfsi->fscache || !NFS_FSCACHE(inode)) - return; - - if ((NFS_SB(sb)->options & NFS_OPTION_FSCACHE)) { - nfsi->fscache = fscache_acquire_cookie( - NFS_SB(sb)->fscache, - &nfs_fscache_inode_object_def, - nfsi); - - dfprintk(FSCACHE, "NFS: get FH cookie (0x%p/0x%p/0x%p)\n", - sb, nfsi, nfsi->fscache); - } -} - -/* - * Release a per-inode cookie. - */ -void nfs_fscache_release_inode_cookie(struct inode *inode) -{ - struct nfs_inode *nfsi = NFS_I(inode); - - dfprintk(FSCACHE, "NFS: clear cookie (0x%p/0x%p)\n", - nfsi, nfsi->fscache); - - fscache_relinquish_cookie(nfsi->fscache, 0); - nfsi->fscache = NULL; -} - -/* - * Retire a per-inode cookie, destroying the data attached to it. - */ -void nfs_fscache_zap_inode_cookie(struct inode *inode) -{ - struct nfs_inode *nfsi = NFS_I(inode); - - dfprintk(FSCACHE, "NFS: zapping cookie (0x%p/0x%p)\n", - nfsi, nfsi->fscache); - - fscache_relinquish_cookie(nfsi->fscache, 1); - nfsi->fscache = NULL; -} - -/* - * Turn off the cache with regard to a per-inode cookie if opened for writing, - * invalidating all the pages in the page cache relating to the associated - * inode to clear the per-page caching. - */ -static void nfs_fscache_disable_inode_cookie(struct inode *inode) -{ - clear_bit(NFS_INO_FSCACHE, &NFS_I(inode)->flags); - - if (NFS_I(inode)->fscache) { - dfprintk(FSCACHE, - "NFS: nfsi 0x%p turning cache off\n", NFS_I(inode)); - - /* Need to uncache any pages attached to this inode that - * fscache knows about before turning off the cache. - */ - fscache_uncache_all_inode_pages(NFS_I(inode)->fscache, inode); - nfs_fscache_zap_inode_cookie(inode); - } -} - -/* - * wait_on_bit() sleep function for uninterruptible waiting - */ -static int nfs_fscache_wait_bit(void *flags) -{ - schedule(); - return 0; -} - -/* - * Lock against someone else trying to also acquire or relinquish a cookie - */ -static inline void nfs_fscache_inode_lock(struct inode *inode) -{ - struct nfs_inode *nfsi = NFS_I(inode); - - while (test_and_set_bit(NFS_INO_FSCACHE_LOCK, &nfsi->flags)) - wait_on_bit(&nfsi->flags, NFS_INO_FSCACHE_LOCK, - nfs_fscache_wait_bit, TASK_UNINTERRUPTIBLE); -} - -/* - * Unlock cookie management lock - */ -static inline void nfs_fscache_inode_unlock(struct inode *inode) -{ - struct nfs_inode *nfsi = NFS_I(inode); - - smp_mb__before_clear_bit(); - clear_bit(NFS_INO_FSCACHE_LOCK, &nfsi->flags); - smp_mb__after_clear_bit(); - wake_up_bit(&nfsi->flags, NFS_INO_FSCACHE_LOCK); -} - -/* - * Decide if we should enable or disable local caching for this inode. - * - For now, with NFS, only regular files that are open read-only will be able - * to use the cache. - * - May be invoked multiple times in parallel by parallel nfs_open() functions. - */ -void nfs_fscache_set_inode_cookie(struct inode *inode, struct file *filp) -{ - if (NFS_FSCACHE(inode)) { - nfs_fscache_inode_lock(inode); - if ((filp->f_flags & O_ACCMODE) != O_RDONLY) - nfs_fscache_disable_inode_cookie(inode); - else - nfs_fscache_enable_inode_cookie(inode); - nfs_fscache_inode_unlock(inode); - } -} - -/* - * Replace a per-inode cookie due to revalidation detecting a file having - * changed on the server. - */ -void nfs_fscache_reset_inode_cookie(struct inode *inode) -{ - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs_server *nfss = NFS_SERVER(inode); - NFS_IFDEBUG(struct fscache_cookie *old = nfsi->fscache); - - nfs_fscache_inode_lock(inode); - if (nfsi->fscache) { - /* retire the current fscache cache and get a new one */ - fscache_relinquish_cookie(nfsi->fscache, 1); - - nfsi->fscache = fscache_acquire_cookie( - nfss->nfs_client->fscache, - &nfs_fscache_inode_object_def, - nfsi); - - dfprintk(FSCACHE, - "NFS: revalidation new cookie (0x%p/0x%p/0x%p/0x%p)\n", - nfss, nfsi, old, nfsi->fscache); - } - nfs_fscache_inode_unlock(inode); -} - -/* - * Release the caching state associated with a page, if the page isn't busy - * interacting with the cache. - * - Returns true (can release page) or false (page busy). - */ -int nfs_fscache_release_page(struct page *page, gfp_t gfp) -{ - if (PageFsCache(page)) { - struct nfs_inode *nfsi = NFS_I(page->mapping->host); - struct fscache_cookie *cookie = nfsi->fscache; - - BUG_ON(!cookie); - dfprintk(FSCACHE, "NFS: fscache releasepage (0x%p/0x%p/0x%p)\n", - cookie, page, nfsi); - - if (!fscache_maybe_release_page(cookie, page, gfp)) - return 0; - - nfs_add_fscache_stats(page->mapping->host, - NFSIOS_FSCACHE_PAGES_UNCACHED, 1); - } - - return 1; -} - -/* - * Release the caching state associated with a page if undergoing complete page - * invalidation. - */ -void __nfs_fscache_invalidate_page(struct page *page, struct inode *inode) -{ - struct nfs_inode *nfsi = NFS_I(inode); - struct fscache_cookie *cookie = nfsi->fscache; - - BUG_ON(!cookie); - - dfprintk(FSCACHE, "NFS: fscache invalidatepage (0x%p/0x%p/0x%p)\n", - cookie, page, nfsi); - - fscache_wait_on_page_write(cookie, page); - - BUG_ON(!PageLocked(page)); - fscache_uncache_page(cookie, page); - nfs_add_fscache_stats(page->mapping->host, - NFSIOS_FSCACHE_PAGES_UNCACHED, 1); -} - -/* - * Handle completion of a page being read from the cache. - * - Called in process (keventd) context. - */ -static void nfs_readpage_from_fscache_complete(struct page *page, - void *context, - int error) -{ - dfprintk(FSCACHE, - "NFS: readpage_from_fscache_complete (0x%p/0x%p/%d)\n", - page, context, error); - - /* if the read completes with an error, we just unlock the page and let - * the VM reissue the readpage */ - if (!error) { - SetPageUptodate(page); - unlock_page(page); - } else { - error = nfs_readpage_async(context, page->mapping->host, page); - if (error) - unlock_page(page); - } -} - -/* - * Retrieve a page from fscache - */ -int __nfs_readpage_from_fscache(struct nfs_open_context *ctx, - struct inode *inode, struct page *page) -{ - int ret; - - dfprintk(FSCACHE, - "NFS: readpage_from_fscache(fsc:%p/p:%p(i:%lx f:%lx)/0x%p)\n", - NFS_I(inode)->fscache, page, page->index, page->flags, inode); - - ret = fscache_read_or_alloc_page(NFS_I(inode)->fscache, - page, - nfs_readpage_from_fscache_complete, - ctx, - GFP_KERNEL); - - switch (ret) { - case 0: /* read BIO submitted (page in fscache) */ - dfprintk(FSCACHE, - "NFS: readpage_from_fscache: BIO submitted\n"); - nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_OK, 1); - return ret; - - case -ENOBUFS: /* inode not in cache */ - case -ENODATA: /* page not in cache */ - nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL, 1); - dfprintk(FSCACHE, - "NFS: readpage_from_fscache %d\n", ret); - return 1; - - default: - dfprintk(FSCACHE, "NFS: readpage_from_fscache %d\n", ret); - nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL, 1); - } - return ret; -} - -/* - * Retrieve a set of pages from fscache - */ -int __nfs_readpages_from_fscache(struct nfs_open_context *ctx, - struct inode *inode, - struct address_space *mapping, - struct list_head *pages, - unsigned *nr_pages) -{ - unsigned npages = *nr_pages; - int ret; - - dfprintk(FSCACHE, "NFS: nfs_getpages_from_fscache (0x%p/%u/0x%p)\n", - NFS_I(inode)->fscache, npages, inode); - - ret = fscache_read_or_alloc_pages(NFS_I(inode)->fscache, - mapping, pages, nr_pages, - nfs_readpage_from_fscache_complete, - ctx, - mapping_gfp_mask(mapping)); - if (*nr_pages < npages) - nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_OK, - npages); - if (*nr_pages > 0) - nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL, - *nr_pages); - - switch (ret) { - case 0: /* read submitted to the cache for all pages */ - BUG_ON(!list_empty(pages)); - BUG_ON(*nr_pages != 0); - dfprintk(FSCACHE, - "NFS: nfs_getpages_from_fscache: submitted\n"); - - return ret; - - case -ENOBUFS: /* some pages aren't cached and can't be */ - case -ENODATA: /* some pages aren't cached */ - dfprintk(FSCACHE, - "NFS: nfs_getpages_from_fscache: no page: %d\n", ret); - return 1; - - default: - dfprintk(FSCACHE, - "NFS: nfs_getpages_from_fscache: ret %d\n", ret); - } - - return ret; -} - -/* - * Store a newly fetched page in fscache - * - PG_fscache must be set on the page - */ -void __nfs_readpage_to_fscache(struct inode *inode, struct page *page, int sync) -{ - int ret; - - dfprintk(FSCACHE, - "NFS: readpage_to_fscache(fsc:%p/p:%p(i:%lx f:%lx)/%d)\n", - NFS_I(inode)->fscache, page, page->index, page->flags, sync); - - ret = fscache_write_page(NFS_I(inode)->fscache, page, GFP_KERNEL); - dfprintk(FSCACHE, - "NFS: readpage_to_fscache: p:%p(i:%lu f:%lx) ret %d\n", - page, page->index, page->flags, ret); - - if (ret != 0) { - fscache_uncache_page(NFS_I(inode)->fscache, page); - nfs_add_fscache_stats(inode, - NFSIOS_FSCACHE_PAGES_WRITTEN_FAIL, 1); - nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_UNCACHED, 1); - } else { - nfs_add_fscache_stats(inode, - NFSIOS_FSCACHE_PAGES_WRITTEN_OK, 1); - } -} diff --git a/ANDROID_3.4.5/fs/nfs/fscache.h b/ANDROID_3.4.5/fs/nfs/fscache.h deleted file mode 100644 index b9c572d0..00000000 --- a/ANDROID_3.4.5/fs/nfs/fscache.h +++ /dev/null @@ -1,222 +0,0 @@ -/* NFS filesystem cache interface definitions - * - * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public Licence - * as published by the Free Software Foundation; either version - * 2 of the Licence, or (at your option) any later version. - */ - -#ifndef _NFS_FSCACHE_H -#define _NFS_FSCACHE_H - -#include <linux/nfs_fs.h> -#include <linux/nfs_mount.h> -#include <linux/nfs4_mount.h> -#include <linux/fscache.h> - -#ifdef CONFIG_NFS_FSCACHE - -/* - * set of NFS FS-Cache objects that form a superblock key - */ -struct nfs_fscache_key { - struct rb_node node; - struct nfs_client *nfs_client; /* the server */ - - /* the elements of the unique key - as used by nfs_compare_super() and - * nfs_compare_mount_options() to distinguish superblocks */ - struct { - struct { - unsigned long s_flags; /* various flags - * (& NFS_MS_MASK) */ - } super; - - struct { - struct nfs_fsid fsid; - int flags; - unsigned int rsize; /* read size */ - unsigned int wsize; /* write size */ - unsigned int acregmin; /* attr cache timeouts */ - unsigned int acregmax; - unsigned int acdirmin; - unsigned int acdirmax; - } nfs_server; - - struct { - rpc_authflavor_t au_flavor; - } rpc_auth; - - /* uniquifier - can be used if nfs_server.flags includes - * NFS_MOUNT_UNSHARED */ - u8 uniq_len; - char uniquifier[0]; - } key; -}; - -/* - * fscache-index.c - */ -extern struct fscache_netfs nfs_fscache_netfs; -extern const struct fscache_cookie_def nfs_fscache_server_index_def; -extern const struct fscache_cookie_def nfs_fscache_super_index_def; -extern const struct fscache_cookie_def nfs_fscache_inode_object_def; - -extern int nfs_fscache_register(void); -extern void nfs_fscache_unregister(void); - -/* - * fscache.c - */ -extern void nfs_fscache_get_client_cookie(struct nfs_client *); -extern void nfs_fscache_release_client_cookie(struct nfs_client *); - -extern void nfs_fscache_get_super_cookie(struct super_block *, - const char *, - struct nfs_clone_mount *); -extern void nfs_fscache_release_super_cookie(struct super_block *); - -extern void nfs_fscache_init_inode_cookie(struct inode *); -extern void nfs_fscache_release_inode_cookie(struct inode *); -extern void nfs_fscache_zap_inode_cookie(struct inode *); -extern void nfs_fscache_set_inode_cookie(struct inode *, struct file *); -extern void nfs_fscache_reset_inode_cookie(struct inode *); - -extern void __nfs_fscache_invalidate_page(struct page *, struct inode *); -extern int nfs_fscache_release_page(struct page *, gfp_t); - -extern int __nfs_readpage_from_fscache(struct nfs_open_context *, - struct inode *, struct page *); -extern int __nfs_readpages_from_fscache(struct nfs_open_context *, - struct inode *, struct address_space *, - struct list_head *, unsigned *); -extern void __nfs_readpage_to_fscache(struct inode *, struct page *, int); - -/* - * wait for a page to complete writing to the cache - */ -static inline void nfs_fscache_wait_on_page_write(struct nfs_inode *nfsi, - struct page *page) -{ - if (PageFsCache(page)) - fscache_wait_on_page_write(nfsi->fscache, page); -} - -/* - * release the caching state associated with a page if undergoing complete page - * invalidation - */ -static inline void nfs_fscache_invalidate_page(struct page *page, - struct inode *inode) -{ - if (PageFsCache(page)) - __nfs_fscache_invalidate_page(page, inode); -} - -/* - * Retrieve a page from an inode data storage object. - */ -static inline int nfs_readpage_from_fscache(struct nfs_open_context *ctx, - struct inode *inode, - struct page *page) -{ - if (NFS_I(inode)->fscache) - return __nfs_readpage_from_fscache(ctx, inode, page); - return -ENOBUFS; -} - -/* - * Retrieve a set of pages from an inode data storage object. - */ -static inline int nfs_readpages_from_fscache(struct nfs_open_context *ctx, - struct inode *inode, - struct address_space *mapping, - struct list_head *pages, - unsigned *nr_pages) -{ - if (NFS_I(inode)->fscache) - return __nfs_readpages_from_fscache(ctx, inode, mapping, pages, - nr_pages); - return -ENOBUFS; -} - -/* - * Store a page newly fetched from the server in an inode data storage object - * in the cache. - */ -static inline void nfs_readpage_to_fscache(struct inode *inode, - struct page *page, - int sync) -{ - if (PageFsCache(page)) - __nfs_readpage_to_fscache(inode, page, sync); -} - -/* - * indicate the client caching state as readable text - */ -static inline const char *nfs_server_fscache_state(struct nfs_server *server) -{ - if (server->fscache && (server->options & NFS_OPTION_FSCACHE)) - return "yes"; - return "no "; -} - - -#else /* CONFIG_NFS_FSCACHE */ -static inline int nfs_fscache_register(void) { return 0; } -static inline void nfs_fscache_unregister(void) {} - -static inline void nfs_fscache_get_client_cookie(struct nfs_client *clp) {} -static inline void nfs_fscache_release_client_cookie(struct nfs_client *clp) {} - -static inline void nfs_fscache_get_super_cookie( - struct super_block *sb, - const char *uniq, - struct nfs_clone_mount *mntdata) -{ -} -static inline void nfs_fscache_release_super_cookie(struct super_block *sb) {} - -static inline void nfs_fscache_init_inode_cookie(struct inode *inode) {} -static inline void nfs_fscache_release_inode_cookie(struct inode *inode) {} -static inline void nfs_fscache_zap_inode_cookie(struct inode *inode) {} -static inline void nfs_fscache_set_inode_cookie(struct inode *inode, - struct file *filp) {} -static inline void nfs_fscache_reset_inode_cookie(struct inode *inode) {} - -static inline int nfs_fscache_release_page(struct page *page, gfp_t gfp) -{ - return 1; /* True: may release page */ -} -static inline void nfs_fscache_invalidate_page(struct page *page, - struct inode *inode) {} -static inline void nfs_fscache_wait_on_page_write(struct nfs_inode *nfsi, - struct page *page) {} - -static inline int nfs_readpage_from_fscache(struct nfs_open_context *ctx, - struct inode *inode, - struct page *page) -{ - return -ENOBUFS; -} -static inline int nfs_readpages_from_fscache(struct nfs_open_context *ctx, - struct inode *inode, - struct address_space *mapping, - struct list_head *pages, - unsigned *nr_pages) -{ - return -ENOBUFS; -} -static inline void nfs_readpage_to_fscache(struct inode *inode, - struct page *page, int sync) {} - -static inline const char *nfs_server_fscache_state(struct nfs_server *server) -{ - return "no "; -} - -#endif /* CONFIG_NFS_FSCACHE */ -#endif /* _NFS_FSCACHE_H */ diff --git a/ANDROID_3.4.5/fs/nfs/getroot.c b/ANDROID_3.4.5/fs/nfs/getroot.c deleted file mode 100644 index 4ca6f5c8..00000000 --- a/ANDROID_3.4.5/fs/nfs/getroot.c +++ /dev/null @@ -1,264 +0,0 @@ -/* getroot.c: get the root dentry for an NFS mount - * - * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include <linux/module.h> -#include <linux/init.h> - -#include <linux/time.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/string.h> -#include <linux/stat.h> -#include <linux/errno.h> -#include <linux/unistd.h> -#include <linux/sunrpc/clnt.h> -#include <linux/sunrpc/stats.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_mount.h> -#include <linux/nfs4_mount.h> -#include <linux/lockd/bind.h> -#include <linux/seq_file.h> -#include <linux/mount.h> -#include <linux/nfs_idmap.h> -#include <linux/vfs.h> -#include <linux/namei.h> -#include <linux/security.h> - -#include <asm/uaccess.h> - -#include "nfs4_fs.h" -#include "delegation.h" -#include "internal.h" - -#define NFSDBG_FACILITY NFSDBG_CLIENT - -/* - * Set the superblock root dentry. - * Note that this function frees the inode in case of error. - */ -static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *inode) -{ - /* The mntroot acts as the dummy root dentry for this superblock */ - if (sb->s_root == NULL) { - sb->s_root = d_make_root(inode); - if (sb->s_root == NULL) - return -ENOMEM; - ihold(inode); - /* - * Ensure that this dentry is invisible to d_find_alias(). - * Otherwise, it may be spliced into the tree by - * d_materialise_unique if a parent directory from the same - * filesystem gets mounted at a later time. - * This again causes shrink_dcache_for_umount_subtree() to - * Oops, since the test for IS_ROOT() will fail. - */ - spin_lock(&sb->s_root->d_inode->i_lock); - spin_lock(&sb->s_root->d_lock); - list_del_init(&sb->s_root->d_alias); - spin_unlock(&sb->s_root->d_lock); - spin_unlock(&sb->s_root->d_inode->i_lock); - } - return 0; -} - -/* - * get an NFS2/NFS3 root dentry from the root filehandle - */ -struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh, - const char *devname) -{ - struct nfs_server *server = NFS_SB(sb); - struct nfs_fsinfo fsinfo; - struct dentry *ret; - struct inode *inode; - void *name = kstrdup(devname, GFP_KERNEL); - int error; - - if (!name) - return ERR_PTR(-ENOMEM); - - /* get the actual root for this mount */ - fsinfo.fattr = nfs_alloc_fattr(); - if (fsinfo.fattr == NULL) { - kfree(name); - return ERR_PTR(-ENOMEM); - } - - error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); - if (error < 0) { - dprintk("nfs_get_root: getattr error = %d\n", -error); - ret = ERR_PTR(error); - goto out; - } - - inode = nfs_fhget(sb, mntfh, fsinfo.fattr); - if (IS_ERR(inode)) { - dprintk("nfs_get_root: get root inode failed\n"); - ret = ERR_CAST(inode); - goto out; - } - - error = nfs_superblock_set_dummy_root(sb, inode); - if (error != 0) { - ret = ERR_PTR(error); - goto out; - } - - /* root dentries normally start off anonymous and get spliced in later - * if the dentry tree reaches them; however if the dentry already - * exists, we'll pick it up at this point and use it as the root - */ - ret = d_obtain_alias(inode); - if (IS_ERR(ret)) { - dprintk("nfs_get_root: get root dentry failed\n"); - goto out; - } - - security_d_instantiate(ret, inode); - spin_lock(&ret->d_lock); - if (IS_ROOT(ret) && !(ret->d_flags & DCACHE_NFSFS_RENAMED)) { - ret->d_fsdata = name; - name = NULL; - } - spin_unlock(&ret->d_lock); -out: - if (name) - kfree(name); - nfs_free_fattr(fsinfo.fattr); - return ret; -} - -#ifdef CONFIG_NFS_V4 - -int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh) -{ - struct nfs_fsinfo fsinfo; - int ret = -ENOMEM; - - dprintk("--> nfs4_get_rootfh()\n"); - - fsinfo.fattr = nfs_alloc_fattr(); - if (fsinfo.fattr == NULL) - goto out; - - /* Start by getting the root filehandle from the server */ - ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); - if (ret < 0) { - dprintk("nfs4_get_rootfh: getroot error = %d\n", -ret); - goto out; - } - - if (!(fsinfo.fattr->valid & NFS_ATTR_FATTR_TYPE) - || !S_ISDIR(fsinfo.fattr->mode)) { - printk(KERN_ERR "nfs4_get_rootfh:" - " getroot encountered non-directory\n"); - ret = -ENOTDIR; - goto out; - } - - if (fsinfo.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) { - printk(KERN_ERR "nfs4_get_rootfh:" - " getroot obtained referral\n"); - ret = -EREMOTE; - goto out; - } - - memcpy(&server->fsid, &fsinfo.fattr->fsid, sizeof(server->fsid)); -out: - nfs_free_fattr(fsinfo.fattr); - dprintk("<-- nfs4_get_rootfh() = %d\n", ret); - return ret; -} - -/* - * get an NFS4 root dentry from the root filehandle - */ -struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh, - const char *devname) -{ - struct nfs_server *server = NFS_SB(sb); - struct nfs_fattr *fattr = NULL; - struct dentry *ret; - struct inode *inode; - void *name = kstrdup(devname, GFP_KERNEL); - int error; - - dprintk("--> nfs4_get_root()\n"); - - if (!name) - return ERR_PTR(-ENOMEM); - - /* get the info about the server and filesystem */ - error = nfs4_server_capabilities(server, mntfh); - if (error < 0) { - dprintk("nfs_get_root: getcaps error = %d\n", - -error); - kfree(name); - return ERR_PTR(error); - } - - fattr = nfs_alloc_fattr(); - if (fattr == NULL) { - kfree(name); - return ERR_PTR(-ENOMEM); - } - - /* get the actual root for this mount */ - error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); - if (error < 0) { - dprintk("nfs_get_root: getattr error = %d\n", -error); - ret = ERR_PTR(error); - goto out; - } - - if (fattr->valid & NFS_ATTR_FATTR_FSID && - !nfs_fsid_equal(&server->fsid, &fattr->fsid)) - memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid)); - - inode = nfs_fhget(sb, mntfh, fattr); - if (IS_ERR(inode)) { - dprintk("nfs_get_root: get root inode failed\n"); - ret = ERR_CAST(inode); - goto out; - } - - error = nfs_superblock_set_dummy_root(sb, inode); - if (error != 0) { - ret = ERR_PTR(error); - goto out; - } - - /* root dentries normally start off anonymous and get spliced in later - * if the dentry tree reaches them; however if the dentry already - * exists, we'll pick it up at this point and use it as the root - */ - ret = d_obtain_alias(inode); - if (IS_ERR(ret)) { - dprintk("nfs_get_root: get root dentry failed\n"); - goto out; - } - - security_d_instantiate(ret, inode); - spin_lock(&ret->d_lock); - if (IS_ROOT(ret) && !(ret->d_flags & DCACHE_NFSFS_RENAMED)) { - ret->d_fsdata = name; - name = NULL; - } - spin_unlock(&ret->d_lock); -out: - if (name) - kfree(name); - nfs_free_fattr(fattr); - dprintk("<-- nfs4_get_root()\n"); - return ret; -} - -#endif /* CONFIG_NFS_V4 */ diff --git a/ANDROID_3.4.5/fs/nfs/idmap.c b/ANDROID_3.4.5/fs/nfs/idmap.c deleted file mode 100644 index 93aa3a4c..00000000 --- a/ANDROID_3.4.5/fs/nfs/idmap.c +++ /dev/null @@ -1,795 +0,0 @@ -/* - * fs/nfs/idmap.c - * - * UID and GID to name mapping for clients. - * - * Copyright (c) 2002 The Regents of the University of Michigan. - * All rights reserved. - * - * Marius Aamodt Eriksen <marius@umich.edu> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include <linux/types.h> -#include <linux/parser.h> -#include <linux/fs.h> -#include <linux/nfs_idmap.h> -#include <net/net_namespace.h> -#include <linux/sunrpc/rpc_pipe_fs.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_fs_sb.h> -#include <linux/key.h> -#include <linux/keyctl.h> -#include <linux/key-type.h> -#include <keys/user-type.h> -#include <linux/module.h> - -#include "internal.h" -#include "netns.h" - -#define NFS_UINT_MAXLEN 11 - -/* Default cache timeout is 10 minutes */ -unsigned int nfs_idmap_cache_timeout = 600; -static const struct cred *id_resolver_cache; -static struct key_type key_type_id_resolver_legacy; - -struct idmap { - struct rpc_pipe *idmap_pipe; - struct key_construction *idmap_key_cons; - struct mutex idmap_mutex; -}; - -/** - * nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields - * @fattr: fully initialised struct nfs_fattr - * @owner_name: owner name string cache - * @group_name: group name string cache - */ -void nfs_fattr_init_names(struct nfs_fattr *fattr, - struct nfs4_string *owner_name, - struct nfs4_string *group_name) -{ - fattr->owner_name = owner_name; - fattr->group_name = group_name; -} - -static void nfs_fattr_free_owner_name(struct nfs_fattr *fattr) -{ - fattr->valid &= ~NFS_ATTR_FATTR_OWNER_NAME; - kfree(fattr->owner_name->data); -} - -static void nfs_fattr_free_group_name(struct nfs_fattr *fattr) -{ - fattr->valid &= ~NFS_ATTR_FATTR_GROUP_NAME; - kfree(fattr->group_name->data); -} - -static bool nfs_fattr_map_owner_name(struct nfs_server *server, struct nfs_fattr *fattr) -{ - struct nfs4_string *owner = fattr->owner_name; - __u32 uid; - - if (!(fattr->valid & NFS_ATTR_FATTR_OWNER_NAME)) - return false; - if (nfs_map_name_to_uid(server, owner->data, owner->len, &uid) == 0) { - fattr->uid = uid; - fattr->valid |= NFS_ATTR_FATTR_OWNER; - } - return true; -} - -static bool nfs_fattr_map_group_name(struct nfs_server *server, struct nfs_fattr *fattr) -{ - struct nfs4_string *group = fattr->group_name; - __u32 gid; - - if (!(fattr->valid & NFS_ATTR_FATTR_GROUP_NAME)) - return false; - if (nfs_map_group_to_gid(server, group->data, group->len, &gid) == 0) { - fattr->gid = gid; - fattr->valid |= NFS_ATTR_FATTR_GROUP; - } - return true; -} - -/** - * nfs_fattr_free_names - free up the NFSv4 owner and group strings - * @fattr: a fully initialised nfs_fattr structure - */ -void nfs_fattr_free_names(struct nfs_fattr *fattr) -{ - if (fattr->valid & NFS_ATTR_FATTR_OWNER_NAME) - nfs_fattr_free_owner_name(fattr); - if (fattr->valid & NFS_ATTR_FATTR_GROUP_NAME) - nfs_fattr_free_group_name(fattr); -} - -/** - * nfs_fattr_map_and_free_names - map owner/group strings into uid/gid and free - * @server: pointer to the filesystem nfs_server structure - * @fattr: a fully initialised nfs_fattr structure - * - * This helper maps the cached NFSv4 owner/group strings in fattr into - * their numeric uid/gid equivalents, and then frees the cached strings. - */ -void nfs_fattr_map_and_free_names(struct nfs_server *server, struct nfs_fattr *fattr) -{ - if (nfs_fattr_map_owner_name(server, fattr)) - nfs_fattr_free_owner_name(fattr); - if (nfs_fattr_map_group_name(server, fattr)) - nfs_fattr_free_group_name(fattr); -} - -static int nfs_map_string_to_numeric(const char *name, size_t namelen, __u32 *res) -{ - unsigned long val; - char buf[16]; - - if (memchr(name, '@', namelen) != NULL || namelen >= sizeof(buf)) - return 0; - memcpy(buf, name, namelen); - buf[namelen] = '\0'; - if (strict_strtoul(buf, 0, &val) != 0) - return 0; - *res = val; - return 1; -} - -static int nfs_map_numeric_to_string(__u32 id, char *buf, size_t buflen) -{ - return snprintf(buf, buflen, "%u", id); -} - -static struct key_type key_type_id_resolver = { - .name = "id_resolver", - .instantiate = user_instantiate, - .match = user_match, - .revoke = user_revoke, - .destroy = user_destroy, - .describe = user_describe, - .read = user_read, -}; - -static int nfs_idmap_init_keyring(void) -{ - struct cred *cred; - struct key *keyring; - int ret = 0; - - printk(KERN_NOTICE "NFS: Registering the %s key type\n", - key_type_id_resolver.name); - - cred = prepare_kernel_cred(NULL); - if (!cred) - return -ENOMEM; - - keyring = key_alloc(&key_type_keyring, ".id_resolver", 0, 0, cred, - (KEY_POS_ALL & ~KEY_POS_SETATTR) | - KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA); - if (IS_ERR(keyring)) { - ret = PTR_ERR(keyring); - goto failed_put_cred; - } - - ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); - if (ret < 0) - goto failed_put_key; - - ret = register_key_type(&key_type_id_resolver); - if (ret < 0) - goto failed_put_key; - - set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); - cred->thread_keyring = keyring; - cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; - id_resolver_cache = cred; - return 0; - -failed_put_key: - key_put(keyring); -failed_put_cred: - put_cred(cred); - return ret; -} - -static void nfs_idmap_quit_keyring(void) -{ - key_revoke(id_resolver_cache->thread_keyring); - unregister_key_type(&key_type_id_resolver); - put_cred(id_resolver_cache); -} - -/* - * Assemble the description to pass to request_key() - * This function will allocate a new string and update dest to point - * at it. The caller is responsible for freeing dest. - * - * On error 0 is returned. Otherwise, the length of dest is returned. - */ -static ssize_t nfs_idmap_get_desc(const char *name, size_t namelen, - const char *type, size_t typelen, char **desc) -{ - char *cp; - size_t desclen = typelen + namelen + 2; - - *desc = kmalloc(desclen, GFP_KERNEL); - if (!*desc) - return -ENOMEM; - - cp = *desc; - memcpy(cp, type, typelen); - cp += typelen; - *cp++ = ':'; - - memcpy(cp, name, namelen); - cp += namelen; - *cp = '\0'; - return desclen; -} - -static ssize_t nfs_idmap_request_key(struct key_type *key_type, - const char *name, size_t namelen, - const char *type, void *data, - size_t data_size, struct idmap *idmap) -{ - const struct cred *saved_cred; - struct key *rkey; - char *desc; - struct user_key_payload *payload; - ssize_t ret; - - ret = nfs_idmap_get_desc(name, namelen, type, strlen(type), &desc); - if (ret <= 0) - goto out; - - saved_cred = override_creds(id_resolver_cache); - if (idmap) - rkey = request_key_with_auxdata(key_type, desc, "", 0, idmap); - else - rkey = request_key(&key_type_id_resolver, desc, ""); - revert_creds(saved_cred); - - kfree(desc); - if (IS_ERR(rkey)) { - ret = PTR_ERR(rkey); - goto out; - } - - rcu_read_lock(); - rkey->perm |= KEY_USR_VIEW; - - ret = key_validate(rkey); - if (ret < 0) - goto out_up; - - payload = rcu_dereference(rkey->payload.data); - if (IS_ERR_OR_NULL(payload)) { - ret = PTR_ERR(payload); - goto out_up; - } - - ret = payload->datalen; - if (ret > 0 && ret <= data_size) - memcpy(data, payload->data, ret); - else - ret = -EINVAL; - -out_up: - rcu_read_unlock(); - key_put(rkey); -out: - return ret; -} - -static ssize_t nfs_idmap_get_key(const char *name, size_t namelen, - const char *type, void *data, - size_t data_size, struct idmap *idmap) -{ - ssize_t ret = nfs_idmap_request_key(&key_type_id_resolver, - name, namelen, type, data, - data_size, NULL); - if (ret < 0) { - mutex_lock(&idmap->idmap_mutex); - ret = nfs_idmap_request_key(&key_type_id_resolver_legacy, - name, namelen, type, data, - data_size, idmap); - mutex_unlock(&idmap->idmap_mutex); - } - return ret; -} - -/* ID -> Name */ -static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf, - size_t buflen, struct idmap *idmap) -{ - char id_str[NFS_UINT_MAXLEN]; - int id_len; - ssize_t ret; - - id_len = snprintf(id_str, sizeof(id_str), "%u", id); - ret = nfs_idmap_get_key(id_str, id_len, type, buf, buflen, idmap); - if (ret < 0) - return -EINVAL; - return ret; -} - -/* Name -> ID */ -static int nfs_idmap_lookup_id(const char *name, size_t namelen, const char *type, - __u32 *id, struct idmap *idmap) -{ - char id_str[NFS_UINT_MAXLEN]; - long id_long; - ssize_t data_size; - int ret = 0; - - data_size = nfs_idmap_get_key(name, namelen, type, id_str, NFS_UINT_MAXLEN, idmap); - if (data_size <= 0) { - ret = -EINVAL; - } else { - ret = strict_strtol(id_str, 10, &id_long); - *id = (__u32)id_long; - } - return ret; -} - -/* idmap classic begins here */ -module_param(nfs_idmap_cache_timeout, int, 0644); - -enum { - Opt_find_uid, Opt_find_gid, Opt_find_user, Opt_find_group, Opt_find_err -}; - -static const match_table_t nfs_idmap_tokens = { - { Opt_find_uid, "uid:%s" }, - { Opt_find_gid, "gid:%s" }, - { Opt_find_user, "user:%s" }, - { Opt_find_group, "group:%s" }, - { Opt_find_err, NULL } -}; - -static int nfs_idmap_legacy_upcall(struct key_construction *, const char *, void *); -static ssize_t idmap_pipe_downcall(struct file *, const char __user *, - size_t); -static void idmap_pipe_destroy_msg(struct rpc_pipe_msg *); - -static const struct rpc_pipe_ops idmap_upcall_ops = { - .upcall = rpc_pipe_generic_upcall, - .downcall = idmap_pipe_downcall, - .destroy_msg = idmap_pipe_destroy_msg, -}; - -static struct key_type key_type_id_resolver_legacy = { - .name = "id_resolver", - .instantiate = user_instantiate, - .match = user_match, - .revoke = user_revoke, - .destroy = user_destroy, - .describe = user_describe, - .read = user_read, - .request_key = nfs_idmap_legacy_upcall, -}; - -static void __nfs_idmap_unregister(struct rpc_pipe *pipe) -{ - if (pipe->dentry) - rpc_unlink(pipe->dentry); -} - -static int __nfs_idmap_register(struct dentry *dir, - struct idmap *idmap, - struct rpc_pipe *pipe) -{ - struct dentry *dentry; - - dentry = rpc_mkpipe_dentry(dir, "idmap", idmap, pipe); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - pipe->dentry = dentry; - return 0; -} - -static void nfs_idmap_unregister(struct nfs_client *clp, - struct rpc_pipe *pipe) -{ - struct net *net = clp->net; - struct super_block *pipefs_sb; - - pipefs_sb = rpc_get_sb_net(net); - if (pipefs_sb) { - __nfs_idmap_unregister(pipe); - rpc_put_sb_net(net); - } -} - -static int nfs_idmap_register(struct nfs_client *clp, - struct idmap *idmap, - struct rpc_pipe *pipe) -{ - struct net *net = clp->net; - struct super_block *pipefs_sb; - int err = 0; - - pipefs_sb = rpc_get_sb_net(net); - if (pipefs_sb) { - if (clp->cl_rpcclient->cl_dentry) - err = __nfs_idmap_register(clp->cl_rpcclient->cl_dentry, - idmap, pipe); - rpc_put_sb_net(net); - } - return err; -} - -int -nfs_idmap_new(struct nfs_client *clp) -{ - struct idmap *idmap; - struct rpc_pipe *pipe; - int error; - - BUG_ON(clp->cl_idmap != NULL); - - idmap = kzalloc(sizeof(*idmap), GFP_KERNEL); - if (idmap == NULL) - return -ENOMEM; - - pipe = rpc_mkpipe_data(&idmap_upcall_ops, 0); - if (IS_ERR(pipe)) { - error = PTR_ERR(pipe); - kfree(idmap); - return error; - } - error = nfs_idmap_register(clp, idmap, pipe); - if (error) { - rpc_destroy_pipe_data(pipe); - kfree(idmap); - return error; - } - idmap->idmap_pipe = pipe; - mutex_init(&idmap->idmap_mutex); - - clp->cl_idmap = idmap; - return 0; -} - -void -nfs_idmap_delete(struct nfs_client *clp) -{ - struct idmap *idmap = clp->cl_idmap; - - if (!idmap) - return; - nfs_idmap_unregister(clp, idmap->idmap_pipe); - rpc_destroy_pipe_data(idmap->idmap_pipe); - clp->cl_idmap = NULL; - kfree(idmap); -} - -static int __rpc_pipefs_event(struct nfs_client *clp, unsigned long event, - struct super_block *sb) -{ - int err = 0; - - switch (event) { - case RPC_PIPEFS_MOUNT: - BUG_ON(clp->cl_rpcclient->cl_dentry == NULL); - err = __nfs_idmap_register(clp->cl_rpcclient->cl_dentry, - clp->cl_idmap, - clp->cl_idmap->idmap_pipe); - break; - case RPC_PIPEFS_UMOUNT: - if (clp->cl_idmap->idmap_pipe) { - struct dentry *parent; - - parent = clp->cl_idmap->idmap_pipe->dentry->d_parent; - __nfs_idmap_unregister(clp->cl_idmap->idmap_pipe); - /* - * Note: This is a dirty hack. SUNRPC hook has been - * called already but simple_rmdir() call for the - * directory returned with error because of idmap pipe - * inside. Thus now we have to remove this directory - * here. - */ - if (rpc_rmdir(parent)) - printk(KERN_ERR "NFS: %s: failed to remove " - "clnt dir!\n", __func__); - } - break; - default: - printk(KERN_ERR "NFS: %s: unknown event: %ld\n", __func__, - event); - return -ENOTSUPP; - } - return err; -} - -static struct nfs_client *nfs_get_client_for_event(struct net *net, int event) -{ - struct nfs_net *nn = net_generic(net, nfs_net_id); - struct dentry *cl_dentry; - struct nfs_client *clp; - - spin_lock(&nn->nfs_client_lock); - list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { - if (clp->rpc_ops != &nfs_v4_clientops) - continue; - cl_dentry = clp->cl_idmap->idmap_pipe->dentry; - if (((event == RPC_PIPEFS_MOUNT) && cl_dentry) || - ((event == RPC_PIPEFS_UMOUNT) && !cl_dentry)) - continue; - atomic_inc(&clp->cl_count); - spin_unlock(&nn->nfs_client_lock); - return clp; - } - spin_unlock(&nn->nfs_client_lock); - return NULL; -} - -static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event, - void *ptr) -{ - struct super_block *sb = ptr; - struct nfs_client *clp; - int error = 0; - - if (!try_module_get(THIS_MODULE)) - return 0; - - while ((clp = nfs_get_client_for_event(sb->s_fs_info, event))) { - error = __rpc_pipefs_event(clp, event, sb); - nfs_put_client(clp); - if (error) - break; - } - module_put(THIS_MODULE); - return error; -} - -#define PIPEFS_NFS_PRIO 1 - -static struct notifier_block nfs_idmap_block = { - .notifier_call = rpc_pipefs_event, - .priority = SUNRPC_PIPEFS_NFS_PRIO, -}; - -int nfs_idmap_init(void) -{ - int ret; - ret = nfs_idmap_init_keyring(); - if (ret != 0) - goto out; - ret = rpc_pipefs_notifier_register(&nfs_idmap_block); - if (ret != 0) - nfs_idmap_quit_keyring(); -out: - return ret; -} - -void nfs_idmap_quit(void) -{ - rpc_pipefs_notifier_unregister(&nfs_idmap_block); - nfs_idmap_quit_keyring(); -} - -static int nfs_idmap_prepare_message(char *desc, struct idmap_msg *im, - struct rpc_pipe_msg *msg) -{ - substring_t substr; - int token, ret; - - memset(im, 0, sizeof(*im)); - memset(msg, 0, sizeof(*msg)); - - im->im_type = IDMAP_TYPE_GROUP; - token = match_token(desc, nfs_idmap_tokens, &substr); - - switch (token) { - case Opt_find_uid: - im->im_type = IDMAP_TYPE_USER; - case Opt_find_gid: - im->im_conv = IDMAP_CONV_NAMETOID; - ret = match_strlcpy(im->im_name, &substr, IDMAP_NAMESZ); - break; - - case Opt_find_user: - im->im_type = IDMAP_TYPE_USER; - case Opt_find_group: - im->im_conv = IDMAP_CONV_IDTONAME; - ret = match_int(&substr, &im->im_id); - break; - - default: - ret = -EINVAL; - goto out; - } - - msg->data = im; - msg->len = sizeof(struct idmap_msg); - -out: - return ret; -} - -static int nfs_idmap_legacy_upcall(struct key_construction *cons, - const char *op, - void *aux) -{ - struct rpc_pipe_msg *msg; - struct idmap_msg *im; - struct idmap *idmap = (struct idmap *)aux; - struct key *key = cons->key; - int ret = -ENOMEM; - - /* msg and im are freed in idmap_pipe_destroy_msg */ - msg = kmalloc(sizeof(*msg), GFP_KERNEL); - if (!msg) - goto out0; - - im = kmalloc(sizeof(*im), GFP_KERNEL); - if (!im) - goto out1; - - ret = nfs_idmap_prepare_message(key->description, im, msg); - if (ret < 0) - goto out2; - - idmap->idmap_key_cons = cons; - - ret = rpc_queue_upcall(idmap->idmap_pipe, msg); - if (ret < 0) - goto out2; - - return ret; - -out2: - kfree(im); -out1: - kfree(msg); -out0: - key_revoke(cons->key); - key_revoke(cons->authkey); - return ret; -} - -static int nfs_idmap_instantiate(struct key *key, struct key *authkey, char *data) -{ - return key_instantiate_and_link(key, data, strlen(data) + 1, - id_resolver_cache->thread_keyring, - authkey); -} - -static int nfs_idmap_read_message(struct idmap_msg *im, struct key *key, struct key *authkey) -{ - char id_str[NFS_UINT_MAXLEN]; - int ret = -EINVAL; - - switch (im->im_conv) { - case IDMAP_CONV_NAMETOID: - sprintf(id_str, "%d", im->im_id); - ret = nfs_idmap_instantiate(key, authkey, id_str); - break; - case IDMAP_CONV_IDTONAME: - ret = nfs_idmap_instantiate(key, authkey, im->im_name); - break; - } - - return ret; -} - -static ssize_t -idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) -{ - struct rpc_inode *rpci = RPC_I(filp->f_path.dentry->d_inode); - struct idmap *idmap = (struct idmap *)rpci->private; - struct key_construction *cons = idmap->idmap_key_cons; - struct idmap_msg im; - size_t namelen_in; - int ret; - - if (mlen != sizeof(im)) { - ret = -ENOSPC; - goto out; - } - - if (copy_from_user(&im, src, mlen) != 0) { - ret = -EFAULT; - goto out; - } - - if (!(im.im_status & IDMAP_STATUS_SUCCESS)) { - ret = mlen; - complete_request_key(idmap->idmap_key_cons, -ENOKEY); - goto out_incomplete; - } - - namelen_in = strnlen(im.im_name, IDMAP_NAMESZ); - if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) { - ret = -EINVAL; - goto out; - } - - ret = nfs_idmap_read_message(&im, cons->key, cons->authkey); - if (ret >= 0) { - key_set_timeout(cons->key, nfs_idmap_cache_timeout); - ret = mlen; - } - -out: - complete_request_key(idmap->idmap_key_cons, ret); -out_incomplete: - return ret; -} - -static void -idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg) -{ - /* Free memory allocated in nfs_idmap_legacy_upcall() */ - kfree(msg->data); - kfree(msg); -} - -int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) -{ - struct idmap *idmap = server->nfs_client->cl_idmap; - - if (nfs_map_string_to_numeric(name, namelen, uid)) - return 0; - return nfs_idmap_lookup_id(name, namelen, "uid", uid, idmap); -} - -int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *gid) -{ - struct idmap *idmap = server->nfs_client->cl_idmap; - - if (nfs_map_string_to_numeric(name, namelen, gid)) - return 0; - return nfs_idmap_lookup_id(name, namelen, "gid", gid, idmap); -} - -int nfs_map_uid_to_name(const struct nfs_server *server, __u32 uid, char *buf, size_t buflen) -{ - struct idmap *idmap = server->nfs_client->cl_idmap; - int ret = -EINVAL; - - if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) - ret = nfs_idmap_lookup_name(uid, "user", buf, buflen, idmap); - if (ret < 0) - ret = nfs_map_numeric_to_string(uid, buf, buflen); - return ret; -} -int nfs_map_gid_to_group(const struct nfs_server *server, __u32 gid, char *buf, size_t buflen) -{ - struct idmap *idmap = server->nfs_client->cl_idmap; - int ret = -EINVAL; - - if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) - ret = nfs_idmap_lookup_name(gid, "group", buf, buflen, idmap); - if (ret < 0) - ret = nfs_map_numeric_to_string(gid, buf, buflen); - return ret; -} diff --git a/ANDROID_3.4.5/fs/nfs/inode.c b/ANDROID_3.4.5/fs/nfs/inode.c deleted file mode 100644 index e8bbfa5b..00000000 --- a/ANDROID_3.4.5/fs/nfs/inode.c +++ /dev/null @@ -1,1752 +0,0 @@ -/* - * linux/fs/nfs/inode.c - * - * Copyright (C) 1992 Rick Sladkey - * - * nfs inode and superblock handling functions - * - * Modularised by Alan Cox <alan@lxorguk.ukuu.org.uk>, while hacking some - * experimental NFS changes. Modularisation taken straight from SYS5 fs. - * - * Change to nfs_read_super() to permit NFS mounts to multi-homed hosts. - * J.S.Peatfield@damtp.cam.ac.uk - * - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/sched.h> -#include <linux/time.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/string.h> -#include <linux/stat.h> -#include <linux/errno.h> -#include <linux/unistd.h> -#include <linux/sunrpc/clnt.h> -#include <linux/sunrpc/stats.h> -#include <linux/sunrpc/metrics.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_mount.h> -#include <linux/nfs4_mount.h> -#include <linux/lockd/bind.h> -#include <linux/seq_file.h> -#include <linux/mount.h> -#include <linux/nfs_idmap.h> -#include <linux/vfs.h> -#include <linux/inet.h> -#include <linux/nfs_xdr.h> -#include <linux/slab.h> -#include <linux/compat.h> -#include <linux/freezer.h> -#include <linux/crc32.h> - -#include <asm/uaccess.h> - -#include "nfs4_fs.h" -#include "callback.h" -#include "delegation.h" -#include "iostat.h" -#include "internal.h" -#include "fscache.h" -#include "dns_resolve.h" -#include "pnfs.h" -#include "netns.h" - -#define NFSDBG_FACILITY NFSDBG_VFS - -#define NFS_64_BIT_INODE_NUMBERS_ENABLED 1 - -/* Default is to see 64-bit inode numbers */ -static bool enable_ino64 = NFS_64_BIT_INODE_NUMBERS_ENABLED; - -static void nfs_invalidate_inode(struct inode *); -static int nfs_update_inode(struct inode *, struct nfs_fattr *); - -static struct kmem_cache * nfs_inode_cachep; - -static inline unsigned long -nfs_fattr_to_ino_t(struct nfs_fattr *fattr) -{ - return nfs_fileid_to_ino_t(fattr->fileid); -} - -/** - * nfs_wait_bit_killable - helper for functions that are sleeping on bit locks - * @word: long word containing the bit lock - */ -int nfs_wait_bit_killable(void *word) -{ - if (fatal_signal_pending(current)) - return -ERESTARTSYS; - freezable_schedule(); - return 0; -} - -/** - * nfs_compat_user_ino64 - returns the user-visible inode number - * @fileid: 64-bit fileid - * - * This function returns a 32-bit inode number if the boot parameter - * nfs.enable_ino64 is zero. - */ -u64 nfs_compat_user_ino64(u64 fileid) -{ -#ifdef CONFIG_COMPAT - compat_ulong_t ino; -#else - unsigned long ino; -#endif - - if (enable_ino64) - return fileid; - ino = fileid; - if (sizeof(ino) < sizeof(fileid)) - ino ^= fileid >> (sizeof(fileid)-sizeof(ino)) * 8; - return ino; -} - -static void nfs_clear_inode(struct inode *inode) -{ - /* - * The following should never happen... - */ - BUG_ON(nfs_have_writebacks(inode)); - BUG_ON(!list_empty(&NFS_I(inode)->open_files)); - nfs_zap_acl_cache(inode); - nfs_access_zap_cache(inode); - nfs_fscache_release_inode_cookie(inode); -} - -void nfs_evict_inode(struct inode *inode) -{ - truncate_inode_pages(&inode->i_data, 0); - end_writeback(inode); - nfs_clear_inode(inode); -} - -/** - * nfs_sync_mapping - helper to flush all mmapped dirty data to disk - */ -int nfs_sync_mapping(struct address_space *mapping) -{ - int ret = 0; - - if (mapping->nrpages != 0) { - unmap_mapping_range(mapping, 0, 0, 0); - ret = nfs_wb_all(mapping->host); - } - return ret; -} - -/* - * Invalidate the local caches - */ -static void nfs_zap_caches_locked(struct inode *inode) -{ - struct nfs_inode *nfsi = NFS_I(inode); - int mode = inode->i_mode; - - nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE); - - nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); - nfsi->attrtimeo_timestamp = jiffies; - - memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); - if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE; - else - nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE; -} - -void nfs_zap_caches(struct inode *inode) -{ - spin_lock(&inode->i_lock); - nfs_zap_caches_locked(inode); - spin_unlock(&inode->i_lock); -} - -void nfs_zap_mapping(struct inode *inode, struct address_space *mapping) -{ - if (mapping->nrpages != 0) { - spin_lock(&inode->i_lock); - NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA; - spin_unlock(&inode->i_lock); - } -} - -void nfs_zap_acl_cache(struct inode *inode) -{ - void (*clear_acl_cache)(struct inode *); - - clear_acl_cache = NFS_PROTO(inode)->clear_acl_cache; - if (clear_acl_cache != NULL) - clear_acl_cache(inode); - spin_lock(&inode->i_lock); - NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_ACL; - spin_unlock(&inode->i_lock); -} - -void nfs_invalidate_atime(struct inode *inode) -{ - spin_lock(&inode->i_lock); - NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATIME; - spin_unlock(&inode->i_lock); -} - -/* - * Invalidate, but do not unhash, the inode. - * NB: must be called with inode->i_lock held! - */ -static void nfs_invalidate_inode(struct inode *inode) -{ - set_bit(NFS_INO_STALE, &NFS_I(inode)->flags); - nfs_zap_caches_locked(inode); -} - -struct nfs_find_desc { - struct nfs_fh *fh; - struct nfs_fattr *fattr; -}; - -/* - * In NFSv3 we can have 64bit inode numbers. In order to support - * this, and re-exported directories (also seen in NFSv2) - * we are forced to allow 2 different inodes to have the same - * i_ino. - */ -static int -nfs_find_actor(struct inode *inode, void *opaque) -{ - struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque; - struct nfs_fh *fh = desc->fh; - struct nfs_fattr *fattr = desc->fattr; - - if (NFS_FILEID(inode) != fattr->fileid) - return 0; - if (nfs_compare_fh(NFS_FH(inode), fh)) - return 0; - if (is_bad_inode(inode) || NFS_STALE(inode)) - return 0; - return 1; -} - -static int -nfs_init_locked(struct inode *inode, void *opaque) -{ - struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque; - struct nfs_fattr *fattr = desc->fattr; - - set_nfs_fileid(inode, fattr->fileid); - nfs_copy_fh(NFS_FH(inode), desc->fh); - return 0; -} - -/* - * This is our front-end to iget that looks up inodes by file handle - * instead of inode number. - */ -struct inode * -nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) -{ - struct nfs_find_desc desc = { - .fh = fh, - .fattr = fattr - }; - struct inode *inode = ERR_PTR(-ENOENT); - unsigned long hash; - - nfs_attr_check_mountpoint(sb, fattr); - - if (((fattr->valid & NFS_ATTR_FATTR_FILEID) == 0) && - !nfs_attr_use_mounted_on_fileid(fattr)) - goto out_no_inode; - if ((fattr->valid & NFS_ATTR_FATTR_TYPE) == 0) - goto out_no_inode; - - hash = nfs_fattr_to_ino_t(fattr); - - inode = iget5_locked(sb, hash, nfs_find_actor, nfs_init_locked, &desc); - if (inode == NULL) { - inode = ERR_PTR(-ENOMEM); - goto out_no_inode; - } - - if (inode->i_state & I_NEW) { - struct nfs_inode *nfsi = NFS_I(inode); - unsigned long now = jiffies; - - /* We set i_ino for the few things that still rely on it, - * such as stat(2) */ - inode->i_ino = hash; - - /* We can't support update_atime(), since the server will reset it */ - inode->i_flags |= S_NOATIME|S_NOCMTIME; - inode->i_mode = fattr->mode; - if ((fattr->valid & NFS_ATTR_FATTR_MODE) == 0 - && nfs_server_capable(inode, NFS_CAP_MODE)) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR - | NFS_INO_INVALID_ACCESS - | NFS_INO_INVALID_ACL; - /* Why so? Because we want revalidate for devices/FIFOs, and - * that's precisely what we have in nfs_file_inode_operations. - */ - inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->file_inode_ops; - if (S_ISREG(inode->i_mode)) { - inode->i_fop = NFS_SB(sb)->nfs_client->rpc_ops->file_ops; - inode->i_data.a_ops = &nfs_file_aops; - inode->i_data.backing_dev_info = &NFS_SB(sb)->backing_dev_info; - } else if (S_ISDIR(inode->i_mode)) { - inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops; - inode->i_fop = &nfs_dir_operations; - inode->i_data.a_ops = &nfs_dir_aops; - if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS)) - set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); - /* Deal with crossing mountpoints */ - if (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT || - fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) { - if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) - inode->i_op = &nfs_referral_inode_operations; - else - inode->i_op = &nfs_mountpoint_inode_operations; - inode->i_fop = NULL; - inode->i_flags |= S_AUTOMOUNT; - } - } else if (S_ISLNK(inode->i_mode)) - inode->i_op = &nfs_symlink_inode_operations; - else - init_special_inode(inode, inode->i_mode, fattr->rdev); - - memset(&inode->i_atime, 0, sizeof(inode->i_atime)); - memset(&inode->i_mtime, 0, sizeof(inode->i_mtime)); - memset(&inode->i_ctime, 0, sizeof(inode->i_ctime)); - inode->i_version = 0; - inode->i_size = 0; - clear_nlink(inode); - inode->i_uid = -2; - inode->i_gid = -2; - inode->i_blocks = 0; - memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); - - nfsi->read_cache_jiffies = fattr->time_start; - nfsi->attr_gencount = fattr->gencount; - if (fattr->valid & NFS_ATTR_FATTR_ATIME) - inode->i_atime = fattr->atime; - else if (nfs_server_capable(inode, NFS_CAP_ATIME)) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR; - if (fattr->valid & NFS_ATTR_FATTR_MTIME) - inode->i_mtime = fattr->mtime; - else if (nfs_server_capable(inode, NFS_CAP_MTIME)) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR - | NFS_INO_INVALID_DATA; - if (fattr->valid & NFS_ATTR_FATTR_CTIME) - inode->i_ctime = fattr->ctime; - else if (nfs_server_capable(inode, NFS_CAP_CTIME)) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR - | NFS_INO_INVALID_ACCESS - | NFS_INO_INVALID_ACL; - if (fattr->valid & NFS_ATTR_FATTR_CHANGE) - inode->i_version = fattr->change_attr; - else if (nfs_server_capable(inode, NFS_CAP_CHANGE_ATTR)) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR - | NFS_INO_INVALID_DATA; - if (fattr->valid & NFS_ATTR_FATTR_SIZE) - inode->i_size = nfs_size_to_loff_t(fattr->size); - else - nfsi->cache_validity |= NFS_INO_INVALID_ATTR - | NFS_INO_INVALID_DATA - | NFS_INO_REVAL_PAGECACHE; - if (fattr->valid & NFS_ATTR_FATTR_NLINK) - set_nlink(inode, fattr->nlink); - else if (nfs_server_capable(inode, NFS_CAP_NLINK)) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR; - if (fattr->valid & NFS_ATTR_FATTR_OWNER) - inode->i_uid = fattr->uid; - else if (nfs_server_capable(inode, NFS_CAP_OWNER)) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR - | NFS_INO_INVALID_ACCESS - | NFS_INO_INVALID_ACL; - if (fattr->valid & NFS_ATTR_FATTR_GROUP) - inode->i_gid = fattr->gid; - else if (nfs_server_capable(inode, NFS_CAP_OWNER_GROUP)) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR - | NFS_INO_INVALID_ACCESS - | NFS_INO_INVALID_ACL; - if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED) - inode->i_blocks = fattr->du.nfs2.blocks; - if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) { - /* - * report the blocks in 512byte units - */ - inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used); - } - nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); - nfsi->attrtimeo_timestamp = now; - nfsi->access_cache = RB_ROOT; - - nfs_fscache_init_inode_cookie(inode); - - unlock_new_inode(inode); - } else - nfs_refresh_inode(inode, fattr); - dprintk("NFS: nfs_fhget(%s/%Ld fh_crc=0x%08x ct=%d)\n", - inode->i_sb->s_id, - (long long)NFS_FILEID(inode), - nfs_display_fhandle_hash(fh), - atomic_read(&inode->i_count)); - -out: - return inode; - -out_no_inode: - dprintk("nfs_fhget: iget failed with error %ld\n", PTR_ERR(inode)); - goto out; -} - -#define NFS_VALID_ATTRS (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE|ATTR_ATIME|ATTR_ATIME_SET|ATTR_MTIME|ATTR_MTIME_SET|ATTR_FILE|ATTR_OPEN) - -int -nfs_setattr(struct dentry *dentry, struct iattr *attr) -{ - struct inode *inode = dentry->d_inode; - struct nfs_fattr *fattr; - int error = -ENOMEM; - - nfs_inc_stats(inode, NFSIOS_VFSSETATTR); - - /* skip mode change if it's just for clearing setuid/setgid */ - if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) - attr->ia_valid &= ~ATTR_MODE; - - if (attr->ia_valid & ATTR_SIZE) { - if (!S_ISREG(inode->i_mode) || attr->ia_size == i_size_read(inode)) - attr->ia_valid &= ~ATTR_SIZE; - } - - /* Optimization: if the end result is no change, don't RPC */ - attr->ia_valid &= NFS_VALID_ATTRS; - if ((attr->ia_valid & ~(ATTR_FILE|ATTR_OPEN)) == 0) - return 0; - - /* Write all dirty data */ - if (S_ISREG(inode->i_mode)) - nfs_wb_all(inode); - - fattr = nfs_alloc_fattr(); - if (fattr == NULL) - goto out; - /* - * Return any delegations if we're going to change ACLs - */ - if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) - nfs_inode_return_delegation(inode); - error = NFS_PROTO(inode)->setattr(dentry, fattr, attr); - if (error == 0) - nfs_refresh_inode(inode, fattr); - nfs_free_fattr(fattr); -out: - return error; -} - -/** - * nfs_vmtruncate - unmap mappings "freed" by truncate() syscall - * @inode: inode of the file used - * @offset: file offset to start truncating - * - * This is a copy of the common vmtruncate, but with the locking - * corrected to take into account the fact that NFS requires - * inode->i_size to be updated under the inode->i_lock. - */ -static int nfs_vmtruncate(struct inode * inode, loff_t offset) -{ - loff_t oldsize; - int err; - - err = inode_newsize_ok(inode, offset); - if (err) - goto out; - - spin_lock(&inode->i_lock); - oldsize = inode->i_size; - i_size_write(inode, offset); - spin_unlock(&inode->i_lock); - - truncate_pagecache(inode, oldsize, offset); -out: - return err; -} - -/** - * nfs_setattr_update_inode - Update inode metadata after a setattr call. - * @inode: pointer to struct inode - * @attr: pointer to struct iattr - * - * Note: we do this in the *proc.c in order to ensure that - * it works for things like exclusive creates too. - */ -void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr) -{ - if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) { - spin_lock(&inode->i_lock); - if ((attr->ia_valid & ATTR_MODE) != 0) { - int mode = attr->ia_mode & S_IALLUGO; - mode |= inode->i_mode & ~S_IALLUGO; - inode->i_mode = mode; - } - if ((attr->ia_valid & ATTR_UID) != 0) - inode->i_uid = attr->ia_uid; - if ((attr->ia_valid & ATTR_GID) != 0) - inode->i_gid = attr->ia_gid; - NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; - spin_unlock(&inode->i_lock); - } - if ((attr->ia_valid & ATTR_SIZE) != 0) { - nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC); - nfs_vmtruncate(inode, attr->ia_size); - } -} - -int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) -{ - struct inode *inode = dentry->d_inode; - int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME; - int err; - - /* Flush out writes to the server in order to update c/mtime. */ - if (S_ISREG(inode->i_mode)) { - err = filemap_write_and_wait(inode->i_mapping); - if (err) - goto out; - } - - /* - * We may force a getattr if the user cares about atime. - * - * Note that we only have to check the vfsmount flags here: - * - NFS always sets S_NOATIME by so checking it would give a - * bogus result - * - NFS never sets MS_NOATIME or MS_NODIRATIME so there is - * no point in checking those. - */ - if ((mnt->mnt_flags & MNT_NOATIME) || - ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode))) - need_atime = 0; - - if (need_atime) - err = __nfs_revalidate_inode(NFS_SERVER(inode), inode); - else - err = nfs_revalidate_inode(NFS_SERVER(inode), inode); - if (!err) { - generic_fillattr(inode, stat); - stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode)); - } -out: - return err; -} - -static void nfs_init_lock_context(struct nfs_lock_context *l_ctx) -{ - atomic_set(&l_ctx->count, 1); - l_ctx->lockowner = current->files; - l_ctx->pid = current->tgid; - INIT_LIST_HEAD(&l_ctx->list); -} - -static struct nfs_lock_context *__nfs_find_lock_context(struct nfs_open_context *ctx) -{ - struct nfs_lock_context *pos; - - list_for_each_entry(pos, &ctx->lock_context.list, list) { - if (pos->lockowner != current->files) - continue; - if (pos->pid != current->tgid) - continue; - atomic_inc(&pos->count); - return pos; - } - return NULL; -} - -struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx) -{ - struct nfs_lock_context *res, *new = NULL; - struct inode *inode = ctx->dentry->d_inode; - - spin_lock(&inode->i_lock); - res = __nfs_find_lock_context(ctx); - if (res == NULL) { - spin_unlock(&inode->i_lock); - new = kmalloc(sizeof(*new), GFP_KERNEL); - if (new == NULL) - return NULL; - nfs_init_lock_context(new); - spin_lock(&inode->i_lock); - res = __nfs_find_lock_context(ctx); - if (res == NULL) { - list_add_tail(&new->list, &ctx->lock_context.list); - new->open_context = ctx; - res = new; - new = NULL; - } - } - spin_unlock(&inode->i_lock); - kfree(new); - return res; -} - -void nfs_put_lock_context(struct nfs_lock_context *l_ctx) -{ - struct nfs_open_context *ctx = l_ctx->open_context; - struct inode *inode = ctx->dentry->d_inode; - - if (!atomic_dec_and_lock(&l_ctx->count, &inode->i_lock)) - return; - list_del(&l_ctx->list); - spin_unlock(&inode->i_lock); - kfree(l_ctx); -} - -/** - * nfs_close_context - Common close_context() routine NFSv2/v3 - * @ctx: pointer to context - * @is_sync: is this a synchronous close - * - * always ensure that the attributes are up to date if we're mounted - * with close-to-open semantics - */ -void nfs_close_context(struct nfs_open_context *ctx, int is_sync) -{ - struct inode *inode; - struct nfs_server *server; - - if (!(ctx->mode & FMODE_WRITE)) - return; - if (!is_sync) - return; - inode = ctx->dentry->d_inode; - if (!list_empty(&NFS_I(inode)->open_files)) - return; - server = NFS_SERVER(inode); - if (server->flags & NFS_MOUNT_NOCTO) - return; - nfs_revalidate_inode(server, inode); -} - -struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, fmode_t f_mode) -{ - struct nfs_open_context *ctx; - struct rpc_cred *cred = rpc_lookup_cred(); - if (IS_ERR(cred)) - return ERR_CAST(cred); - - ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) { - put_rpccred(cred); - return ERR_PTR(-ENOMEM); - } - nfs_sb_active(dentry->d_sb); - ctx->dentry = dget(dentry); - ctx->cred = cred; - ctx->state = NULL; - ctx->mode = f_mode; - ctx->flags = 0; - ctx->error = 0; - nfs_init_lock_context(&ctx->lock_context); - ctx->lock_context.open_context = ctx; - INIT_LIST_HEAD(&ctx->list); - return ctx; -} - -struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) -{ - if (ctx != NULL) - atomic_inc(&ctx->lock_context.count); - return ctx; -} - -static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync) -{ - struct inode *inode = ctx->dentry->d_inode; - struct super_block *sb = ctx->dentry->d_sb; - - if (!list_empty(&ctx->list)) { - if (!atomic_dec_and_lock(&ctx->lock_context.count, &inode->i_lock)) - return; - list_del(&ctx->list); - spin_unlock(&inode->i_lock); - } else if (!atomic_dec_and_test(&ctx->lock_context.count)) - return; - if (inode != NULL) - NFS_PROTO(inode)->close_context(ctx, is_sync); - if (ctx->cred != NULL) - put_rpccred(ctx->cred); - dput(ctx->dentry); - nfs_sb_deactive(sb); - kfree(ctx); -} - -void put_nfs_open_context(struct nfs_open_context *ctx) -{ - __put_nfs_open_context(ctx, 0); -} - -/* - * Ensure that mmap has a recent RPC credential for use when writing out - * shared pages - */ -void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx) -{ - struct inode *inode = filp->f_path.dentry->d_inode; - struct nfs_inode *nfsi = NFS_I(inode); - - filp->private_data = get_nfs_open_context(ctx); - spin_lock(&inode->i_lock); - list_add(&ctx->list, &nfsi->open_files); - spin_unlock(&inode->i_lock); -} - -/* - * Given an inode, search for an open context with the desired characteristics - */ -struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode) -{ - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs_open_context *pos, *ctx = NULL; - - spin_lock(&inode->i_lock); - list_for_each_entry(pos, &nfsi->open_files, list) { - if (cred != NULL && pos->cred != cred) - continue; - if ((pos->mode & (FMODE_READ|FMODE_WRITE)) != mode) - continue; - ctx = get_nfs_open_context(pos); - break; - } - spin_unlock(&inode->i_lock); - return ctx; -} - -static void nfs_file_clear_open_context(struct file *filp) -{ - struct inode *inode = filp->f_path.dentry->d_inode; - struct nfs_open_context *ctx = nfs_file_open_context(filp); - - if (ctx) { - filp->private_data = NULL; - spin_lock(&inode->i_lock); - list_move_tail(&ctx->list, &NFS_I(inode)->open_files); - spin_unlock(&inode->i_lock); - __put_nfs_open_context(ctx, filp->f_flags & O_DIRECT ? 0 : 1); - } -} - -/* - * These allocate and release file read/write context information. - */ -int nfs_open(struct inode *inode, struct file *filp) -{ - struct nfs_open_context *ctx; - - ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode); - if (IS_ERR(ctx)) - return PTR_ERR(ctx); - nfs_file_set_open_context(filp, ctx); - put_nfs_open_context(ctx); - nfs_fscache_set_inode_cookie(inode, filp); - return 0; -} - -int nfs_release(struct inode *inode, struct file *filp) -{ - nfs_file_clear_open_context(filp); - return 0; -} - -/* - * This function is called whenever some part of NFS notices that - * the cached attributes have to be refreshed. - */ -int -__nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) -{ - int status = -ESTALE; - struct nfs_fattr *fattr = NULL; - struct nfs_inode *nfsi = NFS_I(inode); - - dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n", - inode->i_sb->s_id, (long long)NFS_FILEID(inode)); - - if (is_bad_inode(inode)) - goto out; - if (NFS_STALE(inode)) - goto out; - - status = -ENOMEM; - fattr = nfs_alloc_fattr(); - if (fattr == NULL) - goto out; - - nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE); - status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr); - if (status != 0) { - dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n", - inode->i_sb->s_id, - (long long)NFS_FILEID(inode), status); - if (status == -ESTALE) { - nfs_zap_caches(inode); - if (!S_ISDIR(inode->i_mode)) - set_bit(NFS_INO_STALE, &NFS_I(inode)->flags); - } - goto out; - } - - status = nfs_refresh_inode(inode, fattr); - if (status) { - dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n", - inode->i_sb->s_id, - (long long)NFS_FILEID(inode), status); - goto out; - } - - if (nfsi->cache_validity & NFS_INO_INVALID_ACL) - nfs_zap_acl_cache(inode); - - dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n", - inode->i_sb->s_id, - (long long)NFS_FILEID(inode)); - - out: - nfs_free_fattr(fattr); - return status; -} - -int nfs_attribute_timeout(struct inode *inode) -{ - struct nfs_inode *nfsi = NFS_I(inode); - - return !time_in_range_open(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo); -} - -static int nfs_attribute_cache_expired(struct inode *inode) -{ - if (nfs_have_delegated_attributes(inode)) - return 0; - return nfs_attribute_timeout(inode); -} - -/** - * nfs_revalidate_inode - Revalidate the inode attributes - * @server - pointer to nfs_server struct - * @inode - pointer to inode struct - * - * Updates inode attribute information by retrieving the data from the server. - */ -int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) -{ - if (!(NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATTR) - && !nfs_attribute_cache_expired(inode)) - return NFS_STALE(inode) ? -ESTALE : 0; - return __nfs_revalidate_inode(server, inode); -} - -static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping) -{ - struct nfs_inode *nfsi = NFS_I(inode); - - if (mapping->nrpages != 0) { - int ret = invalidate_inode_pages2(mapping); - if (ret < 0) - return ret; - } - spin_lock(&inode->i_lock); - nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; - if (S_ISDIR(inode->i_mode)) - memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); - spin_unlock(&inode->i_lock); - nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); - nfs_fscache_reset_inode_cookie(inode); - dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n", - inode->i_sb->s_id, (long long)NFS_FILEID(inode)); - return 0; -} - -/** - * nfs_revalidate_mapping - Revalidate the pagecache - * @inode - pointer to host inode - * @mapping - pointer to mapping - */ -int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) -{ - struct nfs_inode *nfsi = NFS_I(inode); - int ret = 0; - - if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) - || nfs_attribute_cache_expired(inode) - || NFS_STALE(inode)) { - ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode); - if (ret < 0) - goto out; - } - if (nfsi->cache_validity & NFS_INO_INVALID_DATA) - ret = nfs_invalidate_mapping(inode, mapping); -out: - return ret; -} - -static unsigned long nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr) -{ - struct nfs_inode *nfsi = NFS_I(inode); - unsigned long ret = 0; - - if ((fattr->valid & NFS_ATTR_FATTR_PRECHANGE) - && (fattr->valid & NFS_ATTR_FATTR_CHANGE) - && inode->i_version == fattr->pre_change_attr) { - inode->i_version = fattr->change_attr; - if (S_ISDIR(inode->i_mode)) - nfsi->cache_validity |= NFS_INO_INVALID_DATA; - ret |= NFS_INO_INVALID_ATTR; - } - /* If we have atomic WCC data, we may update some attributes */ - if ((fattr->valid & NFS_ATTR_FATTR_PRECTIME) - && (fattr->valid & NFS_ATTR_FATTR_CTIME) - && timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) { - memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime)); - ret |= NFS_INO_INVALID_ATTR; - } - - if ((fattr->valid & NFS_ATTR_FATTR_PREMTIME) - && (fattr->valid & NFS_ATTR_FATTR_MTIME) - && timespec_equal(&inode->i_mtime, &fattr->pre_mtime)) { - memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); - if (S_ISDIR(inode->i_mode)) - nfsi->cache_validity |= NFS_INO_INVALID_DATA; - ret |= NFS_INO_INVALID_ATTR; - } - if ((fattr->valid & NFS_ATTR_FATTR_PRESIZE) - && (fattr->valid & NFS_ATTR_FATTR_SIZE) - && i_size_read(inode) == nfs_size_to_loff_t(fattr->pre_size) - && nfsi->npages == 0) { - i_size_write(inode, nfs_size_to_loff_t(fattr->size)); - ret |= NFS_INO_INVALID_ATTR; - } - return ret; -} - -/** - * nfs_check_inode_attributes - verify consistency of the inode attribute cache - * @inode - pointer to inode - * @fattr - updated attributes - * - * Verifies the attribute cache. If we have just changed the attributes, - * so that fattr carries weak cache consistency data, then it may - * also update the ctime/mtime/change_attribute. - */ -static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fattr) -{ - struct nfs_inode *nfsi = NFS_I(inode); - loff_t cur_size, new_isize; - unsigned long invalid = 0; - - - /* Has the inode gone and changed behind our back? */ - if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid) - return -EIO; - if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) - return -EIO; - - if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 && - inode->i_version != fattr->change_attr) - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; - - /* Verify a few of the more important attributes */ - if ((fattr->valid & NFS_ATTR_FATTR_MTIME) && !timespec_equal(&inode->i_mtime, &fattr->mtime)) - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; - - if (fattr->valid & NFS_ATTR_FATTR_SIZE) { - cur_size = i_size_read(inode); - new_isize = nfs_size_to_loff_t(fattr->size); - if (cur_size != new_isize && nfsi->npages == 0) - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; - } - - /* Have any file permissions changed? */ - if ((fattr->valid & NFS_ATTR_FATTR_MODE) && (inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)) - invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; - if ((fattr->valid & NFS_ATTR_FATTR_OWNER) && inode->i_uid != fattr->uid) - invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; - if ((fattr->valid & NFS_ATTR_FATTR_GROUP) && inode->i_gid != fattr->gid) - invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; - - /* Has the link count changed? */ - if ((fattr->valid & NFS_ATTR_FATTR_NLINK) && inode->i_nlink != fattr->nlink) - invalid |= NFS_INO_INVALID_ATTR; - - if ((fattr->valid & NFS_ATTR_FATTR_ATIME) && !timespec_equal(&inode->i_atime, &fattr->atime)) - invalid |= NFS_INO_INVALID_ATIME; - - if (invalid != 0) - nfsi->cache_validity |= invalid; - - nfsi->read_cache_jiffies = fattr->time_start; - return 0; -} - -static int nfs_ctime_need_update(const struct inode *inode, const struct nfs_fattr *fattr) -{ - if (!(fattr->valid & NFS_ATTR_FATTR_CTIME)) - return 0; - return timespec_compare(&fattr->ctime, &inode->i_ctime) > 0; -} - -static int nfs_size_need_update(const struct inode *inode, const struct nfs_fattr *fattr) -{ - if (!(fattr->valid & NFS_ATTR_FATTR_SIZE)) - return 0; - return nfs_size_to_loff_t(fattr->size) > i_size_read(inode); -} - -static atomic_long_t nfs_attr_generation_counter; - -static unsigned long nfs_read_attr_generation_counter(void) -{ - return atomic_long_read(&nfs_attr_generation_counter); -} - -unsigned long nfs_inc_attr_generation_counter(void) -{ - return atomic_long_inc_return(&nfs_attr_generation_counter); -} - -void nfs_fattr_init(struct nfs_fattr *fattr) -{ - fattr->valid = 0; - fattr->time_start = jiffies; - fattr->gencount = nfs_inc_attr_generation_counter(); - fattr->owner_name = NULL; - fattr->group_name = NULL; -} - -struct nfs_fattr *nfs_alloc_fattr(void) -{ - struct nfs_fattr *fattr; - - fattr = kmalloc(sizeof(*fattr), GFP_NOFS); - if (fattr != NULL) - nfs_fattr_init(fattr); - return fattr; -} - -struct nfs_fh *nfs_alloc_fhandle(void) -{ - struct nfs_fh *fh; - - fh = kmalloc(sizeof(struct nfs_fh), GFP_NOFS); - if (fh != NULL) - fh->size = 0; - return fh; -} - -#ifdef NFS_DEBUG -/* - * _nfs_display_fhandle_hash - calculate the crc32 hash for the filehandle - * in the same way that wireshark does - * - * @fh: file handle - * - * For debugging only. - */ -u32 _nfs_display_fhandle_hash(const struct nfs_fh *fh) -{ - /* wireshark uses 32-bit AUTODIN crc and does a bitwise - * not on the result */ - return ~crc32(0xFFFFFFFF, &fh->data[0], fh->size); -} - -/* - * _nfs_display_fhandle - display an NFS file handle on the console - * - * @fh: file handle to display - * @caption: display caption - * - * For debugging only. - */ -void _nfs_display_fhandle(const struct nfs_fh *fh, const char *caption) -{ - unsigned short i; - - if (fh == NULL || fh->size == 0) { - printk(KERN_DEFAULT "%s at %p is empty\n", caption, fh); - return; - } - - printk(KERN_DEFAULT "%s at %p is %u bytes, crc: 0x%08x:\n", - caption, fh, fh->size, _nfs_display_fhandle_hash(fh)); - for (i = 0; i < fh->size; i += 16) { - __be32 *pos = (__be32 *)&fh->data[i]; - - switch ((fh->size - i - 1) >> 2) { - case 0: - printk(KERN_DEFAULT " %08x\n", - be32_to_cpup(pos)); - break; - case 1: - printk(KERN_DEFAULT " %08x %08x\n", - be32_to_cpup(pos), be32_to_cpup(pos + 1)); - break; - case 2: - printk(KERN_DEFAULT " %08x %08x %08x\n", - be32_to_cpup(pos), be32_to_cpup(pos + 1), - be32_to_cpup(pos + 2)); - break; - default: - printk(KERN_DEFAULT " %08x %08x %08x %08x\n", - be32_to_cpup(pos), be32_to_cpup(pos + 1), - be32_to_cpup(pos + 2), be32_to_cpup(pos + 3)); - } - } -} -#endif - -/** - * nfs_inode_attrs_need_update - check if the inode attributes need updating - * @inode - pointer to inode - * @fattr - attributes - * - * Attempt to divine whether or not an RPC call reply carrying stale - * attributes got scheduled after another call carrying updated ones. - * - * To do so, the function first assumes that a more recent ctime means - * that the attributes in fattr are newer, however it also attempt to - * catch the case where ctime either didn't change, or went backwards - * (if someone reset the clock on the server) by looking at whether - * or not this RPC call was started after the inode was last updated. - * Note also the check for wraparound of 'attr_gencount' - * - * The function returns 'true' if it thinks the attributes in 'fattr' are - * more recent than the ones cached in the inode. - * - */ -static int nfs_inode_attrs_need_update(const struct inode *inode, const struct nfs_fattr *fattr) -{ - const struct nfs_inode *nfsi = NFS_I(inode); - - return ((long)fattr->gencount - (long)nfsi->attr_gencount) > 0 || - nfs_ctime_need_update(inode, fattr) || - nfs_size_need_update(inode, fattr) || - ((long)nfsi->attr_gencount - (long)nfs_read_attr_generation_counter() > 0); -} - -static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr) -{ - if (nfs_inode_attrs_need_update(inode, fattr)) - return nfs_update_inode(inode, fattr); - return nfs_check_inode_attributes(inode, fattr); -} - -/** - * nfs_refresh_inode - try to update the inode attribute cache - * @inode - pointer to inode - * @fattr - updated attributes - * - * Check that an RPC call that returned attributes has not overlapped with - * other recent updates of the inode metadata, then decide whether it is - * safe to do a full update of the inode attributes, or whether just to - * call nfs_check_inode_attributes. - */ -int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) -{ - int status; - - if ((fattr->valid & NFS_ATTR_FATTR) == 0) - return 0; - spin_lock(&inode->i_lock); - status = nfs_refresh_inode_locked(inode, fattr); - spin_unlock(&inode->i_lock); - - return status; -} - -static int nfs_post_op_update_inode_locked(struct inode *inode, struct nfs_fattr *fattr) -{ - struct nfs_inode *nfsi = NFS_I(inode); - - nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; - if (S_ISDIR(inode->i_mode)) - nfsi->cache_validity |= NFS_INO_INVALID_DATA; - if ((fattr->valid & NFS_ATTR_FATTR) == 0) - return 0; - return nfs_refresh_inode_locked(inode, fattr); -} - -/** - * nfs_post_op_update_inode - try to update the inode attribute cache - * @inode - pointer to inode - * @fattr - updated attributes - * - * After an operation that has changed the inode metadata, mark the - * attribute cache as being invalid, then try to update it. - * - * NB: if the server didn't return any post op attributes, this - * function will force the retrieval of attributes before the next - * NFS request. Thus it should be used only for operations that - * are expected to change one or more attributes, to avoid - * unnecessary NFS requests and trips through nfs_update_inode(). - */ -int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr) -{ - int status; - - spin_lock(&inode->i_lock); - status = nfs_post_op_update_inode_locked(inode, fattr); - spin_unlock(&inode->i_lock); - return status; -} - -/** - * nfs_post_op_update_inode_force_wcc - try to update the inode attribute cache - * @inode - pointer to inode - * @fattr - updated attributes - * - * After an operation that has changed the inode metadata, mark the - * attribute cache as being invalid, then try to update it. Fake up - * weak cache consistency data, if none exist. - * - * This function is mainly designed to be used by the ->write_done() functions. - */ -int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr) -{ - int status; - - spin_lock(&inode->i_lock); - /* Don't do a WCC update if these attributes are already stale */ - if ((fattr->valid & NFS_ATTR_FATTR) == 0 || - !nfs_inode_attrs_need_update(inode, fattr)) { - fattr->valid &= ~(NFS_ATTR_FATTR_PRECHANGE - | NFS_ATTR_FATTR_PRESIZE - | NFS_ATTR_FATTR_PREMTIME - | NFS_ATTR_FATTR_PRECTIME); - goto out_noforce; - } - if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 && - (fattr->valid & NFS_ATTR_FATTR_PRECHANGE) == 0) { - fattr->pre_change_attr = inode->i_version; - fattr->valid |= NFS_ATTR_FATTR_PRECHANGE; - } - if ((fattr->valid & NFS_ATTR_FATTR_CTIME) != 0 && - (fattr->valid & NFS_ATTR_FATTR_PRECTIME) == 0) { - memcpy(&fattr->pre_ctime, &inode->i_ctime, sizeof(fattr->pre_ctime)); - fattr->valid |= NFS_ATTR_FATTR_PRECTIME; - } - if ((fattr->valid & NFS_ATTR_FATTR_MTIME) != 0 && - (fattr->valid & NFS_ATTR_FATTR_PREMTIME) == 0) { - memcpy(&fattr->pre_mtime, &inode->i_mtime, sizeof(fattr->pre_mtime)); - fattr->valid |= NFS_ATTR_FATTR_PREMTIME; - } - if ((fattr->valid & NFS_ATTR_FATTR_SIZE) != 0 && - (fattr->valid & NFS_ATTR_FATTR_PRESIZE) == 0) { - fattr->pre_size = i_size_read(inode); - fattr->valid |= NFS_ATTR_FATTR_PRESIZE; - } -out_noforce: - status = nfs_post_op_update_inode_locked(inode, fattr); - spin_unlock(&inode->i_lock); - return status; -} - -/* - * Many nfs protocol calls return the new file attributes after - * an operation. Here we update the inode to reflect the state - * of the server's inode. - * - * This is a bit tricky because we have to make sure all dirty pages - * have been sent off to the server before calling invalidate_inode_pages. - * To make sure no other process adds more write requests while we try - * our best to flush them, we make them sleep during the attribute refresh. - * - * A very similar scenario holds for the dir cache. - */ -static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) -{ - struct nfs_server *server; - struct nfs_inode *nfsi = NFS_I(inode); - loff_t cur_isize, new_isize; - unsigned long invalid = 0; - unsigned long now = jiffies; - unsigned long save_cache_validity; - - dfprintk(VFS, "NFS: %s(%s/%ld fh_crc=0x%08x ct=%d info=0x%x)\n", - __func__, inode->i_sb->s_id, inode->i_ino, - nfs_display_fhandle_hash(NFS_FH(inode)), - atomic_read(&inode->i_count), fattr->valid); - - if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid) - goto out_fileid; - - /* - * Make sure the inode's type hasn't changed. - */ - if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) - goto out_changed; - - server = NFS_SERVER(inode); - /* Update the fsid? */ - if (S_ISDIR(inode->i_mode) && (fattr->valid & NFS_ATTR_FATTR_FSID) && - !nfs_fsid_equal(&server->fsid, &fattr->fsid) && - !IS_AUTOMOUNT(inode)) - server->fsid = fattr->fsid; - - /* - * Update the read time so we don't revalidate too often. - */ - nfsi->read_cache_jiffies = fattr->time_start; - - save_cache_validity = nfsi->cache_validity; - nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR - | NFS_INO_INVALID_ATIME - | NFS_INO_REVAL_FORCED - | NFS_INO_REVAL_PAGECACHE); - - /* Do atomic weak cache consistency updates */ - invalid |= nfs_wcc_update_inode(inode, fattr); - - /* More cache consistency checks */ - if (fattr->valid & NFS_ATTR_FATTR_CHANGE) { - if (inode->i_version != fattr->change_attr) { - dprintk("NFS: change_attr change on server for file %s/%ld\n", - inode->i_sb->s_id, inode->i_ino); - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; - if (S_ISDIR(inode->i_mode)) - nfs_force_lookup_revalidate(inode); - inode->i_version = fattr->change_attr; - } - } else if (server->caps & NFS_CAP_CHANGE_ATTR) - invalid |= save_cache_validity; - - if (fattr->valid & NFS_ATTR_FATTR_MTIME) { - /* NFSv2/v3: Check if the mtime agrees */ - if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) { - dprintk("NFS: mtime change on server for file %s/%ld\n", - inode->i_sb->s_id, inode->i_ino); - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; - if (S_ISDIR(inode->i_mode)) - nfs_force_lookup_revalidate(inode); - memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); - } - } else if (server->caps & NFS_CAP_MTIME) - invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR - | NFS_INO_INVALID_DATA - | NFS_INO_REVAL_PAGECACHE - | NFS_INO_REVAL_FORCED); - - if (fattr->valid & NFS_ATTR_FATTR_CTIME) { - /* If ctime has changed we should definitely clear access+acl caches */ - if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) { - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; - /* and probably clear data for a directory too as utimes can cause - * havoc with our cache. - */ - if (S_ISDIR(inode->i_mode)) { - invalid |= NFS_INO_INVALID_DATA; - nfs_force_lookup_revalidate(inode); - } - memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime)); - } - } else if (server->caps & NFS_CAP_CTIME) - invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR - | NFS_INO_INVALID_ACCESS - | NFS_INO_INVALID_ACL - | NFS_INO_REVAL_FORCED); - - /* Check if our cached file size is stale */ - if (fattr->valid & NFS_ATTR_FATTR_SIZE) { - new_isize = nfs_size_to_loff_t(fattr->size); - cur_isize = i_size_read(inode); - if (new_isize != cur_isize) { - /* Do we perhaps have any outstanding writes, or has - * the file grown beyond our last write? */ - if ((nfsi->npages == 0 && !test_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) || - new_isize > cur_isize) { - i_size_write(inode, new_isize); - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; - } - dprintk("NFS: isize change on server for file %s/%ld " - "(%Ld to %Ld)\n", - inode->i_sb->s_id, - inode->i_ino, - (long long)cur_isize, - (long long)new_isize); - } - } else - invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR - | NFS_INO_REVAL_PAGECACHE - | NFS_INO_REVAL_FORCED); - - - if (fattr->valid & NFS_ATTR_FATTR_ATIME) - memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime)); - else if (server->caps & NFS_CAP_ATIME) - invalid |= save_cache_validity & (NFS_INO_INVALID_ATIME - | NFS_INO_REVAL_FORCED); - - if (fattr->valid & NFS_ATTR_FATTR_MODE) { - if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)) { - umode_t newmode = inode->i_mode & S_IFMT; - newmode |= fattr->mode & S_IALLUGO; - inode->i_mode = newmode; - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; - } - } else if (server->caps & NFS_CAP_MODE) - invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR - | NFS_INO_INVALID_ACCESS - | NFS_INO_INVALID_ACL - | NFS_INO_REVAL_FORCED); - - if (fattr->valid & NFS_ATTR_FATTR_OWNER) { - if (inode->i_uid != fattr->uid) { - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; - inode->i_uid = fattr->uid; - } - } else if (server->caps & NFS_CAP_OWNER) - invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR - | NFS_INO_INVALID_ACCESS - | NFS_INO_INVALID_ACL - | NFS_INO_REVAL_FORCED); - - if (fattr->valid & NFS_ATTR_FATTR_GROUP) { - if (inode->i_gid != fattr->gid) { - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; - inode->i_gid = fattr->gid; - } - } else if (server->caps & NFS_CAP_OWNER_GROUP) - invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR - | NFS_INO_INVALID_ACCESS - | NFS_INO_INVALID_ACL - | NFS_INO_REVAL_FORCED); - - if (fattr->valid & NFS_ATTR_FATTR_NLINK) { - if (inode->i_nlink != fattr->nlink) { - invalid |= NFS_INO_INVALID_ATTR; - if (S_ISDIR(inode->i_mode)) - invalid |= NFS_INO_INVALID_DATA; - set_nlink(inode, fattr->nlink); - } - } else if (server->caps & NFS_CAP_NLINK) - invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR - | NFS_INO_REVAL_FORCED); - - if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) { - /* - * report the blocks in 512byte units - */ - inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used); - } - if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED) - inode->i_blocks = fattr->du.nfs2.blocks; - - /* Update attrtimeo value if we're out of the unstable period */ - if (invalid & NFS_INO_INVALID_ATTR) { - nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE); - nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); - nfsi->attrtimeo_timestamp = now; - nfsi->attr_gencount = nfs_inc_attr_generation_counter(); - } else { - if (!time_in_range_open(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) { - if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode)) - nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode); - nfsi->attrtimeo_timestamp = now; - } - } - invalid &= ~NFS_INO_INVALID_ATTR; - /* Don't invalidate the data if we were to blame */ - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) - || S_ISLNK(inode->i_mode))) - invalid &= ~NFS_INO_INVALID_DATA; - if (!nfs_have_delegation(inode, FMODE_READ) || - (save_cache_validity & NFS_INO_REVAL_FORCED)) - nfsi->cache_validity |= invalid; - - return 0; - out_changed: - /* - * Big trouble! The inode has become a different object. - */ - printk(KERN_DEBUG "NFS: %s: inode %ld mode changed, %07o to %07o\n", - __func__, inode->i_ino, inode->i_mode, fattr->mode); - out_err: - /* - * 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); - return -ESTALE; - - out_fileid: - printk(KERN_ERR "NFS: server %s error: fileid changed\n" - "fsid %s: expected fileid 0x%Lx, got 0x%Lx\n", - NFS_SERVER(inode)->nfs_client->cl_hostname, inode->i_sb->s_id, - (long long)nfsi->fileid, (long long)fattr->fileid); - goto out_err; -} - - -#ifdef CONFIG_NFS_V4 - -/* - * Clean out any remaining NFSv4 state that might be left over due - * to open() calls that passed nfs_atomic_lookup, but failed to call - * nfs_open(). - */ -void nfs4_evict_inode(struct inode *inode) -{ - truncate_inode_pages(&inode->i_data, 0); - end_writeback(inode); - pnfs_return_layout(inode); - pnfs_destroy_layout(NFS_I(inode)); - /* If we are holding a delegation, return it! */ - nfs_inode_return_delegation_noreclaim(inode); - /* First call standard NFS clear_inode() code */ - nfs_clear_inode(inode); -} -#endif - -struct inode *nfs_alloc_inode(struct super_block *sb) -{ - struct nfs_inode *nfsi; - nfsi = (struct nfs_inode *)kmem_cache_alloc(nfs_inode_cachep, GFP_KERNEL); - if (!nfsi) - return NULL; - nfsi->flags = 0UL; - nfsi->cache_validity = 0UL; -#ifdef CONFIG_NFS_V3_ACL - nfsi->acl_access = ERR_PTR(-EAGAIN); - nfsi->acl_default = ERR_PTR(-EAGAIN); -#endif -#ifdef CONFIG_NFS_V4 - nfsi->nfs4_acl = NULL; -#endif /* CONFIG_NFS_V4 */ - return &nfsi->vfs_inode; -} - -static void nfs_i_callback(struct rcu_head *head) -{ - struct inode *inode = container_of(head, struct inode, i_rcu); - kmem_cache_free(nfs_inode_cachep, NFS_I(inode)); -} - -void nfs_destroy_inode(struct inode *inode) -{ - call_rcu(&inode->i_rcu, nfs_i_callback); -} - -static inline void nfs4_init_once(struct nfs_inode *nfsi) -{ -#ifdef CONFIG_NFS_V4 - INIT_LIST_HEAD(&nfsi->open_states); - nfsi->delegation = NULL; - nfsi->delegation_state = 0; - init_rwsem(&nfsi->rwsem); - nfsi->layout = NULL; - atomic_set(&nfsi->commits_outstanding, 0); -#endif -} - -static void init_once(void *foo) -{ - struct nfs_inode *nfsi = (struct nfs_inode *) foo; - - inode_init_once(&nfsi->vfs_inode); - INIT_LIST_HEAD(&nfsi->open_files); - INIT_LIST_HEAD(&nfsi->access_cache_entry_lru); - INIT_LIST_HEAD(&nfsi->access_cache_inode_lru); - INIT_LIST_HEAD(&nfsi->commit_list); - nfsi->npages = 0; - nfsi->ncommit = 0; - atomic_set(&nfsi->silly_count, 1); - INIT_HLIST_HEAD(&nfsi->silly_list); - init_waitqueue_head(&nfsi->waitqueue); - nfs4_init_once(nfsi); -} - -static int __init nfs_init_inodecache(void) -{ - nfs_inode_cachep = kmem_cache_create("nfs_inode_cache", - sizeof(struct nfs_inode), - 0, (SLAB_RECLAIM_ACCOUNT| - SLAB_MEM_SPREAD), - init_once); - if (nfs_inode_cachep == NULL) - return -ENOMEM; - - return 0; -} - -static void nfs_destroy_inodecache(void) -{ - kmem_cache_destroy(nfs_inode_cachep); -} - -struct workqueue_struct *nfsiod_workqueue; - -/* - * start up the nfsiod workqueue - */ -static int nfsiod_start(void) -{ - struct workqueue_struct *wq; - dprintk("RPC: creating workqueue nfsiod\n"); - wq = alloc_workqueue("nfsiod", WQ_MEM_RECLAIM, 0); - if (wq == NULL) - return -ENOMEM; - nfsiod_workqueue = wq; - return 0; -} - -/* - * Destroy the nfsiod workqueue - */ -static void nfsiod_stop(void) -{ - struct workqueue_struct *wq; - - wq = nfsiod_workqueue; - if (wq == NULL) - return; - nfsiod_workqueue = NULL; - destroy_workqueue(wq); -} - -int nfs_net_id; -EXPORT_SYMBOL_GPL(nfs_net_id); - -static int nfs_net_init(struct net *net) -{ - nfs_clients_init(net); - return nfs_dns_resolver_cache_init(net); -} - -static void nfs_net_exit(struct net *net) -{ - nfs_dns_resolver_cache_destroy(net); - nfs_cleanup_cb_ident_idr(net); -} - -static struct pernet_operations nfs_net_ops = { - .init = nfs_net_init, - .exit = nfs_net_exit, - .id = &nfs_net_id, - .size = sizeof(struct nfs_net), -}; - -/* - * Initialize NFS - */ -static int __init init_nfs_fs(void) -{ - int err; - - err = nfs_idmap_init(); - if (err < 0) - goto out10; - - err = nfs_dns_resolver_init(); - if (err < 0) - goto out9; - - err = register_pernet_subsys(&nfs_net_ops); - if (err < 0) - goto out8; - - err = nfs_fscache_register(); - if (err < 0) - goto out7; - - err = nfsiod_start(); - if (err) - goto out6; - - err = nfs_fs_proc_init(); - if (err) - goto out5; - - err = nfs_init_nfspagecache(); - if (err) - goto out4; - - err = nfs_init_inodecache(); - if (err) - goto out3; - - err = nfs_init_readpagecache(); - if (err) - goto out2; - - err = nfs_init_writepagecache(); - if (err) - goto out1; - - err = nfs_init_directcache(); - if (err) - goto out0; - -#ifdef CONFIG_PROC_FS - rpc_proc_register(&init_net, &nfs_rpcstat); -#endif - if ((err = register_nfs_fs()) != 0) - goto out; - return 0; -out: -#ifdef CONFIG_PROC_FS - rpc_proc_unregister(&init_net, "nfs"); -#endif - nfs_destroy_directcache(); -out0: - nfs_destroy_writepagecache(); -out1: - nfs_destroy_readpagecache(); -out2: - nfs_destroy_inodecache(); -out3: - nfs_destroy_nfspagecache(); -out4: - nfs_fs_proc_exit(); -out5: - nfsiod_stop(); -out6: - nfs_fscache_unregister(); -out7: - unregister_pernet_subsys(&nfs_net_ops); -out8: - nfs_dns_resolver_destroy(); -out9: - nfs_idmap_quit(); -out10: - return err; -} - -static void __exit exit_nfs_fs(void) -{ - nfs_destroy_directcache(); - nfs_destroy_writepagecache(); - nfs_destroy_readpagecache(); - nfs_destroy_inodecache(); - nfs_destroy_nfspagecache(); - nfs_fscache_unregister(); - unregister_pernet_subsys(&nfs_net_ops); - nfs_dns_resolver_destroy(); - nfs_idmap_quit(); -#ifdef CONFIG_PROC_FS - rpc_proc_unregister(&init_net, "nfs"); -#endif - unregister_nfs_fs(); - nfs_fs_proc_exit(); - nfsiod_stop(); -} - -/* Not quite true; I just maintain it */ -MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); -MODULE_LICENSE("GPL"); -module_param(enable_ino64, bool, 0644); - -module_init(init_nfs_fs) -module_exit(exit_nfs_fs) diff --git a/ANDROID_3.4.5/fs/nfs/internal.h b/ANDROID_3.4.5/fs/nfs/internal.h deleted file mode 100644 index b777bdab..00000000 --- a/ANDROID_3.4.5/fs/nfs/internal.h +++ /dev/null @@ -1,468 +0,0 @@ -/* - * NFS internal definitions - */ - -#include "nfs4_fs.h" -#include <linux/mount.h> -#include <linux/security.h> - -#define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS) - -struct nfs_string; - -/* Maximum number of readahead requests - * FIXME: this should really be a sysctl so that users may tune it to suit - * their needs. People that do NFS over a slow network, might for - * instance want to reduce it to something closer to 1 for improved - * interactive response. - */ -#define NFS_MAX_READAHEAD (RPC_DEF_SLOT_TABLE - 1) - -/* - * Determine if sessions are in use. - */ -static inline int nfs4_has_session(const struct nfs_client *clp) -{ -#ifdef CONFIG_NFS_V4_1 - if (clp->cl_session) - return 1; -#endif /* CONFIG_NFS_V4_1 */ - return 0; -} - -static inline int nfs4_has_persistent_session(const struct nfs_client *clp) -{ -#ifdef CONFIG_NFS_V4_1 - if (nfs4_has_session(clp)) - return (clp->cl_session->flags & SESSION4_PERSIST); -#endif /* CONFIG_NFS_V4_1 */ - return 0; -} - -static inline void nfs_attr_check_mountpoint(struct super_block *parent, struct nfs_fattr *fattr) -{ - if (!nfs_fsid_equal(&NFS_SB(parent)->fsid, &fattr->fsid)) - fattr->valid |= NFS_ATTR_FATTR_MOUNTPOINT; -} - -static inline int nfs_attr_use_mounted_on_fileid(struct nfs_fattr *fattr) -{ - if (((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) == 0) || - (((fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT) == 0) && - ((fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) == 0))) - return 0; - - fattr->fileid = fattr->mounted_on_fileid; - return 1; -} - -struct nfs_clone_mount { - const struct super_block *sb; - const struct dentry *dentry; - struct nfs_fh *fh; - struct nfs_fattr *fattr; - char *hostname; - char *mnt_path; - struct sockaddr *addr; - size_t addrlen; - rpc_authflavor_t authflavor; -}; - -/* - * Note: RFC 1813 doesn't limit the number of auth flavors that - * a server can return, so make something up. - */ -#define NFS_MAX_SECFLAVORS (12) - -/* - * Value used if the user did not specify a port value. - */ -#define NFS_UNSPEC_PORT (-1) - -/* - * Maximum number of pages that readdir can use for creating - * a vmapped array of pages. - */ -#define NFS_MAX_READDIR_PAGES 8 - -/* - * In-kernel mount arguments - */ -struct nfs_parsed_mount_data { - int flags; - int rsize, wsize; - int timeo, retrans; - int acregmin, acregmax, - acdirmin, acdirmax; - int namlen; - unsigned int options; - unsigned int bsize; - unsigned int auth_flavor_len; - rpc_authflavor_t auth_flavors[1]; - char *client_address; - unsigned int version; - unsigned int minorversion; - char *fscache_uniq; - - struct { - struct sockaddr_storage address; - size_t addrlen; - char *hostname; - u32 version; - int port; - unsigned short protocol; - } mount_server; - - struct { - struct sockaddr_storage address; - size_t addrlen; - char *hostname; - char *export_path; - int port; - unsigned short protocol; - } nfs_server; - - struct security_mnt_opts lsm_opts; - struct net *net; -}; - -/* mount_clnt.c */ -struct nfs_mount_request { - struct sockaddr *sap; - size_t salen; - char *hostname; - char *dirpath; - u32 version; - unsigned short protocol; - struct nfs_fh *fh; - int noresvport; - unsigned int *auth_flav_len; - rpc_authflavor_t *auth_flavs; - struct net *net; -}; - -extern int nfs_mount(struct nfs_mount_request *info); -extern void nfs_umount(const struct nfs_mount_request *info); - -/* client.c */ -extern const struct rpc_program nfs_program; -extern void nfs_clients_init(struct net *net); - -extern void nfs_cleanup_cb_ident_idr(struct net *); -extern void nfs_put_client(struct nfs_client *); -extern struct nfs_client *nfs4_find_client_ident(struct net *, int); -extern struct nfs_client * -nfs4_find_client_sessionid(struct net *, const struct sockaddr *, - struct nfs4_sessionid *); -extern struct nfs_server *nfs_create_server( - const struct nfs_parsed_mount_data *, - struct nfs_fh *); -extern struct nfs_server *nfs4_create_server( - const struct nfs_parsed_mount_data *, - struct nfs_fh *); -extern struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *, - struct nfs_fh *); -extern void nfs_free_server(struct nfs_server *server); -extern struct nfs_server *nfs_clone_server(struct nfs_server *, - struct nfs_fh *, - struct nfs_fattr *, - rpc_authflavor_t); -extern void nfs_mark_client_ready(struct nfs_client *clp, int state); -extern int nfs4_check_client_ready(struct nfs_client *clp); -extern struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp, - const struct sockaddr *ds_addr, - int ds_addrlen, int ds_proto); -#ifdef CONFIG_PROC_FS -extern int __init nfs_fs_proc_init(void); -extern void nfs_fs_proc_exit(void); -#else -static inline int nfs_fs_proc_init(void) -{ - return 0; -} -static inline void nfs_fs_proc_exit(void) -{ -} -#endif - -/* nfs4namespace.c */ -#ifdef CONFIG_NFS_V4 -extern struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry); -#else -static inline -struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry) -{ - return ERR_PTR(-ENOENT); -} -#endif - -/* callback_xdr.c */ -extern struct svc_version nfs4_callback_version1; -extern struct svc_version nfs4_callback_version4; - -/* pagelist.c */ -extern int __init nfs_init_nfspagecache(void); -extern void nfs_destroy_nfspagecache(void); -extern int __init nfs_init_readpagecache(void); -extern void nfs_destroy_readpagecache(void); -extern int __init nfs_init_writepagecache(void); -extern void nfs_destroy_writepagecache(void); - -extern int __init nfs_init_directcache(void); -extern void nfs_destroy_directcache(void); - -/* nfs2xdr.c */ -extern int nfs_stat_to_errno(enum nfs_stat); -extern struct rpc_procinfo nfs_procedures[]; -extern int nfs2_decode_dirent(struct xdr_stream *, - struct nfs_entry *, int); - -/* nfs3xdr.c */ -extern struct rpc_procinfo nfs3_procedures[]; -extern int nfs3_decode_dirent(struct xdr_stream *, - struct nfs_entry *, int); - -/* nfs4xdr.c */ -#ifdef CONFIG_NFS_V4 -extern int nfs4_decode_dirent(struct xdr_stream *, - struct nfs_entry *, int); -#endif -#ifdef CONFIG_NFS_V4_1 -extern const u32 nfs41_maxread_overhead; -extern const u32 nfs41_maxwrite_overhead; -#endif - -/* nfs4proc.c */ -#ifdef CONFIG_NFS_V4 -extern struct rpc_procinfo nfs4_procedures[]; -#endif - -extern int nfs4_init_ds_session(struct nfs_client *clp); - -/* proc.c */ -void nfs_close_context(struct nfs_open_context *ctx, int is_sync); -extern int nfs_init_client(struct nfs_client *clp, - const struct rpc_timeout *timeparms, - const char *ip_addr, rpc_authflavor_t authflavour, - int noresvport); - -/* dir.c */ -extern int nfs_access_cache_shrinker(struct shrinker *shrink, - struct shrink_control *sc); - -/* inode.c */ -extern struct workqueue_struct *nfsiod_workqueue; -extern struct inode *nfs_alloc_inode(struct super_block *sb); -extern void nfs_destroy_inode(struct inode *); -extern int nfs_write_inode(struct inode *, struct writeback_control *); -extern void nfs_evict_inode(struct inode *); -#ifdef CONFIG_NFS_V4 -extern void nfs4_evict_inode(struct inode *); -#endif -void nfs_zap_acl_cache(struct inode *inode); -extern int nfs_wait_bit_killable(void *word); - -/* super.c */ -extern struct file_system_type nfs_xdev_fs_type; -#ifdef CONFIG_NFS_V4 -extern struct file_system_type nfs4_xdev_fs_type; -extern struct file_system_type nfs4_referral_fs_type; -#endif - -extern struct rpc_stat nfs_rpcstat; - -extern int __init register_nfs_fs(void); -extern void __exit unregister_nfs_fs(void); -extern void nfs_sb_active(struct super_block *sb); -extern void nfs_sb_deactive(struct super_block *sb); - -/* namespace.c */ -extern char *nfs_path(char **p, struct dentry *dentry, - char *buffer, ssize_t buflen); -extern struct vfsmount *nfs_d_automount(struct path *path); -#ifdef CONFIG_NFS_V4 -rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *); -#endif - -/* getroot.c */ -extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *, - const char *); -#ifdef CONFIG_NFS_V4 -extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *, - const char *); - -extern int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh); -#endif - -struct nfs_pageio_descriptor; -/* read.c */ -extern int nfs_initiate_read(struct nfs_read_data *data, struct rpc_clnt *clnt, - const struct rpc_call_ops *call_ops); -extern void nfs_read_prepare(struct rpc_task *task, void *calldata); -extern int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, - struct list_head *head); - -extern void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio, - struct inode *inode); -extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio); -extern void nfs_readdata_release(struct nfs_read_data *rdata); - -/* write.c */ -extern int nfs_generic_flush(struct nfs_pageio_descriptor *desc, - struct list_head *head); -extern void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio, - struct inode *inode, int ioflags); -extern void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio); -extern void nfs_writedata_release(struct nfs_write_data *wdata); -extern void nfs_commit_free(struct nfs_write_data *p); -extern int nfs_initiate_write(struct nfs_write_data *data, - struct rpc_clnt *clnt, - const struct rpc_call_ops *call_ops, - int how); -extern void nfs_write_prepare(struct rpc_task *task, void *calldata); -extern int nfs_initiate_commit(struct nfs_write_data *data, - struct rpc_clnt *clnt, - const struct rpc_call_ops *call_ops, - int how); -extern void nfs_init_commit(struct nfs_write_data *data, - struct list_head *head, - struct pnfs_layout_segment *lseg); -void nfs_retry_commit(struct list_head *page_list, - struct pnfs_layout_segment *lseg); -void nfs_commit_clear_lock(struct nfs_inode *nfsi); -void nfs_commitdata_release(void *data); -void nfs_commit_release_pages(struct nfs_write_data *data); -void nfs_request_add_commit_list(struct nfs_page *req, struct list_head *head); -void nfs_request_remove_commit_list(struct nfs_page *req); - -#ifdef CONFIG_MIGRATION -extern int nfs_migrate_page(struct address_space *, - struct page *, struct page *, enum migrate_mode); -#else -#define nfs_migrate_page NULL -#endif - -/* nfs4proc.c */ -extern void __nfs4_read_done_cb(struct nfs_read_data *); -extern void nfs4_reset_read(struct rpc_task *task, struct nfs_read_data *data); -extern int nfs4_init_client(struct nfs_client *clp, - const struct rpc_timeout *timeparms, - const char *ip_addr, - rpc_authflavor_t authflavour, - int noresvport); -extern void nfs4_reset_write(struct rpc_task *task, struct nfs_write_data *data); -extern int _nfs4_call_sync(struct rpc_clnt *clnt, - struct nfs_server *server, - struct rpc_message *msg, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int cache_reply); -extern int _nfs4_call_sync_session(struct rpc_clnt *clnt, - struct nfs_server *server, - struct rpc_message *msg, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int cache_reply); - -/* - * Determine the device name as a string - */ -static inline char *nfs_devname(struct dentry *dentry, - char *buffer, ssize_t buflen) -{ - char *dummy; - return nfs_path(&dummy, dentry, buffer, buflen); -} - -/* - * Determine the actual block size (and log2 thereof) - */ -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 char nrbits; - - for (nrbits = 31; nrbits && !(bsize & (1 << nrbits)); nrbits--) - ; - bsize = 1 << nrbits; - if (nrbitsp) - *nrbitsp = nrbits; - } - - return bsize; -} - -/* - * Calculate the number of 512byte blocks used. - */ -static inline blkcnt_t nfs_calc_block_size(u64 tsize) -{ - blkcnt_t used = (tsize + 511) >> 9; - 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 < NFS_MIN_FILE_IO_SIZE) - bsize = NFS_DEF_FILE_IO_SIZE; - else if (bsize >= NFS_MAX_FILE_IO_SIZE) - bsize = NFS_MAX_FILE_IO_SIZE; - - return nfs_block_bits(bsize, nrbitsp); -} - -/* - * Determine the maximum file size for a superblock - */ -static inline -void nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize) -{ - sb->s_maxbytes = (loff_t)maxfilesize; - if (sb->s_maxbytes > MAX_LFS_FILESIZE || sb->s_maxbytes <= 0) - sb->s_maxbytes = MAX_LFS_FILESIZE; -} - -/* - * Determine the number of bytes of data the page contains - */ -static inline -unsigned int nfs_page_length(struct page *page) -{ - loff_t i_size = i_size_read(page->mapping->host); - - if (i_size > 0) { - pgoff_t end_index = (i_size - 1) >> PAGE_CACHE_SHIFT; - if (page->index < end_index) - return PAGE_CACHE_SIZE; - if (page->index == end_index) - return ((i_size - 1) & ~PAGE_CACHE_MASK) + 1; - } - return 0; -} - -/* - * Convert a umode to a dirent->d_type - */ -static inline -unsigned char nfs_umode_to_dtype(umode_t mode) -{ - return (mode >> 12) & 15; -} - -/* - * Determine the number of pages in an array of length 'len' and - * with a base offset of 'base' - */ -static inline -unsigned int nfs_page_array_len(unsigned int base, size_t len) -{ - return ((unsigned long)len + (unsigned long)base + - PAGE_SIZE - 1) >> PAGE_SHIFT; -} - diff --git a/ANDROID_3.4.5/fs/nfs/iostat.h b/ANDROID_3.4.5/fs/nfs/iostat.h deleted file mode 100644 index c5832487..00000000 --- a/ANDROID_3.4.5/fs/nfs/iostat.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * linux/fs/nfs/iostat.h - * - * Declarations for NFS client per-mount statistics - * - * Copyright (C) 2005, 2006 Chuck Lever <cel@netapp.com> - * - */ - -#ifndef _NFS_IOSTAT -#define _NFS_IOSTAT - -#include <linux/percpu.h> -#include <linux/cache.h> -#include <linux/nfs_iostat.h> - -struct nfs_iostats { - unsigned long long bytes[__NFSIOS_BYTESMAX]; -#ifdef CONFIG_NFS_FSCACHE - unsigned long long fscache[__NFSIOS_FSCACHEMAX]; -#endif - unsigned long events[__NFSIOS_COUNTSMAX]; -} ____cacheline_aligned; - -static inline void nfs_inc_server_stats(const struct nfs_server *server, - enum nfs_stat_eventcounters stat) -{ - this_cpu_inc(server->io_stats->events[stat]); -} - -static inline void nfs_inc_stats(const struct inode *inode, - enum nfs_stat_eventcounters stat) -{ - nfs_inc_server_stats(NFS_SERVER(inode), stat); -} - -static inline void nfs_add_server_stats(const struct nfs_server *server, - enum nfs_stat_bytecounters stat, - long addend) -{ - this_cpu_add(server->io_stats->bytes[stat], addend); -} - -static inline void nfs_add_stats(const struct inode *inode, - enum nfs_stat_bytecounters stat, - long addend) -{ - nfs_add_server_stats(NFS_SERVER(inode), stat, addend); -} - -#ifdef CONFIG_NFS_FSCACHE -static inline void nfs_add_fscache_stats(struct inode *inode, - enum nfs_stat_fscachecounters stat, - long addend) -{ - this_cpu_add(NFS_SERVER(inode)->io_stats->fscache[stat], addend); -} -#endif - -static inline struct nfs_iostats __percpu *nfs_alloc_iostats(void) -{ - return alloc_percpu(struct nfs_iostats); -} - -static inline void nfs_free_iostats(struct nfs_iostats __percpu *stats) -{ - if (stats != NULL) - free_percpu(stats); -} - -#endif /* _NFS_IOSTAT */ diff --git a/ANDROID_3.4.5/fs/nfs/mount_clnt.c b/ANDROID_3.4.5/fs/nfs/mount_clnt.c deleted file mode 100644 index 8e65c7f1..00000000 --- a/ANDROID_3.4.5/fs/nfs/mount_clnt.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * In-kernel MOUNT protocol client - * - * Copyright (C) 1997, Olaf Kirch <okir@monad.swb.de> - */ - -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/uio.h> -#include <linux/net.h> -#include <linux/in.h> -#include <linux/sunrpc/clnt.h> -#include <linux/sunrpc/sched.h> -#include <linux/nfs_fs.h> -#include "internal.h" - -#ifdef NFS_DEBUG -# define NFSDBG_FACILITY NFSDBG_MOUNT -#endif - -/* - * Defined by RFC 1094, section A.3; and RFC 1813, section 5.1.4 - */ -#define MNTPATHLEN (1024) - -/* - * XDR data type sizes - */ -#define encode_dirpath_sz (1 + XDR_QUADLEN(MNTPATHLEN)) -#define MNT_status_sz (1) -#define MNT_fhs_status_sz (1) -#define MNT_fhandle_sz XDR_QUADLEN(NFS2_FHSIZE) -#define MNT_fhandle3_sz (1 + XDR_QUADLEN(NFS3_FHSIZE)) -#define MNT_authflav3_sz (1 + NFS_MAX_SECFLAVORS) - -/* - * XDR argument and result sizes - */ -#define MNT_enc_dirpath_sz encode_dirpath_sz -#define MNT_dec_mountres_sz (MNT_status_sz + MNT_fhandle_sz) -#define MNT_dec_mountres3_sz (MNT_status_sz + MNT_fhandle_sz + \ - MNT_authflav3_sz) - -/* - * Defined by RFC 1094, section A.5 - */ -enum { - MOUNTPROC_NULL = 0, - MOUNTPROC_MNT = 1, - MOUNTPROC_DUMP = 2, - MOUNTPROC_UMNT = 3, - MOUNTPROC_UMNTALL = 4, - MOUNTPROC_EXPORT = 5, -}; - -/* - * Defined by RFC 1813, section 5.2 - */ -enum { - MOUNTPROC3_NULL = 0, - MOUNTPROC3_MNT = 1, - MOUNTPROC3_DUMP = 2, - MOUNTPROC3_UMNT = 3, - MOUNTPROC3_UMNTALL = 4, - MOUNTPROC3_EXPORT = 5, -}; - -static const struct rpc_program mnt_program; - -/* - * Defined by OpenGroup XNFS Version 3W, chapter 8 - */ -enum mountstat { - MNT_OK = 0, - MNT_EPERM = 1, - MNT_ENOENT = 2, - MNT_EACCES = 13, - MNT_EINVAL = 22, -}; - -static struct { - u32 status; - int errno; -} mnt_errtbl[] = { - { .status = MNT_OK, .errno = 0, }, - { .status = MNT_EPERM, .errno = -EPERM, }, - { .status = MNT_ENOENT, .errno = -ENOENT, }, - { .status = MNT_EACCES, .errno = -EACCES, }, - { .status = MNT_EINVAL, .errno = -EINVAL, }, -}; - -/* - * Defined by RFC 1813, section 5.1.5 - */ -enum mountstat3 { - MNT3_OK = 0, /* no error */ - MNT3ERR_PERM = 1, /* Not owner */ - MNT3ERR_NOENT = 2, /* No such file or directory */ - MNT3ERR_IO = 5, /* I/O error */ - MNT3ERR_ACCES = 13, /* Permission denied */ - MNT3ERR_NOTDIR = 20, /* Not a directory */ - MNT3ERR_INVAL = 22, /* Invalid argument */ - MNT3ERR_NAMETOOLONG = 63, /* Filename too long */ - MNT3ERR_NOTSUPP = 10004, /* Operation not supported */ - MNT3ERR_SERVERFAULT = 10006, /* A failure on the server */ -}; - -static struct { - u32 status; - int errno; -} mnt3_errtbl[] = { - { .status = MNT3_OK, .errno = 0, }, - { .status = MNT3ERR_PERM, .errno = -EPERM, }, - { .status = MNT3ERR_NOENT, .errno = -ENOENT, }, - { .status = MNT3ERR_IO, .errno = -EIO, }, - { .status = MNT3ERR_ACCES, .errno = -EACCES, }, - { .status = MNT3ERR_NOTDIR, .errno = -ENOTDIR, }, - { .status = MNT3ERR_INVAL, .errno = -EINVAL, }, - { .status = MNT3ERR_NAMETOOLONG, .errno = -ENAMETOOLONG, }, - { .status = MNT3ERR_NOTSUPP, .errno = -ENOTSUPP, }, - { .status = MNT3ERR_SERVERFAULT, .errno = -EREMOTEIO, }, -}; - -struct mountres { - int errno; - struct nfs_fh *fh; - unsigned int *auth_count; - rpc_authflavor_t *auth_flavors; -}; - -struct mnt_fhstatus { - u32 status; - struct nfs_fh *fh; -}; - -/** - * nfs_mount - Obtain an NFS file handle for the given host and path - * @info: pointer to mount request arguments - * - * Uses default timeout parameters specified by underlying transport. - */ -int nfs_mount(struct nfs_mount_request *info) -{ - struct mountres result = { - .fh = info->fh, - .auth_count = info->auth_flav_len, - .auth_flavors = info->auth_flavs, - }; - struct rpc_message msg = { - .rpc_argp = info->dirpath, - .rpc_resp = &result, - }; - struct rpc_create_args args = { - .net = info->net, - .protocol = info->protocol, - .address = info->sap, - .addrsize = info->salen, - .servername = info->hostname, - .program = &mnt_program, - .version = info->version, - .authflavor = RPC_AUTH_UNIX, - }; - struct rpc_clnt *mnt_clnt; - int status; - - dprintk("NFS: sending MNT request for %s:%s\n", - (info->hostname ? info->hostname : "server"), - info->dirpath); - - if (info->noresvport) - args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; - - mnt_clnt = rpc_create(&args); - if (IS_ERR(mnt_clnt)) - goto out_clnt_err; - - if (info->version == NFS_MNT3_VERSION) - msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC3_MNT]; - else - msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC_MNT]; - - status = rpc_call_sync(mnt_clnt, &msg, 0); - rpc_shutdown_client(mnt_clnt); - - if (status < 0) - goto out_call_err; - if (result.errno != 0) - goto out_mnt_err; - - dprintk("NFS: MNT request succeeded\n"); - status = 0; - -out: - return status; - -out_clnt_err: - status = PTR_ERR(mnt_clnt); - dprintk("NFS: failed to create MNT RPC client, status=%d\n", status); - goto out; - -out_call_err: - dprintk("NFS: MNT request failed, status=%d\n", status); - goto out; - -out_mnt_err: - dprintk("NFS: MNT server returned result %d\n", result.errno); - status = result.errno; - goto out; -} - -/** - * nfs_umount - Notify a server that we have unmounted this export - * @info: pointer to umount request arguments - * - * MOUNTPROC_UMNT is advisory, so we set a short timeout, and always - * use UDP. - */ -void nfs_umount(const struct nfs_mount_request *info) -{ - static const struct rpc_timeout nfs_umnt_timeout = { - .to_initval = 1 * HZ, - .to_maxval = 3 * HZ, - .to_retries = 2, - }; - struct rpc_create_args args = { - .net = info->net, - .protocol = IPPROTO_UDP, - .address = info->sap, - .addrsize = info->salen, - .timeout = &nfs_umnt_timeout, - .servername = info->hostname, - .program = &mnt_program, - .version = info->version, - .authflavor = RPC_AUTH_UNIX, - .flags = RPC_CLNT_CREATE_NOPING, - }; - struct rpc_message msg = { - .rpc_argp = info->dirpath, - }; - struct rpc_clnt *clnt; - int status; - - if (info->noresvport) - args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; - - clnt = rpc_create(&args); - if (IS_ERR(clnt)) - goto out_clnt_err; - - dprintk("NFS: sending UMNT request for %s:%s\n", - (info->hostname ? info->hostname : "server"), info->dirpath); - - if (info->version == NFS_MNT3_VERSION) - msg.rpc_proc = &clnt->cl_procinfo[MOUNTPROC3_UMNT]; - else - msg.rpc_proc = &clnt->cl_procinfo[MOUNTPROC_UMNT]; - - status = rpc_call_sync(clnt, &msg, 0); - rpc_shutdown_client(clnt); - - if (unlikely(status < 0)) - goto out_call_err; - - return; - -out_clnt_err: - dprintk("NFS: failed to create UMNT RPC client, status=%ld\n", - PTR_ERR(clnt)); - return; - -out_call_err: - dprintk("NFS: UMNT request failed, status=%d\n", status); -} - -/* - * XDR encode/decode functions for MOUNT - */ - -static void encode_mntdirpath(struct xdr_stream *xdr, const char *pathname) -{ - const u32 pathname_len = strlen(pathname); - __be32 *p; - - BUG_ON(pathname_len > MNTPATHLEN); - p = xdr_reserve_space(xdr, 4 + pathname_len); - xdr_encode_opaque(p, pathname, pathname_len); -} - -static void mnt_xdr_enc_dirpath(struct rpc_rqst *req, struct xdr_stream *xdr, - const char *dirpath) -{ - encode_mntdirpath(xdr, dirpath); -} - -/* - * RFC 1094: "A non-zero status indicates some sort of error. In this - * case, the status is a UNIX error number." This can be problematic - * if the server and client use different errno values for the same - * error. - * - * However, the OpenGroup XNFS spec provides a simple mapping that is - * independent of local errno values on the server and the client. - */ -static int decode_status(struct xdr_stream *xdr, struct mountres *res) -{ - unsigned int i; - u32 status; - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - return -EIO; - status = be32_to_cpup(p); - - for (i = 0; i < ARRAY_SIZE(mnt_errtbl); i++) { - if (mnt_errtbl[i].status == status) { - res->errno = mnt_errtbl[i].errno; - return 0; - } - } - - dprintk("NFS: unrecognized MNT status code: %u\n", status); - res->errno = -EACCES; - return 0; -} - -static int decode_fhandle(struct xdr_stream *xdr, struct mountres *res) -{ - struct nfs_fh *fh = res->fh; - __be32 *p; - - p = xdr_inline_decode(xdr, NFS2_FHSIZE); - if (unlikely(p == NULL)) - return -EIO; - - fh->size = NFS2_FHSIZE; - memcpy(fh->data, p, NFS2_FHSIZE); - return 0; -} - -static int mnt_xdr_dec_mountres(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct mountres *res) -{ - int status; - - status = decode_status(xdr, res); - if (unlikely(status != 0 || res->errno != 0)) - return status; - return decode_fhandle(xdr, res); -} - -static int decode_fhs_status(struct xdr_stream *xdr, struct mountres *res) -{ - unsigned int i; - u32 status; - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - return -EIO; - status = be32_to_cpup(p); - - for (i = 0; i < ARRAY_SIZE(mnt3_errtbl); i++) { - if (mnt3_errtbl[i].status == status) { - res->errno = mnt3_errtbl[i].errno; - return 0; - } - } - - dprintk("NFS: unrecognized MNT3 status code: %u\n", status); - res->errno = -EACCES; - return 0; -} - -static int decode_fhandle3(struct xdr_stream *xdr, struct mountres *res) -{ - struct nfs_fh *fh = res->fh; - u32 size; - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - return -EIO; - - size = be32_to_cpup(p); - if (size > NFS3_FHSIZE || size == 0) - return -EIO; - - p = xdr_inline_decode(xdr, size); - if (unlikely(p == NULL)) - return -EIO; - - fh->size = size; - memcpy(fh->data, p, size); - return 0; -} - -static int decode_auth_flavors(struct xdr_stream *xdr, struct mountres *res) -{ - rpc_authflavor_t *flavors = res->auth_flavors; - unsigned int *count = res->auth_count; - u32 entries, i; - __be32 *p; - - if (*count == 0) - return 0; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - return -EIO; - entries = be32_to_cpup(p); - dprintk("NFS: received %u auth flavors\n", entries); - if (entries > NFS_MAX_SECFLAVORS) - entries = NFS_MAX_SECFLAVORS; - - p = xdr_inline_decode(xdr, 4 * entries); - if (unlikely(p == NULL)) - return -EIO; - - if (entries > *count) - entries = *count; - - for (i = 0; i < entries; i++) { - flavors[i] = be32_to_cpup(p++); - dprintk("NFS: auth flavor[%u]: %d\n", i, flavors[i]); - } - *count = i; - - return 0; -} - -static int mnt_xdr_dec_mountres3(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct mountres *res) -{ - int status; - - status = decode_fhs_status(xdr, res); - if (unlikely(status != 0 || res->errno != 0)) - return status; - status = decode_fhandle3(xdr, res); - if (unlikely(status != 0)) { - res->errno = -EBADHANDLE; - return 0; - } - return decode_auth_flavors(xdr, res); -} - -static struct rpc_procinfo mnt_procedures[] = { - [MOUNTPROC_MNT] = { - .p_proc = MOUNTPROC_MNT, - .p_encode = (kxdreproc_t)mnt_xdr_enc_dirpath, - .p_decode = (kxdrdproc_t)mnt_xdr_dec_mountres, - .p_arglen = MNT_enc_dirpath_sz, - .p_replen = MNT_dec_mountres_sz, - .p_statidx = MOUNTPROC_MNT, - .p_name = "MOUNT", - }, - [MOUNTPROC_UMNT] = { - .p_proc = MOUNTPROC_UMNT, - .p_encode = (kxdreproc_t)mnt_xdr_enc_dirpath, - .p_arglen = MNT_enc_dirpath_sz, - .p_statidx = MOUNTPROC_UMNT, - .p_name = "UMOUNT", - }, -}; - -static struct rpc_procinfo mnt3_procedures[] = { - [MOUNTPROC3_MNT] = { - .p_proc = MOUNTPROC3_MNT, - .p_encode = (kxdreproc_t)mnt_xdr_enc_dirpath, - .p_decode = (kxdrdproc_t)mnt_xdr_dec_mountres3, - .p_arglen = MNT_enc_dirpath_sz, - .p_replen = MNT_dec_mountres3_sz, - .p_statidx = MOUNTPROC3_MNT, - .p_name = "MOUNT", - }, - [MOUNTPROC3_UMNT] = { - .p_proc = MOUNTPROC3_UMNT, - .p_encode = (kxdreproc_t)mnt_xdr_enc_dirpath, - .p_arglen = MNT_enc_dirpath_sz, - .p_statidx = MOUNTPROC3_UMNT, - .p_name = "UMOUNT", - }, -}; - - -static const struct rpc_version mnt_version1 = { - .number = 1, - .nrprocs = ARRAY_SIZE(mnt_procedures), - .procs = mnt_procedures, -}; - -static const struct rpc_version mnt_version3 = { - .number = 3, - .nrprocs = ARRAY_SIZE(mnt3_procedures), - .procs = mnt3_procedures, -}; - -static const struct rpc_version *mnt_version[] = { - NULL, - &mnt_version1, - NULL, - &mnt_version3, -}; - -static struct rpc_stat mnt_stats; - -static const struct rpc_program mnt_program = { - .name = "mount", - .number = NFS_MNT_PROGRAM, - .nrvers = ARRAY_SIZE(mnt_version), - .version = mnt_version, - .stats = &mnt_stats, -}; diff --git a/ANDROID_3.4.5/fs/nfs/namespace.c b/ANDROID_3.4.5/fs/nfs/namespace.c deleted file mode 100644 index d51868e5..00000000 --- a/ANDROID_3.4.5/fs/nfs/namespace.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * linux/fs/nfs/namespace.c - * - * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com> - * - Modified by David Howells <dhowells@redhat.com> - * - * NFS namespace - */ - -#include <linux/dcache.h> -#include <linux/gfp.h> -#include <linux/mount.h> -#include <linux/namei.h> -#include <linux/nfs_fs.h> -#include <linux/string.h> -#include <linux/sunrpc/clnt.h> -#include <linux/vfs.h> -#include <linux/sunrpc/gss_api.h> -#include "internal.h" - -#define NFSDBG_FACILITY NFSDBG_VFS - -static void nfs_expire_automounts(struct work_struct *work); - -static LIST_HEAD(nfs_automount_list); -static DECLARE_DELAYED_WORK(nfs_automount_task, nfs_expire_automounts); -int nfs_mountpoint_expiry_timeout = 500 * HZ; - -static struct vfsmount *nfs_do_submount(struct dentry *dentry, - struct nfs_fh *fh, - struct nfs_fattr *fattr, - rpc_authflavor_t authflavor); - -/* - * nfs_path - reconstruct the path given an arbitrary dentry - * @base - used to return pointer to the end of devname part of path - * @dentry - pointer to dentry - * @buffer - result buffer - * @buflen - length of buffer - * - * Helper function for constructing the server pathname - * by arbitrary hashed dentry. - * - * This is mainly for use in figuring out the path on the - * server side when automounting on top of an existing partition - * and in generating /proc/mounts and friends. - */ -char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen) -{ - char *end; - int namelen; - unsigned seq; - const char *base; - -rename_retry: - end = buffer+buflen; - *--end = '\0'; - buflen--; - - seq = read_seqbegin(&rename_lock); - rcu_read_lock(); - while (1) { - spin_lock(&dentry->d_lock); - if (IS_ROOT(dentry)) - break; - namelen = dentry->d_name.len; - buflen -= namelen + 1; - if (buflen < 0) - goto Elong_unlock; - end -= namelen; - memcpy(end, dentry->d_name.name, namelen); - *--end = '/'; - spin_unlock(&dentry->d_lock); - dentry = dentry->d_parent; - } - if (read_seqretry(&rename_lock, seq)) { - spin_unlock(&dentry->d_lock); - rcu_read_unlock(); - goto rename_retry; - } - if (*end != '/') { - if (--buflen < 0) { - spin_unlock(&dentry->d_lock); - rcu_read_unlock(); - goto Elong; - } - *--end = '/'; - } - *p = end; - base = dentry->d_fsdata; - if (!base) { - spin_unlock(&dentry->d_lock); - rcu_read_unlock(); - WARN_ON(1); - return end; - } - namelen = strlen(base); - /* Strip off excess slashes in base string */ - while (namelen > 0 && base[namelen - 1] == '/') - namelen--; - buflen -= namelen; - if (buflen < 0) { - spin_unlock(&dentry->d_lock); - rcu_read_unlock(); - goto Elong; - } - end -= namelen; - memcpy(end, base, namelen); - spin_unlock(&dentry->d_lock); - rcu_read_unlock(); - return end; -Elong_unlock: - spin_unlock(&dentry->d_lock); - rcu_read_unlock(); - if (read_seqretry(&rename_lock, seq)) - goto rename_retry; -Elong: - return ERR_PTR(-ENAMETOOLONG); -} - -#ifdef CONFIG_NFS_V4 -rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors) -{ - struct gss_api_mech *mech; - struct xdr_netobj oid; - int i; - rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX; - - for (i = 0; i < flavors->num_flavors; i++) { - struct nfs4_secinfo_flavor *flavor; - flavor = &flavors->flavors[i]; - - if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) { - pseudoflavor = flavor->flavor; - break; - } else if (flavor->flavor == RPC_AUTH_GSS) { - oid.len = flavor->gss.sec_oid4.len; - oid.data = flavor->gss.sec_oid4.data; - mech = gss_mech_get_by_OID(&oid); - if (!mech) - continue; - pseudoflavor = gss_svc_to_pseudoflavor(mech, flavor->gss.service); - gss_mech_put(mech); - break; - } - } - - return pseudoflavor; -} - -static struct rpc_clnt *nfs_lookup_mountpoint(struct inode *dir, - struct qstr *name, - struct nfs_fh *fh, - struct nfs_fattr *fattr) -{ - int err; - - if (NFS_PROTO(dir)->version == 4) - return nfs4_proc_lookup_mountpoint(dir, name, fh, fattr); - - err = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, name, fh, fattr); - if (err) - return ERR_PTR(err); - return rpc_clone_client(NFS_SERVER(dir)->client); -} -#else /* CONFIG_NFS_V4 */ -static inline struct rpc_clnt *nfs_lookup_mountpoint(struct inode *dir, - struct qstr *name, - struct nfs_fh *fh, - struct nfs_fattr *fattr) -{ - int err = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, name, fh, fattr); - if (err) - return ERR_PTR(err); - return rpc_clone_client(NFS_SERVER(dir)->client); -} -#endif /* CONFIG_NFS_V4 */ - -/* - * nfs_d_automount - Handle crossing a mountpoint on the server - * @path - The mountpoint - * - * When we encounter a mountpoint on the server, we want to set up - * a mountpoint on the client too, to prevent inode numbers from - * colliding, and to allow "df" to work properly. - * On NFSv4, we also want to allow for the fact that different - * filesystems may be migrated to different servers in a failover - * situation, and that different filesystems may want to use - * different security flavours. - */ -struct vfsmount *nfs_d_automount(struct path *path) -{ - struct vfsmount *mnt; - struct dentry *parent; - struct nfs_fh *fh = NULL; - struct nfs_fattr *fattr = NULL; - struct rpc_clnt *client; - - dprintk("--> nfs_d_automount()\n"); - - mnt = ERR_PTR(-ESTALE); - if (IS_ROOT(path->dentry)) - goto out_nofree; - - mnt = ERR_PTR(-ENOMEM); - fh = nfs_alloc_fhandle(); - fattr = nfs_alloc_fattr(); - if (fh == NULL || fattr == NULL) - goto out; - - dprintk("%s: enter\n", __func__); - - /* Look it up again to get its attributes */ - parent = dget_parent(path->dentry); - client = nfs_lookup_mountpoint(parent->d_inode, &path->dentry->d_name, fh, fattr); - dput(parent); - if (IS_ERR(client)) { - mnt = ERR_CAST(client); - goto out; - } - - if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) - mnt = nfs_do_refmount(client, path->dentry); - else - mnt = nfs_do_submount(path->dentry, fh, fattr, client->cl_auth->au_flavor); - rpc_shutdown_client(client); - - if (IS_ERR(mnt)) - goto out; - - dprintk("%s: done, success\n", __func__); - mntget(mnt); /* prevent immediate expiration */ - mnt_set_expiry(mnt, &nfs_automount_list); - schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); - -out: - nfs_free_fattr(fattr); - nfs_free_fhandle(fh); -out_nofree: - if (IS_ERR(mnt)) - dprintk("<-- %s(): error %ld\n", __func__, PTR_ERR(mnt)); - else - dprintk("<-- %s() = %p\n", __func__, mnt); - return mnt; -} - -const struct inode_operations nfs_mountpoint_inode_operations = { - .getattr = nfs_getattr, -}; - -const struct inode_operations nfs_referral_inode_operations = { -}; - -static void nfs_expire_automounts(struct work_struct *work) -{ - struct list_head *list = &nfs_automount_list; - - mark_mounts_for_expiry(list); - if (!list_empty(list)) - schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); -} - -void nfs_release_automount_timer(void) -{ - if (list_empty(&nfs_automount_list)) - cancel_delayed_work(&nfs_automount_task); -} - -/* - * Clone a mountpoint of the appropriate type - */ -static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server, - const char *devname, - struct nfs_clone_mount *mountdata) -{ -#ifdef CONFIG_NFS_V4 - struct vfsmount *mnt = ERR_PTR(-EINVAL); - switch (server->nfs_client->rpc_ops->version) { - case 2: - case 3: - mnt = vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata); - break; - case 4: - mnt = vfs_kern_mount(&nfs4_xdev_fs_type, 0, devname, mountdata); - } - return mnt; -#else - return vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata); -#endif -} - -/** - * nfs_do_submount - set up mountpoint when crossing a filesystem boundary - * @dentry - parent directory - * @fh - filehandle for new root dentry - * @fattr - attributes for new root inode - * @authflavor - security flavor to use when performing the mount - * - */ -static struct vfsmount *nfs_do_submount(struct dentry *dentry, - struct nfs_fh *fh, - struct nfs_fattr *fattr, - rpc_authflavor_t authflavor) -{ - struct nfs_clone_mount mountdata = { - .sb = dentry->d_sb, - .dentry = dentry, - .fh = fh, - .fattr = fattr, - .authflavor = authflavor, - }; - struct vfsmount *mnt = ERR_PTR(-ENOMEM); - char *page = (char *) __get_free_page(GFP_USER); - char *devname; - - dprintk("--> nfs_do_submount()\n"); - - dprintk("%s: submounting on %s/%s\n", __func__, - dentry->d_parent->d_name.name, - dentry->d_name.name); - if (page == NULL) - goto out; - devname = nfs_devname(dentry, page, PAGE_SIZE); - mnt = (struct vfsmount *)devname; - if (IS_ERR(devname)) - goto free_page; - mnt = nfs_do_clone_mount(NFS_SB(dentry->d_sb), devname, &mountdata); -free_page: - free_page((unsigned long)page); -out: - dprintk("%s: done\n", __func__); - - dprintk("<-- nfs_do_submount() = %p\n", mnt); - return mnt; -} diff --git a/ANDROID_3.4.5/fs/nfs/netns.h b/ANDROID_3.4.5/fs/nfs/netns.h deleted file mode 100644 index aa14ec30..00000000 --- a/ANDROID_3.4.5/fs/nfs/netns.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef __NFS_NETNS_H__ -#define __NFS_NETNS_H__ - -#include <net/net_namespace.h> -#include <net/netns/generic.h> - -struct bl_dev_msg { - int32_t status; - uint32_t major, minor; -}; - -struct nfs_net { - struct cache_detail *nfs_dns_resolve; - struct rpc_pipe *bl_device_pipe; - struct bl_dev_msg bl_mount_reply; - wait_queue_head_t bl_wq; - struct list_head nfs_client_list; - struct list_head nfs_volume_list; -#ifdef CONFIG_NFS_V4 - struct idr cb_ident_idr; /* Protected by nfs_client_lock */ -#endif - spinlock_t nfs_client_lock; -}; - -extern int nfs_net_id; - -#endif diff --git a/ANDROID_3.4.5/fs/nfs/nfs2xdr.c b/ANDROID_3.4.5/fs/nfs/nfs2xdr.c deleted file mode 100644 index 1f56000f..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfs2xdr.c +++ /dev/null @@ -1,1157 +0,0 @@ -/* - * 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 - */ - -#include <linux/param.h> -#include <linux/time.h> -#include <linux/mm.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/in.h> -#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> -#include "internal.h" - -#define NFSDBG_FACILITY NFSDBG_XDR - -/* Mapping from NFS error code to "errno" error code. */ -#define errno_NFSERR_IO EIO - -/* - * Declare the space requirements for NFS arguments and replies as - * number of 32bit-words - */ -#define NFS_fhandle_sz (8) -#define NFS_sattr_sz (8) -#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) - -#define NFS_diropargs_sz (NFS_fhandle_sz+NFS_filename_sz) -#define NFS_removeargs_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) -#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+1+NFS_sattr_sz) -#define NFS_readdirargs_sz (NFS_fhandle_sz+2) - -#define NFS_attrstat_sz (1+NFS_fattr_sz) -#define NFS_diropres_sz (1+NFS_fhandle_sz+NFS_fattr_sz) -#define NFS_readlinkres_sz (2) -#define NFS_readres_sz (1+NFS_fattr_sz+1) -#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) - - -/* - * While encoding arguments, set up the reply buffer in advance to - * receive reply data directly into the page cache. - */ -static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages, - unsigned int base, unsigned int len, - unsigned int bufsize) -{ - struct rpc_auth *auth = req->rq_cred->cr_auth; - unsigned int replen; - - replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize; - xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len); -} - -/* - * Handle decode buffer overflows out-of-line. - */ -static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) -{ - dprintk("NFS: %s prematurely hit the end of our receive buffer. " - "Remaining buffer length is %tu words.\n", - func, xdr->end - xdr->p); -} - - -/* - * Encode/decode NFSv2 basic data types - * - * Basic NFSv2 data types are defined in section 2.3 of RFC 1094: - * "NFS: Network File System Protocol Specification". - * - * Not all basic data types have their own encoding and decoding - * functions. For run-time efficiency, some data types are encoded - * or decoded inline. - */ - -/* - * typedef opaque nfsdata<>; - */ -static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result) -{ - u32 recvd, count; - size_t hdrlen; - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - count = be32_to_cpup(p); - hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; - recvd = xdr->buf->len - hdrlen; - if (unlikely(count > recvd)) - goto out_cheating; -out: - xdr_read_pages(xdr, count); - result->eof = 0; /* NFSv2 does not pass EOF flag on the wire. */ - result->count = count; - return count; -out_cheating: - dprintk("NFS: server cheating in read result: " - "count %u > recvd %u\n", count, recvd); - count = recvd; - goto out; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * enum stat { - * NFS_OK = 0, - * NFSERR_PERM = 1, - * NFSERR_NOENT = 2, - * NFSERR_IO = 5, - * NFSERR_NXIO = 6, - * NFSERR_ACCES = 13, - * NFSERR_EXIST = 17, - * NFSERR_NODEV = 19, - * NFSERR_NOTDIR = 20, - * NFSERR_ISDIR = 21, - * NFSERR_FBIG = 27, - * NFSERR_NOSPC = 28, - * NFSERR_ROFS = 30, - * NFSERR_NAMETOOLONG = 63, - * NFSERR_NOTEMPTY = 66, - * NFSERR_DQUOT = 69, - * NFSERR_STALE = 70, - * NFSERR_WFLUSH = 99 - * }; - */ -static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - *status = be32_to_cpup(p); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * 2.3.2. ftype - * - * enum ftype { - * NFNON = 0, - * NFREG = 1, - * NFDIR = 2, - * NFBLK = 3, - * NFCHR = 4, - * NFLNK = 5 - * }; - * - */ -static __be32 *xdr_decode_ftype(__be32 *p, u32 *type) -{ - *type = be32_to_cpup(p++); - if (unlikely(*type > NF2FIFO)) - *type = NFBAD; - return p; -} - -/* - * 2.3.3. fhandle - * - * typedef opaque fhandle[FHSIZE]; - */ -static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh) -{ - __be32 *p; - - BUG_ON(fh->size != NFS2_FHSIZE); - p = xdr_reserve_space(xdr, NFS2_FHSIZE); - memcpy(p, fh->data, NFS2_FHSIZE); -} - -static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, NFS2_FHSIZE); - if (unlikely(p == NULL)) - goto out_overflow; - fh->size = NFS2_FHSIZE; - memcpy(fh->data, p, NFS2_FHSIZE); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * 2.3.4. timeval - * - * struct timeval { - * unsigned int seconds; - * unsigned int useconds; - * }; - */ -static __be32 *xdr_encode_time(__be32 *p, const struct timespec *timep) -{ - *p++ = cpu_to_be32(timep->tv_sec); - if (timep->tv_nsec != 0) - *p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC); - else - *p++ = cpu_to_be32(0); - return p; -} - -/* - * Passing the invalid value useconds=1000000 is a Sun convention for - * "set to current server time". It's needed to make permissions checks - * for the "touch" program across v2 mounts to Solaris and Irix servers - * work correctly. See description of sattr in section 6.1 of "NFS - * Illustrated" by Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5. - */ -static __be32 *xdr_encode_current_server_time(__be32 *p, - const struct timespec *timep) -{ - *p++ = cpu_to_be32(timep->tv_sec); - *p++ = cpu_to_be32(1000000); - return p; -} - -static __be32 *xdr_decode_time(__be32 *p, struct timespec *timep) -{ - timep->tv_sec = be32_to_cpup(p++); - timep->tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC; - return p; -} - -/* - * 2.3.5. fattr - * - * struct fattr { - * ftype type; - * unsigned int mode; - * unsigned int nlink; - * unsigned int uid; - * unsigned int gid; - * unsigned int size; - * unsigned int blocksize; - * unsigned int rdev; - * unsigned int blocks; - * unsigned int fsid; - * unsigned int fileid; - * timeval atime; - * timeval mtime; - * timeval ctime; - * }; - * - */ -static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) -{ - u32 rdev, type; - __be32 *p; - - p = xdr_inline_decode(xdr, NFS_fattr_sz << 2); - if (unlikely(p == NULL)) - goto out_overflow; - - fattr->valid |= NFS_ATTR_FATTR_V2; - - p = xdr_decode_ftype(p, &type); - - fattr->mode = be32_to_cpup(p++); - fattr->nlink = be32_to_cpup(p++); - fattr->uid = be32_to_cpup(p++); - fattr->gid = be32_to_cpup(p++); - fattr->size = be32_to_cpup(p++); - fattr->du.nfs2.blocksize = be32_to_cpup(p++); - - rdev = be32_to_cpup(p++); - fattr->rdev = new_decode_dev(rdev); - if (type == (u32)NFCHR && rdev == (u32)NFS2_FIFO_DEV) { - fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO; - fattr->rdev = 0; - } - - fattr->du.nfs2.blocks = be32_to_cpup(p++); - fattr->fsid.major = be32_to_cpup(p++); - fattr->fsid.minor = 0; - fattr->fileid = be32_to_cpup(p++); - - p = xdr_decode_time(p, &fattr->atime); - p = xdr_decode_time(p, &fattr->mtime); - xdr_decode_time(p, &fattr->ctime); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * 2.3.6. sattr - * - * struct sattr { - * unsigned int mode; - * unsigned int uid; - * unsigned int gid; - * unsigned int size; - * timeval atime; - * timeval mtime; - * }; - */ - -#define NFS2_SATTR_NOT_SET (0xffffffff) - -static __be32 *xdr_time_not_set(__be32 *p) -{ - *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); - *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); - return p; -} - -static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr) -{ - __be32 *p; - - p = xdr_reserve_space(xdr, NFS_sattr_sz << 2); - - if (attr->ia_valid & ATTR_MODE) - *p++ = cpu_to_be32(attr->ia_mode); - else - *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); - if (attr->ia_valid & ATTR_UID) - *p++ = cpu_to_be32(attr->ia_uid); - else - *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); - if (attr->ia_valid & ATTR_GID) - *p++ = cpu_to_be32(attr->ia_gid); - else - *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); - if (attr->ia_valid & ATTR_SIZE) - *p++ = cpu_to_be32((u32)attr->ia_size); - else - *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); - - if (attr->ia_valid & ATTR_ATIME_SET) - p = xdr_encode_time(p, &attr->ia_atime); - else if (attr->ia_valid & ATTR_ATIME) - p = xdr_encode_current_server_time(p, &attr->ia_atime); - else - p = xdr_time_not_set(p); - if (attr->ia_valid & ATTR_MTIME_SET) - xdr_encode_time(p, &attr->ia_mtime); - else if (attr->ia_valid & ATTR_MTIME) - xdr_encode_current_server_time(p, &attr->ia_mtime); - else - xdr_time_not_set(p); -} - -/* - * 2.3.7. filename - * - * typedef string filename<MAXNAMLEN>; - */ -static void encode_filename(struct xdr_stream *xdr, - const char *name, u32 length) -{ - __be32 *p; - - BUG_ON(length > NFS2_MAXNAMLEN); - p = xdr_reserve_space(xdr, 4 + length); - xdr_encode_opaque(p, name, length); -} - -static int decode_filename_inline(struct xdr_stream *xdr, - const char **name, u32 *length) -{ - __be32 *p; - u32 count; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - count = be32_to_cpup(p); - if (count > NFS3_MAXNAMLEN) - goto out_nametoolong; - p = xdr_inline_decode(xdr, count); - if (unlikely(p == NULL)) - goto out_overflow; - *name = (const char *)p; - *length = count; - return 0; -out_nametoolong: - dprintk("NFS: returned filename too long: %u\n", count); - return -ENAMETOOLONG; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * 2.3.8. path - * - * typedef string path<MAXPATHLEN>; - */ -static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length) -{ - __be32 *p; - - BUG_ON(length > NFS2_MAXPATHLEN); - p = xdr_reserve_space(xdr, 4); - *p = cpu_to_be32(length); - xdr_write_pages(xdr, pages, 0, length); -} - -static int decode_path(struct xdr_stream *xdr) -{ - u32 length, recvd; - size_t hdrlen; - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - length = be32_to_cpup(p); - if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN)) - goto out_size; - hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; - recvd = xdr->buf->len - hdrlen; - if (unlikely(length > recvd)) - goto out_cheating; - - xdr_read_pages(xdr, length); - xdr_terminate_string(xdr->buf, length); - return 0; -out_size: - dprintk("NFS: returned pathname too long: %u\n", length); - return -ENAMETOOLONG; -out_cheating: - dprintk("NFS: server cheating in pathname result: " - "length %u > received %u\n", length, recvd); - return -EIO; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * 2.3.9. attrstat - * - * union attrstat switch (stat status) { - * case NFS_OK: - * fattr attributes; - * default: - * void; - * }; - */ -static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result) -{ - enum nfs_stat status; - int error; - - error = decode_stat(xdr, &status); - if (unlikely(error)) - goto out; - if (status != NFS_OK) - goto out_default; - error = decode_fattr(xdr, result); -out: - return error; -out_default: - return nfs_stat_to_errno(status); -} - -/* - * 2.3.10. diropargs - * - * struct diropargs { - * fhandle dir; - * filename name; - * }; - */ -static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh, - const char *name, u32 length) -{ - encode_fhandle(xdr, fh); - encode_filename(xdr, name, length); -} - -/* - * 2.3.11. diropres - * - * union diropres switch (stat status) { - * case NFS_OK: - * struct { - * fhandle file; - * fattr attributes; - * } diropok; - * default: - * void; - * }; - */ -static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result) -{ - int error; - - error = decode_fhandle(xdr, result->fh); - if (unlikely(error)) - goto out; - error = decode_fattr(xdr, result->fattr); -out: - return error; -} - -static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result) -{ - enum nfs_stat status; - int error; - - error = decode_stat(xdr, &status); - if (unlikely(error)) - goto out; - if (status != NFS_OK) - goto out_default; - error = decode_diropok(xdr, result); -out: - return error; -out_default: - return nfs_stat_to_errno(status); -} - - -/* - * NFSv2 XDR encode functions - * - * NFSv2 argument types are defined in section 2.2 of RFC 1094: - * "NFS: Network File System Protocol Specification". - */ - -static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_fh *fh) -{ - encode_fhandle(xdr, fh); -} - -/* - * 2.2.3. sattrargs - * - * struct sattrargs { - * fhandle file; - * sattr attributes; - * }; - */ -static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_sattrargs *args) -{ - encode_fhandle(xdr, args->fh); - encode_sattr(xdr, args->sattr); -} - -static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_diropargs *args) -{ - encode_diropargs(xdr, args->fh, args->name, args->len); -} - -static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_readlinkargs *args) -{ - encode_fhandle(xdr, args->fh); - prepare_reply_buffer(req, args->pages, args->pgbase, - args->pglen, NFS_readlinkres_sz); -} - -/* - * 2.2.7. readargs - * - * struct readargs { - * fhandle file; - * unsigned offset; - * unsigned count; - * unsigned totalcount; - * }; - */ -static void encode_readargs(struct xdr_stream *xdr, - const struct nfs_readargs *args) -{ - u32 offset = args->offset; - u32 count = args->count; - __be32 *p; - - encode_fhandle(xdr, args->fh); - - p = xdr_reserve_space(xdr, 4 + 4 + 4); - *p++ = cpu_to_be32(offset); - *p++ = cpu_to_be32(count); - *p = cpu_to_be32(count); -} - -static void nfs2_xdr_enc_readargs(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_readargs *args) -{ - encode_readargs(xdr, args); - prepare_reply_buffer(req, args->pages, args->pgbase, - args->count, NFS_readres_sz); - req->rq_rcv_buf.flags |= XDRBUF_READ; -} - -/* - * 2.2.9. writeargs - * - * struct writeargs { - * fhandle file; - * unsigned beginoffset; - * unsigned offset; - * unsigned totalcount; - * nfsdata data; - * }; - */ -static void encode_writeargs(struct xdr_stream *xdr, - const struct nfs_writeargs *args) -{ - u32 offset = args->offset; - u32 count = args->count; - __be32 *p; - - encode_fhandle(xdr, args->fh); - - p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4); - *p++ = cpu_to_be32(offset); - *p++ = cpu_to_be32(offset); - *p++ = cpu_to_be32(count); - - /* nfsdata */ - *p = cpu_to_be32(count); - xdr_write_pages(xdr, args->pages, args->pgbase, count); -} - -static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_writeargs *args) -{ - encode_writeargs(xdr, args); - xdr->buf->flags |= XDRBUF_WRITE; -} - -/* - * 2.2.10. createargs - * - * struct createargs { - * diropargs where; - * sattr attributes; - * }; - */ -static void nfs2_xdr_enc_createargs(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_createargs *args) -{ - encode_diropargs(xdr, args->fh, args->name, args->len); - encode_sattr(xdr, args->sattr); -} - -static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_removeargs *args) -{ - encode_diropargs(xdr, args->fh, args->name.name, args->name.len); -} - -/* - * 2.2.12. renameargs - * - * struct renameargs { - * diropargs from; - * diropargs to; - * }; - */ -static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_renameargs *args) -{ - const struct qstr *old = args->old_name; - const struct qstr *new = args->new_name; - - encode_diropargs(xdr, args->old_dir, old->name, old->len); - encode_diropargs(xdr, args->new_dir, new->name, new->len); -} - -/* - * 2.2.13. linkargs - * - * struct linkargs { - * fhandle from; - * diropargs to; - * }; - */ -static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_linkargs *args) -{ - encode_fhandle(xdr, args->fromfh); - encode_diropargs(xdr, args->tofh, args->toname, args->tolen); -} - -/* - * 2.2.14. symlinkargs - * - * struct symlinkargs { - * diropargs from; - * path to; - * sattr attributes; - * }; - */ -static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_symlinkargs *args) -{ - encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen); - encode_path(xdr, args->pages, args->pathlen); - encode_sattr(xdr, args->sattr); -} - -/* - * 2.2.17. readdirargs - * - * struct readdirargs { - * fhandle dir; - * nfscookie cookie; - * unsigned count; - * }; - */ -static void encode_readdirargs(struct xdr_stream *xdr, - const struct nfs_readdirargs *args) -{ - __be32 *p; - - encode_fhandle(xdr, args->fh); - - p = xdr_reserve_space(xdr, 4 + 4); - *p++ = cpu_to_be32(args->cookie); - *p = cpu_to_be32(args->count); -} - -static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_readdirargs *args) -{ - encode_readdirargs(xdr, args); - prepare_reply_buffer(req, args->pages, 0, - args->count, NFS_readdirres_sz); -} - -/* - * NFSv2 XDR decode functions - * - * NFSv2 result types are defined in section 2.2 of RFC 1094: - * "NFS: Network File System Protocol Specification". - */ - -static int nfs2_xdr_dec_stat(struct rpc_rqst *req, struct xdr_stream *xdr, - void *__unused) -{ - enum nfs_stat status; - int error; - - error = decode_stat(xdr, &status); - if (unlikely(error)) - goto out; - if (status != NFS_OK) - goto out_default; -out: - return error; -out_default: - return nfs_stat_to_errno(status); -} - -static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_fattr *result) -{ - return decode_attrstat(xdr, result); -} - -static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_diropok *result) -{ - return decode_diropres(xdr, result); -} - -/* - * 2.2.6. readlinkres - * - * union readlinkres switch (stat status) { - * case NFS_OK: - * path data; - * default: - * void; - * }; - */ -static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req, - struct xdr_stream *xdr, void *__unused) -{ - enum nfs_stat status; - int error; - - error = decode_stat(xdr, &status); - if (unlikely(error)) - goto out; - if (status != NFS_OK) - goto out_default; - error = decode_path(xdr); -out: - return error; -out_default: - return nfs_stat_to_errno(status); -} - -/* - * 2.2.7. readres - * - * union readres switch (stat status) { - * case NFS_OK: - * fattr attributes; - * nfsdata data; - * default: - * void; - * }; - */ -static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_readres *result) -{ - enum nfs_stat status; - int error; - - error = decode_stat(xdr, &status); - if (unlikely(error)) - goto out; - if (status != NFS_OK) - goto out_default; - error = decode_fattr(xdr, result->fattr); - if (unlikely(error)) - goto out; - error = decode_nfsdata(xdr, result); -out: - return error; -out_default: - return nfs_stat_to_errno(status); -} - -static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_writeres *result) -{ - /* All NFSv2 writes are "file sync" writes */ - result->verf->committed = NFS_FILE_SYNC; - return decode_attrstat(xdr, result->fattr); -} - -/** - * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in - * the local page cache. - * @xdr: XDR stream where entry resides - * @entry: buffer to fill in with entry data - * @plus: boolean indicating whether this should be a readdirplus entry - * - * Returns zero if successful, otherwise a negative errno value is - * returned. - * - * This function is not invoked during READDIR reply decoding, but - * rather whenever an application invokes the getdents(2) system call - * on a directory already in our cache. - * - * 2.2.17. entry - * - * struct entry { - * unsigned fileid; - * filename name; - * nfscookie cookie; - * entry *nextentry; - * }; - */ -int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, - int plus) -{ - __be32 *p; - int error; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - if (*p++ == xdr_zero) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - if (*p++ == xdr_zero) - return -EAGAIN; - entry->eof = 1; - return -EBADCOOKIE; - } - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - entry->ino = be32_to_cpup(p); - - error = decode_filename_inline(xdr, &entry->name, &entry->len); - if (unlikely(error)) - return error; - - /* - * The type (size and byte order) of nfscookie isn't defined in - * RFC 1094. This implementation assumes that it's an XDR uint32. - */ - entry->prev_cookie = entry->cookie; - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - entry->cookie = be32_to_cpup(p); - - entry->d_type = DT_UNKNOWN; - - return 0; - -out_overflow: - print_overflow_msg(__func__, xdr); - return -EAGAIN; -} - -/* - * 2.2.17. readdirres - * - * union readdirres switch (stat status) { - * case NFS_OK: - * struct { - * entry *entries; - * bool eof; - * } readdirok; - * default: - * void; - * }; - * - * Read the directory contents into the page cache, but don't - * touch them. The actual decoding is done by nfs2_decode_dirent() - * during subsequent nfs_readdir() calls. - */ -static int decode_readdirok(struct xdr_stream *xdr) -{ - u32 recvd, pglen; - size_t hdrlen; - - pglen = xdr->buf->page_len; - hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; - recvd = xdr->buf->len - hdrlen; - if (unlikely(pglen > recvd)) - goto out_cheating; -out: - xdr_read_pages(xdr, pglen); - return pglen; -out_cheating: - dprintk("NFS: server cheating in readdir result: " - "pglen %u > recvd %u\n", pglen, recvd); - pglen = recvd; - goto out; -} - -static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req, - struct xdr_stream *xdr, void *__unused) -{ - enum nfs_stat status; - int error; - - error = decode_stat(xdr, &status); - if (unlikely(error)) - goto out; - if (status != NFS_OK) - goto out_default; - error = decode_readdirok(xdr); -out: - return error; -out_default: - return nfs_stat_to_errno(status); -} - -/* - * 2.2.18. statfsres - * - * union statfsres (stat status) { - * case NFS_OK: - * struct { - * unsigned tsize; - * unsigned bsize; - * unsigned blocks; - * unsigned bfree; - * unsigned bavail; - * } info; - * default: - * void; - * }; - */ -static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, NFS_info_sz << 2); - if (unlikely(p == NULL)) - goto out_overflow; - result->tsize = be32_to_cpup(p++); - result->bsize = be32_to_cpup(p++); - result->blocks = be32_to_cpup(p++); - result->bfree = be32_to_cpup(p++); - result->bavail = be32_to_cpup(p); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs2_fsstat *result) -{ - enum nfs_stat status; - int error; - - error = decode_stat(xdr, &status); - if (unlikely(error)) - goto out; - if (status != NFS_OK) - goto out_default; - error = decode_info(xdr, result); -out: - return error; -out_default: - return nfs_stat_to_errno(status); -} - - -/* - * We need to translate between nfs status return values and - * the local errno values which may not be the same. - */ -static const 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_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, -EREMOTEIO }, - { NFSERR_BADTYPE, -EBADTYPE }, - { NFSERR_JUKEBOX, -EJUKEBOX }, - { -1, -EIO } -}; - -/** - * nfs_stat_to_errno - convert an NFS status code to a local errno - * @status: NFS status code to convert - * - * Returns a local errno value, or -EIO if the NFS status code is - * not recognized. This function is used jointly by NFSv2 and NFSv3. - */ -int nfs_stat_to_errno(enum nfs_stat status) -{ - int i; - - for (i = 0; nfs_errtbl[i].stat != -1; i++) { - if (nfs_errtbl[i].stat == (int)status) - return nfs_errtbl[i].errno; - } - dprintk("NFS: Unrecognized nfs status value: %u\n", status); - return nfs_errtbl[i].errno; -} - -#define PROC(proc, argtype, restype, timer) \ -[NFSPROC_##proc] = { \ - .p_proc = NFSPROC_##proc, \ - .p_encode = (kxdreproc_t)nfs2_xdr_enc_##argtype, \ - .p_decode = (kxdrdproc_t)nfs2_xdr_dec_##restype, \ - .p_arglen = NFS_##argtype##_sz, \ - .p_replen = NFS_##restype##_sz, \ - .p_timer = timer, \ - .p_statidx = NFSPROC_##proc, \ - .p_name = #proc, \ - } -struct rpc_procinfo nfs_procedures[] = { - PROC(GETATTR, fhandle, attrstat, 1), - PROC(SETATTR, sattrargs, attrstat, 0), - PROC(LOOKUP, diropargs, diropres, 2), - PROC(READLINK, readlinkargs, readlinkres, 3), - PROC(READ, readargs, readres, 3), - PROC(WRITE, writeargs, writeres, 4), - PROC(CREATE, createargs, diropres, 0), - PROC(REMOVE, removeargs, stat, 0), - PROC(RENAME, renameargs, stat, 0), - PROC(LINK, linkargs, stat, 0), - PROC(SYMLINK, symlinkargs, stat, 0), - PROC(MKDIR, createargs, diropres, 0), - PROC(RMDIR, diropargs, stat, 0), - PROC(READDIR, readdirargs, readdirres, 3), - PROC(STATFS, fhandle, statfsres, 0), -}; - -const struct rpc_version nfs_version2 = { - .number = 2, - .nrprocs = ARRAY_SIZE(nfs_procedures), - .procs = nfs_procedures -}; diff --git a/ANDROID_3.4.5/fs/nfs/nfs3acl.c b/ANDROID_3.4.5/fs/nfs/nfs3acl.c deleted file mode 100644 index e4498dc3..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfs3acl.c +++ /dev/null @@ -1,440 +0,0 @@ -#include <linux/fs.h> -#include <linux/gfp.h> -#include <linux/nfs.h> -#include <linux/nfs3.h> -#include <linux/nfs_fs.h> -#include <linux/posix_acl_xattr.h> -#include <linux/nfsacl.h> - -#include "internal.h" - -#define NFSDBG_FACILITY NFSDBG_PROC - -ssize_t nfs3_listxattr(struct dentry *dentry, char *buffer, size_t size) -{ - struct inode *inode = dentry->d_inode; - struct posix_acl *acl; - int pos=0, len=0; - -# define output(s) do { \ - if (pos + sizeof(s) <= size) { \ - memcpy(buffer + pos, s, sizeof(s)); \ - pos += sizeof(s); \ - } \ - len += sizeof(s); \ - } while(0) - - acl = nfs3_proc_getacl(inode, ACL_TYPE_ACCESS); - if (IS_ERR(acl)) - return PTR_ERR(acl); - if (acl) { - output("system.posix_acl_access"); - posix_acl_release(acl); - } - - if (S_ISDIR(inode->i_mode)) { - acl = nfs3_proc_getacl(inode, ACL_TYPE_DEFAULT); - if (IS_ERR(acl)) - return PTR_ERR(acl); - if (acl) { - output("system.posix_acl_default"); - posix_acl_release(acl); - } - } - -# undef output - - if (!buffer || len <= size) - return len; - return -ERANGE; -} - -ssize_t nfs3_getxattr(struct dentry *dentry, const char *name, - void *buffer, size_t size) -{ - struct inode *inode = dentry->d_inode; - struct posix_acl *acl; - int type, error = 0; - - if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0) - type = ACL_TYPE_ACCESS; - else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0) - type = ACL_TYPE_DEFAULT; - else - return -EOPNOTSUPP; - - acl = nfs3_proc_getacl(inode, type); - if (IS_ERR(acl)) - return PTR_ERR(acl); - else if (acl) { - if (type == ACL_TYPE_ACCESS && acl->a_count == 0) - error = -ENODATA; - else - error = posix_acl_to_xattr(acl, buffer, size); - posix_acl_release(acl); - } else - error = -ENODATA; - - return error; -} - -int nfs3_setxattr(struct dentry *dentry, const char *name, - const void *value, size_t size, int flags) -{ - struct inode *inode = dentry->d_inode; - struct posix_acl *acl; - int type, error; - - if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0) - type = ACL_TYPE_ACCESS; - else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0) - type = ACL_TYPE_DEFAULT; - else - return -EOPNOTSUPP; - - acl = posix_acl_from_xattr(value, size); - if (IS_ERR(acl)) - return PTR_ERR(acl); - error = nfs3_proc_setacl(inode, type, acl); - posix_acl_release(acl); - - return error; -} - -int nfs3_removexattr(struct dentry *dentry, const char *name) -{ - struct inode *inode = dentry->d_inode; - int type; - - if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0) - type = ACL_TYPE_ACCESS; - else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0) - type = ACL_TYPE_DEFAULT; - else - return -EOPNOTSUPP; - - return nfs3_proc_setacl(inode, type, NULL); -} - -static void __nfs3_forget_cached_acls(struct nfs_inode *nfsi) -{ - if (!IS_ERR(nfsi->acl_access)) { - posix_acl_release(nfsi->acl_access); - nfsi->acl_access = ERR_PTR(-EAGAIN); - } - if (!IS_ERR(nfsi->acl_default)) { - posix_acl_release(nfsi->acl_default); - nfsi->acl_default = ERR_PTR(-EAGAIN); - } -} - -void nfs3_forget_cached_acls(struct inode *inode) -{ - dprintk("NFS: nfs3_forget_cached_acls(%s/%ld)\n", inode->i_sb->s_id, - inode->i_ino); - spin_lock(&inode->i_lock); - __nfs3_forget_cached_acls(NFS_I(inode)); - spin_unlock(&inode->i_lock); -} - -static struct posix_acl *nfs3_get_cached_acl(struct inode *inode, int type) -{ - struct nfs_inode *nfsi = NFS_I(inode); - struct posix_acl *acl = ERR_PTR(-EINVAL); - - spin_lock(&inode->i_lock); - switch(type) { - case ACL_TYPE_ACCESS: - acl = nfsi->acl_access; - break; - - case ACL_TYPE_DEFAULT: - acl = nfsi->acl_default; - break; - - default: - goto out; - } - if (IS_ERR(acl)) - acl = ERR_PTR(-EAGAIN); - else - acl = posix_acl_dup(acl); -out: - spin_unlock(&inode->i_lock); - dprintk("NFS: nfs3_get_cached_acl(%s/%ld, %d) = %p\n", inode->i_sb->s_id, - inode->i_ino, type, acl); - return acl; -} - -static void nfs3_cache_acls(struct inode *inode, struct posix_acl *acl, - struct posix_acl *dfacl) -{ - struct nfs_inode *nfsi = NFS_I(inode); - - dprintk("nfs3_cache_acls(%s/%ld, %p, %p)\n", inode->i_sb->s_id, - inode->i_ino, acl, dfacl); - spin_lock(&inode->i_lock); - __nfs3_forget_cached_acls(NFS_I(inode)); - if (!IS_ERR(acl)) - nfsi->acl_access = posix_acl_dup(acl); - if (!IS_ERR(dfacl)) - nfsi->acl_default = posix_acl_dup(dfacl); - spin_unlock(&inode->i_lock); -} - -struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct page *pages[NFSACL_MAXPAGES] = { }; - struct nfs3_getaclargs args = { - .fh = NFS_FH(inode), - /* The xdr layer may allocate pages here. */ - .pages = pages, - }; - struct nfs3_getaclres res = { - NULL, - }; - struct rpc_message msg = { - .rpc_argp = &args, - .rpc_resp = &res, - }; - struct posix_acl *acl; - int status, count; - - if (!nfs_server_capable(inode, NFS_CAP_ACLS)) - return ERR_PTR(-EOPNOTSUPP); - - status = nfs_revalidate_inode(server, inode); - if (status < 0) - return ERR_PTR(status); - acl = nfs3_get_cached_acl(inode, type); - if (acl != ERR_PTR(-EAGAIN)) - return acl; - acl = NULL; - - /* - * Only get the access acl when explicitly requested: We don't - * need it for access decisions, and only some applications use - * it. Applications which request the access acl first are not - * penalized from this optimization. - */ - if (type == ACL_TYPE_ACCESS) - args.mask |= NFS_ACLCNT|NFS_ACL; - if (S_ISDIR(inode->i_mode)) - args.mask |= NFS_DFACLCNT|NFS_DFACL; - if (args.mask == 0) - return NULL; - - dprintk("NFS call getacl\n"); - msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_GETACL]; - res.fattr = nfs_alloc_fattr(); - if (res.fattr == NULL) - return ERR_PTR(-ENOMEM); - - status = rpc_call_sync(server->client_acl, &msg, 0); - dprintk("NFS reply getacl: %d\n", status); - - /* pages may have been allocated at the xdr layer. */ - for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++) - __free_page(args.pages[count]); - - switch (status) { - case 0: - status = nfs_refresh_inode(inode, res.fattr); - break; - case -EPFNOSUPPORT: - case -EPROTONOSUPPORT: - dprintk("NFS_V3_ACL extension not supported; disabling\n"); - server->caps &= ~NFS_CAP_ACLS; - case -ENOTSUPP: - status = -EOPNOTSUPP; - default: - goto getout; - } - if ((args.mask & res.mask) != args.mask) { - status = -EIO; - goto getout; - } - - if (res.acl_access != NULL) { - if (posix_acl_equiv_mode(res.acl_access, NULL) == 0) { - posix_acl_release(res.acl_access); - res.acl_access = NULL; - } - } - nfs3_cache_acls(inode, - (res.mask & NFS_ACL) ? res.acl_access : ERR_PTR(-EINVAL), - (res.mask & NFS_DFACL) ? res.acl_default : ERR_PTR(-EINVAL)); - - switch(type) { - case ACL_TYPE_ACCESS: - acl = res.acl_access; - res.acl_access = NULL; - break; - - case ACL_TYPE_DEFAULT: - acl = res.acl_default; - res.acl_default = NULL; - } - -getout: - posix_acl_release(res.acl_access); - posix_acl_release(res.acl_default); - nfs_free_fattr(res.fattr); - - if (status != 0) { - posix_acl_release(acl); - acl = ERR_PTR(status); - } - return acl; -} - -static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl, - struct posix_acl *dfacl) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct nfs_fattr *fattr; - struct page *pages[NFSACL_MAXPAGES]; - struct nfs3_setaclargs args = { - .inode = inode, - .mask = NFS_ACL, - .acl_access = acl, - .pages = pages, - }; - struct rpc_message msg = { - .rpc_argp = &args, - .rpc_resp = &fattr, - }; - int status; - - status = -EOPNOTSUPP; - if (!nfs_server_capable(inode, NFS_CAP_ACLS)) - goto out; - - /* We are doing this here because XDR marshalling does not - * return any results, it BUGs. */ - status = -ENOSPC; - if (acl != NULL && acl->a_count > NFS_ACL_MAX_ENTRIES) - goto out; - if (dfacl != NULL && dfacl->a_count > NFS_ACL_MAX_ENTRIES) - goto out; - if (S_ISDIR(inode->i_mode)) { - args.mask |= NFS_DFACL; - args.acl_default = dfacl; - args.len = nfsacl_size(acl, dfacl); - } else - args.len = nfsacl_size(acl, NULL); - - if (args.len > NFS_ACL_INLINE_BUFSIZE) { - unsigned int npages = 1 + ((args.len - 1) >> PAGE_SHIFT); - - status = -ENOMEM; - do { - args.pages[args.npages] = alloc_page(GFP_KERNEL); - if (args.pages[args.npages] == NULL) - goto out_freepages; - args.npages++; - } while (args.npages < npages); - } - - dprintk("NFS call setacl\n"); - status = -ENOMEM; - fattr = nfs_alloc_fattr(); - if (fattr == NULL) - goto out_freepages; - - msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_SETACL]; - msg.rpc_resp = fattr; - status = rpc_call_sync(server->client_acl, &msg, 0); - nfs_access_zap_cache(inode); - nfs_zap_acl_cache(inode); - dprintk("NFS reply setacl: %d\n", status); - - switch (status) { - case 0: - status = nfs_refresh_inode(inode, fattr); - nfs3_cache_acls(inode, acl, dfacl); - break; - case -EPFNOSUPPORT: - case -EPROTONOSUPPORT: - dprintk("NFS_V3_ACL SETACL RPC not supported" - "(will not retry)\n"); - server->caps &= ~NFS_CAP_ACLS; - case -ENOTSUPP: - status = -EOPNOTSUPP; - } - nfs_free_fattr(fattr); -out_freepages: - while (args.npages != 0) { - args.npages--; - __free_page(args.pages[args.npages]); - } -out: - return status; -} - -int nfs3_proc_setacl(struct inode *inode, int type, struct posix_acl *acl) -{ - struct posix_acl *alloc = NULL, *dfacl = NULL; - int status; - - if (S_ISDIR(inode->i_mode)) { - switch(type) { - case ACL_TYPE_ACCESS: - alloc = dfacl = nfs3_proc_getacl(inode, - ACL_TYPE_DEFAULT); - if (IS_ERR(alloc)) - goto fail; - break; - - case ACL_TYPE_DEFAULT: - dfacl = acl; - alloc = acl = nfs3_proc_getacl(inode, - ACL_TYPE_ACCESS); - if (IS_ERR(alloc)) - goto fail; - break; - - default: - return -EINVAL; - } - } else if (type != ACL_TYPE_ACCESS) - return -EINVAL; - - if (acl == NULL) { - alloc = acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); - if (IS_ERR(alloc)) - goto fail; - } - status = nfs3_proc_setacls(inode, acl, dfacl); - posix_acl_release(alloc); - return status; - -fail: - return PTR_ERR(alloc); -} - -int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode, - umode_t mode) -{ - struct posix_acl *dfacl, *acl; - int error = 0; - - dfacl = nfs3_proc_getacl(dir, ACL_TYPE_DEFAULT); - if (IS_ERR(dfacl)) { - error = PTR_ERR(dfacl); - return (error == -EOPNOTSUPP) ? 0 : error; - } - if (!dfacl) - return 0; - acl = posix_acl_dup(dfacl); - error = posix_acl_create(&acl, GFP_KERNEL, &mode); - if (error < 0) - goto out_release_dfacl; - error = nfs3_proc_setacls(inode, acl, S_ISDIR(inode->i_mode) ? - dfacl : NULL); - posix_acl_release(acl); -out_release_dfacl: - posix_acl_release(dfacl); - return error; -} diff --git a/ANDROID_3.4.5/fs/nfs/nfs3proc.c b/ANDROID_3.4.5/fs/nfs/nfs3proc.c deleted file mode 100644 index 5242eae6..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfs3proc.c +++ /dev/null @@ -1,915 +0,0 @@ -/* - * linux/fs/nfs/nfs3proc.c - * - * Client-side NFSv3 procedures stubs. - * - * Copyright (C) 1997, Olaf Kirch - */ - -#include <linux/mm.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/sunrpc/clnt.h> -#include <linux/slab.h> -#include <linux/nfs.h> -#include <linux/nfs3.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_page.h> -#include <linux/lockd/bind.h> -#include <linux/nfs_mount.h> -#include <linux/freezer.h> - -#include "iostat.h" -#include "internal.h" - -#define NFSDBG_FACILITY NFSDBG_PROC - -/* A wrapper to handle the EJUKEBOX and EKEYEXPIRED error messages */ -static int -nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) -{ - int res; - do { - res = rpc_call_sync(clnt, msg, flags); - if (res != -EJUKEBOX && res != -EKEYEXPIRED) - break; - freezable_schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME); - res = -ERESTARTSYS; - } while (!fatal_signal_pending(current)); - return res; -} - -#define rpc_call_sync(clnt, msg, flags) nfs3_rpc_wrapper(clnt, msg, flags) - -static int -nfs3_async_handle_jukebox(struct rpc_task *task, struct inode *inode) -{ - if (task->tk_status != -EJUKEBOX && task->tk_status != -EKEYEXPIRED) - return 0; - if (task->tk_status == -EJUKEBOX) - nfs_inc_stats(inode, NFSIOS_DELAY); - task->tk_status = 0; - rpc_restart_call(task); - rpc_delay(task, NFS_JUKEBOX_RETRY_TIME); - return 1; -} - -static int -do_proc_get_root(struct rpc_clnt *client, struct nfs_fh *fhandle, - struct nfs_fsinfo *info) -{ - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_FSINFO], - .rpc_argp = fhandle, - .rpc_resp = info, - }; - int status; - - dprintk("%s: call fsinfo\n", __func__); - nfs_fattr_init(info->fattr); - status = rpc_call_sync(client, &msg, 0); - dprintk("%s: reply fsinfo: %d\n", __func__, status); - if (!(info->fattr->valid & NFS_ATTR_FATTR)) { - msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR]; - msg.rpc_resp = info->fattr; - status = rpc_call_sync(client, &msg, 0); - dprintk("%s: reply getattr: %d\n", __func__, status); - } - return status; -} - -/* - * Bare-bones access to getattr: this is for nfs_get_root/nfs_get_sb - */ -static int -nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info) -{ - int status; - - status = do_proc_get_root(server->client, fhandle, info); - if (status && server->nfs_client->cl_rpcclient != server->client) - status = do_proc_get_root(server->nfs_client->cl_rpcclient, fhandle, info); - return status; -} - -/* - * One function for each procedure in the NFS protocol. - */ -static int -nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) -{ - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR], - .rpc_argp = fhandle, - .rpc_resp = fattr, - }; - int status; - - dprintk("NFS call getattr\n"); - nfs_fattr_init(fattr); - status = rpc_call_sync(server->client, &msg, 0); - dprintk("NFS reply getattr: %d\n", status); - return status; -} - -static int -nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, - struct iattr *sattr) -{ - struct inode *inode = dentry->d_inode; - struct nfs3_sattrargs arg = { - .fh = NFS_FH(inode), - .sattr = sattr, - }; - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_SETATTR], - .rpc_argp = &arg, - .rpc_resp = fattr, - }; - int status; - - dprintk("NFS call setattr\n"); - if (sattr->ia_valid & ATTR_FILE) - msg.rpc_cred = nfs_file_cred(sattr->ia_file); - nfs_fattr_init(fattr); - status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); - if (status == 0) - nfs_setattr_update_inode(inode, sattr); - dprintk("NFS reply setattr: %d\n", status); - return status; -} - -static int -nfs3_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) -{ - struct nfs3_diropargs arg = { - .fh = NFS_FH(dir), - .name = name->name, - .len = name->len - }; - struct nfs3_diropres res = { - .fh = fhandle, - .fattr = fattr - }; - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_LOOKUP], - .rpc_argp = &arg, - .rpc_resp = &res, - }; - int status; - - dprintk("NFS call lookup %s\n", name->name); - res.dir_attr = nfs_alloc_fattr(); - if (res.dir_attr == NULL) - return -ENOMEM; - - nfs_fattr_init(fattr); - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - nfs_refresh_inode(dir, res.dir_attr); - if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR)) { - msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR]; - msg.rpc_argp = fhandle; - msg.rpc_resp = fattr; - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - } - nfs_free_fattr(res.dir_attr); - dprintk("NFS reply lookup: %d\n", status); - return status; -} - -static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry) -{ - struct nfs3_accessargs arg = { - .fh = NFS_FH(inode), - }; - struct nfs3_accessres res; - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_ACCESS], - .rpc_argp = &arg, - .rpc_resp = &res, - .rpc_cred = entry->cred, - }; - int mode = entry->mask; - int status = -ENOMEM; - - dprintk("NFS call access\n"); - - if (mode & MAY_READ) - arg.access |= NFS3_ACCESS_READ; - if (S_ISDIR(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; - } - - res.fattr = nfs_alloc_fattr(); - if (res.fattr == NULL) - goto out; - - status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); - nfs_refresh_inode(inode, res.fattr); - if (status == 0) { - entry->mask = 0; - if (res.access & NFS3_ACCESS_READ) - entry->mask |= MAY_READ; - if (res.access & (NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND | NFS3_ACCESS_DELETE)) - entry->mask |= MAY_WRITE; - if (res.access & (NFS3_ACCESS_LOOKUP|NFS3_ACCESS_EXECUTE)) - entry->mask |= MAY_EXEC; - } - nfs_free_fattr(res.fattr); -out: - dprintk("NFS reply access: %d\n", status); - return status; -} - -static int nfs3_proc_readlink(struct inode *inode, struct page *page, - unsigned int pgbase, unsigned int pglen) -{ - struct nfs_fattr *fattr; - struct nfs3_readlinkargs args = { - .fh = NFS_FH(inode), - .pgbase = pgbase, - .pglen = pglen, - .pages = &page - }; - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_READLINK], - .rpc_argp = &args, - }; - int status = -ENOMEM; - - dprintk("NFS call readlink\n"); - fattr = nfs_alloc_fattr(); - if (fattr == NULL) - goto out; - msg.rpc_resp = fattr; - - status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); - nfs_refresh_inode(inode, fattr); - nfs_free_fattr(fattr); -out: - dprintk("NFS reply readlink: %d\n", status); - return status; -} - -struct nfs3_createdata { - struct rpc_message msg; - union { - struct nfs3_createargs create; - struct nfs3_mkdirargs mkdir; - struct nfs3_symlinkargs symlink; - struct nfs3_mknodargs mknod; - } arg; - struct nfs3_diropres res; - struct nfs_fh fh; - struct nfs_fattr fattr; - struct nfs_fattr dir_attr; -}; - -static struct nfs3_createdata *nfs3_alloc_createdata(void) -{ - struct nfs3_createdata *data; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (data != NULL) { - data->msg.rpc_argp = &data->arg; - data->msg.rpc_resp = &data->res; - data->res.fh = &data->fh; - data->res.fattr = &data->fattr; - data->res.dir_attr = &data->dir_attr; - nfs_fattr_init(data->res.fattr); - nfs_fattr_init(data->res.dir_attr); - } - return data; -} - -static int nfs3_do_create(struct inode *dir, struct dentry *dentry, struct nfs3_createdata *data) -{ - int status; - - status = rpc_call_sync(NFS_CLIENT(dir), &data->msg, 0); - nfs_post_op_update_inode(dir, data->res.dir_attr); - if (status == 0) - status = nfs_instantiate(dentry, data->res.fh, data->res.fattr); - return status; -} - -static void nfs3_free_createdata(struct nfs3_createdata *data) -{ - kfree(data); -} - -/* - * Create a regular file. - */ -static int -nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, - int flags, struct nfs_open_context *ctx) -{ - struct nfs3_createdata *data; - umode_t mode = sattr->ia_mode; - int status = -ENOMEM; - - dprintk("NFS call create %s\n", dentry->d_name.name); - - data = nfs3_alloc_createdata(); - if (data == NULL) - goto out; - - data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_CREATE]; - data->arg.create.fh = NFS_FH(dir); - data->arg.create.name = dentry->d_name.name; - data->arg.create.len = dentry->d_name.len; - data->arg.create.sattr = sattr; - - data->arg.create.createmode = NFS3_CREATE_UNCHECKED; - if (flags & O_EXCL) { - data->arg.create.createmode = NFS3_CREATE_EXCLUSIVE; - data->arg.create.verifier[0] = jiffies; - data->arg.create.verifier[1] = current->pid; - } - - sattr->ia_mode &= ~current_umask(); - - for (;;) { - status = nfs3_do_create(dir, dentry, data); - - if (status != -ENOTSUPP) - break; - /* If the server doesn't support the exclusive creation - * semantics, try again with simple 'guarded' mode. */ - switch (data->arg.create.createmode) { - case NFS3_CREATE_EXCLUSIVE: - data->arg.create.createmode = NFS3_CREATE_GUARDED; - break; - - case NFS3_CREATE_GUARDED: - data->arg.create.createmode = NFS3_CREATE_UNCHECKED; - break; - - case NFS3_CREATE_UNCHECKED: - goto out; - } - nfs_fattr_init(data->res.dir_attr); - nfs_fattr_init(data->res.fattr); - } - - if (status != 0) - goto out; - - /* When we created the file with exclusive semantics, make - * sure we set the attributes afterwards. */ - if (data->arg.create.createmode == NFS3_CREATE_EXCLUSIVE) { - dprintk("NFS call setattr (post-create)\n"); - - if (!(sattr->ia_valid & ATTR_ATIME_SET)) - sattr->ia_valid |= ATTR_ATIME; - if (!(sattr->ia_valid & ATTR_MTIME_SET)) - sattr->ia_valid |= ATTR_MTIME; - - /* 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) */ - status = nfs3_proc_setattr(dentry, data->res.fattr, sattr); - nfs_post_op_update_inode(dentry->d_inode, data->res.fattr); - dprintk("NFS reply setattr (post-create): %d\n", status); - if (status != 0) - goto out; - } - status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode); -out: - nfs3_free_createdata(data); - dprintk("NFS reply create: %d\n", status); - return status; -} - -static int -nfs3_proc_remove(struct inode *dir, struct qstr *name) -{ - struct nfs_removeargs arg = { - .fh = NFS_FH(dir), - .name.len = name->len, - .name.name = name->name, - }; - struct nfs_removeres res; - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_REMOVE], - .rpc_argp = &arg, - .rpc_resp = &res, - }; - int status = -ENOMEM; - - dprintk("NFS call remove %s\n", name->name); - res.dir_attr = nfs_alloc_fattr(); - if (res.dir_attr == NULL) - goto out; - - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - nfs_post_op_update_inode(dir, res.dir_attr); - nfs_free_fattr(res.dir_attr); -out: - dprintk("NFS reply remove: %d\n", status); - return status; -} - -static void -nfs3_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) -{ - msg->rpc_proc = &nfs3_procedures[NFS3PROC_REMOVE]; -} - -static void nfs3_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data) -{ - rpc_call_start(task); -} - -static int -nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir) -{ - struct nfs_removeres *res; - if (nfs3_async_handle_jukebox(task, dir)) - return 0; - res = task->tk_msg.rpc_resp; - nfs_post_op_update_inode(dir, res->dir_attr); - return 1; -} - -static void -nfs3_proc_rename_setup(struct rpc_message *msg, struct inode *dir) -{ - msg->rpc_proc = &nfs3_procedures[NFS3PROC_RENAME]; -} - -static void nfs3_proc_rename_rpc_prepare(struct rpc_task *task, struct nfs_renamedata *data) -{ - rpc_call_start(task); -} - -static int -nfs3_proc_rename_done(struct rpc_task *task, struct inode *old_dir, - struct inode *new_dir) -{ - struct nfs_renameres *res; - - if (nfs3_async_handle_jukebox(task, old_dir)) - return 0; - res = task->tk_msg.rpc_resp; - - nfs_post_op_update_inode(old_dir, res->old_fattr); - nfs_post_op_update_inode(new_dir, res->new_fattr); - return 1; -} - -static int -nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name, - struct inode *new_dir, struct qstr *new_name) -{ - struct nfs_renameargs arg = { - .old_dir = NFS_FH(old_dir), - .old_name = old_name, - .new_dir = NFS_FH(new_dir), - .new_name = new_name, - }; - struct nfs_renameres res; - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_RENAME], - .rpc_argp = &arg, - .rpc_resp = &res, - }; - int status = -ENOMEM; - - dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name); - - res.old_fattr = nfs_alloc_fattr(); - res.new_fattr = nfs_alloc_fattr(); - if (res.old_fattr == NULL || res.new_fattr == NULL) - goto out; - - status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0); - nfs_post_op_update_inode(old_dir, res.old_fattr); - nfs_post_op_update_inode(new_dir, res.new_fattr); -out: - nfs_free_fattr(res.old_fattr); - nfs_free_fattr(res.new_fattr); - dprintk("NFS reply rename: %d\n", status); - return status; -} - -static int -nfs3_proc_link(struct inode *inode, struct inode *dir, struct qstr *name) -{ - struct nfs3_linkargs arg = { - .fromfh = NFS_FH(inode), - .tofh = NFS_FH(dir), - .toname = name->name, - .tolen = name->len - }; - struct nfs3_linkres res; - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_LINK], - .rpc_argp = &arg, - .rpc_resp = &res, - }; - int status = -ENOMEM; - - dprintk("NFS call link %s\n", name->name); - res.fattr = nfs_alloc_fattr(); - res.dir_attr = nfs_alloc_fattr(); - if (res.fattr == NULL || res.dir_attr == NULL) - goto out; - - status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); - nfs_post_op_update_inode(dir, res.dir_attr); - nfs_post_op_update_inode(inode, res.fattr); -out: - nfs_free_fattr(res.dir_attr); - nfs_free_fattr(res.fattr); - dprintk("NFS reply link: %d\n", status); - return status; -} - -static int -nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page, - unsigned int len, struct iattr *sattr) -{ - struct nfs3_createdata *data; - int status = -ENOMEM; - - if (len > NFS3_MAXPATHLEN) - return -ENAMETOOLONG; - - dprintk("NFS call symlink %s\n", dentry->d_name.name); - - data = nfs3_alloc_createdata(); - if (data == NULL) - goto out; - data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_SYMLINK]; - data->arg.symlink.fromfh = NFS_FH(dir); - data->arg.symlink.fromname = dentry->d_name.name; - data->arg.symlink.fromlen = dentry->d_name.len; - data->arg.symlink.pages = &page; - data->arg.symlink.pathlen = len; - data->arg.symlink.sattr = sattr; - - status = nfs3_do_create(dir, dentry, data); - - nfs3_free_createdata(data); -out: - dprintk("NFS reply symlink: %d\n", status); - return status; -} - -static int -nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr) -{ - struct nfs3_createdata *data; - umode_t mode = sattr->ia_mode; - int status = -ENOMEM; - - dprintk("NFS call mkdir %s\n", dentry->d_name.name); - - sattr->ia_mode &= ~current_umask(); - - data = nfs3_alloc_createdata(); - if (data == NULL) - goto out; - - data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_MKDIR]; - data->arg.mkdir.fh = NFS_FH(dir); - data->arg.mkdir.name = dentry->d_name.name; - data->arg.mkdir.len = dentry->d_name.len; - data->arg.mkdir.sattr = sattr; - - status = nfs3_do_create(dir, dentry, data); - if (status != 0) - goto out; - - status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode); -out: - nfs3_free_createdata(data); - dprintk("NFS reply mkdir: %d\n", status); - return status; -} - -static int -nfs3_proc_rmdir(struct inode *dir, struct qstr *name) -{ - struct nfs_fattr *dir_attr; - struct nfs3_diropargs arg = { - .fh = NFS_FH(dir), - .name = name->name, - .len = name->len - }; - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_RMDIR], - .rpc_argp = &arg, - }; - int status = -ENOMEM; - - dprintk("NFS call rmdir %s\n", name->name); - dir_attr = nfs_alloc_fattr(); - if (dir_attr == NULL) - goto out; - - msg.rpc_resp = dir_attr; - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - nfs_post_op_update_inode(dir, dir_attr); - nfs_free_fattr(dir_attr); -out: - 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 *dentry, struct rpc_cred *cred, - u64 cookie, struct page **pages, unsigned int count, int plus) -{ - struct inode *dir = dentry->d_inode; - __be32 *verf = NFS_COOKIEVERF(dir); - struct nfs3_readdirargs arg = { - .fh = NFS_FH(dir), - .cookie = cookie, - .verf = {verf[0], verf[1]}, - .plus = plus, - .count = count, - .pages = pages - }; - struct nfs3_readdirres res = { - .verf = verf, - .plus = plus - }; - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_READDIR], - .rpc_argp = &arg, - .rpc_resp = &res, - .rpc_cred = cred - }; - int status = -ENOMEM; - - if (plus) - msg.rpc_proc = &nfs3_procedures[NFS3PROC_READDIRPLUS]; - - dprintk("NFS call readdir%s %d\n", - plus? "plus" : "", (unsigned int) cookie); - - res.dir_attr = nfs_alloc_fattr(); - if (res.dir_attr == NULL) - goto out; - - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - - nfs_invalidate_atime(dir); - nfs_refresh_inode(dir, res.dir_attr); - - nfs_free_fattr(res.dir_attr); -out: - dprintk("NFS reply readdir%s: %d\n", - plus? "plus" : "", status); - return status; -} - -static int -nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, - dev_t rdev) -{ - struct nfs3_createdata *data; - umode_t mode = sattr->ia_mode; - int status = -ENOMEM; - - dprintk("NFS call mknod %s %u:%u\n", dentry->d_name.name, - MAJOR(rdev), MINOR(rdev)); - - sattr->ia_mode &= ~current_umask(); - - data = nfs3_alloc_createdata(); - if (data == NULL) - goto out; - - data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_MKNOD]; - data->arg.mknod.fh = NFS_FH(dir); - data->arg.mknod.name = dentry->d_name.name; - data->arg.mknod.len = dentry->d_name.len; - data->arg.mknod.sattr = sattr; - data->arg.mknod.rdev = rdev; - - switch (sattr->ia_mode & S_IFMT) { - case S_IFBLK: - data->arg.mknod.type = NF3BLK; - break; - case S_IFCHR: - data->arg.mknod.type = NF3CHR; - break; - case S_IFIFO: - data->arg.mknod.type = NF3FIFO; - break; - case S_IFSOCK: - data->arg.mknod.type = NF3SOCK; - break; - default: - status = -EINVAL; - goto out; - } - - status = nfs3_do_create(dir, dentry, data); - if (status != 0) - goto out; - status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode); -out: - nfs3_free_createdata(data); - dprintk("NFS reply mknod: %d\n", status); - return status; -} - -static int -nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsstat *stat) -{ - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_FSSTAT], - .rpc_argp = fhandle, - .rpc_resp = stat, - }; - int status; - - dprintk("NFS call fsstat\n"); - nfs_fattr_init(stat->fattr); - status = rpc_call_sync(server->client, &msg, 0); - dprintk("NFS reply fsstat: %d\n", status); - return status; -} - -static int -do_proc_fsinfo(struct rpc_clnt *client, struct nfs_fh *fhandle, - struct nfs_fsinfo *info) -{ - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_FSINFO], - .rpc_argp = fhandle, - .rpc_resp = info, - }; - int status; - - dprintk("NFS call fsinfo\n"); - nfs_fattr_init(info->fattr); - status = rpc_call_sync(client, &msg, 0); - dprintk("NFS reply fsinfo: %d\n", status); - return status; -} - -/* - * Bare-bones access to fsinfo: this is for nfs_get_root/nfs_get_sb via - * nfs_create_server - */ -static int -nfs3_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info) -{ - int status; - - status = do_proc_fsinfo(server->client, fhandle, info); - if (status && server->nfs_client->cl_rpcclient != server->client) - status = do_proc_fsinfo(server->nfs_client->cl_rpcclient, fhandle, info); - return status; -} - -static int -nfs3_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_pathconf *info) -{ - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_PATHCONF], - .rpc_argp = fhandle, - .rpc_resp = info, - }; - int status; - - dprintk("NFS call pathconf\n"); - nfs_fattr_init(info->fattr); - status = rpc_call_sync(server->client, &msg, 0); - dprintk("NFS reply pathconf: %d\n", status); - return status; -} - -static int nfs3_read_done(struct rpc_task *task, struct nfs_read_data *data) -{ - if (nfs3_async_handle_jukebox(task, data->inode)) - return -EAGAIN; - - nfs_invalidate_atime(data->inode); - nfs_refresh_inode(data->inode, &data->fattr); - return 0; -} - -static void nfs3_proc_read_setup(struct nfs_read_data *data, struct rpc_message *msg) -{ - msg->rpc_proc = &nfs3_procedures[NFS3PROC_READ]; -} - -static void nfs3_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) -{ - rpc_call_start(task); -} - -static int nfs3_write_done(struct rpc_task *task, struct nfs_write_data *data) -{ - if (nfs3_async_handle_jukebox(task, data->inode)) - return -EAGAIN; - if (task->tk_status >= 0) - nfs_post_op_update_inode_force_wcc(data->inode, data->res.fattr); - return 0; -} - -static void nfs3_proc_write_setup(struct nfs_write_data *data, struct rpc_message *msg) -{ - msg->rpc_proc = &nfs3_procedures[NFS3PROC_WRITE]; -} - -static void nfs3_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) -{ - rpc_call_start(task); -} - -static int nfs3_commit_done(struct rpc_task *task, struct nfs_write_data *data) -{ - if (nfs3_async_handle_jukebox(task, data->inode)) - return -EAGAIN; - nfs_refresh_inode(data->inode, data->res.fattr); - return 0; -} - -static void nfs3_proc_commit_setup(struct nfs_write_data *data, struct rpc_message *msg) -{ - msg->rpc_proc = &nfs3_procedures[NFS3PROC_COMMIT]; -} - -static int -nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl) -{ - struct inode *inode = filp->f_path.dentry->d_inode; - - return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl); -} - -const struct nfs_rpc_ops nfs_v3_clientops = { - .version = 3, /* protocol version */ - .dentry_ops = &nfs_dentry_operations, - .dir_inode_ops = &nfs3_dir_inode_operations, - .file_inode_ops = &nfs3_file_inode_operations, - .file_ops = &nfs_file_operations, - .getroot = nfs3_proc_get_root, - .getattr = nfs3_proc_getattr, - .setattr = nfs3_proc_setattr, - .lookup = nfs3_proc_lookup, - .access = nfs3_proc_access, - .readlink = nfs3_proc_readlink, - .create = nfs3_proc_create, - .remove = nfs3_proc_remove, - .unlink_setup = nfs3_proc_unlink_setup, - .unlink_rpc_prepare = nfs3_proc_unlink_rpc_prepare, - .unlink_done = nfs3_proc_unlink_done, - .rename = nfs3_proc_rename, - .rename_setup = nfs3_proc_rename_setup, - .rename_rpc_prepare = nfs3_proc_rename_rpc_prepare, - .rename_done = nfs3_proc_rename_done, - .link = nfs3_proc_link, - .symlink = nfs3_proc_symlink, - .mkdir = nfs3_proc_mkdir, - .rmdir = nfs3_proc_rmdir, - .readdir = nfs3_proc_readdir, - .mknod = nfs3_proc_mknod, - .statfs = nfs3_proc_statfs, - .fsinfo = nfs3_proc_fsinfo, - .pathconf = nfs3_proc_pathconf, - .decode_dirent = nfs3_decode_dirent, - .read_setup = nfs3_proc_read_setup, - .read_rpc_prepare = nfs3_proc_read_rpc_prepare, - .read_done = nfs3_read_done, - .write_setup = nfs3_proc_write_setup, - .write_rpc_prepare = nfs3_proc_write_rpc_prepare, - .write_done = nfs3_write_done, - .commit_setup = nfs3_proc_commit_setup, - .commit_done = nfs3_commit_done, - .lock = nfs3_proc_lock, - .clear_acl_cache = nfs3_forget_cached_acls, - .close_context = nfs_close_context, - .init_client = nfs_init_client, -}; diff --git a/ANDROID_3.4.5/fs/nfs/nfs3xdr.c b/ANDROID_3.4.5/fs/nfs/nfs3xdr.c deleted file mode 100644 index a77cc9a3..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfs3xdr.c +++ /dev/null @@ -1,2498 +0,0 @@ -/* - * linux/fs/nfs/nfs3xdr.c - * - * XDR functions to encode/decode NFSv3 RPC arguments and results. - * - * Copyright (C) 1996, 1997 Olaf Kirch - */ - -#include <linux/param.h> -#include <linux/time.h> -#include <linux/mm.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> -#include <linux/nfsacl.h> -#include "internal.h" - -#define NFSDBG_FACILITY NFSDBG_XDR - -/* Mapping from NFS error code to "errno" error code. */ -#define errno_NFSERR_IO EIO - -/* - * Declare the space requirements for NFS arguments and replies as - * number of 32bit-words - */ -#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_cookieverf_sz (NFS3_COOKIEVERFSIZE>>2) -#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_diropargs_sz (NFS3_fh_sz+NFS3_filename_sz) - -#define NFS3_getattrargs_sz (NFS3_fh_sz) -#define NFS3_setattrargs_sz (NFS3_fh_sz+NFS3_sattr_sz+3) -#define NFS3_lookupargs_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+1+NFS3_sattr_sz) -#define NFS3_mknodargs_sz (NFS3_diropargs_sz+2+NFS3_sattr_sz) -#define NFS3_removeargs_sz (NFS3_fh_sz+NFS3_filename_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+NFS3_cookieverf_sz+3) -#define NFS3_readdirplusargs_sz (NFS3_fh_sz+NFS3_cookieverf_sz+4) -#define NFS3_commitargs_sz (NFS3_fh_sz+3) - -#define NFS3_getattrres_sz (1+NFS3_fattr_sz) -#define NFS3_setattrres_sz (1+NFS3_wcc_data_sz) -#define NFS3_removeres_sz (NFS3_setattrres_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+1) -#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) - -#define ACL3_getaclargs_sz (NFS3_fh_sz+1) -#define ACL3_setaclargs_sz (NFS3_fh_sz+1+ \ - XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE)) -#define ACL3_getaclres_sz (1+NFS3_post_op_attr_sz+1+ \ - XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE)) -#define ACL3_setaclres_sz (1+NFS3_post_op_attr_sz) - -/* - * Map file type to S_IFMT bits - */ -static const umode_t nfs_type2fmt[] = { - [NF3BAD] = 0, - [NF3REG] = S_IFREG, - [NF3DIR] = S_IFDIR, - [NF3BLK] = S_IFBLK, - [NF3CHR] = S_IFCHR, - [NF3LNK] = S_IFLNK, - [NF3SOCK] = S_IFSOCK, - [NF3FIFO] = S_IFIFO, -}; - -/* - * While encoding arguments, set up the reply buffer in advance to - * receive reply data directly into the page cache. - */ -static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages, - unsigned int base, unsigned int len, - unsigned int bufsize) -{ - struct rpc_auth *auth = req->rq_cred->cr_auth; - unsigned int replen; - - replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize; - xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len); -} - -/* - * Handle decode buffer overflows out-of-line. - */ -static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) -{ - dprintk("NFS: %s prematurely hit the end of our receive buffer. " - "Remaining buffer length is %tu words.\n", - func, xdr->end - xdr->p); -} - - -/* - * Encode/decode NFSv3 basic data types - * - * Basic NFSv3 data types are defined in section 2.5 of RFC 1813: - * "NFS Version 3 Protocol Specification". - * - * Not all basic data types have their own encoding and decoding - * functions. For run-time efficiency, some data types are encoded - * or decoded inline. - */ - -static void encode_uint32(struct xdr_stream *xdr, u32 value) -{ - __be32 *p = xdr_reserve_space(xdr, 4); - *p = cpu_to_be32(value); -} - -static int decode_uint32(struct xdr_stream *xdr, u32 *value) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - *value = be32_to_cpup(p); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_uint64(struct xdr_stream *xdr, u64 *value) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 8); - if (unlikely(p == NULL)) - goto out_overflow; - xdr_decode_hyper(p, value); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * fileid3 - * - * typedef uint64 fileid3; - */ -static __be32 *xdr_decode_fileid3(__be32 *p, u64 *fileid) -{ - return xdr_decode_hyper(p, fileid); -} - -static int decode_fileid3(struct xdr_stream *xdr, u64 *fileid) -{ - return decode_uint64(xdr, fileid); -} - -/* - * filename3 - * - * typedef string filename3<>; - */ -static void encode_filename3(struct xdr_stream *xdr, - const char *name, u32 length) -{ - __be32 *p; - - BUG_ON(length > NFS3_MAXNAMLEN); - p = xdr_reserve_space(xdr, 4 + length); - xdr_encode_opaque(p, name, length); -} - -static int decode_inline_filename3(struct xdr_stream *xdr, - const char **name, u32 *length) -{ - __be32 *p; - u32 count; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - count = be32_to_cpup(p); - if (count > NFS3_MAXNAMLEN) - goto out_nametoolong; - p = xdr_inline_decode(xdr, count); - if (unlikely(p == NULL)) - goto out_overflow; - *name = (const char *)p; - *length = count; - return 0; - -out_nametoolong: - dprintk("NFS: returned filename too long: %u\n", count); - return -ENAMETOOLONG; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * nfspath3 - * - * typedef string nfspath3<>; - */ -static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages, - const u32 length) -{ - BUG_ON(length > NFS3_MAXPATHLEN); - encode_uint32(xdr, length); - xdr_write_pages(xdr, pages, 0, length); -} - -static int decode_nfspath3(struct xdr_stream *xdr) -{ - u32 recvd, count; - size_t hdrlen; - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - count = be32_to_cpup(p); - if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN)) - goto out_nametoolong; - hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; - recvd = xdr->buf->len - hdrlen; - if (unlikely(count > recvd)) - goto out_cheating; - - xdr_read_pages(xdr, count); - xdr_terminate_string(xdr->buf, count); - return 0; - -out_nametoolong: - dprintk("NFS: returned pathname too long: %u\n", count); - return -ENAMETOOLONG; -out_cheating: - dprintk("NFS: server cheating in pathname result: " - "count %u > recvd %u\n", count, recvd); - return -EIO; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * cookie3 - * - * typedef uint64 cookie3 - */ -static __be32 *xdr_encode_cookie3(__be32 *p, u64 cookie) -{ - return xdr_encode_hyper(p, cookie); -} - -static int decode_cookie3(struct xdr_stream *xdr, u64 *cookie) -{ - return decode_uint64(xdr, cookie); -} - -/* - * cookieverf3 - * - * typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE]; - */ -static __be32 *xdr_encode_cookieverf3(__be32 *p, const __be32 *verifier) -{ - memcpy(p, verifier, NFS3_COOKIEVERFSIZE); - return p + XDR_QUADLEN(NFS3_COOKIEVERFSIZE); -} - -static int decode_cookieverf3(struct xdr_stream *xdr, __be32 *verifier) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE); - if (unlikely(p == NULL)) - goto out_overflow; - memcpy(verifier, p, NFS3_COOKIEVERFSIZE); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * createverf3 - * - * typedef opaque createverf3[NFS3_CREATEVERFSIZE]; - */ -static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier) -{ - __be32 *p; - - p = xdr_reserve_space(xdr, NFS3_CREATEVERFSIZE); - memcpy(p, verifier, NFS3_CREATEVERFSIZE); -} - -static int decode_writeverf3(struct xdr_stream *xdr, __be32 *verifier) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE); - if (unlikely(p == NULL)) - goto out_overflow; - memcpy(verifier, p, NFS3_WRITEVERFSIZE); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * size3 - * - * typedef uint64 size3; - */ -static __be32 *xdr_decode_size3(__be32 *p, u64 *size) -{ - return xdr_decode_hyper(p, size); -} - -/* - * nfsstat3 - * - * enum nfsstat3 { - * NFS3_OK = 0, - * ... - * } - */ -#define NFS3_OK NFS_OK - -static int decode_nfsstat3(struct xdr_stream *xdr, enum nfs_stat *status) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - *status = be32_to_cpup(p); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * ftype3 - * - * enum ftype3 { - * NF3REG = 1, - * NF3DIR = 2, - * NF3BLK = 3, - * NF3CHR = 4, - * NF3LNK = 5, - * NF3SOCK = 6, - * NF3FIFO = 7 - * }; - */ -static void encode_ftype3(struct xdr_stream *xdr, const u32 type) -{ - BUG_ON(type > NF3FIFO); - encode_uint32(xdr, type); -} - -static __be32 *xdr_decode_ftype3(__be32 *p, umode_t *mode) -{ - u32 type; - - type = be32_to_cpup(p++); - if (type > NF3FIFO) - type = NF3NON; - *mode = nfs_type2fmt[type]; - return p; -} - -/* - * specdata3 - * - * struct specdata3 { - * uint32 specdata1; - * uint32 specdata2; - * }; - */ -static void encode_specdata3(struct xdr_stream *xdr, const dev_t rdev) -{ - __be32 *p; - - p = xdr_reserve_space(xdr, 8); - *p++ = cpu_to_be32(MAJOR(rdev)); - *p = cpu_to_be32(MINOR(rdev)); -} - -static __be32 *xdr_decode_specdata3(__be32 *p, dev_t *rdev) -{ - unsigned int major, minor; - - major = be32_to_cpup(p++); - minor = be32_to_cpup(p++); - *rdev = MKDEV(major, minor); - if (MAJOR(*rdev) != major || MINOR(*rdev) != minor) - *rdev = 0; - return p; -} - -/* - * nfs_fh3 - * - * struct nfs_fh3 { - * opaque data<NFS3_FHSIZE>; - * }; - */ -static void encode_nfs_fh3(struct xdr_stream *xdr, const struct nfs_fh *fh) -{ - __be32 *p; - - BUG_ON(fh->size > NFS3_FHSIZE); - p = xdr_reserve_space(xdr, 4 + fh->size); - xdr_encode_opaque(p, fh->data, fh->size); -} - -static int decode_nfs_fh3(struct xdr_stream *xdr, struct nfs_fh *fh) -{ - u32 length; - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - length = be32_to_cpup(p++); - if (unlikely(length > NFS3_FHSIZE)) - goto out_toobig; - p = xdr_inline_decode(xdr, length); - if (unlikely(p == NULL)) - goto out_overflow; - fh->size = length; - memcpy(fh->data, p, length); - return 0; -out_toobig: - dprintk("NFS: file handle size (%u) too big\n", length); - return -E2BIG; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static void zero_nfs_fh3(struct nfs_fh *fh) -{ - memset(fh, 0, sizeof(*fh)); -} - -/* - * nfstime3 - * - * struct nfstime3 { - * uint32 seconds; - * uint32 nseconds; - * }; - */ -static __be32 *xdr_encode_nfstime3(__be32 *p, const struct timespec *timep) -{ - *p++ = cpu_to_be32(timep->tv_sec); - *p++ = cpu_to_be32(timep->tv_nsec); - return p; -} - -static __be32 *xdr_decode_nfstime3(__be32 *p, struct timespec *timep) -{ - timep->tv_sec = be32_to_cpup(p++); - timep->tv_nsec = be32_to_cpup(p++); - return p; -} - -/* - * sattr3 - * - * enum time_how { - * DONT_CHANGE = 0, - * SET_TO_SERVER_TIME = 1, - * SET_TO_CLIENT_TIME = 2 - * }; - * - * union set_mode3 switch (bool set_it) { - * case TRUE: - * mode3 mode; - * default: - * void; - * }; - * - * union set_uid3 switch (bool set_it) { - * case TRUE: - * uid3 uid; - * default: - * void; - * }; - * - * union set_gid3 switch (bool set_it) { - * case TRUE: - * gid3 gid; - * default: - * void; - * }; - * - * union set_size3 switch (bool set_it) { - * case TRUE: - * size3 size; - * default: - * void; - * }; - * - * union set_atime switch (time_how set_it) { - * case SET_TO_CLIENT_TIME: - * nfstime3 atime; - * default: - * void; - * }; - * - * union set_mtime switch (time_how set_it) { - * case SET_TO_CLIENT_TIME: - * nfstime3 mtime; - * default: - * void; - * }; - * - * struct sattr3 { - * set_mode3 mode; - * set_uid3 uid; - * set_gid3 gid; - * set_size3 size; - * set_atime atime; - * set_mtime mtime; - * }; - */ -static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr) -{ - u32 nbytes; - __be32 *p; - - /* - * In order to make only a single xdr_reserve_space() call, - * pre-compute the total number of bytes to be reserved. - * Six boolean values, one for each set_foo field, are always - * present in the encoded result, so start there. - */ - nbytes = 6 * 4; - if (attr->ia_valid & ATTR_MODE) - nbytes += 4; - if (attr->ia_valid & ATTR_UID) - nbytes += 4; - if (attr->ia_valid & ATTR_GID) - nbytes += 4; - if (attr->ia_valid & ATTR_SIZE) - nbytes += 8; - if (attr->ia_valid & ATTR_ATIME_SET) - nbytes += 8; - if (attr->ia_valid & ATTR_MTIME_SET) - nbytes += 8; - p = xdr_reserve_space(xdr, nbytes); - - if (attr->ia_valid & ATTR_MODE) { - *p++ = xdr_one; - *p++ = cpu_to_be32(attr->ia_mode & S_IALLUGO); - } else - *p++ = xdr_zero; - - if (attr->ia_valid & ATTR_UID) { - *p++ = xdr_one; - *p++ = cpu_to_be32(attr->ia_uid); - } else - *p++ = xdr_zero; - - if (attr->ia_valid & ATTR_GID) { - *p++ = xdr_one; - *p++ = cpu_to_be32(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_nfstime3(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; - xdr_encode_nfstime3(p, &attr->ia_mtime); - } else if (attr->ia_valid & ATTR_MTIME) { - *p = xdr_one; - } else - *p = xdr_zero; -} - -/* - * fattr3 - * - * struct fattr3 { - * ftype3 type; - * mode3 mode; - * uint32 nlink; - * uid3 uid; - * gid3 gid; - * size3 size; - * size3 used; - * specdata3 rdev; - * uint64 fsid; - * fileid3 fileid; - * nfstime3 atime; - * nfstime3 mtime; - * nfstime3 ctime; - * }; - */ -static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr) -{ - umode_t fmode; - __be32 *p; - - p = xdr_inline_decode(xdr, NFS3_fattr_sz << 2); - if (unlikely(p == NULL)) - goto out_overflow; - - p = xdr_decode_ftype3(p, &fmode); - - fattr->mode = (be32_to_cpup(p++) & ~S_IFMT) | fmode; - fattr->nlink = be32_to_cpup(p++); - fattr->uid = be32_to_cpup(p++); - fattr->gid = be32_to_cpup(p++); - - p = xdr_decode_size3(p, &fattr->size); - p = xdr_decode_size3(p, &fattr->du.nfs3.used); - p = xdr_decode_specdata3(p, &fattr->rdev); - - p = xdr_decode_hyper(p, &fattr->fsid.major); - fattr->fsid.minor = 0; - - p = xdr_decode_fileid3(p, &fattr->fileid); - p = xdr_decode_nfstime3(p, &fattr->atime); - p = xdr_decode_nfstime3(p, &fattr->mtime); - xdr_decode_nfstime3(p, &fattr->ctime); - - fattr->valid |= NFS_ATTR_FATTR_V3; - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * post_op_attr - * - * union post_op_attr switch (bool attributes_follow) { - * case TRUE: - * fattr3 attributes; - * case FALSE: - * void; - * }; - */ -static int decode_post_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - if (*p != xdr_zero) - return decode_fattr3(xdr, fattr); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * wcc_attr - * struct wcc_attr { - * size3 size; - * nfstime3 mtime; - * nfstime3 ctime; - * }; - */ -static int decode_wcc_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, NFS3_wcc_attr_sz << 2); - if (unlikely(p == NULL)) - goto out_overflow; - - fattr->valid |= NFS_ATTR_FATTR_PRESIZE - | NFS_ATTR_FATTR_PREMTIME - | NFS_ATTR_FATTR_PRECTIME; - - p = xdr_decode_size3(p, &fattr->pre_size); - p = xdr_decode_nfstime3(p, &fattr->pre_mtime); - xdr_decode_nfstime3(p, &fattr->pre_ctime); - - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * pre_op_attr - * union pre_op_attr switch (bool attributes_follow) { - * case TRUE: - * wcc_attr attributes; - * case FALSE: - * void; - * }; - * - * wcc_data - * - * struct wcc_data { - * pre_op_attr before; - * post_op_attr after; - * }; - */ -static int decode_pre_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - if (*p != xdr_zero) - return decode_wcc_attr(xdr, fattr); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_wcc_data(struct xdr_stream *xdr, struct nfs_fattr *fattr) -{ - int error; - - error = decode_pre_op_attr(xdr, fattr); - if (unlikely(error)) - goto out; - error = decode_post_op_attr(xdr, fattr); -out: - return error; -} - -/* - * post_op_fh3 - * - * union post_op_fh3 switch (bool handle_follows) { - * case TRUE: - * nfs_fh3 handle; - * case FALSE: - * void; - * }; - */ -static int decode_post_op_fh3(struct xdr_stream *xdr, struct nfs_fh *fh) -{ - __be32 *p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - if (*p != xdr_zero) - return decode_nfs_fh3(xdr, fh); - zero_nfs_fh3(fh); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * diropargs3 - * - * struct diropargs3 { - * nfs_fh3 dir; - * filename3 name; - * }; - */ -static void encode_diropargs3(struct xdr_stream *xdr, const struct nfs_fh *fh, - const char *name, u32 length) -{ - encode_nfs_fh3(xdr, fh); - encode_filename3(xdr, name, length); -} - - -/* - * NFSv3 XDR encode functions - * - * NFSv3 argument types are defined in section 3.3 of RFC 1813: - * "NFS Version 3 Protocol Specification". - */ - -/* - * 3.3.1 GETATTR3args - * - * struct GETATTR3args { - * nfs_fh3 object; - * }; - */ -static void nfs3_xdr_enc_getattr3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_fh *fh) -{ - encode_nfs_fh3(xdr, fh); -} - -/* - * 3.3.2 SETATTR3args - * - * union sattrguard3 switch (bool check) { - * case TRUE: - * nfstime3 obj_ctime; - * case FALSE: - * void; - * }; - * - * struct SETATTR3args { - * nfs_fh3 object; - * sattr3 new_attributes; - * sattrguard3 guard; - * }; - */ -static void encode_sattrguard3(struct xdr_stream *xdr, - const struct nfs3_sattrargs *args) -{ - __be32 *p; - - if (args->guard) { - p = xdr_reserve_space(xdr, 4 + 8); - *p++ = xdr_one; - xdr_encode_nfstime3(p, &args->guardtime); - } else { - p = xdr_reserve_space(xdr, 4); - *p = xdr_zero; - } -} - -static void nfs3_xdr_enc_setattr3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs3_sattrargs *args) -{ - encode_nfs_fh3(xdr, args->fh); - encode_sattr3(xdr, args->sattr); - encode_sattrguard3(xdr, args); -} - -/* - * 3.3.3 LOOKUP3args - * - * struct LOOKUP3args { - * diropargs3 what; - * }; - */ -static void nfs3_xdr_enc_lookup3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs3_diropargs *args) -{ - encode_diropargs3(xdr, args->fh, args->name, args->len); -} - -/* - * 3.3.4 ACCESS3args - * - * struct ACCESS3args { - * nfs_fh3 object; - * uint32 access; - * }; - */ -static void encode_access3args(struct xdr_stream *xdr, - const struct nfs3_accessargs *args) -{ - encode_nfs_fh3(xdr, args->fh); - encode_uint32(xdr, args->access); -} - -static void nfs3_xdr_enc_access3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs3_accessargs *args) -{ - encode_access3args(xdr, args); -} - -/* - * 3.3.5 READLINK3args - * - * struct READLINK3args { - * nfs_fh3 symlink; - * }; - */ -static void nfs3_xdr_enc_readlink3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs3_readlinkargs *args) -{ - encode_nfs_fh3(xdr, args->fh); - prepare_reply_buffer(req, args->pages, args->pgbase, - args->pglen, NFS3_readlinkres_sz); -} - -/* - * 3.3.6 READ3args - * - * struct READ3args { - * nfs_fh3 file; - * offset3 offset; - * count3 count; - * }; - */ -static void encode_read3args(struct xdr_stream *xdr, - const struct nfs_readargs *args) -{ - __be32 *p; - - encode_nfs_fh3(xdr, args->fh); - - p = xdr_reserve_space(xdr, 8 + 4); - p = xdr_encode_hyper(p, args->offset); - *p = cpu_to_be32(args->count); -} - -static void nfs3_xdr_enc_read3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_readargs *args) -{ - encode_read3args(xdr, args); - prepare_reply_buffer(req, args->pages, args->pgbase, - args->count, NFS3_readres_sz); - req->rq_rcv_buf.flags |= XDRBUF_READ; -} - -/* - * 3.3.7 WRITE3args - * - * enum stable_how { - * UNSTABLE = 0, - * DATA_SYNC = 1, - * FILE_SYNC = 2 - * }; - * - * struct WRITE3args { - * nfs_fh3 file; - * offset3 offset; - * count3 count; - * stable_how stable; - * opaque data<>; - * }; - */ -static void encode_write3args(struct xdr_stream *xdr, - const struct nfs_writeargs *args) -{ - __be32 *p; - - encode_nfs_fh3(xdr, args->fh); - - p = xdr_reserve_space(xdr, 8 + 4 + 4 + 4); - p = xdr_encode_hyper(p, args->offset); - *p++ = cpu_to_be32(args->count); - *p++ = cpu_to_be32(args->stable); - *p = cpu_to_be32(args->count); - xdr_write_pages(xdr, args->pages, args->pgbase, args->count); -} - -static void nfs3_xdr_enc_write3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_writeargs *args) -{ - encode_write3args(xdr, args); - xdr->buf->flags |= XDRBUF_WRITE; -} - -/* - * 3.3.8 CREATE3args - * - * enum createmode3 { - * UNCHECKED = 0, - * GUARDED = 1, - * EXCLUSIVE = 2 - * }; - * - * union createhow3 switch (createmode3 mode) { - * case UNCHECKED: - * case GUARDED: - * sattr3 obj_attributes; - * case EXCLUSIVE: - * createverf3 verf; - * }; - * - * struct CREATE3args { - * diropargs3 where; - * createhow3 how; - * }; - */ -static void encode_createhow3(struct xdr_stream *xdr, - const struct nfs3_createargs *args) -{ - encode_uint32(xdr, args->createmode); - switch (args->createmode) { - case NFS3_CREATE_UNCHECKED: - case NFS3_CREATE_GUARDED: - encode_sattr3(xdr, args->sattr); - break; - case NFS3_CREATE_EXCLUSIVE: - encode_createverf3(xdr, args->verifier); - break; - default: - BUG(); - } -} - -static void nfs3_xdr_enc_create3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs3_createargs *args) -{ - encode_diropargs3(xdr, args->fh, args->name, args->len); - encode_createhow3(xdr, args); -} - -/* - * 3.3.9 MKDIR3args - * - * struct MKDIR3args { - * diropargs3 where; - * sattr3 attributes; - * }; - */ -static void nfs3_xdr_enc_mkdir3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs3_mkdirargs *args) -{ - encode_diropargs3(xdr, args->fh, args->name, args->len); - encode_sattr3(xdr, args->sattr); -} - -/* - * 3.3.10 SYMLINK3args - * - * struct symlinkdata3 { - * sattr3 symlink_attributes; - * nfspath3 symlink_data; - * }; - * - * struct SYMLINK3args { - * diropargs3 where; - * symlinkdata3 symlink; - * }; - */ -static void encode_symlinkdata3(struct xdr_stream *xdr, - const struct nfs3_symlinkargs *args) -{ - encode_sattr3(xdr, args->sattr); - encode_nfspath3(xdr, args->pages, args->pathlen); -} - -static void nfs3_xdr_enc_symlink3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs3_symlinkargs *args) -{ - encode_diropargs3(xdr, args->fromfh, args->fromname, args->fromlen); - encode_symlinkdata3(xdr, args); -} - -/* - * 3.3.11 MKNOD3args - * - * struct devicedata3 { - * sattr3 dev_attributes; - * specdata3 spec; - * }; - * - * union mknoddata3 switch (ftype3 type) { - * case NF3CHR: - * case NF3BLK: - * devicedata3 device; - * case NF3SOCK: - * case NF3FIFO: - * sattr3 pipe_attributes; - * default: - * void; - * }; - * - * struct MKNOD3args { - * diropargs3 where; - * mknoddata3 what; - * }; - */ -static void encode_devicedata3(struct xdr_stream *xdr, - const struct nfs3_mknodargs *args) -{ - encode_sattr3(xdr, args->sattr); - encode_specdata3(xdr, args->rdev); -} - -static void encode_mknoddata3(struct xdr_stream *xdr, - const struct nfs3_mknodargs *args) -{ - encode_ftype3(xdr, args->type); - switch (args->type) { - case NF3CHR: - case NF3BLK: - encode_devicedata3(xdr, args); - break; - case NF3SOCK: - case NF3FIFO: - encode_sattr3(xdr, args->sattr); - break; - case NF3REG: - case NF3DIR: - break; - default: - BUG(); - } -} - -static void nfs3_xdr_enc_mknod3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs3_mknodargs *args) -{ - encode_diropargs3(xdr, args->fh, args->name, args->len); - encode_mknoddata3(xdr, args); -} - -/* - * 3.3.12 REMOVE3args - * - * struct REMOVE3args { - * diropargs3 object; - * }; - */ -static void nfs3_xdr_enc_remove3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_removeargs *args) -{ - encode_diropargs3(xdr, args->fh, args->name.name, args->name.len); -} - -/* - * 3.3.14 RENAME3args - * - * struct RENAME3args { - * diropargs3 from; - * diropargs3 to; - * }; - */ -static void nfs3_xdr_enc_rename3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_renameargs *args) -{ - const struct qstr *old = args->old_name; - const struct qstr *new = args->new_name; - - encode_diropargs3(xdr, args->old_dir, old->name, old->len); - encode_diropargs3(xdr, args->new_dir, new->name, new->len); -} - -/* - * 3.3.15 LINK3args - * - * struct LINK3args { - * nfs_fh3 file; - * diropargs3 link; - * }; - */ -static void nfs3_xdr_enc_link3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs3_linkargs *args) -{ - encode_nfs_fh3(xdr, args->fromfh); - encode_diropargs3(xdr, args->tofh, args->toname, args->tolen); -} - -/* - * 3.3.16 READDIR3args - * - * struct READDIR3args { - * nfs_fh3 dir; - * cookie3 cookie; - * cookieverf3 cookieverf; - * count3 count; - * }; - */ -static void encode_readdir3args(struct xdr_stream *xdr, - const struct nfs3_readdirargs *args) -{ - __be32 *p; - - encode_nfs_fh3(xdr, args->fh); - - p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4); - p = xdr_encode_cookie3(p, args->cookie); - p = xdr_encode_cookieverf3(p, args->verf); - *p = cpu_to_be32(args->count); -} - -static void nfs3_xdr_enc_readdir3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs3_readdirargs *args) -{ - encode_readdir3args(xdr, args); - prepare_reply_buffer(req, args->pages, 0, - args->count, NFS3_readdirres_sz); -} - -/* - * 3.3.17 READDIRPLUS3args - * - * struct READDIRPLUS3args { - * nfs_fh3 dir; - * cookie3 cookie; - * cookieverf3 cookieverf; - * count3 dircount; - * count3 maxcount; - * }; - */ -static void encode_readdirplus3args(struct xdr_stream *xdr, - const struct nfs3_readdirargs *args) -{ - __be32 *p; - - encode_nfs_fh3(xdr, args->fh); - - p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4 + 4); - p = xdr_encode_cookie3(p, args->cookie); - p = xdr_encode_cookieverf3(p, args->verf); - - /* - * readdirplus: need dircount + buffer size. - * We just make sure we make dircount big enough - */ - *p++ = cpu_to_be32(args->count >> 3); - - *p = cpu_to_be32(args->count); -} - -static void nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs3_readdirargs *args) -{ - encode_readdirplus3args(xdr, args); - prepare_reply_buffer(req, args->pages, 0, - args->count, NFS3_readdirres_sz); -} - -/* - * 3.3.21 COMMIT3args - * - * struct COMMIT3args { - * nfs_fh3 file; - * offset3 offset; - * count3 count; - * }; - */ -static void encode_commit3args(struct xdr_stream *xdr, - const struct nfs_writeargs *args) -{ - __be32 *p; - - encode_nfs_fh3(xdr, args->fh); - - p = xdr_reserve_space(xdr, 8 + 4); - p = xdr_encode_hyper(p, args->offset); - *p = cpu_to_be32(args->count); -} - -static void nfs3_xdr_enc_commit3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs_writeargs *args) -{ - encode_commit3args(xdr, args); -} - -#ifdef CONFIG_NFS_V3_ACL - -static void nfs3_xdr_enc_getacl3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs3_getaclargs *args) -{ - encode_nfs_fh3(xdr, args->fh); - encode_uint32(xdr, args->mask); - if (args->mask & (NFS_ACL | NFS_DFACL)) - prepare_reply_buffer(req, args->pages, 0, - NFSACL_MAXPAGES << PAGE_SHIFT, - ACL3_getaclres_sz); -} - -static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs3_setaclargs *args) -{ - unsigned int base; - int error; - - encode_nfs_fh3(xdr, NFS_FH(args->inode)); - encode_uint32(xdr, args->mask); - - base = req->rq_slen; - if (args->npages != 0) - xdr_write_pages(xdr, args->pages, 0, args->len); - else - xdr_reserve_space(xdr, NFS_ACL_INLINE_BUFSIZE); - - error = nfsacl_encode(xdr->buf, base, args->inode, - (args->mask & NFS_ACL) ? - args->acl_access : NULL, 1, 0); - BUG_ON(error < 0); - error = nfsacl_encode(xdr->buf, base + error, args->inode, - (args->mask & NFS_DFACL) ? - args->acl_default : NULL, 1, - NFS_ACL_DEFAULT); - BUG_ON(error < 0); -} - -#endif /* CONFIG_NFS_V3_ACL */ - -/* - * NFSv3 XDR decode functions - * - * NFSv3 result types are defined in section 3.3 of RFC 1813: - * "NFS Version 3 Protocol Specification". - */ - -/* - * 3.3.1 GETATTR3res - * - * struct GETATTR3resok { - * fattr3 obj_attributes; - * }; - * - * union GETATTR3res switch (nfsstat3 status) { - * case NFS3_OK: - * GETATTR3resok resok; - * default: - * void; - * }; - */ -static int nfs3_xdr_dec_getattr3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_fattr *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_default; - error = decode_fattr3(xdr, result); -out: - return error; -out_default: - return nfs_stat_to_errno(status); -} - -/* - * 3.3.2 SETATTR3res - * - * struct SETATTR3resok { - * wcc_data obj_wcc; - * }; - * - * struct SETATTR3resfail { - * wcc_data obj_wcc; - * }; - * - * union SETATTR3res switch (nfsstat3 status) { - * case NFS3_OK: - * SETATTR3resok resok; - * default: - * SETATTR3resfail resfail; - * }; - */ -static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_fattr *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - error = decode_wcc_data(xdr, result); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_status; -out: - return error; -out_status: - return nfs_stat_to_errno(status); -} - -/* - * 3.3.3 LOOKUP3res - * - * struct LOOKUP3resok { - * nfs_fh3 object; - * post_op_attr obj_attributes; - * post_op_attr dir_attributes; - * }; - * - * struct LOOKUP3resfail { - * post_op_attr dir_attributes; - * }; - * - * union LOOKUP3res switch (nfsstat3 status) { - * case NFS3_OK: - * LOOKUP3resok resok; - * default: - * LOOKUP3resfail resfail; - * }; - */ -static int nfs3_xdr_dec_lookup3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs3_diropres *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_default; - error = decode_nfs_fh3(xdr, result->fh); - if (unlikely(error)) - goto out; - error = decode_post_op_attr(xdr, result->fattr); - if (unlikely(error)) - goto out; - error = decode_post_op_attr(xdr, result->dir_attr); -out: - return error; -out_default: - error = decode_post_op_attr(xdr, result->dir_attr); - if (unlikely(error)) - goto out; - return nfs_stat_to_errno(status); -} - -/* - * 3.3.4 ACCESS3res - * - * struct ACCESS3resok { - * post_op_attr obj_attributes; - * uint32 access; - * }; - * - * struct ACCESS3resfail { - * post_op_attr obj_attributes; - * }; - * - * union ACCESS3res switch (nfsstat3 status) { - * case NFS3_OK: - * ACCESS3resok resok; - * default: - * ACCESS3resfail resfail; - * }; - */ -static int nfs3_xdr_dec_access3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs3_accessres *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - error = decode_post_op_attr(xdr, result->fattr); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_default; - error = decode_uint32(xdr, &result->access); -out: - return error; -out_default: - return nfs_stat_to_errno(status); -} - -/* - * 3.3.5 READLINK3res - * - * struct READLINK3resok { - * post_op_attr symlink_attributes; - * nfspath3 data; - * }; - * - * struct READLINK3resfail { - * post_op_attr symlink_attributes; - * }; - * - * union READLINK3res switch (nfsstat3 status) { - * case NFS3_OK: - * READLINK3resok resok; - * default: - * READLINK3resfail resfail; - * }; - */ -static int nfs3_xdr_dec_readlink3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_fattr *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - error = decode_post_op_attr(xdr, result); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_default; - error = decode_nfspath3(xdr); -out: - return error; -out_default: - return nfs_stat_to_errno(status); -} - -/* - * 3.3.6 READ3res - * - * struct READ3resok { - * post_op_attr file_attributes; - * count3 count; - * bool eof; - * opaque data<>; - * }; - * - * struct READ3resfail { - * post_op_attr file_attributes; - * }; - * - * union READ3res switch (nfsstat3 status) { - * case NFS3_OK: - * READ3resok resok; - * default: - * READ3resfail resfail; - * }; - */ -static int decode_read3resok(struct xdr_stream *xdr, - struct nfs_readres *result) -{ - u32 eof, count, ocount, recvd; - size_t hdrlen; - __be32 *p; - - p = xdr_inline_decode(xdr, 4 + 4 + 4); - if (unlikely(p == NULL)) - goto out_overflow; - count = be32_to_cpup(p++); - eof = be32_to_cpup(p++); - ocount = be32_to_cpup(p++); - if (unlikely(ocount != count)) - goto out_mismatch; - hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; - recvd = xdr->buf->len - hdrlen; - if (unlikely(count > recvd)) - goto out_cheating; - -out: - xdr_read_pages(xdr, count); - result->eof = eof; - result->count = count; - return count; -out_mismatch: - dprintk("NFS: READ count doesn't match length of opaque: " - "count %u != ocount %u\n", count, ocount); - return -EIO; -out_cheating: - dprintk("NFS: server cheating in read result: " - "count %u > recvd %u\n", count, recvd); - count = recvd; - eof = 0; - goto out; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_readres *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - error = decode_post_op_attr(xdr, result->fattr); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_status; - error = decode_read3resok(xdr, result); -out: - return error; -out_status: - return nfs_stat_to_errno(status); -} - -/* - * 3.3.7 WRITE3res - * - * enum stable_how { - * UNSTABLE = 0, - * DATA_SYNC = 1, - * FILE_SYNC = 2 - * }; - * - * struct WRITE3resok { - * wcc_data file_wcc; - * count3 count; - * stable_how committed; - * writeverf3 verf; - * }; - * - * struct WRITE3resfail { - * wcc_data file_wcc; - * }; - * - * union WRITE3res switch (nfsstat3 status) { - * case NFS3_OK: - * WRITE3resok resok; - * default: - * WRITE3resfail resfail; - * }; - */ -static int decode_write3resok(struct xdr_stream *xdr, - struct nfs_writeres *result) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 4 + 4 + NFS3_WRITEVERFSIZE); - if (unlikely(p == NULL)) - goto out_overflow; - result->count = be32_to_cpup(p++); - result->verf->committed = be32_to_cpup(p++); - if (unlikely(result->verf->committed > NFS_FILE_SYNC)) - goto out_badvalue; - memcpy(result->verf->verifier, p, NFS3_WRITEVERFSIZE); - return result->count; -out_badvalue: - dprintk("NFS: bad stable_how value: %u\n", result->verf->committed); - return -EIO; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_writeres *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - error = decode_wcc_data(xdr, result->fattr); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_status; - error = decode_write3resok(xdr, result); -out: - return error; -out_status: - return nfs_stat_to_errno(status); -} - -/* - * 3.3.8 CREATE3res - * - * struct CREATE3resok { - * post_op_fh3 obj; - * post_op_attr obj_attributes; - * wcc_data dir_wcc; - * }; - * - * struct CREATE3resfail { - * wcc_data dir_wcc; - * }; - * - * union CREATE3res switch (nfsstat3 status) { - * case NFS3_OK: - * CREATE3resok resok; - * default: - * CREATE3resfail resfail; - * }; - */ -static int decode_create3resok(struct xdr_stream *xdr, - struct nfs3_diropres *result) -{ - int error; - - error = decode_post_op_fh3(xdr, result->fh); - if (unlikely(error)) - goto out; - error = decode_post_op_attr(xdr, result->fattr); - if (unlikely(error)) - goto out; - /* The server isn't required to return a file handle. - * If it didn't, force the client to perform a LOOKUP - * to determine the correct file handle and attribute - * values for the new object. */ - if (result->fh->size == 0) - result->fattr->valid = 0; - error = decode_wcc_data(xdr, result->dir_attr); -out: - return error; -} - -static int nfs3_xdr_dec_create3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs3_diropres *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_default; - error = decode_create3resok(xdr, result); -out: - return error; -out_default: - error = decode_wcc_data(xdr, result->dir_attr); - if (unlikely(error)) - goto out; - return nfs_stat_to_errno(status); -} - -/* - * 3.3.12 REMOVE3res - * - * struct REMOVE3resok { - * wcc_data dir_wcc; - * }; - * - * struct REMOVE3resfail { - * wcc_data dir_wcc; - * }; - * - * union REMOVE3res switch (nfsstat3 status) { - * case NFS3_OK: - * REMOVE3resok resok; - * default: - * REMOVE3resfail resfail; - * }; - */ -static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_removeres *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - error = decode_wcc_data(xdr, result->dir_attr); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_status; -out: - return error; -out_status: - return nfs_stat_to_errno(status); -} - -/* - * 3.3.14 RENAME3res - * - * struct RENAME3resok { - * wcc_data fromdir_wcc; - * wcc_data todir_wcc; - * }; - * - * struct RENAME3resfail { - * wcc_data fromdir_wcc; - * wcc_data todir_wcc; - * }; - * - * union RENAME3res switch (nfsstat3 status) { - * case NFS3_OK: - * RENAME3resok resok; - * default: - * RENAME3resfail resfail; - * }; - */ -static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_renameres *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - error = decode_wcc_data(xdr, result->old_fattr); - if (unlikely(error)) - goto out; - error = decode_wcc_data(xdr, result->new_fattr); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_status; -out: - return error; -out_status: - return nfs_stat_to_errno(status); -} - -/* - * 3.3.15 LINK3res - * - * struct LINK3resok { - * post_op_attr file_attributes; - * wcc_data linkdir_wcc; - * }; - * - * struct LINK3resfail { - * post_op_attr file_attributes; - * wcc_data linkdir_wcc; - * }; - * - * union LINK3res switch (nfsstat3 status) { - * case NFS3_OK: - * LINK3resok resok; - * default: - * LINK3resfail resfail; - * }; - */ -static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs3_linkres *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - error = decode_post_op_attr(xdr, result->fattr); - if (unlikely(error)) - goto out; - error = decode_wcc_data(xdr, result->dir_attr); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_status; -out: - return error; -out_status: - return nfs_stat_to_errno(status); -} - -/** - * nfs3_decode_dirent - Decode a single NFSv3 directory entry stored in - * the local page cache - * @xdr: XDR stream where entry resides - * @entry: buffer to fill in with entry data - * @plus: boolean indicating whether this should be a readdirplus entry - * - * Returns zero if successful, otherwise a negative errno value is - * returned. - * - * This function is not invoked during READDIR reply decoding, but - * rather whenever an application invokes the getdents(2) system call - * on a directory already in our cache. - * - * 3.3.16 entry3 - * - * struct entry3 { - * fileid3 fileid; - * filename3 name; - * cookie3 cookie; - * fhandle3 filehandle; - * post_op_attr3 attributes; - * entry3 *nextentry; - * }; - * - * 3.3.17 entryplus3 - * struct entryplus3 { - * fileid3 fileid; - * filename3 name; - * cookie3 cookie; - * post_op_attr name_attributes; - * post_op_fh3 name_handle; - * entryplus3 *nextentry; - * }; - */ -int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, - int plus) -{ - struct nfs_entry old = *entry; - __be32 *p; - int error; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - if (*p == xdr_zero) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - if (*p == xdr_zero) - return -EAGAIN; - entry->eof = 1; - return -EBADCOOKIE; - } - - error = decode_fileid3(xdr, &entry->ino); - if (unlikely(error)) - return error; - - error = decode_inline_filename3(xdr, &entry->name, &entry->len); - if (unlikely(error)) - return error; - - entry->prev_cookie = entry->cookie; - error = decode_cookie3(xdr, &entry->cookie); - if (unlikely(error)) - return error; - - entry->d_type = DT_UNKNOWN; - - if (plus) { - entry->fattr->valid = 0; - error = decode_post_op_attr(xdr, entry->fattr); - if (unlikely(error)) - return error; - if (entry->fattr->valid & NFS_ATTR_FATTR_V3) - entry->d_type = nfs_umode_to_dtype(entry->fattr->mode); - - /* In fact, a post_op_fh3: */ - p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; - if (*p != xdr_zero) { - error = decode_nfs_fh3(xdr, entry->fh); - if (unlikely(error)) { - if (error == -E2BIG) - goto out_truncated; - return error; - } - } else - zero_nfs_fh3(entry->fh); - } - - return 0; - -out_overflow: - print_overflow_msg(__func__, xdr); - return -EAGAIN; -out_truncated: - dprintk("NFS: directory entry contains invalid file handle\n"); - *entry = old; - return -EAGAIN; -} - -/* - * 3.3.16 READDIR3res - * - * struct dirlist3 { - * entry3 *entries; - * bool eof; - * }; - * - * struct READDIR3resok { - * post_op_attr dir_attributes; - * cookieverf3 cookieverf; - * dirlist3 reply; - * }; - * - * struct READDIR3resfail { - * post_op_attr dir_attributes; - * }; - * - * union READDIR3res switch (nfsstat3 status) { - * case NFS3_OK: - * READDIR3resok resok; - * default: - * READDIR3resfail resfail; - * }; - * - * Read the directory contents into the page cache, but otherwise - * don't touch them. The actual decoding is done by nfs3_decode_entry() - * during subsequent nfs_readdir() calls. - */ -static int decode_dirlist3(struct xdr_stream *xdr) -{ - u32 recvd, pglen; - size_t hdrlen; - - pglen = xdr->buf->page_len; - hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; - recvd = xdr->buf->len - hdrlen; - if (unlikely(pglen > recvd)) - goto out_cheating; -out: - xdr_read_pages(xdr, pglen); - return pglen; -out_cheating: - dprintk("NFS: server cheating in readdir result: " - "pglen %u > recvd %u\n", pglen, recvd); - pglen = recvd; - goto out; -} - -static int decode_readdir3resok(struct xdr_stream *xdr, - struct nfs3_readdirres *result) -{ - int error; - - error = decode_post_op_attr(xdr, result->dir_attr); - if (unlikely(error)) - goto out; - /* XXX: do we need to check if result->verf != NULL ? */ - error = decode_cookieverf3(xdr, result->verf); - if (unlikely(error)) - goto out; - error = decode_dirlist3(xdr); -out: - return error; -} - -static int nfs3_xdr_dec_readdir3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs3_readdirres *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_default; - error = decode_readdir3resok(xdr, result); -out: - return error; -out_default: - error = decode_post_op_attr(xdr, result->dir_attr); - if (unlikely(error)) - goto out; - return nfs_stat_to_errno(status); -} - -/* - * 3.3.18 FSSTAT3res - * - * struct FSSTAT3resok { - * post_op_attr obj_attributes; - * size3 tbytes; - * size3 fbytes; - * size3 abytes; - * size3 tfiles; - * size3 ffiles; - * size3 afiles; - * uint32 invarsec; - * }; - * - * struct FSSTAT3resfail { - * post_op_attr obj_attributes; - * }; - * - * union FSSTAT3res switch (nfsstat3 status) { - * case NFS3_OK: - * FSSTAT3resok resok; - * default: - * FSSTAT3resfail resfail; - * }; - */ -static int decode_fsstat3resok(struct xdr_stream *xdr, - struct nfs_fsstat *result) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 8 * 6 + 4); - if (unlikely(p == NULL)) - goto out_overflow; - p = xdr_decode_size3(p, &result->tbytes); - p = xdr_decode_size3(p, &result->fbytes); - p = xdr_decode_size3(p, &result->abytes); - p = xdr_decode_size3(p, &result->tfiles); - p = xdr_decode_size3(p, &result->ffiles); - xdr_decode_size3(p, &result->afiles); - /* ignore invarsec */ - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_fsstat *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - error = decode_post_op_attr(xdr, result->fattr); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_status; - error = decode_fsstat3resok(xdr, result); -out: - return error; -out_status: - return nfs_stat_to_errno(status); -} - -/* - * 3.3.19 FSINFO3res - * - * struct FSINFO3resok { - * post_op_attr obj_attributes; - * uint32 rtmax; - * uint32 rtpref; - * uint32 rtmult; - * uint32 wtmax; - * uint32 wtpref; - * uint32 wtmult; - * uint32 dtpref; - * size3 maxfilesize; - * nfstime3 time_delta; - * uint32 properties; - * }; - * - * struct FSINFO3resfail { - * post_op_attr obj_attributes; - * }; - * - * union FSINFO3res switch (nfsstat3 status) { - * case NFS3_OK: - * FSINFO3resok resok; - * default: - * FSINFO3resfail resfail; - * }; - */ -static int decode_fsinfo3resok(struct xdr_stream *xdr, - struct nfs_fsinfo *result) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 4 * 7 + 8 + 8 + 4); - if (unlikely(p == NULL)) - goto out_overflow; - result->rtmax = be32_to_cpup(p++); - result->rtpref = be32_to_cpup(p++); - result->rtmult = be32_to_cpup(p++); - result->wtmax = be32_to_cpup(p++); - result->wtpref = be32_to_cpup(p++); - result->wtmult = be32_to_cpup(p++); - result->dtpref = be32_to_cpup(p++); - p = xdr_decode_size3(p, &result->maxfilesize); - xdr_decode_nfstime3(p, &result->time_delta); - - /* ignore properties */ - result->lease_time = 0; - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_fsinfo *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - error = decode_post_op_attr(xdr, result->fattr); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_status; - error = decode_fsinfo3resok(xdr, result); -out: - return error; -out_status: - return nfs_stat_to_errno(status); -} - -/* - * 3.3.20 PATHCONF3res - * - * struct PATHCONF3resok { - * post_op_attr obj_attributes; - * uint32 linkmax; - * uint32 name_max; - * bool no_trunc; - * bool chown_restricted; - * bool case_insensitive; - * bool case_preserving; - * }; - * - * struct PATHCONF3resfail { - * post_op_attr obj_attributes; - * }; - * - * union PATHCONF3res switch (nfsstat3 status) { - * case NFS3_OK: - * PATHCONF3resok resok; - * default: - * PATHCONF3resfail resfail; - * }; - */ -static int decode_pathconf3resok(struct xdr_stream *xdr, - struct nfs_pathconf *result) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 4 * 6); - if (unlikely(p == NULL)) - goto out_overflow; - result->max_link = be32_to_cpup(p++); - result->max_namelen = be32_to_cpup(p); - /* ignore remaining fields */ - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_pathconf *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - error = decode_post_op_attr(xdr, result->fattr); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_status; - error = decode_pathconf3resok(xdr, result); -out: - return error; -out_status: - return nfs_stat_to_errno(status); -} - -/* - * 3.3.21 COMMIT3res - * - * struct COMMIT3resok { - * wcc_data file_wcc; - * writeverf3 verf; - * }; - * - * struct COMMIT3resfail { - * wcc_data file_wcc; - * }; - * - * union COMMIT3res switch (nfsstat3 status) { - * case NFS3_OK: - * COMMIT3resok resok; - * default: - * COMMIT3resfail resfail; - * }; - */ -static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_writeres *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - error = decode_wcc_data(xdr, result->fattr); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_status; - error = decode_writeverf3(xdr, result->verf->verifier); -out: - return error; -out_status: - return nfs_stat_to_errno(status); -} - -#ifdef CONFIG_NFS_V3_ACL - -static inline int decode_getacl3resok(struct xdr_stream *xdr, - struct nfs3_getaclres *result) -{ - struct posix_acl **acl; - unsigned int *aclcnt; - size_t hdrlen; - int error; - - error = decode_post_op_attr(xdr, result->fattr); - if (unlikely(error)) - goto out; - error = decode_uint32(xdr, &result->mask); - if (unlikely(error)) - goto out; - error = -EINVAL; - if (result->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT)) - goto out; - - hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; - - acl = NULL; - if (result->mask & NFS_ACL) - acl = &result->acl_access; - aclcnt = NULL; - if (result->mask & NFS_ACLCNT) - aclcnt = &result->acl_access_count; - error = nfsacl_decode(xdr->buf, hdrlen, aclcnt, acl); - if (unlikely(error <= 0)) - goto out; - - acl = NULL; - if (result->mask & NFS_DFACL) - acl = &result->acl_default; - aclcnt = NULL; - if (result->mask & NFS_DFACLCNT) - aclcnt = &result->acl_default_count; - error = nfsacl_decode(xdr->buf, hdrlen + error, aclcnt, acl); - if (unlikely(error <= 0)) - return error; - error = 0; -out: - return error; -} - -static int nfs3_xdr_dec_getacl3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs3_getaclres *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_default; - error = decode_getacl3resok(xdr, result); -out: - return error; -out_default: - return nfs_stat_to_errno(status); -} - -static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_fattr *result) -{ - enum nfs_stat status; - int error; - - error = decode_nfsstat3(xdr, &status); - if (unlikely(error)) - goto out; - if (status != NFS3_OK) - goto out_default; - error = decode_post_op_attr(xdr, result); -out: - return error; -out_default: - return nfs_stat_to_errno(status); -} - -#endif /* CONFIG_NFS_V3_ACL */ - -#define PROC(proc, argtype, restype, timer) \ -[NFS3PROC_##proc] = { \ - .p_proc = NFS3PROC_##proc, \ - .p_encode = (kxdreproc_t)nfs3_xdr_enc_##argtype##3args, \ - .p_decode = (kxdrdproc_t)nfs3_xdr_dec_##restype##3res, \ - .p_arglen = NFS3_##argtype##args_sz, \ - .p_replen = NFS3_##restype##res_sz, \ - .p_timer = timer, \ - .p_statidx = NFS3PROC_##proc, \ - .p_name = #proc, \ - } - -struct rpc_procinfo nfs3_procedures[] = { - PROC(GETATTR, getattr, getattr, 1), - PROC(SETATTR, setattr, setattr, 0), - PROC(LOOKUP, lookup, lookup, 2), - PROC(ACCESS, access, access, 1), - PROC(READLINK, readlink, readlink, 3), - PROC(READ, read, read, 3), - PROC(WRITE, write, write, 4), - PROC(CREATE, create, create, 0), - PROC(MKDIR, mkdir, create, 0), - PROC(SYMLINK, symlink, create, 0), - PROC(MKNOD, mknod, create, 0), - PROC(REMOVE, remove, remove, 0), - PROC(RMDIR, lookup, setattr, 0), - PROC(RENAME, rename, rename, 0), - PROC(LINK, link, link, 0), - PROC(READDIR, readdir, readdir, 3), - PROC(READDIRPLUS, readdirplus, readdir, 3), - PROC(FSSTAT, getattr, fsstat, 0), - PROC(FSINFO, getattr, fsinfo, 0), - PROC(PATHCONF, getattr, pathconf, 0), - PROC(COMMIT, commit, commit, 5), -}; - -const struct rpc_version nfs_version3 = { - .number = 3, - .nrprocs = ARRAY_SIZE(nfs3_procedures), - .procs = nfs3_procedures -}; - -#ifdef CONFIG_NFS_V3_ACL -static struct rpc_procinfo nfs3_acl_procedures[] = { - [ACLPROC3_GETACL] = { - .p_proc = ACLPROC3_GETACL, - .p_encode = (kxdreproc_t)nfs3_xdr_enc_getacl3args, - .p_decode = (kxdrdproc_t)nfs3_xdr_dec_getacl3res, - .p_arglen = ACL3_getaclargs_sz, - .p_replen = ACL3_getaclres_sz, - .p_timer = 1, - .p_name = "GETACL", - }, - [ACLPROC3_SETACL] = { - .p_proc = ACLPROC3_SETACL, - .p_encode = (kxdreproc_t)nfs3_xdr_enc_setacl3args, - .p_decode = (kxdrdproc_t)nfs3_xdr_dec_setacl3res, - .p_arglen = ACL3_setaclargs_sz, - .p_replen = ACL3_setaclres_sz, - .p_timer = 0, - .p_name = "SETACL", - }, -}; - -const struct rpc_version nfsacl_version3 = { - .number = 3, - .nrprocs = sizeof(nfs3_acl_procedures)/ - sizeof(nfs3_acl_procedures[0]), - .procs = nfs3_acl_procedures, -}; -#endif /* CONFIG_NFS_V3_ACL */ diff --git a/ANDROID_3.4.5/fs/nfs/nfs4_fs.h b/ANDROID_3.4.5/fs/nfs/nfs4_fs.h deleted file mode 100644 index 8d750210..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfs4_fs.h +++ /dev/null @@ -1,379 +0,0 @@ -/* - * linux/fs/nfs/nfs4_fs.h - * - * Copyright (C) 2005 Trond Myklebust - * - * NFSv4-specific filesystem definitions and declarations - */ - -#ifndef __LINUX_FS_NFS_NFS4_FS_H -#define __LINUX_FS_NFS_NFS4_FS_H - -#ifdef CONFIG_NFS_V4 - -struct idmap; - -enum nfs4_client_state { - NFS4CLNT_MANAGER_RUNNING = 0, - NFS4CLNT_CHECK_LEASE, - NFS4CLNT_LEASE_EXPIRED, - NFS4CLNT_RECLAIM_REBOOT, - NFS4CLNT_RECLAIM_NOGRACE, - NFS4CLNT_DELEGRETURN, - NFS4CLNT_SESSION_RESET, - NFS4CLNT_RECALL_SLOT, - NFS4CLNT_LEASE_CONFIRM, - NFS4CLNT_SERVER_SCOPE_MISMATCH, -}; - -enum nfs4_session_state { - NFS4_SESSION_INITING, - NFS4_SESSION_DRAINING, -}; - -#define NFS4_RENEW_TIMEOUT 0x01 -#define NFS4_RENEW_DELEGATION_CB 0x02 - -struct nfs4_minor_version_ops { - u32 minor_version; - - int (*call_sync)(struct rpc_clnt *clnt, - struct nfs_server *server, - struct rpc_message *msg, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int cache_reply); - bool (*match_stateid)(const nfs4_stateid *, - const nfs4_stateid *); - int (*find_root_sec)(struct nfs_server *, struct nfs_fh *, - struct nfs_fsinfo *); - const struct nfs4_state_recovery_ops *reboot_recovery_ops; - const struct nfs4_state_recovery_ops *nograce_recovery_ops; - const struct nfs4_state_maintenance_ops *state_renewal_ops; -}; - -struct nfs_unique_id { - struct rb_node rb_node; - __u64 id; -}; - -#define NFS_SEQID_CONFIRMED 1 -struct nfs_seqid_counter { - ktime_t create_time; - int owner_id; - int flags; - u32 counter; - spinlock_t lock; /* Protects the list */ - struct list_head list; /* Defines sequence of RPC calls */ - struct rpc_wait_queue wait; /* RPC call delay queue */ -}; - -struct nfs_seqid { - struct nfs_seqid_counter *sequence; - struct list_head list; - struct rpc_task *task; -}; - -static inline void nfs_confirm_seqid(struct nfs_seqid_counter *seqid, int status) -{ - if (seqid_mutating_err(-status)) - seqid->flags |= NFS_SEQID_CONFIRMED; -} - -/* - * NFS4 state_owners and lock_owners are simply labels for ordered - * sequences of RPC calls. Their sole purpose is to provide once-only - * semantics by allowing the server to identify replayed requests. - */ -struct nfs4_state_owner { - struct nfs_server *so_server; - struct list_head so_lru; - unsigned long so_expires; - struct rb_node so_server_node; - - struct rpc_cred *so_cred; /* Associated cred */ - - spinlock_t so_lock; - atomic_t so_count; - unsigned long so_flags; - struct list_head so_states; - struct nfs_seqid_counter so_seqid; -}; - -enum { - NFS_OWNER_RECLAIM_REBOOT, - NFS_OWNER_RECLAIM_NOGRACE -}; - -#define NFS_LOCK_NEW 0 -#define NFS_LOCK_RECLAIM 1 -#define NFS_LOCK_EXPIRED 2 - -/* - * struct nfs4_state maintains the client-side state for a given - * (state_owner,inode) tuple (OPEN) or state_owner (LOCK). - * - * OPEN: - * In order to know when to OPEN_DOWNGRADE or CLOSE the state on the server, - * we need to know how many files are open for reading or writing on a - * given inode. This information too is stored here. - * - * LOCK: one nfs4_state (LOCK) to hold the lock stateid nfs4_state(OPEN) - */ - -struct nfs4_lock_owner { - unsigned int lo_type; -#define NFS4_ANY_LOCK_TYPE (0U) -#define NFS4_FLOCK_LOCK_TYPE (1U << 0) -#define NFS4_POSIX_LOCK_TYPE (1U << 1) - union { - fl_owner_t posix_owner; - pid_t flock_owner; - } lo_u; -}; - -struct nfs4_lock_state { - struct list_head ls_locks; /* Other lock stateids */ - struct nfs4_state * ls_state; /* Pointer to open state */ -#define NFS_LOCK_INITIALIZED 1 - int ls_flags; - struct nfs_seqid_counter ls_seqid; - nfs4_stateid ls_stateid; - atomic_t ls_count; - struct nfs4_lock_owner ls_owner; -}; - -/* bits for nfs4_state->flags */ -enum { - LK_STATE_IN_USE, - NFS_DELEGATED_STATE, /* Current stateid is delegation */ - NFS_O_RDONLY_STATE, /* OPEN stateid has read-only state */ - NFS_O_WRONLY_STATE, /* OPEN stateid has write-only state */ - NFS_O_RDWR_STATE, /* OPEN stateid has read/write state */ - NFS_STATE_RECLAIM_REBOOT, /* OPEN stateid server rebooted */ - NFS_STATE_RECLAIM_NOGRACE, /* OPEN stateid needs to recover state */ - NFS_STATE_POSIX_LOCKS, /* Posix locks are supported */ -}; - -struct nfs4_state { - struct list_head open_states; /* List of states for the same state_owner */ - struct list_head inode_states; /* List of states for the same inode */ - struct list_head lock_states; /* List of subservient lock stateids */ - - struct nfs4_state_owner *owner; /* Pointer to the open owner */ - struct inode *inode; /* Pointer to the inode */ - - unsigned long flags; /* Do we hold any locks? */ - spinlock_t state_lock; /* Protects the lock_states list */ - - seqlock_t seqlock; /* Protects the stateid/open_stateid */ - nfs4_stateid stateid; /* Current stateid: may be delegation */ - nfs4_stateid open_stateid; /* OPEN stateid */ - - /* The following 3 fields are protected by owner->so_lock */ - unsigned int n_rdonly; /* Number of read-only references */ - unsigned int n_wronly; /* Number of write-only references */ - unsigned int n_rdwr; /* Number of read/write references */ - fmode_t state; /* State on the server (R,W, or RW) */ - atomic_t count; -}; - - -struct nfs4_exception { - long timeout; - int retry; - struct nfs4_state *state; - struct inode *inode; -}; - -struct nfs4_state_recovery_ops { - int owner_flag_bit; - int state_flag_bit; - int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *); - int (*recover_lock)(struct nfs4_state *, struct file_lock *); - int (*establish_clid)(struct nfs_client *, struct rpc_cred *); - struct rpc_cred * (*get_clid_cred)(struct nfs_client *); - int (*reclaim_complete)(struct nfs_client *); -}; - -struct nfs4_state_maintenance_ops { - int (*sched_state_renewal)(struct nfs_client *, struct rpc_cred *, unsigned); - struct rpc_cred * (*get_state_renewal_cred_locked)(struct nfs_client *); - int (*renew_lease)(struct nfs_client *, struct rpc_cred *); -}; - -extern const struct dentry_operations nfs4_dentry_operations; -extern const struct inode_operations nfs4_dir_inode_operations; - -/* nfs4namespace.c */ -struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *, struct inode *, struct qstr *); - -/* nfs4proc.c */ -extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *, struct nfs4_setclientid_res *); -extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct nfs4_setclientid_res *arg, struct rpc_cred *); -extern int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred); -extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *); -extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *); -extern int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc); -extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); -extern int nfs4_proc_fs_locations(struct rpc_clnt *, struct inode *, const struct qstr *, - struct nfs4_fs_locations *, struct page *); -extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, struct qstr *, - struct nfs_fh *, struct nfs_fattr *); -extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *); -extern int nfs4_release_lockowner(struct nfs4_lock_state *); -extern const struct xattr_handler *nfs4_xattr_handlers[]; - -#if defined(CONFIG_NFS_V4_1) -static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server) -{ - return server->nfs_client->cl_session; -} - -extern bool nfs4_set_task_privileged(struct rpc_task *task, void *dummy); -extern int nfs4_setup_sequence(const struct nfs_server *server, - struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, - struct rpc_task *task); -extern int nfs41_setup_sequence(struct nfs4_session *session, - struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, - struct rpc_task *task); -extern void nfs4_destroy_session(struct nfs4_session *session); -extern struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp); -extern int nfs4_proc_create_session(struct nfs_client *); -extern int nfs4_proc_destroy_session(struct nfs4_session *); -extern int nfs4_init_session(struct nfs_server *server); -extern int nfs4_proc_get_lease_time(struct nfs_client *clp, - struct nfs_fsinfo *fsinfo); -extern int nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, - bool sync); - -static inline bool -is_ds_only_client(struct nfs_client *clp) -{ - return (clp->cl_exchange_flags & EXCHGID4_FLAG_MASK_PNFS) == - EXCHGID4_FLAG_USE_PNFS_DS; -} - -static inline bool -is_ds_client(struct nfs_client *clp) -{ - return clp->cl_exchange_flags & EXCHGID4_FLAG_USE_PNFS_DS; -} -#else /* CONFIG_NFS_v4_1 */ -static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server) -{ - return NULL; -} - -static inline int nfs4_setup_sequence(const struct nfs_server *server, - struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, - struct rpc_task *task) -{ - return 0; -} - -static inline int nfs4_init_session(struct nfs_server *server) -{ - return 0; -} - -static inline bool -is_ds_only_client(struct nfs_client *clp) -{ - return false; -} - -static inline bool -is_ds_client(struct nfs_client *clp) -{ - return false; -} -#endif /* CONFIG_NFS_V4_1 */ - -extern const struct nfs4_minor_version_ops *nfs_v4_minor_ops[]; - -extern const u32 nfs4_fattr_bitmap[2]; -extern const u32 nfs4_statfs_bitmap[2]; -extern const u32 nfs4_pathconf_bitmap[2]; -extern const u32 nfs4_fsinfo_bitmap[3]; -extern const u32 nfs4_fs_locations_bitmap[2]; - -/* nfs4renewd.c */ -extern void nfs4_schedule_state_renewal(struct nfs_client *); -extern void nfs4_renewd_prepare_shutdown(struct nfs_server *); -extern void nfs4_kill_renewd(struct nfs_client *); -extern void nfs4_renew_state(struct work_struct *); - -/* nfs4state.c */ -struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp); -struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp); -#if defined(CONFIG_NFS_V4_1) -struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp); -struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp); -extern void nfs4_schedule_session_recovery(struct nfs4_session *); -#else -static inline void nfs4_schedule_session_recovery(struct nfs4_session *session) -{ -} -#endif /* CONFIG_NFS_V4_1 */ - -extern struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *, gfp_t); -extern void nfs4_put_state_owner(struct nfs4_state_owner *); -extern void nfs4_purge_state_owners(struct nfs_server *); -extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); -extern void nfs4_put_open_state(struct nfs4_state *); -extern void nfs4_close_state(struct nfs4_state *, fmode_t); -extern void nfs4_close_sync(struct nfs4_state *, fmode_t); -extern void nfs4_state_set_mode_locked(struct nfs4_state *, fmode_t); -extern void nfs_inode_find_state_and_recover(struct inode *inode, - const nfs4_stateid *stateid); -extern void nfs4_schedule_lease_recovery(struct nfs_client *); -extern void nfs4_schedule_state_manager(struct nfs_client *); -extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp); -extern void nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *); -extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags); -extern void nfs41_handle_recall_slot(struct nfs_client *clp); -extern void nfs41_handle_server_scope(struct nfs_client *, - struct server_scope **); -extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); -extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl); -extern void nfs4_select_rw_stateid(nfs4_stateid *, struct nfs4_state *, - fmode_t, fl_owner_t, pid_t); - -extern struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask); -extern int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task); -extern void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid); -extern void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid); -extern void nfs_release_seqid(struct nfs_seqid *seqid); -extern void nfs_free_seqid(struct nfs_seqid *seqid); - -extern void nfs4_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp); - -extern const nfs4_stateid zero_stateid; - -/* nfs4xdr.c */ -extern struct rpc_procinfo nfs4_procedures[]; - -struct nfs4_mount_data; - -/* callback_xdr.c */ -extern struct svc_version nfs4_callback_version1; -extern struct svc_version nfs4_callback_version4; - -static inline void nfs4_stateid_copy(nfs4_stateid *dst, const nfs4_stateid *src) -{ - memcpy(dst, src, sizeof(*dst)); -} - -static inline bool nfs4_stateid_match(const nfs4_stateid *dst, const nfs4_stateid *src) -{ - return memcmp(dst, src, sizeof(*dst)) == 0; -} - -#else - -#define nfs4_close_state(a, b) do { } while (0) -#define nfs4_close_sync(a, b) do { } while (0) - -#endif /* CONFIG_NFS_V4 */ -#endif /* __LINUX_FS_NFS_NFS4_FS.H */ diff --git a/ANDROID_3.4.5/fs/nfs/nfs4filelayout.c b/ANDROID_3.4.5/fs/nfs/nfs4filelayout.c deleted file mode 100644 index 5acfd9ea..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfs4filelayout.c +++ /dev/null @@ -1,1109 +0,0 @@ -/* - * Module for the pnfs nfs4 file layout driver. - * Defines all I/O and Policy interface operations, plus code - * to register itself with the pNFS client. - * - * Copyright (c) 2002 - * The Regents of the University of Michigan - * All Rights Reserved - * - * Dean Hildebrand <dhildebz@umich.edu> - * - * Permission is granted to use, copy, create derivative works, and - * redistribute this software and such derivative works for any purpose, - * so long as the name of the University of Michigan is not used in - * any advertising or publicity pertaining to the use or distribution - * of this software without specific, written prior authorization. If - * the above copyright notice or any other identification of the - * University of Michigan is included in any copy of any portion of - * this software, then the disclaimer below must also be included. - * - * This software is provided as is, without representation or warranty - * of any kind either express or implied, including without limitation - * the implied warranties of merchantability, fitness for a particular - * purpose, or noninfringement. The Regents of the University of - * Michigan shall not be liable for any damages, including special, - * indirect, incidental, or consequential damages, with respect to any - * claim arising out of or in connection with the use of the software, - * even if it has been or is hereafter advised of the possibility of - * such damages. - */ - -#include <linux/nfs_fs.h> -#include <linux/nfs_page.h> -#include <linux/module.h> - -#include <linux/sunrpc/metrics.h> - -#include "internal.h" -#include "delegation.h" -#include "nfs4filelayout.h" - -#define NFSDBG_FACILITY NFSDBG_PNFS_LD - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Dean Hildebrand <dhildebz@umich.edu>"); -MODULE_DESCRIPTION("The NFSv4 file layout driver"); - -#define FILELAYOUT_POLL_RETRY_MAX (15*HZ) - -static loff_t -filelayout_get_dense_offset(struct nfs4_filelayout_segment *flseg, - loff_t offset) -{ - u32 stripe_width = flseg->stripe_unit * flseg->dsaddr->stripe_count; - u64 stripe_no; - u32 rem; - - offset -= flseg->pattern_offset; - stripe_no = div_u64(offset, stripe_width); - div_u64_rem(offset, flseg->stripe_unit, &rem); - - return stripe_no * flseg->stripe_unit + rem; -} - -/* This function is used by the layout driver to calculate the - * offset of the file on the dserver based on whether the - * layout type is STRIPE_DENSE or STRIPE_SPARSE - */ -static loff_t -filelayout_get_dserver_offset(struct pnfs_layout_segment *lseg, loff_t offset) -{ - struct nfs4_filelayout_segment *flseg = FILELAYOUT_LSEG(lseg); - - switch (flseg->stripe_type) { - case STRIPE_SPARSE: - return offset; - - case STRIPE_DENSE: - return filelayout_get_dense_offset(flseg, offset); - } - - BUG(); -} - -static int filelayout_async_handle_error(struct rpc_task *task, - struct nfs4_state *state, - struct nfs_client *clp, - int *reset) -{ - struct nfs_server *mds_server = NFS_SERVER(state->inode); - struct nfs_client *mds_client = mds_server->nfs_client; - - if (task->tk_status >= 0) - return 0; - *reset = 0; - - switch (task->tk_status) { - /* MDS state errors */ - case -NFS4ERR_DELEG_REVOKED: - case -NFS4ERR_ADMIN_REVOKED: - case -NFS4ERR_BAD_STATEID: - nfs_remove_bad_delegation(state->inode); - case -NFS4ERR_OPENMODE: - nfs4_schedule_stateid_recovery(mds_server, state); - goto wait_on_recovery; - case -NFS4ERR_EXPIRED: - nfs4_schedule_stateid_recovery(mds_server, state); - nfs4_schedule_lease_recovery(mds_client); - goto wait_on_recovery; - /* DS session errors */ - case -NFS4ERR_BADSESSION: - case -NFS4ERR_BADSLOT: - case -NFS4ERR_BAD_HIGH_SLOT: - case -NFS4ERR_DEADSESSION: - case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: - case -NFS4ERR_SEQ_FALSE_RETRY: - case -NFS4ERR_SEQ_MISORDERED: - dprintk("%s ERROR %d, Reset session. Exchangeid " - "flags 0x%x\n", __func__, task->tk_status, - clp->cl_exchange_flags); - nfs4_schedule_session_recovery(clp->cl_session); - break; - case -NFS4ERR_DELAY: - case -NFS4ERR_GRACE: - case -EKEYEXPIRED: - rpc_delay(task, FILELAYOUT_POLL_RETRY_MAX); - break; - case -NFS4ERR_RETRY_UNCACHED_REP: - break; - default: - dprintk("%s DS error. Retry through MDS %d\n", __func__, - task->tk_status); - *reset = 1; - break; - } -out: - task->tk_status = 0; - return -EAGAIN; -wait_on_recovery: - rpc_sleep_on(&mds_client->cl_rpcwaitq, task, NULL); - if (test_bit(NFS4CLNT_MANAGER_RUNNING, &mds_client->cl_state) == 0) - rpc_wake_up_queued_task(&mds_client->cl_rpcwaitq, task); - goto out; -} - -/* NFS_PROTO call done callback routines */ - -static int filelayout_read_done_cb(struct rpc_task *task, - struct nfs_read_data *data) -{ - int reset = 0; - - dprintk("%s DS read\n", __func__); - - if (filelayout_async_handle_error(task, data->args.context->state, - data->ds_clp, &reset) == -EAGAIN) { - dprintk("%s calling restart ds_clp %p ds_clp->cl_session %p\n", - __func__, data->ds_clp, data->ds_clp->cl_session); - if (reset) { - pnfs_set_lo_fail(data->lseg); - nfs4_reset_read(task, data); - } - rpc_restart_call_prepare(task); - return -EAGAIN; - } - - return 0; -} - -/* - * We reference the rpc_cred of the first WRITE that triggers the need for - * a LAYOUTCOMMIT, and use it to send the layoutcommit compound. - * rfc5661 is not clear about which credential should be used. - */ -static void -filelayout_set_layoutcommit(struct nfs_write_data *wdata) -{ - if (FILELAYOUT_LSEG(wdata->lseg)->commit_through_mds || - wdata->res.verf->committed == NFS_FILE_SYNC) - return; - - pnfs_set_layoutcommit(wdata); - dprintk("%s ionde %lu pls_end_pos %lu\n", __func__, wdata->inode->i_ino, - (unsigned long) NFS_I(wdata->inode)->layout->plh_lwb); -} - -/* - * Call ops for the async read/write cases - * In the case of dense layouts, the offset needs to be reset to its - * original value. - */ -static void filelayout_read_prepare(struct rpc_task *task, void *data) -{ - struct nfs_read_data *rdata = (struct nfs_read_data *)data; - - rdata->read_done_cb = filelayout_read_done_cb; - - if (nfs41_setup_sequence(rdata->ds_clp->cl_session, - &rdata->args.seq_args, &rdata->res.seq_res, - task)) - return; - - rpc_call_start(task); -} - -static void filelayout_read_call_done(struct rpc_task *task, void *data) -{ - struct nfs_read_data *rdata = (struct nfs_read_data *)data; - - dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status); - - /* Note this may cause RPC to be resent */ - rdata->mds_ops->rpc_call_done(task, data); -} - -static void filelayout_read_count_stats(struct rpc_task *task, void *data) -{ - struct nfs_read_data *rdata = (struct nfs_read_data *)data; - - rpc_count_iostats(task, NFS_SERVER(rdata->inode)->client->cl_metrics); -} - -static void filelayout_read_release(void *data) -{ - struct nfs_read_data *rdata = (struct nfs_read_data *)data; - - put_lseg(rdata->lseg); - rdata->mds_ops->rpc_release(data); -} - -static int filelayout_write_done_cb(struct rpc_task *task, - struct nfs_write_data *data) -{ - int reset = 0; - - if (filelayout_async_handle_error(task, data->args.context->state, - data->ds_clp, &reset) == -EAGAIN) { - dprintk("%s calling restart ds_clp %p ds_clp->cl_session %p\n", - __func__, data->ds_clp, data->ds_clp->cl_session); - if (reset) { - pnfs_set_lo_fail(data->lseg); - nfs4_reset_write(task, data); - } - rpc_restart_call_prepare(task); - return -EAGAIN; - } - - filelayout_set_layoutcommit(data); - return 0; -} - -/* Fake up some data that will cause nfs_commit_release to retry the writes. */ -static void prepare_to_resend_writes(struct nfs_write_data *data) -{ - struct nfs_page *first = nfs_list_entry(data->pages.next); - - data->task.tk_status = 0; - memcpy(data->verf.verifier, first->wb_verf.verifier, - sizeof(first->wb_verf.verifier)); - data->verf.verifier[0]++; /* ensure verifier mismatch */ -} - -static int filelayout_commit_done_cb(struct rpc_task *task, - struct nfs_write_data *data) -{ - int reset = 0; - - if (filelayout_async_handle_error(task, data->args.context->state, - data->ds_clp, &reset) == -EAGAIN) { - dprintk("%s calling restart ds_clp %p ds_clp->cl_session %p\n", - __func__, data->ds_clp, data->ds_clp->cl_session); - if (reset) { - prepare_to_resend_writes(data); - pnfs_set_lo_fail(data->lseg); - } else - rpc_restart_call_prepare(task); - return -EAGAIN; - } - - return 0; -} - -static void filelayout_write_prepare(struct rpc_task *task, void *data) -{ - struct nfs_write_data *wdata = (struct nfs_write_data *)data; - - if (nfs41_setup_sequence(wdata->ds_clp->cl_session, - &wdata->args.seq_args, &wdata->res.seq_res, - task)) - return; - - rpc_call_start(task); -} - -static void filelayout_write_call_done(struct rpc_task *task, void *data) -{ - struct nfs_write_data *wdata = (struct nfs_write_data *)data; - - /* Note this may cause RPC to be resent */ - wdata->mds_ops->rpc_call_done(task, data); -} - -static void filelayout_write_count_stats(struct rpc_task *task, void *data) -{ - struct nfs_write_data *wdata = (struct nfs_write_data *)data; - - rpc_count_iostats(task, NFS_SERVER(wdata->inode)->client->cl_metrics); -} - -static void filelayout_write_release(void *data) -{ - struct nfs_write_data *wdata = (struct nfs_write_data *)data; - - put_lseg(wdata->lseg); - wdata->mds_ops->rpc_release(data); -} - -static void filelayout_commit_release(void *data) -{ - struct nfs_write_data *wdata = (struct nfs_write_data *)data; - - nfs_commit_release_pages(wdata); - if (atomic_dec_and_test(&NFS_I(wdata->inode)->commits_outstanding)) - nfs_commit_clear_lock(NFS_I(wdata->inode)); - put_lseg(wdata->lseg); - nfs_commitdata_release(wdata); -} - -static const struct rpc_call_ops filelayout_read_call_ops = { - .rpc_call_prepare = filelayout_read_prepare, - .rpc_call_done = filelayout_read_call_done, - .rpc_count_stats = filelayout_read_count_stats, - .rpc_release = filelayout_read_release, -}; - -static const struct rpc_call_ops filelayout_write_call_ops = { - .rpc_call_prepare = filelayout_write_prepare, - .rpc_call_done = filelayout_write_call_done, - .rpc_count_stats = filelayout_write_count_stats, - .rpc_release = filelayout_write_release, -}; - -static const struct rpc_call_ops filelayout_commit_call_ops = { - .rpc_call_prepare = filelayout_write_prepare, - .rpc_call_done = filelayout_write_call_done, - .rpc_count_stats = filelayout_write_count_stats, - .rpc_release = filelayout_commit_release, -}; - -static enum pnfs_try_status -filelayout_read_pagelist(struct nfs_read_data *data) -{ - struct pnfs_layout_segment *lseg = data->lseg; - struct nfs4_pnfs_ds *ds; - loff_t offset = data->args.offset; - u32 j, idx; - struct nfs_fh *fh; - int status; - - dprintk("--> %s ino %lu pgbase %u req %Zu@%llu\n", - __func__, data->inode->i_ino, - data->args.pgbase, (size_t)data->args.count, offset); - - if (test_bit(NFS_DEVICEID_INVALID, &FILELAYOUT_DEVID_NODE(lseg)->flags)) - return PNFS_NOT_ATTEMPTED; - - /* Retrieve the correct rpc_client for the byte range */ - j = nfs4_fl_calc_j_index(lseg, offset); - idx = nfs4_fl_calc_ds_index(lseg, j); - ds = nfs4_fl_prepare_ds(lseg, idx); - if (!ds) { - /* Either layout fh index faulty, or ds connect failed */ - set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags); - set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags); - return PNFS_NOT_ATTEMPTED; - } - dprintk("%s USE DS: %s\n", __func__, ds->ds_remotestr); - - /* No multipath support. Use first DS */ - data->ds_clp = ds->ds_clp; - fh = nfs4_fl_select_ds_fh(lseg, j); - if (fh) - data->args.fh = fh; - - data->args.offset = filelayout_get_dserver_offset(lseg, offset); - data->mds_offset = offset; - - /* Perform an asynchronous read to ds */ - status = nfs_initiate_read(data, ds->ds_clp->cl_rpcclient, - &filelayout_read_call_ops); - BUG_ON(status != 0); - return PNFS_ATTEMPTED; -} - -/* Perform async writes. */ -static enum pnfs_try_status -filelayout_write_pagelist(struct nfs_write_data *data, int sync) -{ - struct pnfs_layout_segment *lseg = data->lseg; - struct nfs4_pnfs_ds *ds; - loff_t offset = data->args.offset; - u32 j, idx; - struct nfs_fh *fh; - int status; - - if (test_bit(NFS_DEVICEID_INVALID, &FILELAYOUT_DEVID_NODE(lseg)->flags)) - return PNFS_NOT_ATTEMPTED; - - /* Retrieve the correct rpc_client for the byte range */ - j = nfs4_fl_calc_j_index(lseg, offset); - idx = nfs4_fl_calc_ds_index(lseg, j); - ds = nfs4_fl_prepare_ds(lseg, idx); - if (!ds) { - printk(KERN_ERR "NFS: %s: prepare_ds failed, use MDS\n", - __func__); - set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags); - set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags); - return PNFS_NOT_ATTEMPTED; - } - dprintk("%s ino %lu sync %d req %Zu@%llu DS: %s\n", __func__, - data->inode->i_ino, sync, (size_t) data->args.count, offset, - ds->ds_remotestr); - - data->write_done_cb = filelayout_write_done_cb; - data->ds_clp = ds->ds_clp; - fh = nfs4_fl_select_ds_fh(lseg, j); - if (fh) - data->args.fh = fh; - /* - * Get the file offset on the dserver. Set the write offset to - * this offset and save the original offset. - */ - data->args.offset = filelayout_get_dserver_offset(lseg, offset); - - /* Perform an asynchronous write */ - status = nfs_initiate_write(data, ds->ds_clp->cl_rpcclient, - &filelayout_write_call_ops, sync); - BUG_ON(status != 0); - return PNFS_ATTEMPTED; -} - -/* - * filelayout_check_layout() - * - * Make sure layout segment parameters are sane WRT the device. - * At this point no generic layer initialization of the lseg has occurred, - * and nothing has been added to the layout_hdr cache. - * - */ -static int -filelayout_check_layout(struct pnfs_layout_hdr *lo, - struct nfs4_filelayout_segment *fl, - struct nfs4_layoutget_res *lgr, - struct nfs4_deviceid *id, - gfp_t gfp_flags) -{ - struct nfs4_deviceid_node *d; - struct nfs4_file_layout_dsaddr *dsaddr; - int status = -EINVAL; - struct nfs_server *nfss = NFS_SERVER(lo->plh_inode); - - dprintk("--> %s\n", __func__); - - /* FIXME: remove this check when layout segment support is added */ - if (lgr->range.offset != 0 || - lgr->range.length != NFS4_MAX_UINT64) { - dprintk("%s Only whole file layouts supported. Use MDS i/o\n", - __func__); - goto out; - } - - if (fl->pattern_offset > lgr->range.offset) { - dprintk("%s pattern_offset %lld too large\n", - __func__, fl->pattern_offset); - goto out; - } - - if (!fl->stripe_unit || fl->stripe_unit % PAGE_SIZE) { - dprintk("%s Invalid stripe unit (%u)\n", - __func__, fl->stripe_unit); - goto out; - } - - /* find and reference the deviceid */ - d = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode)->pnfs_curr_ld, - NFS_SERVER(lo->plh_inode)->nfs_client, id); - if (d == NULL) { - dsaddr = get_device_info(lo->plh_inode, id, gfp_flags); - if (dsaddr == NULL) - goto out; - } else - dsaddr = container_of(d, struct nfs4_file_layout_dsaddr, id_node); - /* Found deviceid is being reaped */ - if (test_bit(NFS_DEVICEID_INVALID, &dsaddr->id_node.flags)) - goto out_put; - - fl->dsaddr = dsaddr; - - if (fl->first_stripe_index >= dsaddr->stripe_count) { - dprintk("%s Bad first_stripe_index %u\n", - __func__, fl->first_stripe_index); - goto out_put; - } - - if ((fl->stripe_type == STRIPE_SPARSE && - fl->num_fh > 1 && fl->num_fh != dsaddr->ds_num) || - (fl->stripe_type == STRIPE_DENSE && - fl->num_fh != dsaddr->stripe_count)) { - dprintk("%s num_fh %u not valid for given packing\n", - __func__, fl->num_fh); - goto out_put; - } - - if (fl->stripe_unit % nfss->rsize || fl->stripe_unit % nfss->wsize) { - dprintk("%s Stripe unit (%u) not aligned with rsize %u " - "wsize %u\n", __func__, fl->stripe_unit, nfss->rsize, - nfss->wsize); - } - - status = 0; -out: - dprintk("--> %s returns %d\n", __func__, status); - return status; -out_put: - nfs4_fl_put_deviceid(dsaddr); - goto out; -} - -static void filelayout_free_fh_array(struct nfs4_filelayout_segment *fl) -{ - int i; - - for (i = 0; i < fl->num_fh; i++) { - if (!fl->fh_array[i]) - break; - kfree(fl->fh_array[i]); - } - kfree(fl->fh_array); - fl->fh_array = NULL; -} - -static void -_filelayout_free_lseg(struct nfs4_filelayout_segment *fl) -{ - filelayout_free_fh_array(fl); - kfree(fl); -} - -static int -filelayout_decode_layout(struct pnfs_layout_hdr *flo, - struct nfs4_filelayout_segment *fl, - struct nfs4_layoutget_res *lgr, - struct nfs4_deviceid *id, - gfp_t gfp_flags) -{ - struct xdr_stream stream; - struct xdr_buf buf; - struct page *scratch; - __be32 *p; - uint32_t nfl_util; - int i; - - dprintk("%s: set_layout_map Begin\n", __func__); - - scratch = alloc_page(gfp_flags); - if (!scratch) - return -ENOMEM; - - xdr_init_decode_pages(&stream, &buf, lgr->layoutp->pages, lgr->layoutp->len); - xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE); - - /* 20 = ufl_util (4), first_stripe_index (4), pattern_offset (8), - * num_fh (4) */ - p = xdr_inline_decode(&stream, NFS4_DEVICEID4_SIZE + 20); - if (unlikely(!p)) - goto out_err; - - memcpy(id, p, sizeof(*id)); - p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE); - nfs4_print_deviceid(id); - - nfl_util = be32_to_cpup(p++); - if (nfl_util & NFL4_UFLG_COMMIT_THRU_MDS) - fl->commit_through_mds = 1; - if (nfl_util & NFL4_UFLG_DENSE) - fl->stripe_type = STRIPE_DENSE; - else - fl->stripe_type = STRIPE_SPARSE; - fl->stripe_unit = nfl_util & ~NFL4_UFLG_MASK; - - fl->first_stripe_index = be32_to_cpup(p++); - p = xdr_decode_hyper(p, &fl->pattern_offset); - fl->num_fh = be32_to_cpup(p++); - - dprintk("%s: nfl_util 0x%X num_fh %u fsi %u po %llu\n", - __func__, nfl_util, fl->num_fh, fl->first_stripe_index, - fl->pattern_offset); - - /* Note that a zero value for num_fh is legal for STRIPE_SPARSE. - * Futher checking is done in filelayout_check_layout */ - if (fl->num_fh > - max(NFS4_PNFS_MAX_STRIPE_CNT, NFS4_PNFS_MAX_MULTI_CNT)) - goto out_err; - - if (fl->num_fh > 0) { - fl->fh_array = kzalloc(fl->num_fh * sizeof(struct nfs_fh *), - gfp_flags); - if (!fl->fh_array) - goto out_err; - } - - for (i = 0; i < fl->num_fh; i++) { - /* Do we want to use a mempool here? */ - fl->fh_array[i] = kmalloc(sizeof(struct nfs_fh), gfp_flags); - if (!fl->fh_array[i]) - goto out_err_free; - - p = xdr_inline_decode(&stream, 4); - if (unlikely(!p)) - goto out_err_free; - fl->fh_array[i]->size = be32_to_cpup(p++); - if (sizeof(struct nfs_fh) < fl->fh_array[i]->size) { - printk(KERN_ERR "NFS: Too big fh %d received %d\n", - i, fl->fh_array[i]->size); - goto out_err_free; - } - - p = xdr_inline_decode(&stream, fl->fh_array[i]->size); - if (unlikely(!p)) - goto out_err_free; - memcpy(fl->fh_array[i]->data, p, fl->fh_array[i]->size); - dprintk("DEBUG: %s: fh len %d\n", __func__, - fl->fh_array[i]->size); - } - - __free_page(scratch); - return 0; - -out_err_free: - filelayout_free_fh_array(fl); -out_err: - __free_page(scratch); - return -EIO; -} - -static void -filelayout_free_lseg(struct pnfs_layout_segment *lseg) -{ - struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg); - - dprintk("--> %s\n", __func__); - nfs4_fl_put_deviceid(fl->dsaddr); - kfree(fl->commit_buckets); - _filelayout_free_lseg(fl); -} - -static struct pnfs_layout_segment * -filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid, - struct nfs4_layoutget_res *lgr, - gfp_t gfp_flags) -{ - struct nfs4_filelayout_segment *fl; - int rc; - struct nfs4_deviceid id; - - dprintk("--> %s\n", __func__); - fl = kzalloc(sizeof(*fl), gfp_flags); - if (!fl) - return NULL; - - rc = filelayout_decode_layout(layoutid, fl, lgr, &id, gfp_flags); - if (rc != 0 || filelayout_check_layout(layoutid, fl, lgr, &id, gfp_flags)) { - _filelayout_free_lseg(fl); - return NULL; - } - - /* This assumes there is only one IOMODE_RW lseg. What - * we really want to do is have a layout_hdr level - * dictionary of <multipath_list4, fh> keys, each - * associated with a struct list_head, populated by calls - * to filelayout_write_pagelist(). - * */ - if ((!fl->commit_through_mds) && (lgr->range.iomode == IOMODE_RW)) { - int i; - int size = (fl->stripe_type == STRIPE_SPARSE) ? - fl->dsaddr->ds_num : fl->dsaddr->stripe_count; - - fl->commit_buckets = kcalloc(size, sizeof(struct nfs4_fl_commit_bucket), gfp_flags); - if (!fl->commit_buckets) { - filelayout_free_lseg(&fl->generic_hdr); - return NULL; - } - fl->number_of_buckets = size; - for (i = 0; i < size; i++) { - INIT_LIST_HEAD(&fl->commit_buckets[i].written); - INIT_LIST_HEAD(&fl->commit_buckets[i].committing); - } - } - return &fl->generic_hdr; -} - -/* - * filelayout_pg_test(). Called by nfs_can_coalesce_requests() - * - * return true : coalesce page - * return false : don't coalesce page - */ -static bool -filelayout_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev, - struct nfs_page *req) -{ - u64 p_stripe, r_stripe; - u32 stripe_unit; - - if (!pnfs_generic_pg_test(pgio, prev, req) || - !nfs_generic_pg_test(pgio, prev, req)) - return false; - - p_stripe = (u64)prev->wb_index << PAGE_CACHE_SHIFT; - r_stripe = (u64)req->wb_index << PAGE_CACHE_SHIFT; - stripe_unit = FILELAYOUT_LSEG(pgio->pg_lseg)->stripe_unit; - - do_div(p_stripe, stripe_unit); - do_div(r_stripe, stripe_unit); - - return (p_stripe == r_stripe); -} - -static void -filelayout_pg_init_read(struct nfs_pageio_descriptor *pgio, - struct nfs_page *req) -{ - BUG_ON(pgio->pg_lseg != NULL); - - pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, - req->wb_context, - 0, - NFS4_MAX_UINT64, - IOMODE_READ, - GFP_KERNEL); - /* If no lseg, fall back to read through mds */ - if (pgio->pg_lseg == NULL) - nfs_pageio_reset_read_mds(pgio); -} - -static void -filelayout_pg_init_write(struct nfs_pageio_descriptor *pgio, - struct nfs_page *req) -{ - BUG_ON(pgio->pg_lseg != NULL); - - pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, - req->wb_context, - 0, - NFS4_MAX_UINT64, - IOMODE_RW, - GFP_NOFS); - /* If no lseg, fall back to write through mds */ - if (pgio->pg_lseg == NULL) - nfs_pageio_reset_write_mds(pgio); -} - -static const struct nfs_pageio_ops filelayout_pg_read_ops = { - .pg_init = filelayout_pg_init_read, - .pg_test = filelayout_pg_test, - .pg_doio = pnfs_generic_pg_readpages, -}; - -static const struct nfs_pageio_ops filelayout_pg_write_ops = { - .pg_init = filelayout_pg_init_write, - .pg_test = filelayout_pg_test, - .pg_doio = pnfs_generic_pg_writepages, -}; - -static u32 select_bucket_index(struct nfs4_filelayout_segment *fl, u32 j) -{ - if (fl->stripe_type == STRIPE_SPARSE) - return nfs4_fl_calc_ds_index(&fl->generic_hdr, j); - else - return j; -} - -/* The generic layer is about to remove the req from the commit list. - * If this will make the bucket empty, it will need to put the lseg reference. - */ -static void -filelayout_clear_request_commit(struct nfs_page *req) -{ - struct pnfs_layout_segment *freeme = NULL; - struct inode *inode = req->wb_context->dentry->d_inode; - - spin_lock(&inode->i_lock); - if (!test_and_clear_bit(PG_COMMIT_TO_DS, &req->wb_flags)) - goto out; - if (list_is_singular(&req->wb_list)) { - struct pnfs_layout_segment *lseg; - - /* From here we can find the bucket, but for the moment, - * since there is only one relevant lseg... - */ - list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) { - if (lseg->pls_range.iomode == IOMODE_RW) { - freeme = lseg; - break; - } - } - } -out: - nfs_request_remove_commit_list(req); - spin_unlock(&inode->i_lock); - put_lseg(freeme); -} - -static struct list_head * -filelayout_choose_commit_list(struct nfs_page *req, - struct pnfs_layout_segment *lseg) -{ - struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg); - u32 i, j; - struct list_head *list; - - if (fl->commit_through_mds) - return &NFS_I(req->wb_context->dentry->d_inode)->commit_list; - - /* Note that we are calling nfs4_fl_calc_j_index on each page - * that ends up being committed to a data server. An attractive - * alternative is to add a field to nfs_write_data and nfs_page - * to store the value calculated in filelayout_write_pagelist - * and just use that here. - */ - j = nfs4_fl_calc_j_index(lseg, - (loff_t)req->wb_index << PAGE_CACHE_SHIFT); - i = select_bucket_index(fl, j); - list = &fl->commit_buckets[i].written; - if (list_empty(list)) { - /* Non-empty buckets hold a reference on the lseg. That ref - * is normally transferred to the COMMIT call and released - * there. It could also be released if the last req is pulled - * off due to a rewrite, in which case it will be done in - * filelayout_remove_commit_req - */ - get_lseg(lseg); - } - set_bit(PG_COMMIT_TO_DS, &req->wb_flags); - return list; -} - -static void -filelayout_mark_request_commit(struct nfs_page *req, - struct pnfs_layout_segment *lseg) -{ - struct list_head *list; - - list = filelayout_choose_commit_list(req, lseg); - nfs_request_add_commit_list(req, list); -} - -static u32 calc_ds_index_from_commit(struct pnfs_layout_segment *lseg, u32 i) -{ - struct nfs4_filelayout_segment *flseg = FILELAYOUT_LSEG(lseg); - - if (flseg->stripe_type == STRIPE_SPARSE) - return i; - else - return nfs4_fl_calc_ds_index(lseg, i); -} - -static struct nfs_fh * -select_ds_fh_from_commit(struct pnfs_layout_segment *lseg, u32 i) -{ - struct nfs4_filelayout_segment *flseg = FILELAYOUT_LSEG(lseg); - - if (flseg->stripe_type == STRIPE_SPARSE) { - if (flseg->num_fh == 1) - i = 0; - else if (flseg->num_fh == 0) - /* Use the MDS OPEN fh set in nfs_read_rpcsetup */ - return NULL; - } - return flseg->fh_array[i]; -} - -static int filelayout_initiate_commit(struct nfs_write_data *data, int how) -{ - struct pnfs_layout_segment *lseg = data->lseg; - struct nfs4_pnfs_ds *ds; - u32 idx; - struct nfs_fh *fh; - - idx = calc_ds_index_from_commit(lseg, data->ds_commit_index); - ds = nfs4_fl_prepare_ds(lseg, idx); - if (!ds) { - printk(KERN_ERR "NFS: %s: prepare_ds failed, use MDS\n", - __func__); - set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags); - set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags); - prepare_to_resend_writes(data); - filelayout_commit_release(data); - return -EAGAIN; - } - dprintk("%s ino %lu, how %d\n", __func__, data->inode->i_ino, how); - data->write_done_cb = filelayout_commit_done_cb; - data->ds_clp = ds->ds_clp; - fh = select_ds_fh_from_commit(lseg, data->ds_commit_index); - if (fh) - data->args.fh = fh; - return nfs_initiate_commit(data, ds->ds_clp->cl_rpcclient, - &filelayout_commit_call_ops, how); -} - -/* - * This is only useful while we are using whole file layouts. - */ -static struct pnfs_layout_segment * -find_only_write_lseg_locked(struct inode *inode) -{ - struct pnfs_layout_segment *lseg; - - list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) - if (lseg->pls_range.iomode == IOMODE_RW) - return lseg; - return NULL; -} - -static struct pnfs_layout_segment *find_only_write_lseg(struct inode *inode) -{ - struct pnfs_layout_segment *rv; - - spin_lock(&inode->i_lock); - rv = find_only_write_lseg_locked(inode); - if (rv) - get_lseg(rv); - spin_unlock(&inode->i_lock); - return rv; -} - -static int -filelayout_scan_ds_commit_list(struct nfs4_fl_commit_bucket *bucket, int max, - spinlock_t *lock) -{ - struct list_head *src = &bucket->written; - struct list_head *dst = &bucket->committing; - struct nfs_page *req, *tmp; - int ret = 0; - - list_for_each_entry_safe(req, tmp, src, wb_list) { - if (!nfs_lock_request(req)) - continue; - if (cond_resched_lock(lock)) - list_safe_reset_next(req, tmp, wb_list); - nfs_request_remove_commit_list(req); - clear_bit(PG_COMMIT_TO_DS, &req->wb_flags); - nfs_list_add_request(req, dst); - ret++; - if (ret == max) - break; - } - return ret; -} - -/* Move reqs from written to committing lists, returning count of number moved. - * Note called with i_lock held. - */ -static int filelayout_scan_commit_lists(struct inode *inode, int max, - spinlock_t *lock) -{ - struct pnfs_layout_segment *lseg; - struct nfs4_filelayout_segment *fl; - int i, rv = 0, cnt; - - lseg = find_only_write_lseg_locked(inode); - if (!lseg) - goto out_done; - fl = FILELAYOUT_LSEG(lseg); - if (fl->commit_through_mds) - goto out_done; - for (i = 0; i < fl->number_of_buckets && max != 0; i++) { - cnt = filelayout_scan_ds_commit_list(&fl->commit_buckets[i], - max, lock); - max -= cnt; - rv += cnt; - } -out_done: - return rv; -} - -static unsigned int -alloc_ds_commits(struct inode *inode, struct list_head *list) -{ - struct pnfs_layout_segment *lseg; - struct nfs4_filelayout_segment *fl; - struct nfs_write_data *data; - int i, j; - unsigned int nreq = 0; - - /* Won't need this when non-whole file layout segments are supported - * instead we will use a pnfs_layout_hdr structure */ - lseg = find_only_write_lseg(inode); - if (!lseg) - return 0; - fl = FILELAYOUT_LSEG(lseg); - for (i = 0; i < fl->number_of_buckets; i++) { - if (list_empty(&fl->commit_buckets[i].committing)) - continue; - data = nfs_commitdata_alloc(); - if (!data) - break; - data->ds_commit_index = i; - data->lseg = lseg; - list_add(&data->pages, list); - nreq++; - } - - /* Clean up on error */ - for (j = i; j < fl->number_of_buckets; j++) { - if (list_empty(&fl->commit_buckets[i].committing)) - continue; - nfs_retry_commit(&fl->commit_buckets[i].committing, lseg); - put_lseg(lseg); /* associated with emptying bucket */ - } - put_lseg(lseg); - /* Caller will clean up entries put on list */ - return nreq; -} - -/* This follows nfs_commit_list pretty closely */ -static int -filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages, - int how) -{ - struct nfs_write_data *data, *tmp; - LIST_HEAD(list); - unsigned int nreq = 0; - - if (!list_empty(mds_pages)) { - data = nfs_commitdata_alloc(); - if (data != NULL) { - data->lseg = NULL; - list_add(&data->pages, &list); - nreq++; - } else - nfs_retry_commit(mds_pages, NULL); - } - - nreq += alloc_ds_commits(inode, &list); - - if (nreq == 0) { - nfs_commit_clear_lock(NFS_I(inode)); - goto out; - } - - atomic_add(nreq, &NFS_I(inode)->commits_outstanding); - - list_for_each_entry_safe(data, tmp, &list, pages) { - list_del_init(&data->pages); - if (!data->lseg) { - nfs_init_commit(data, mds_pages, NULL); - nfs_initiate_commit(data, NFS_CLIENT(inode), - data->mds_ops, how); - } else { - nfs_init_commit(data, &FILELAYOUT_LSEG(data->lseg)->commit_buckets[data->ds_commit_index].committing, data->lseg); - filelayout_initiate_commit(data, how); - } - } -out: - return PNFS_ATTEMPTED; -} - -static void -filelayout_free_deveiceid_node(struct nfs4_deviceid_node *d) -{ - nfs4_fl_free_deviceid(container_of(d, struct nfs4_file_layout_dsaddr, id_node)); -} - -static struct pnfs_layoutdriver_type filelayout_type = { - .id = LAYOUT_NFSV4_1_FILES, - .name = "LAYOUT_NFSV4_1_FILES", - .owner = THIS_MODULE, - .alloc_lseg = filelayout_alloc_lseg, - .free_lseg = filelayout_free_lseg, - .pg_read_ops = &filelayout_pg_read_ops, - .pg_write_ops = &filelayout_pg_write_ops, - .mark_request_commit = filelayout_mark_request_commit, - .clear_request_commit = filelayout_clear_request_commit, - .scan_commit_lists = filelayout_scan_commit_lists, - .commit_pagelist = filelayout_commit_pagelist, - .read_pagelist = filelayout_read_pagelist, - .write_pagelist = filelayout_write_pagelist, - .free_deviceid_node = filelayout_free_deveiceid_node, -}; - -static int __init nfs4filelayout_init(void) -{ - printk(KERN_INFO "%s: NFSv4 File Layout Driver Registering...\n", - __func__); - return pnfs_register_layoutdriver(&filelayout_type); -} - -static void __exit nfs4filelayout_exit(void) -{ - printk(KERN_INFO "%s: NFSv4 File Layout Driver Unregistering...\n", - __func__); - pnfs_unregister_layoutdriver(&filelayout_type); -} - -MODULE_ALIAS("nfs-layouttype4-1"); - -module_init(nfs4filelayout_init); -module_exit(nfs4filelayout_exit); diff --git a/ANDROID_3.4.5/fs/nfs/nfs4filelayout.h b/ANDROID_3.4.5/fs/nfs/nfs4filelayout.h deleted file mode 100644 index 21190bb1..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfs4filelayout.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * NFSv4 file layout driver data structures. - * - * Copyright (c) 2002 - * The Regents of the University of Michigan - * All Rights Reserved - * - * Dean Hildebrand <dhildebz@umich.edu> - * - * Permission is granted to use, copy, create derivative works, and - * redistribute this software and such derivative works for any purpose, - * so long as the name of the University of Michigan is not used in - * any advertising or publicity pertaining to the use or distribution - * of this software without specific, written prior authorization. If - * the above copyright notice or any other identification of the - * University of Michigan is included in any copy of any portion of - * this software, then the disclaimer below must also be included. - * - * This software is provided as is, without representation or warranty - * of any kind either express or implied, including without limitation - * the implied warranties of merchantability, fitness for a particular - * purpose, or noninfringement. The Regents of the University of - * Michigan shall not be liable for any damages, including special, - * indirect, incidental, or consequential damages, with respect to any - * claim arising out of or in connection with the use of the software, - * even if it has been or is hereafter advised of the possibility of - * such damages. - */ - -#ifndef FS_NFS_NFS4FILELAYOUT_H -#define FS_NFS_NFS4FILELAYOUT_H - -#include "pnfs.h" - -/* - * Field testing shows we need to support up to 4096 stripe indices. - * We store each index as a u8 (u32 on the wire) to keep the memory footprint - * reasonable. This in turn means we support a maximum of 256 - * RFC 5661 multipath_list4 structures. - */ -#define NFS4_PNFS_MAX_STRIPE_CNT 4096 -#define NFS4_PNFS_MAX_MULTI_CNT 256 /* 256 fit into a u8 stripe_index */ - -enum stripetype4 { - STRIPE_SPARSE = 1, - STRIPE_DENSE = 2 -}; - -/* Individual ip address */ -struct nfs4_pnfs_ds_addr { - struct sockaddr_storage da_addr; - size_t da_addrlen; - struct list_head da_node; /* nfs4_pnfs_dev_hlist dev_dslist */ - char *da_remotestr; /* human readable addr+port */ -}; - -struct nfs4_pnfs_ds { - struct list_head ds_node; /* nfs4_pnfs_dev_hlist dev_dslist */ - char *ds_remotestr; /* comma sep list of addrs */ - struct list_head ds_addrs; - struct nfs_client *ds_clp; - atomic_t ds_count; -}; - -/* nfs4_file_layout_dsaddr flags */ -#define NFS4_DEVICE_ID_NEG_ENTRY 0x00000001 - -struct nfs4_file_layout_dsaddr { - struct nfs4_deviceid_node id_node; - unsigned long flags; - u32 stripe_count; - u8 *stripe_indices; - u32 ds_num; - struct nfs4_pnfs_ds *ds_list[1]; -}; - -struct nfs4_fl_commit_bucket { - struct list_head written; - struct list_head committing; -}; - -struct nfs4_filelayout_segment { - struct pnfs_layout_segment generic_hdr; - u32 stripe_type; - u32 commit_through_mds; - u32 stripe_unit; - u32 first_stripe_index; - u64 pattern_offset; - struct nfs4_file_layout_dsaddr *dsaddr; /* Point to GETDEVINFO data */ - unsigned int num_fh; - struct nfs_fh **fh_array; - struct nfs4_fl_commit_bucket *commit_buckets; /* Sort commits to ds */ - int number_of_buckets; -}; - -static inline struct nfs4_filelayout_segment * -FILELAYOUT_LSEG(struct pnfs_layout_segment *lseg) -{ - return container_of(lseg, - struct nfs4_filelayout_segment, - generic_hdr); -} - -static inline struct nfs4_deviceid_node * -FILELAYOUT_DEVID_NODE(struct pnfs_layout_segment *lseg) -{ - return &FILELAYOUT_LSEG(lseg)->dsaddr->id_node; -} - -extern struct nfs_fh * -nfs4_fl_select_ds_fh(struct pnfs_layout_segment *lseg, u32 j); - -extern void print_ds(struct nfs4_pnfs_ds *ds); -u32 nfs4_fl_calc_j_index(struct pnfs_layout_segment *lseg, loff_t offset); -u32 nfs4_fl_calc_ds_index(struct pnfs_layout_segment *lseg, u32 j); -struct nfs4_pnfs_ds *nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, - u32 ds_idx); -extern void nfs4_fl_put_deviceid(struct nfs4_file_layout_dsaddr *dsaddr); -extern void nfs4_fl_free_deviceid(struct nfs4_file_layout_dsaddr *dsaddr); -struct nfs4_file_layout_dsaddr * -get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gfp_t gfp_flags); - -#endif /* FS_NFS_NFS4FILELAYOUT_H */ diff --git a/ANDROID_3.4.5/fs/nfs/nfs4filelayoutdev.c b/ANDROID_3.4.5/fs/nfs/nfs4filelayoutdev.c deleted file mode 100644 index c9cff9ad..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfs4filelayoutdev.c +++ /dev/null @@ -1,838 +0,0 @@ -/* - * Device operations for the pnfs nfs4 file layout driver. - * - * Copyright (c) 2002 - * The Regents of the University of Michigan - * All Rights Reserved - * - * Dean Hildebrand <dhildebz@umich.edu> - * Garth Goodson <Garth.Goodson@netapp.com> - * - * Permission is granted to use, copy, create derivative works, and - * redistribute this software and such derivative works for any purpose, - * so long as the name of the University of Michigan is not used in - * any advertising or publicity pertaining to the use or distribution - * of this software without specific, written prior authorization. If - * the above copyright notice or any other identification of the - * University of Michigan is included in any copy of any portion of - * this software, then the disclaimer below must also be included. - * - * This software is provided as is, without representation or warranty - * of any kind either express or implied, including without limitation - * the implied warranties of merchantability, fitness for a particular - * purpose, or noninfringement. The Regents of the University of - * Michigan shall not be liable for any damages, including special, - * indirect, incidental, or consequential damages, with respect to any - * claim arising out of or in connection with the use of the software, - * even if it has been or is hereafter advised of the possibility of - * such damages. - */ - -#include <linux/nfs_fs.h> -#include <linux/vmalloc.h> - -#include "internal.h" -#include "nfs4filelayout.h" - -#define NFSDBG_FACILITY NFSDBG_PNFS_LD - -/* - * Data server cache - * - * Data servers can be mapped to different device ids. - * nfs4_pnfs_ds reference counting - * - set to 1 on allocation - * - incremented when a device id maps a data server already in the cache. - * - decremented when deviceid is removed from the cache. - */ -static DEFINE_SPINLOCK(nfs4_ds_cache_lock); -static LIST_HEAD(nfs4_data_server_cache); - -/* Debug routines */ -void -print_ds(struct nfs4_pnfs_ds *ds) -{ - if (ds == NULL) { - printk("%s NULL device\n", __func__); - return; - } - printk(" ds %s\n" - " ref count %d\n" - " client %p\n" - " cl_exchange_flags %x\n", - ds->ds_remotestr, - atomic_read(&ds->ds_count), ds->ds_clp, - ds->ds_clp ? ds->ds_clp->cl_exchange_flags : 0); -} - -static bool -same_sockaddr(struct sockaddr *addr1, struct sockaddr *addr2) -{ - struct sockaddr_in *a, *b; - struct sockaddr_in6 *a6, *b6; - - if (addr1->sa_family != addr2->sa_family) - return false; - - switch (addr1->sa_family) { - case AF_INET: - a = (struct sockaddr_in *)addr1; - b = (struct sockaddr_in *)addr2; - - if (a->sin_addr.s_addr == b->sin_addr.s_addr && - a->sin_port == b->sin_port) - return true; - break; - - case AF_INET6: - a6 = (struct sockaddr_in6 *)addr1; - b6 = (struct sockaddr_in6 *)addr2; - - /* LINKLOCAL addresses must have matching scope_id */ - if (ipv6_addr_scope(&a6->sin6_addr) == - IPV6_ADDR_SCOPE_LINKLOCAL && - a6->sin6_scope_id != b6->sin6_scope_id) - return false; - - if (ipv6_addr_equal(&a6->sin6_addr, &b6->sin6_addr) && - a6->sin6_port == b6->sin6_port) - return true; - break; - - default: - dprintk("%s: unhandled address family: %u\n", - __func__, addr1->sa_family); - return false; - } - - return false; -} - -static bool -_same_data_server_addrs_locked(const struct list_head *dsaddrs1, - const struct list_head *dsaddrs2) -{ - struct nfs4_pnfs_ds_addr *da1, *da2; - - /* step through both lists, comparing as we go */ - for (da1 = list_first_entry(dsaddrs1, typeof(*da1), da_node), - da2 = list_first_entry(dsaddrs2, typeof(*da2), da_node); - da1 != NULL && da2 != NULL; - da1 = list_entry(da1->da_node.next, typeof(*da1), da_node), - da2 = list_entry(da2->da_node.next, typeof(*da2), da_node)) { - if (!same_sockaddr((struct sockaddr *)&da1->da_addr, - (struct sockaddr *)&da2->da_addr)) - return false; - } - if (da1 == NULL && da2 == NULL) - return true; - - return false; -} - -/* - * Lookup DS by addresses. nfs4_ds_cache_lock is held - */ -static struct nfs4_pnfs_ds * -_data_server_lookup_locked(const struct list_head *dsaddrs) -{ - struct nfs4_pnfs_ds *ds; - - list_for_each_entry(ds, &nfs4_data_server_cache, ds_node) - if (_same_data_server_addrs_locked(&ds->ds_addrs, dsaddrs)) - return ds; - return NULL; -} - -/* - * Create an rpc connection to the nfs4_pnfs_ds data server - * Currently only supports IPv4 and IPv6 addresses - */ -static int -nfs4_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds) -{ - struct nfs_client *clp = ERR_PTR(-EIO); - struct nfs4_pnfs_ds_addr *da; - int status = 0; - - dprintk("--> %s DS %s au_flavor %d\n", __func__, ds->ds_remotestr, - mds_srv->nfs_client->cl_rpcclient->cl_auth->au_flavor); - - BUG_ON(list_empty(&ds->ds_addrs)); - - list_for_each_entry(da, &ds->ds_addrs, da_node) { - dprintk("%s: DS %s: trying address %s\n", - __func__, ds->ds_remotestr, da->da_remotestr); - - clp = nfs4_set_ds_client(mds_srv->nfs_client, - (struct sockaddr *)&da->da_addr, - da->da_addrlen, IPPROTO_TCP); - if (!IS_ERR(clp)) - break; - } - - if (IS_ERR(clp)) { - status = PTR_ERR(clp); - goto out; - } - - if ((clp->cl_exchange_flags & EXCHGID4_FLAG_MASK_PNFS) != 0) { - if (!is_ds_client(clp)) { - status = -ENODEV; - goto out_put; - } - ds->ds_clp = clp; - dprintk("%s [existing] server=%s\n", __func__, - ds->ds_remotestr); - goto out; - } - - /* - * Do not set NFS_CS_CHECK_LEASE_TIME instead set the DS lease to - * be equal to the MDS lease. Renewal is scheduled in create_session. - */ - spin_lock(&mds_srv->nfs_client->cl_lock); - clp->cl_lease_time = mds_srv->nfs_client->cl_lease_time; - spin_unlock(&mds_srv->nfs_client->cl_lock); - clp->cl_last_renewal = jiffies; - - /* New nfs_client */ - status = nfs4_init_ds_session(clp); - if (status) - goto out_put; - - ds->ds_clp = clp; - dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr); -out: - return status; -out_put: - nfs_put_client(clp); - goto out; -} - -static void -destroy_ds(struct nfs4_pnfs_ds *ds) -{ - struct nfs4_pnfs_ds_addr *da; - - dprintk("--> %s\n", __func__); - ifdebug(FACILITY) - print_ds(ds); - - if (ds->ds_clp) - nfs_put_client(ds->ds_clp); - - while (!list_empty(&ds->ds_addrs)) { - da = list_first_entry(&ds->ds_addrs, - struct nfs4_pnfs_ds_addr, - da_node); - list_del_init(&da->da_node); - kfree(da->da_remotestr); - kfree(da); - } - - kfree(ds->ds_remotestr); - kfree(ds); -} - -void -nfs4_fl_free_deviceid(struct nfs4_file_layout_dsaddr *dsaddr) -{ - struct nfs4_pnfs_ds *ds; - int i; - - nfs4_print_deviceid(&dsaddr->id_node.deviceid); - - for (i = 0; i < dsaddr->ds_num; i++) { - ds = dsaddr->ds_list[i]; - if (ds != NULL) { - if (atomic_dec_and_lock(&ds->ds_count, - &nfs4_ds_cache_lock)) { - list_del_init(&ds->ds_node); - spin_unlock(&nfs4_ds_cache_lock); - destroy_ds(ds); - } - } - } - kfree(dsaddr->stripe_indices); - kfree(dsaddr); -} - -/* - * Create a string with a human readable address and port to avoid - * complicated setup around many dprinks. - */ -static char * -nfs4_pnfs_remotestr(struct list_head *dsaddrs, gfp_t gfp_flags) -{ - struct nfs4_pnfs_ds_addr *da; - char *remotestr; - size_t len; - char *p; - - len = 3; /* '{', '}' and eol */ - list_for_each_entry(da, dsaddrs, da_node) { - len += strlen(da->da_remotestr) + 1; /* string plus comma */ - } - - remotestr = kzalloc(len, gfp_flags); - if (!remotestr) - return NULL; - - p = remotestr; - *(p++) = '{'; - len--; - list_for_each_entry(da, dsaddrs, da_node) { - size_t ll = strlen(da->da_remotestr); - - if (ll > len) - goto out_err; - - memcpy(p, da->da_remotestr, ll); - p += ll; - len -= ll; - - if (len < 1) - goto out_err; - (*p++) = ','; - len--; - } - if (len < 2) - goto out_err; - *(p++) = '}'; - *p = '\0'; - return remotestr; -out_err: - kfree(remotestr); - return NULL; -} - -static struct nfs4_pnfs_ds * -nfs4_pnfs_ds_add(struct list_head *dsaddrs, gfp_t gfp_flags) -{ - struct nfs4_pnfs_ds *tmp_ds, *ds = NULL; - char *remotestr; - - if (list_empty(dsaddrs)) { - dprintk("%s: no addresses defined\n", __func__); - goto out; - } - - ds = kzalloc(sizeof(*ds), gfp_flags); - if (!ds) - goto out; - - /* this is only used for debugging, so it's ok if its NULL */ - remotestr = nfs4_pnfs_remotestr(dsaddrs, gfp_flags); - - spin_lock(&nfs4_ds_cache_lock); - tmp_ds = _data_server_lookup_locked(dsaddrs); - if (tmp_ds == NULL) { - INIT_LIST_HEAD(&ds->ds_addrs); - list_splice_init(dsaddrs, &ds->ds_addrs); - ds->ds_remotestr = remotestr; - atomic_set(&ds->ds_count, 1); - INIT_LIST_HEAD(&ds->ds_node); - ds->ds_clp = NULL; - list_add(&ds->ds_node, &nfs4_data_server_cache); - dprintk("%s add new data server %s\n", __func__, - ds->ds_remotestr); - } else { - kfree(remotestr); - kfree(ds); - atomic_inc(&tmp_ds->ds_count); - dprintk("%s data server %s found, inc'ed ds_count to %d\n", - __func__, tmp_ds->ds_remotestr, - atomic_read(&tmp_ds->ds_count)); - ds = tmp_ds; - } - spin_unlock(&nfs4_ds_cache_lock); -out: - return ds; -} - -/* - * Currently only supports ipv4, ipv6 and one multi-path address. - */ -static struct nfs4_pnfs_ds_addr * -decode_ds_addr(struct net *net, struct xdr_stream *streamp, gfp_t gfp_flags) -{ - struct nfs4_pnfs_ds_addr *da = NULL; - char *buf, *portstr; - __be16 port; - int nlen, rlen; - int tmp[2]; - __be32 *p; - char *netid, *match_netid; - size_t len, match_netid_len; - char *startsep = ""; - char *endsep = ""; - - - /* r_netid */ - p = xdr_inline_decode(streamp, 4); - if (unlikely(!p)) - goto out_err; - nlen = be32_to_cpup(p++); - - p = xdr_inline_decode(streamp, nlen); - if (unlikely(!p)) - goto out_err; - - netid = kmalloc(nlen+1, gfp_flags); - if (unlikely(!netid)) - goto out_err; - - netid[nlen] = '\0'; - memcpy(netid, p, nlen); - - /* r_addr: ip/ip6addr with port in dec octets - see RFC 5665 */ - p = xdr_inline_decode(streamp, 4); - if (unlikely(!p)) - goto out_free_netid; - rlen = be32_to_cpup(p); - - p = xdr_inline_decode(streamp, rlen); - if (unlikely(!p)) - goto out_free_netid; - - /* port is ".ABC.DEF", 8 chars max */ - if (rlen > INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN + 8) { - dprintk("%s: Invalid address, length %d\n", __func__, - rlen); - goto out_free_netid; - } - buf = kmalloc(rlen + 1, gfp_flags); - if (!buf) { - dprintk("%s: Not enough memory\n", __func__); - goto out_free_netid; - } - buf[rlen] = '\0'; - memcpy(buf, p, rlen); - - /* replace port '.' with '-' */ - portstr = strrchr(buf, '.'); - if (!portstr) { - dprintk("%s: Failed finding expected dot in port\n", - __func__); - goto out_free_buf; - } - *portstr = '-'; - - /* find '.' between address and port */ - portstr = strrchr(buf, '.'); - if (!portstr) { - dprintk("%s: Failed finding expected dot between address and " - "port\n", __func__); - goto out_free_buf; - } - *portstr = '\0'; - - da = kzalloc(sizeof(*da), gfp_flags); - if (unlikely(!da)) - goto out_free_buf; - - INIT_LIST_HEAD(&da->da_node); - - if (!rpc_pton(net, buf, portstr-buf, (struct sockaddr *)&da->da_addr, - sizeof(da->da_addr))) { - dprintk("%s: error parsing address %s\n", __func__, buf); - goto out_free_da; - } - - portstr++; - sscanf(portstr, "%d-%d", &tmp[0], &tmp[1]); - port = htons((tmp[0] << 8) | (tmp[1])); - - switch (da->da_addr.ss_family) { - case AF_INET: - ((struct sockaddr_in *)&da->da_addr)->sin_port = port; - da->da_addrlen = sizeof(struct sockaddr_in); - match_netid = "tcp"; - match_netid_len = 3; - break; - - case AF_INET6: - ((struct sockaddr_in6 *)&da->da_addr)->sin6_port = port; - da->da_addrlen = sizeof(struct sockaddr_in6); - match_netid = "tcp6"; - match_netid_len = 4; - startsep = "["; - endsep = "]"; - break; - - default: - dprintk("%s: unsupported address family: %u\n", - __func__, da->da_addr.ss_family); - goto out_free_da; - } - - if (nlen != match_netid_len || strncmp(netid, match_netid, nlen)) { - dprintk("%s: ERROR: r_netid \"%s\" != \"%s\"\n", - __func__, netid, match_netid); - goto out_free_da; - } - - /* save human readable address */ - len = strlen(startsep) + strlen(buf) + strlen(endsep) + 7; - da->da_remotestr = kzalloc(len, gfp_flags); - - /* NULL is ok, only used for dprintk */ - if (da->da_remotestr) - snprintf(da->da_remotestr, len, "%s%s%s:%u", startsep, - buf, endsep, ntohs(port)); - - dprintk("%s: Parsed DS addr %s\n", __func__, da->da_remotestr); - kfree(buf); - kfree(netid); - return da; - -out_free_da: - kfree(da); -out_free_buf: - dprintk("%s: Error parsing DS addr: %s\n", __func__, buf); - kfree(buf); -out_free_netid: - kfree(netid); -out_err: - return NULL; -} - -/* Decode opaque device data and return the result */ -static struct nfs4_file_layout_dsaddr* -decode_device(struct inode *ino, struct pnfs_device *pdev, gfp_t gfp_flags) -{ - int i; - u32 cnt, num; - u8 *indexp; - __be32 *p; - u8 *stripe_indices; - u8 max_stripe_index; - struct nfs4_file_layout_dsaddr *dsaddr = NULL; - struct xdr_stream stream; - struct xdr_buf buf; - struct page *scratch; - struct list_head dsaddrs; - struct nfs4_pnfs_ds_addr *da; - - /* set up xdr stream */ - scratch = alloc_page(gfp_flags); - if (!scratch) - goto out_err; - - xdr_init_decode_pages(&stream, &buf, pdev->pages, pdev->pglen); - xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE); - - /* Get the stripe count (number of stripe index) */ - p = xdr_inline_decode(&stream, 4); - if (unlikely(!p)) - goto out_err_free_scratch; - - cnt = be32_to_cpup(p); - dprintk("%s stripe count %d\n", __func__, cnt); - if (cnt > NFS4_PNFS_MAX_STRIPE_CNT) { - printk(KERN_WARNING "NFS: %s: stripe count %d greater than " - "supported maximum %d\n", __func__, - cnt, NFS4_PNFS_MAX_STRIPE_CNT); - goto out_err_free_scratch; - } - - /* read stripe indices */ - stripe_indices = kcalloc(cnt, sizeof(u8), gfp_flags); - if (!stripe_indices) - goto out_err_free_scratch; - - p = xdr_inline_decode(&stream, cnt << 2); - if (unlikely(!p)) - goto out_err_free_stripe_indices; - - indexp = &stripe_indices[0]; - max_stripe_index = 0; - for (i = 0; i < cnt; i++) { - *indexp = be32_to_cpup(p++); - max_stripe_index = max(max_stripe_index, *indexp); - indexp++; - } - - /* Check the multipath list count */ - p = xdr_inline_decode(&stream, 4); - if (unlikely(!p)) - goto out_err_free_stripe_indices; - - num = be32_to_cpup(p); - dprintk("%s ds_num %u\n", __func__, num); - if (num > NFS4_PNFS_MAX_MULTI_CNT) { - printk(KERN_WARNING "NFS: %s: multipath count %d greater than " - "supported maximum %d\n", __func__, - num, NFS4_PNFS_MAX_MULTI_CNT); - goto out_err_free_stripe_indices; - } - - /* validate stripe indices are all < num */ - if (max_stripe_index >= num) { - printk(KERN_WARNING "NFS: %s: stripe index %u >= num ds %u\n", - __func__, max_stripe_index, num); - goto out_err_free_stripe_indices; - } - - dsaddr = kzalloc(sizeof(*dsaddr) + - (sizeof(struct nfs4_pnfs_ds *) * (num - 1)), - gfp_flags); - if (!dsaddr) - goto out_err_free_stripe_indices; - - dsaddr->stripe_count = cnt; - dsaddr->stripe_indices = stripe_indices; - stripe_indices = NULL; - dsaddr->ds_num = num; - nfs4_init_deviceid_node(&dsaddr->id_node, - NFS_SERVER(ino)->pnfs_curr_ld, - NFS_SERVER(ino)->nfs_client, - &pdev->dev_id); - - INIT_LIST_HEAD(&dsaddrs); - - for (i = 0; i < dsaddr->ds_num; i++) { - int j; - u32 mp_count; - - p = xdr_inline_decode(&stream, 4); - if (unlikely(!p)) - goto out_err_free_deviceid; - - mp_count = be32_to_cpup(p); /* multipath count */ - for (j = 0; j < mp_count; j++) { - da = decode_ds_addr(NFS_SERVER(ino)->nfs_client->net, - &stream, gfp_flags); - if (da) - list_add_tail(&da->da_node, &dsaddrs); - } - if (list_empty(&dsaddrs)) { - dprintk("%s: no suitable DS addresses found\n", - __func__); - goto out_err_free_deviceid; - } - - dsaddr->ds_list[i] = nfs4_pnfs_ds_add(&dsaddrs, gfp_flags); - if (!dsaddr->ds_list[i]) - goto out_err_drain_dsaddrs; - - /* If DS was already in cache, free ds addrs */ - while (!list_empty(&dsaddrs)) { - da = list_first_entry(&dsaddrs, - struct nfs4_pnfs_ds_addr, - da_node); - list_del_init(&da->da_node); - kfree(da->da_remotestr); - kfree(da); - } - } - - __free_page(scratch); - return dsaddr; - -out_err_drain_dsaddrs: - while (!list_empty(&dsaddrs)) { - da = list_first_entry(&dsaddrs, struct nfs4_pnfs_ds_addr, - da_node); - list_del_init(&da->da_node); - kfree(da->da_remotestr); - kfree(da); - } -out_err_free_deviceid: - nfs4_fl_free_deviceid(dsaddr); - /* stripe_indicies was part of dsaddr */ - goto out_err_free_scratch; -out_err_free_stripe_indices: - kfree(stripe_indices); -out_err_free_scratch: - __free_page(scratch); -out_err: - dprintk("%s ERROR: returning NULL\n", __func__); - return NULL; -} - -/* - * Decode the opaque device specified in 'dev' and add it to the cache of - * available devices. - */ -static struct nfs4_file_layout_dsaddr * -decode_and_add_device(struct inode *inode, struct pnfs_device *dev, gfp_t gfp_flags) -{ - struct nfs4_deviceid_node *d; - struct nfs4_file_layout_dsaddr *n, *new; - - new = decode_device(inode, dev, gfp_flags); - if (!new) { - printk(KERN_WARNING "NFS: %s: Could not decode or add device\n", - __func__); - return NULL; - } - - d = nfs4_insert_deviceid_node(&new->id_node); - n = container_of(d, struct nfs4_file_layout_dsaddr, id_node); - if (n != new) { - nfs4_fl_free_deviceid(new); - return n; - } - - return new; -} - -/* - * Retrieve the information for dev_id, add it to the list - * of available devices, and return it. - */ -struct nfs4_file_layout_dsaddr * -get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gfp_t gfp_flags) -{ - struct pnfs_device *pdev = NULL; - u32 max_resp_sz; - int max_pages; - struct page **pages = NULL; - struct nfs4_file_layout_dsaddr *dsaddr = NULL; - int rc, i; - struct nfs_server *server = NFS_SERVER(inode); - - /* - * Use the session max response size as the basis for setting - * GETDEVICEINFO's maxcount - */ - max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz; - max_pages = nfs_page_array_len(0, max_resp_sz); - dprintk("%s inode %p max_resp_sz %u max_pages %d\n", - __func__, inode, max_resp_sz, max_pages); - - pdev = kzalloc(sizeof(struct pnfs_device), gfp_flags); - if (pdev == NULL) - return NULL; - - pages = kzalloc(max_pages * sizeof(struct page *), gfp_flags); - if (pages == NULL) { - kfree(pdev); - return NULL; - } - for (i = 0; i < max_pages; i++) { - pages[i] = alloc_page(gfp_flags); - if (!pages[i]) - goto out_free; - } - - memcpy(&pdev->dev_id, dev_id, sizeof(*dev_id)); - pdev->layout_type = LAYOUT_NFSV4_1_FILES; - pdev->pages = pages; - pdev->pgbase = 0; - pdev->pglen = PAGE_SIZE * max_pages; - pdev->mincount = 0; - - rc = nfs4_proc_getdeviceinfo(server, pdev); - dprintk("%s getdevice info returns %d\n", __func__, rc); - if (rc) - goto out_free; - - /* - * Found new device, need to decode it and then add it to the - * list of known devices for this mountpoint. - */ - dsaddr = decode_and_add_device(inode, pdev, gfp_flags); -out_free: - for (i = 0; i < max_pages; i++) - __free_page(pages[i]); - kfree(pages); - kfree(pdev); - dprintk("<-- %s dsaddr %p\n", __func__, dsaddr); - return dsaddr; -} - -void -nfs4_fl_put_deviceid(struct nfs4_file_layout_dsaddr *dsaddr) -{ - nfs4_put_deviceid_node(&dsaddr->id_node); -} - -/* - * Want res = (offset - layout->pattern_offset)/ layout->stripe_unit - * Then: ((res + fsi) % dsaddr->stripe_count) - */ -u32 -nfs4_fl_calc_j_index(struct pnfs_layout_segment *lseg, loff_t offset) -{ - struct nfs4_filelayout_segment *flseg = FILELAYOUT_LSEG(lseg); - u64 tmp; - - tmp = offset - flseg->pattern_offset; - do_div(tmp, flseg->stripe_unit); - tmp += flseg->first_stripe_index; - return do_div(tmp, flseg->dsaddr->stripe_count); -} - -u32 -nfs4_fl_calc_ds_index(struct pnfs_layout_segment *lseg, u32 j) -{ - return FILELAYOUT_LSEG(lseg)->dsaddr->stripe_indices[j]; -} - -struct nfs_fh * -nfs4_fl_select_ds_fh(struct pnfs_layout_segment *lseg, u32 j) -{ - struct nfs4_filelayout_segment *flseg = FILELAYOUT_LSEG(lseg); - u32 i; - - if (flseg->stripe_type == STRIPE_SPARSE) { - if (flseg->num_fh == 1) - i = 0; - else if (flseg->num_fh == 0) - /* Use the MDS OPEN fh set in nfs_read_rpcsetup */ - return NULL; - else - i = nfs4_fl_calc_ds_index(lseg, j); - } else - i = j; - return flseg->fh_array[i]; -} - -static void -filelayout_mark_devid_negative(struct nfs4_file_layout_dsaddr *dsaddr, - int err, const char *ds_remotestr) -{ - u32 *p = (u32 *)&dsaddr->id_node.deviceid; - - printk(KERN_ERR "NFS: data server %s connection error %d." - " Deviceid [%x%x%x%x] marked out of use.\n", - ds_remotestr, err, p[0], p[1], p[2], p[3]); - - spin_lock(&nfs4_ds_cache_lock); - dsaddr->flags |= NFS4_DEVICE_ID_NEG_ENTRY; - spin_unlock(&nfs4_ds_cache_lock); -} - -struct nfs4_pnfs_ds * -nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx) -{ - struct nfs4_file_layout_dsaddr *dsaddr = FILELAYOUT_LSEG(lseg)->dsaddr; - struct nfs4_pnfs_ds *ds = dsaddr->ds_list[ds_idx]; - - if (ds == NULL) { - printk(KERN_ERR "NFS: %s: No data server for offset index %d\n", - __func__, ds_idx); - return NULL; - } - - if (!ds->ds_clp) { - struct nfs_server *s = NFS_SERVER(lseg->pls_layout->plh_inode); - int err; - - if (dsaddr->flags & NFS4_DEVICE_ID_NEG_ENTRY) { - /* Already tried to connect, don't try again */ - dprintk("%s Deviceid marked out of use\n", __func__); - return NULL; - } - err = nfs4_ds_connect(s, ds); - if (err) { - filelayout_mark_devid_negative(dsaddr, err, - ds->ds_remotestr); - return NULL; - } - } - return ds; -} diff --git a/ANDROID_3.4.5/fs/nfs/nfs4namespace.c b/ANDROID_3.4.5/fs/nfs/nfs4namespace.c deleted file mode 100644 index a7f3dedc..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfs4namespace.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * linux/fs/nfs/nfs4namespace.c - * - * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com> - * - Modified by David Howells <dhowells@redhat.com> - * - * NFSv4 namespace - */ - -#include <linux/dcache.h> -#include <linux/mount.h> -#include <linux/namei.h> -#include <linux/nfs_fs.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/sunrpc/clnt.h> -#include <linux/vfs.h> -#include <linux/inet.h> -#include "internal.h" -#include "nfs4_fs.h" -#include "dns_resolve.h" - -#define NFSDBG_FACILITY NFSDBG_VFS - -/* - * Convert the NFSv4 pathname components into a standard posix path. - * - * Note that the resulting string will be placed at the end of the buffer - */ -static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname, - char *buffer, ssize_t buflen) -{ - char *end = buffer + buflen; - int n; - - *--end = '\0'; - buflen--; - - n = pathname->ncomponents; - while (--n >= 0) { - const struct nfs4_string *component = &pathname->components[n]; - buflen -= component->len + 1; - if (buflen < 0) - goto Elong; - end -= component->len; - memcpy(end, component->data, component->len); - *--end = '/'; - } - return end; -Elong: - return ERR_PTR(-ENAMETOOLONG); -} - -/* - * return the path component of "<server>:<path>" - * nfspath - the "<server>:<path>" string - * end - one past the last char that could contain "<server>:" - * returns NULL on failure - */ -static char *nfs_path_component(const char *nfspath, const char *end) -{ - char *p; - - if (*nfspath == '[') { - /* parse [] escaped IPv6 addrs */ - p = strchr(nfspath, ']'); - if (p != NULL && ++p < end && *p == ':') - return p + 1; - } else { - /* otherwise split on first colon */ - p = strchr(nfspath, ':'); - if (p != NULL && p < end) - return p + 1; - } - return NULL; -} - -/* - * Determine the mount path as a string - */ -static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen) -{ - char *limit; - char *path = nfs_path(&limit, dentry, buffer, buflen); - if (!IS_ERR(path)) { - char *path_component = nfs_path_component(path, limit); - if (path_component) - return path_component; - } - return path; -} - -/* - * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we - * believe to be the server path to this dentry - */ -static int nfs4_validate_fspath(struct dentry *dentry, - const struct nfs4_fs_locations *locations, - char *page, char *page2) -{ - const char *path, *fs_path; - - path = nfs4_path(dentry, page, PAGE_SIZE); - if (IS_ERR(path)) - return PTR_ERR(path); - - fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE); - if (IS_ERR(fs_path)) - return PTR_ERR(fs_path); - - if (strncmp(path, fs_path, strlen(fs_path)) != 0) { - dprintk("%s: path %s does not begin with fsroot %s\n", - __func__, path, fs_path); - return -ENOENT; - } - - return 0; -} - -static size_t nfs_parse_server_name(char *string, size_t len, - struct sockaddr *sa, size_t salen, struct nfs_server *server) -{ - struct net *net = rpc_net_ns(server->client); - ssize_t ret; - - ret = rpc_pton(net, string, len, sa, salen); - if (ret == 0) { - ret = nfs_dns_resolve_name(net, string, len, sa, salen); - if (ret < 0) - ret = 0; - } - return ret; -} - -static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name) -{ - struct page *page; - struct nfs4_secinfo_flavors *flavors; - rpc_authflavor_t flavor; - int err; - - page = alloc_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - flavors = page_address(page); - - err = nfs4_proc_secinfo(inode, name, flavors); - if (err < 0) { - flavor = err; - goto out; - } - - flavor = nfs_find_best_sec(flavors); - -out: - put_page(page); - return flavor; -} - -/* - * Please call rpc_shutdown_client() when you are done with this client. - */ -struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *clnt, struct inode *inode, - struct qstr *name) -{ - struct rpc_clnt *clone; - struct rpc_auth *auth; - rpc_authflavor_t flavor; - - flavor = nfs4_negotiate_security(inode, name); - if (flavor < 0) - return ERR_PTR(flavor); - - clone = rpc_clone_client(clnt); - if (IS_ERR(clone)) - return clone; - - auth = rpcauth_create(flavor, clone); - if (!auth) { - rpc_shutdown_client(clone); - clone = ERR_PTR(-EIO); - } - - return clone; -} - -static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, - char *page, char *page2, - const struct nfs4_fs_location *location) -{ - const size_t addr_bufsize = sizeof(struct sockaddr_storage); - struct vfsmount *mnt = ERR_PTR(-ENOENT); - char *mnt_path; - unsigned int maxbuflen; - unsigned int s; - - mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE); - if (IS_ERR(mnt_path)) - return ERR_CAST(mnt_path); - mountdata->mnt_path = mnt_path; - maxbuflen = mnt_path - 1 - page2; - - mountdata->addr = kmalloc(addr_bufsize, GFP_KERNEL); - if (mountdata->addr == NULL) - return ERR_PTR(-ENOMEM); - - for (s = 0; s < location->nservers; s++) { - const struct nfs4_string *buf = &location->servers[s]; - - if (buf->len <= 0 || buf->len >= maxbuflen) - continue; - - if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len)) - continue; - - mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len, - mountdata->addr, addr_bufsize, - NFS_SB(mountdata->sb)); - if (mountdata->addrlen == 0) - continue; - - rpc_set_port(mountdata->addr, NFS_PORT); - - memcpy(page2, buf->data, buf->len); - page2[buf->len] = '\0'; - mountdata->hostname = page2; - - snprintf(page, PAGE_SIZE, "%s:%s", - mountdata->hostname, - mountdata->mnt_path); - - mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, page, mountdata); - if (!IS_ERR(mnt)) - break; - } - kfree(mountdata->addr); - return mnt; -} - -/** - * nfs_follow_referral - set up mountpoint when hitting a referral on moved error - * @dentry - parent directory - * @locations - array of NFSv4 server location information - * - */ -static struct vfsmount *nfs_follow_referral(struct dentry *dentry, - const struct nfs4_fs_locations *locations) -{ - struct vfsmount *mnt = ERR_PTR(-ENOENT); - struct nfs_clone_mount mountdata = { - .sb = dentry->d_sb, - .dentry = dentry, - .authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor, - }; - char *page = NULL, *page2 = NULL; - int loc, error; - - if (locations == NULL || locations->nlocations <= 0) - goto out; - - dprintk("%s: referral at %s/%s\n", __func__, - dentry->d_parent->d_name.name, dentry->d_name.name); - - page = (char *) __get_free_page(GFP_USER); - if (!page) - goto out; - - page2 = (char *) __get_free_page(GFP_USER); - if (!page2) - goto out; - - /* Ensure fs path is a prefix of current dentry path */ - error = nfs4_validate_fspath(dentry, locations, page, page2); - if (error < 0) { - mnt = ERR_PTR(error); - goto out; - } - - for (loc = 0; loc < locations->nlocations; loc++) { - const struct nfs4_fs_location *location = &locations->locations[loc]; - - if (location == NULL || location->nservers <= 0 || - location->rootpath.ncomponents == 0) - continue; - - mnt = try_location(&mountdata, page, page2, location); - if (!IS_ERR(mnt)) - break; - } - -out: - free_page((unsigned long) page); - free_page((unsigned long) page2); - dprintk("%s: done\n", __func__); - return mnt; -} - -/* - * nfs_do_refmount - handle crossing a referral on server - * @dentry - dentry of referral - * - */ -struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry) -{ - struct vfsmount *mnt = ERR_PTR(-ENOMEM); - struct dentry *parent; - struct nfs4_fs_locations *fs_locations = NULL; - struct page *page; - int err; - - /* BUG_ON(IS_ROOT(dentry)); */ - dprintk("%s: enter\n", __func__); - - page = alloc_page(GFP_KERNEL); - if (page == NULL) - goto out; - - fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); - if (fs_locations == NULL) - goto out_free; - - /* Get locations */ - mnt = ERR_PTR(-ENOENT); - - parent = dget_parent(dentry); - dprintk("%s: getting locations for %s/%s\n", - __func__, parent->d_name.name, dentry->d_name.name); - - err = nfs4_proc_fs_locations(client, parent->d_inode, &dentry->d_name, fs_locations, page); - dput(parent); - if (err != 0 || - fs_locations->nlocations <= 0 || - fs_locations->fs_path.ncomponents <= 0) - goto out_free; - - mnt = nfs_follow_referral(dentry, fs_locations); -out_free: - __free_page(page); - kfree(fs_locations); -out: - dprintk("%s: done\n", __func__); - return mnt; -} diff --git a/ANDROID_3.4.5/fs/nfs/nfs4proc.c b/ANDROID_3.4.5/fs/nfs/nfs4proc.c deleted file mode 100644 index d60c2d87..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfs4proc.c +++ /dev/null @@ -1,6625 +0,0 @@ -/* - * fs/nfs/nfs4proc.c - * - * Client-side procedure declarations for NFSv4. - * - * Copyright (c) 2002 The Regents of the University of Michigan. - * All rights reserved. - * - * Kendrick Smith <kmsmith@umich.edu> - * Andy Adamson <andros@umich.edu> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <linux/mm.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/ratelimit.h> -#include <linux/printk.h> -#include <linux/slab.h> -#include <linux/sunrpc/clnt.h> -#include <linux/sunrpc/gss_api.h> -#include <linux/nfs.h> -#include <linux/nfs4.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_page.h> -#include <linux/nfs_mount.h> -#include <linux/namei.h> -#include <linux/mount.h> -#include <linux/module.h> -#include <linux/nfs_idmap.h> -#include <linux/sunrpc/bc_xprt.h> -#include <linux/xattr.h> -#include <linux/utsname.h> -#include <linux/freezer.h> - -#include "nfs4_fs.h" -#include "delegation.h" -#include "internal.h" -#include "iostat.h" -#include "callback.h" -#include "pnfs.h" - -#define NFSDBG_FACILITY NFSDBG_PROC - -#define NFS4_POLL_RETRY_MIN (HZ/10) -#define NFS4_POLL_RETRY_MAX (15*HZ) - -#define NFS4_MAX_LOOP_ON_RECOVER (10) - -static unsigned short max_session_slots = NFS4_DEF_SLOT_TABLE_SIZE; - -struct nfs4_opendata; -static int _nfs4_proc_open(struct nfs4_opendata *data); -static int _nfs4_recover_proc_open(struct nfs4_opendata *data); -static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); -static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *); -static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr); -static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr); -static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, - struct nfs_fattr *fattr, struct iattr *sattr, - struct nfs4_state *state); -#ifdef CONFIG_NFS_V4_1 -static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *); -static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *); -#endif -/* Prevent leaks of NFSv4 errors into userland */ -static int nfs4_map_errors(int err) -{ - if (err >= -1000) - return err; - switch (err) { - case -NFS4ERR_RESOURCE: - return -EREMOTEIO; - case -NFS4ERR_WRONGSEC: - return -EPERM; - case -NFS4ERR_BADOWNER: - case -NFS4ERR_BADNAME: - return -EINVAL; - case -NFS4ERR_SHARE_DENIED: - return -EACCES; - default: - dprintk("%s could not handle NFSv4 error %d\n", - __func__, -err); - break; - } - return -EIO; -} - -/* - * This is our standard bitmap for GETATTR requests. - */ -const u32 nfs4_fattr_bitmap[2] = { - FATTR4_WORD0_TYPE - | FATTR4_WORD0_CHANGE - | FATTR4_WORD0_SIZE - | FATTR4_WORD0_FSID - | FATTR4_WORD0_FILEID, - FATTR4_WORD1_MODE - | FATTR4_WORD1_NUMLINKS - | FATTR4_WORD1_OWNER - | FATTR4_WORD1_OWNER_GROUP - | FATTR4_WORD1_RAWDEV - | FATTR4_WORD1_SPACE_USED - | FATTR4_WORD1_TIME_ACCESS - | FATTR4_WORD1_TIME_METADATA - | FATTR4_WORD1_TIME_MODIFY -}; - -const u32 nfs4_statfs_bitmap[2] = { - FATTR4_WORD0_FILES_AVAIL - | FATTR4_WORD0_FILES_FREE - | FATTR4_WORD0_FILES_TOTAL, - FATTR4_WORD1_SPACE_AVAIL - | FATTR4_WORD1_SPACE_FREE - | FATTR4_WORD1_SPACE_TOTAL -}; - -const u32 nfs4_pathconf_bitmap[2] = { - FATTR4_WORD0_MAXLINK - | FATTR4_WORD0_MAXNAME, - 0 -}; - -const u32 nfs4_fsinfo_bitmap[3] = { FATTR4_WORD0_MAXFILESIZE - | FATTR4_WORD0_MAXREAD - | FATTR4_WORD0_MAXWRITE - | FATTR4_WORD0_LEASE_TIME, - FATTR4_WORD1_TIME_DELTA - | FATTR4_WORD1_FS_LAYOUT_TYPES, - FATTR4_WORD2_LAYOUT_BLKSIZE -}; - -const u32 nfs4_fs_locations_bitmap[2] = { - FATTR4_WORD0_TYPE - | FATTR4_WORD0_CHANGE - | FATTR4_WORD0_SIZE - | FATTR4_WORD0_FSID - | FATTR4_WORD0_FILEID - | FATTR4_WORD0_FS_LOCATIONS, - FATTR4_WORD1_MODE - | FATTR4_WORD1_NUMLINKS - | FATTR4_WORD1_OWNER - | FATTR4_WORD1_OWNER_GROUP - | FATTR4_WORD1_RAWDEV - | FATTR4_WORD1_SPACE_USED - | FATTR4_WORD1_TIME_ACCESS - | FATTR4_WORD1_TIME_METADATA - | FATTR4_WORD1_TIME_MODIFY - | FATTR4_WORD1_MOUNTED_ON_FILEID -}; - -static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dentry, - struct nfs4_readdir_arg *readdir) -{ - __be32 *start, *p; - - BUG_ON(readdir->count < 80); - if (cookie > 2) { - readdir->cookie = cookie; - memcpy(&readdir->verifier, verifier, sizeof(readdir->verifier)); - return; - } - - readdir->cookie = 0; - memset(&readdir->verifier, 0, sizeof(readdir->verifier)); - if (cookie == 2) - return; - - /* - * NFSv4 servers do not return entries for '.' and '..' - * Therefore, we fake these entries here. We let '.' - * have cookie 0 and '..' have cookie 1. Note that - * when talking to the server, we always send cookie 0 - * instead of 1 or 2. - */ - start = p = kmap_atomic(*readdir->pages); - - if (cookie == 0) { - *p++ = xdr_one; /* next */ - *p++ = xdr_zero; /* cookie, first word */ - *p++ = xdr_one; /* cookie, second word */ - *p++ = xdr_one; /* entry len */ - memcpy(p, ".\0\0\0", 4); /* entry */ - p++; - *p++ = xdr_one; /* bitmap length */ - *p++ = htonl(FATTR4_WORD0_FILEID); /* bitmap */ - *p++ = htonl(8); /* attribute buffer length */ - p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_inode)); - } - - *p++ = xdr_one; /* next */ - *p++ = xdr_zero; /* cookie, first word */ - *p++ = xdr_two; /* cookie, second word */ - *p++ = xdr_two; /* entry len */ - memcpy(p, "..\0\0", 4); /* entry */ - p++; - *p++ = xdr_one; /* bitmap length */ - *p++ = htonl(FATTR4_WORD0_FILEID); /* bitmap */ - *p++ = htonl(8); /* attribute buffer length */ - p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_parent->d_inode)); - - readdir->pgbase = (char *)p - (char *)start; - readdir->count -= readdir->pgbase; - kunmap_atomic(start); -} - -static int nfs4_wait_clnt_recover(struct nfs_client *clp) -{ - int res; - - might_sleep(); - - res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING, - nfs_wait_bit_killable, TASK_KILLABLE); - return res; -} - -static int nfs4_delay(struct rpc_clnt *clnt, long *timeout) -{ - int res = 0; - - might_sleep(); - - if (*timeout <= 0) - *timeout = NFS4_POLL_RETRY_MIN; - if (*timeout > NFS4_POLL_RETRY_MAX) - *timeout = NFS4_POLL_RETRY_MAX; - freezable_schedule_timeout_killable(*timeout); - if (fatal_signal_pending(current)) - res = -ERESTARTSYS; - *timeout <<= 1; - return res; -} - -/* This is the error handling routine for processes that are allowed - * to sleep. - */ -static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_exception *exception) -{ - struct nfs_client *clp = server->nfs_client; - struct nfs4_state *state = exception->state; - struct inode *inode = exception->inode; - int ret = errorcode; - - exception->retry = 0; - switch(errorcode) { - case 0: - return 0; - case -NFS4ERR_OPENMODE: - if (inode && nfs_have_delegation(inode, FMODE_READ)) { - nfs_inode_return_delegation(inode); - exception->retry = 1; - return 0; - } - if (state == NULL) - break; - nfs4_schedule_stateid_recovery(server, state); - goto wait_on_recovery; - case -NFS4ERR_DELEG_REVOKED: - case -NFS4ERR_ADMIN_REVOKED: - case -NFS4ERR_BAD_STATEID: - if (state == NULL) - break; - nfs_remove_bad_delegation(state->inode); - nfs4_schedule_stateid_recovery(server, state); - goto wait_on_recovery; - case -NFS4ERR_EXPIRED: - if (state != NULL) - nfs4_schedule_stateid_recovery(server, state); - case -NFS4ERR_STALE_STATEID: - case -NFS4ERR_STALE_CLIENTID: - nfs4_schedule_lease_recovery(clp); - goto wait_on_recovery; -#if defined(CONFIG_NFS_V4_1) - case -NFS4ERR_BADSESSION: - case -NFS4ERR_BADSLOT: - case -NFS4ERR_BAD_HIGH_SLOT: - case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: - case -NFS4ERR_DEADSESSION: - case -NFS4ERR_SEQ_FALSE_RETRY: - case -NFS4ERR_SEQ_MISORDERED: - dprintk("%s ERROR: %d Reset session\n", __func__, - errorcode); - nfs4_schedule_session_recovery(clp->cl_session); - exception->retry = 1; - break; -#endif /* defined(CONFIG_NFS_V4_1) */ - case -NFS4ERR_FILE_OPEN: - if (exception->timeout > HZ) { - /* We have retried a decent amount, time to - * fail - */ - ret = -EBUSY; - break; - } - case -NFS4ERR_GRACE: - case -NFS4ERR_DELAY: - case -EKEYEXPIRED: - ret = nfs4_delay(server->client, &exception->timeout); - if (ret != 0) - break; - case -NFS4ERR_RETRY_UNCACHED_REP: - case -NFS4ERR_OLD_STATEID: - exception->retry = 1; - break; - case -NFS4ERR_BADOWNER: - /* The following works around a Linux server bug! */ - case -NFS4ERR_BADNAME: - if (server->caps & NFS_CAP_UIDGID_NOMAP) { - server->caps &= ~NFS_CAP_UIDGID_NOMAP; - exception->retry = 1; - printk(KERN_WARNING "NFS: v4 server %s " - "does not accept raw " - "uid/gids. " - "Reenabling the idmapper.\n", - server->nfs_client->cl_hostname); - } - } - /* We failed to handle the error */ - return nfs4_map_errors(ret); -wait_on_recovery: - ret = nfs4_wait_clnt_recover(clp); - if (ret == 0) - exception->retry = 1; - return ret; -} - - -static void do_renew_lease(struct nfs_client *clp, unsigned long timestamp) -{ - spin_lock(&clp->cl_lock); - if (time_before(clp->cl_last_renewal,timestamp)) - clp->cl_last_renewal = timestamp; - spin_unlock(&clp->cl_lock); -} - -static void renew_lease(const struct nfs_server *server, unsigned long timestamp) -{ - do_renew_lease(server->nfs_client, timestamp); -} - -#if defined(CONFIG_NFS_V4_1) - -/* - * nfs4_free_slot - free a slot and efficiently update slot table. - * - * freeing a slot is trivially done by clearing its respective bit - * in the bitmap. - * If the freed slotid equals highest_used_slotid we want to update it - * so that the server would be able to size down the slot table if needed, - * otherwise we know that the highest_used_slotid is still in use. - * When updating highest_used_slotid there may be "holes" in the bitmap - * so we need to scan down from highest_used_slotid to 0 looking for the now - * highest slotid in use. - * If none found, highest_used_slotid is set to NFS4_NO_SLOT. - * - * Must be called while holding tbl->slot_tbl_lock - */ -static void -nfs4_free_slot(struct nfs4_slot_table *tbl, u32 slotid) -{ - BUG_ON(slotid >= NFS4_MAX_SLOT_TABLE); - /* clear used bit in bitmap */ - __clear_bit(slotid, tbl->used_slots); - - /* update highest_used_slotid when it is freed */ - if (slotid == tbl->highest_used_slotid) { - slotid = find_last_bit(tbl->used_slots, tbl->max_slots); - if (slotid < tbl->max_slots) - tbl->highest_used_slotid = slotid; - else - tbl->highest_used_slotid = NFS4_NO_SLOT; - } - dprintk("%s: slotid %u highest_used_slotid %d\n", __func__, - slotid, tbl->highest_used_slotid); -} - -bool nfs4_set_task_privileged(struct rpc_task *task, void *dummy) -{ - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - return true; -} - -/* - * Signal state manager thread if session fore channel is drained - */ -static void nfs4_check_drain_fc_complete(struct nfs4_session *ses) -{ - if (!test_bit(NFS4_SESSION_DRAINING, &ses->session_state)) { - rpc_wake_up_first(&ses->fc_slot_table.slot_tbl_waitq, - nfs4_set_task_privileged, NULL); - return; - } - - if (ses->fc_slot_table.highest_used_slotid != NFS4_NO_SLOT) - return; - - dprintk("%s COMPLETE: Session Fore Channel Drained\n", __func__); - complete(&ses->fc_slot_table.complete); -} - -/* - * Signal state manager thread if session back channel is drained - */ -void nfs4_check_drain_bc_complete(struct nfs4_session *ses) -{ - if (!test_bit(NFS4_SESSION_DRAINING, &ses->session_state) || - ses->bc_slot_table.highest_used_slotid != NFS4_NO_SLOT) - return; - dprintk("%s COMPLETE: Session Back Channel Drained\n", __func__); - complete(&ses->bc_slot_table.complete); -} - -static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) -{ - struct nfs4_slot_table *tbl; - - tbl = &res->sr_session->fc_slot_table; - if (!res->sr_slot) { - /* just wake up the next guy waiting since - * we may have not consumed a slot after all */ - dprintk("%s: No slot\n", __func__); - return; - } - - spin_lock(&tbl->slot_tbl_lock); - nfs4_free_slot(tbl, res->sr_slot - tbl->slots); - nfs4_check_drain_fc_complete(res->sr_session); - spin_unlock(&tbl->slot_tbl_lock); - res->sr_slot = NULL; -} - -static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) -{ - unsigned long timestamp; - struct nfs_client *clp; - - /* - * sr_status remains 1 if an RPC level error occurred. The server - * may or may not have processed the sequence operation.. - * Proceed as if the server received and processed the sequence - * operation. - */ - if (res->sr_status == 1) - res->sr_status = NFS_OK; - - /* don't increment the sequence number if the task wasn't sent */ - if (!RPC_WAS_SENT(task)) - goto out; - - /* Check the SEQUENCE operation status */ - switch (res->sr_status) { - case 0: - /* Update the slot's sequence and clientid lease timer */ - ++res->sr_slot->seq_nr; - timestamp = res->sr_renewal_time; - clp = res->sr_session->clp; - do_renew_lease(clp, timestamp); - /* Check sequence flags */ - if (res->sr_status_flags != 0) - nfs4_schedule_lease_recovery(clp); - break; - case -NFS4ERR_DELAY: - /* The server detected a resend of the RPC call and - * returned NFS4ERR_DELAY as per Section 2.10.6.2 - * of RFC5661. - */ - dprintk("%s: slot=%td seq=%d: Operation in progress\n", - __func__, - res->sr_slot - res->sr_session->fc_slot_table.slots, - res->sr_slot->seq_nr); - goto out_retry; - default: - /* Just update the slot sequence no. */ - ++res->sr_slot->seq_nr; - } -out: - /* The session may be reset by one of the error handlers. */ - dprintk("%s: Error %d free the slot \n", __func__, res->sr_status); - nfs41_sequence_free_slot(res); - return 1; -out_retry: - if (!rpc_restart_call(task)) - goto out; - rpc_delay(task, NFS4_POLL_RETRY_MAX); - return 0; -} - -static int nfs4_sequence_done(struct rpc_task *task, - struct nfs4_sequence_res *res) -{ - if (res->sr_session == NULL) - return 1; - return nfs41_sequence_done(task, res); -} - -/* - * nfs4_find_slot - efficiently look for a free slot - * - * nfs4_find_slot looks for an unset bit in the used_slots bitmap. - * If found, we mark the slot as used, update the highest_used_slotid, - * and respectively set up the sequence operation args. - * The slot number is returned if found, or NFS4_NO_SLOT otherwise. - * - * Note: must be called with under the slot_tbl_lock. - */ -static u32 -nfs4_find_slot(struct nfs4_slot_table *tbl) -{ - u32 slotid; - u32 ret_id = NFS4_NO_SLOT; - - dprintk("--> %s used_slots=%04lx highest_used=%u max_slots=%u\n", - __func__, tbl->used_slots[0], tbl->highest_used_slotid, - tbl->max_slots); - slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slots); - if (slotid >= tbl->max_slots) - goto out; - __set_bit(slotid, tbl->used_slots); - if (slotid > tbl->highest_used_slotid || - tbl->highest_used_slotid == NFS4_NO_SLOT) - tbl->highest_used_slotid = slotid; - ret_id = slotid; -out: - dprintk("<-- %s used_slots=%04lx highest_used=%d slotid=%d \n", - __func__, tbl->used_slots[0], tbl->highest_used_slotid, ret_id); - return ret_id; -} - -static void nfs41_init_sequence(struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, int cache_reply) -{ - args->sa_session = NULL; - args->sa_cache_this = 0; - if (cache_reply) - args->sa_cache_this = 1; - res->sr_session = NULL; - res->sr_slot = NULL; -} - -int nfs41_setup_sequence(struct nfs4_session *session, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - struct rpc_task *task) -{ - struct nfs4_slot *slot; - struct nfs4_slot_table *tbl; - u32 slotid; - - dprintk("--> %s\n", __func__); - /* slot already allocated? */ - if (res->sr_slot != NULL) - return 0; - - tbl = &session->fc_slot_table; - - spin_lock(&tbl->slot_tbl_lock); - if (test_bit(NFS4_SESSION_DRAINING, &session->session_state) && - !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) { - /* The state manager will wait until the slot table is empty */ - rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); - spin_unlock(&tbl->slot_tbl_lock); - dprintk("%s session is draining\n", __func__); - return -EAGAIN; - } - - if (!rpc_queue_empty(&tbl->slot_tbl_waitq) && - !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) { - rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); - spin_unlock(&tbl->slot_tbl_lock); - dprintk("%s enforce FIFO order\n", __func__); - return -EAGAIN; - } - - slotid = nfs4_find_slot(tbl); - if (slotid == NFS4_NO_SLOT) { - rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); - spin_unlock(&tbl->slot_tbl_lock); - dprintk("<-- %s: no free slots\n", __func__); - return -EAGAIN; - } - spin_unlock(&tbl->slot_tbl_lock); - - rpc_task_set_priority(task, RPC_PRIORITY_NORMAL); - slot = tbl->slots + slotid; - args->sa_session = session; - args->sa_slotid = slotid; - - dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr); - - res->sr_session = session; - res->sr_slot = slot; - res->sr_renewal_time = jiffies; - res->sr_status_flags = 0; - /* - * sr_status is only set in decode_sequence, and so will remain - * set to 1 if an rpc level failure occurs. - */ - res->sr_status = 1; - return 0; -} -EXPORT_SYMBOL_GPL(nfs41_setup_sequence); - -int nfs4_setup_sequence(const struct nfs_server *server, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - struct rpc_task *task) -{ - struct nfs4_session *session = nfs4_get_session(server); - int ret = 0; - - if (session == NULL) - goto out; - - dprintk("--> %s clp %p session %p sr_slot %td\n", - __func__, session->clp, session, res->sr_slot ? - res->sr_slot - session->fc_slot_table.slots : -1); - - ret = nfs41_setup_sequence(session, args, res, task); -out: - dprintk("<-- %s status=%d\n", __func__, ret); - return ret; -} - -struct nfs41_call_sync_data { - const struct nfs_server *seq_server; - struct nfs4_sequence_args *seq_args; - struct nfs4_sequence_res *seq_res; -}; - -static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata) -{ - struct nfs41_call_sync_data *data = calldata; - - dprintk("--> %s data->seq_server %p\n", __func__, data->seq_server); - - if (nfs4_setup_sequence(data->seq_server, data->seq_args, - data->seq_res, task)) - return; - rpc_call_start(task); -} - -static void nfs41_call_priv_sync_prepare(struct rpc_task *task, void *calldata) -{ - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - nfs41_call_sync_prepare(task, calldata); -} - -static void nfs41_call_sync_done(struct rpc_task *task, void *calldata) -{ - struct nfs41_call_sync_data *data = calldata; - - nfs41_sequence_done(task, data->seq_res); -} - -static const struct rpc_call_ops nfs41_call_sync_ops = { - .rpc_call_prepare = nfs41_call_sync_prepare, - .rpc_call_done = nfs41_call_sync_done, -}; - -static const struct rpc_call_ops nfs41_call_priv_sync_ops = { - .rpc_call_prepare = nfs41_call_priv_sync_prepare, - .rpc_call_done = nfs41_call_sync_done, -}; - -static int nfs4_call_sync_sequence(struct rpc_clnt *clnt, - struct nfs_server *server, - struct rpc_message *msg, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int privileged) -{ - int ret; - struct rpc_task *task; - struct nfs41_call_sync_data data = { - .seq_server = server, - .seq_args = args, - .seq_res = res, - }; - struct rpc_task_setup task_setup = { - .rpc_client = clnt, - .rpc_message = msg, - .callback_ops = &nfs41_call_sync_ops, - .callback_data = &data - }; - - if (privileged) - task_setup.callback_ops = &nfs41_call_priv_sync_ops; - task = rpc_run_task(&task_setup); - if (IS_ERR(task)) - ret = PTR_ERR(task); - else { - ret = task->tk_status; - rpc_put_task(task); - } - return ret; -} - -int _nfs4_call_sync_session(struct rpc_clnt *clnt, - struct nfs_server *server, - struct rpc_message *msg, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int cache_reply) -{ - nfs41_init_sequence(args, res, cache_reply); - return nfs4_call_sync_sequence(clnt, server, msg, args, res, 0); -} - -#else -static inline -void nfs41_init_sequence(struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, int cache_reply) -{ -} - -static int nfs4_sequence_done(struct rpc_task *task, - struct nfs4_sequence_res *res) -{ - return 1; -} -#endif /* CONFIG_NFS_V4_1 */ - -int _nfs4_call_sync(struct rpc_clnt *clnt, - struct nfs_server *server, - struct rpc_message *msg, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int cache_reply) -{ - nfs41_init_sequence(args, res, cache_reply); - return rpc_call_sync(clnt, msg, 0); -} - -static inline -int nfs4_call_sync(struct rpc_clnt *clnt, - struct nfs_server *server, - struct rpc_message *msg, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int cache_reply) -{ - return server->nfs_client->cl_mvops->call_sync(clnt, server, msg, - args, res, cache_reply); -} - -static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo) -{ - struct nfs_inode *nfsi = NFS_I(dir); - - spin_lock(&dir->i_lock); - nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA; - if (!cinfo->atomic || cinfo->before != dir->i_version) - nfs_force_lookup_revalidate(dir); - dir->i_version = cinfo->after; - spin_unlock(&dir->i_lock); -} - -struct nfs4_opendata { - struct kref kref; - struct nfs_openargs o_arg; - struct nfs_openres o_res; - struct nfs_open_confirmargs c_arg; - struct nfs_open_confirmres c_res; - struct nfs4_string owner_name; - struct nfs4_string group_name; - struct nfs_fattr f_attr; - struct nfs_fattr dir_attr; - struct dentry *dir; - struct dentry *dentry; - struct nfs4_state_owner *owner; - struct nfs4_state *state; - struct iattr attrs; - unsigned long timestamp; - unsigned int rpc_done : 1; - int rpc_status; - int cancelled; -}; - - -static void nfs4_init_opendata_res(struct nfs4_opendata *p) -{ - p->o_res.f_attr = &p->f_attr; - p->o_res.dir_attr = &p->dir_attr; - p->o_res.seqid = p->o_arg.seqid; - p->c_res.seqid = p->c_arg.seqid; - p->o_res.server = p->o_arg.server; - nfs_fattr_init(&p->f_attr); - nfs_fattr_init(&p->dir_attr); - nfs_fattr_init_names(&p->f_attr, &p->owner_name, &p->group_name); -} - -static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, - struct nfs4_state_owner *sp, fmode_t fmode, int flags, - const struct iattr *attrs, - gfp_t gfp_mask) -{ - struct dentry *parent = dget_parent(dentry); - struct inode *dir = parent->d_inode; - struct nfs_server *server = NFS_SERVER(dir); - struct nfs4_opendata *p; - - p = kzalloc(sizeof(*p), gfp_mask); - if (p == NULL) - goto err; - p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid, gfp_mask); - if (p->o_arg.seqid == NULL) - goto err_free; - nfs_sb_active(dentry->d_sb); - p->dentry = dget(dentry); - p->dir = parent; - p->owner = sp; - atomic_inc(&sp->so_count); - p->o_arg.fh = NFS_FH(dir); - p->o_arg.open_flags = flags; - p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE); - p->o_arg.clientid = server->nfs_client->cl_clientid; - p->o_arg.id.create_time = ktime_to_ns(sp->so_seqid.create_time); - p->o_arg.id.uniquifier = sp->so_seqid.owner_id; - p->o_arg.name = &dentry->d_name; - p->o_arg.server = server; - p->o_arg.bitmask = server->attr_bitmask; - p->o_arg.dir_bitmask = server->cache_consistency_bitmask; - p->o_arg.claim = NFS4_OPEN_CLAIM_NULL; - if (attrs != NULL && attrs->ia_valid != 0) { - __be32 verf[2]; - - p->o_arg.u.attrs = &p->attrs; - memcpy(&p->attrs, attrs, sizeof(p->attrs)); - - verf[0] = jiffies; - verf[1] = current->pid; - memcpy(p->o_arg.u.verifier.data, verf, - sizeof(p->o_arg.u.verifier.data)); - } - p->c_arg.fh = &p->o_res.fh; - p->c_arg.stateid = &p->o_res.stateid; - p->c_arg.seqid = p->o_arg.seqid; - nfs4_init_opendata_res(p); - kref_init(&p->kref); - return p; -err_free: - kfree(p); -err: - dput(parent); - return NULL; -} - -static void nfs4_opendata_free(struct kref *kref) -{ - struct nfs4_opendata *p = container_of(kref, - struct nfs4_opendata, kref); - struct super_block *sb = p->dentry->d_sb; - - nfs_free_seqid(p->o_arg.seqid); - if (p->state != NULL) - nfs4_put_open_state(p->state); - nfs4_put_state_owner(p->owner); - dput(p->dir); - dput(p->dentry); - nfs_sb_deactive(sb); - nfs_fattr_free_names(&p->f_attr); - kfree(p); -} - -static void nfs4_opendata_put(struct nfs4_opendata *p) -{ - if (p != NULL) - kref_put(&p->kref, nfs4_opendata_free); -} - -static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task) -{ - int ret; - - ret = rpc_wait_for_completion_task(task); - return ret; -} - -static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode) -{ - int ret = 0; - - if (open_mode & (O_EXCL|O_TRUNC)) - goto out; - switch (mode & (FMODE_READ|FMODE_WRITE)) { - case FMODE_READ: - ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0 - && state->n_rdonly != 0; - break; - case FMODE_WRITE: - ret |= test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0 - && state->n_wronly != 0; - break; - case FMODE_READ|FMODE_WRITE: - ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0 - && state->n_rdwr != 0; - } -out: - return ret; -} - -static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode) -{ - if (delegation == NULL) - return 0; - if ((delegation->type & fmode) != fmode) - return 0; - if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags)) - return 0; - nfs_mark_delegation_referenced(delegation); - return 1; -} - -static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode) -{ - switch (fmode) { - case FMODE_WRITE: - state->n_wronly++; - break; - case FMODE_READ: - state->n_rdonly++; - break; - case FMODE_READ|FMODE_WRITE: - state->n_rdwr++; - } - nfs4_state_set_mode_locked(state, state->state | fmode); -} - -static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode) -{ - if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0) - nfs4_stateid_copy(&state->stateid, stateid); - nfs4_stateid_copy(&state->open_stateid, stateid); - switch (fmode) { - case FMODE_READ: - set_bit(NFS_O_RDONLY_STATE, &state->flags); - break; - case FMODE_WRITE: - set_bit(NFS_O_WRONLY_STATE, &state->flags); - break; - case FMODE_READ|FMODE_WRITE: - set_bit(NFS_O_RDWR_STATE, &state->flags); - } -} - -static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode) -{ - write_seqlock(&state->seqlock); - nfs_set_open_stateid_locked(state, stateid, fmode); - write_sequnlock(&state->seqlock); -} - -static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode) -{ - /* - * Protect the call to nfs4_state_set_mode_locked and - * serialise the stateid update - */ - write_seqlock(&state->seqlock); - if (deleg_stateid != NULL) { - nfs4_stateid_copy(&state->stateid, deleg_stateid); - set_bit(NFS_DELEGATED_STATE, &state->flags); - } - if (open_stateid != NULL) - nfs_set_open_stateid_locked(state, open_stateid, fmode); - write_sequnlock(&state->seqlock); - spin_lock(&state->owner->so_lock); - update_open_stateflags(state, fmode); - spin_unlock(&state->owner->so_lock); -} - -static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, fmode_t fmode) -{ - struct nfs_inode *nfsi = NFS_I(state->inode); - struct nfs_delegation *deleg_cur; - int ret = 0; - - fmode &= (FMODE_READ|FMODE_WRITE); - - rcu_read_lock(); - deleg_cur = rcu_dereference(nfsi->delegation); - if (deleg_cur == NULL) - goto no_delegation; - - spin_lock(&deleg_cur->lock); - if (nfsi->delegation != deleg_cur || - (deleg_cur->type & fmode) != fmode) - goto no_delegation_unlock; - - if (delegation == NULL) - delegation = &deleg_cur->stateid; - else if (!nfs4_stateid_match(&deleg_cur->stateid, delegation)) - goto no_delegation_unlock; - - nfs_mark_delegation_referenced(deleg_cur); - __update_open_stateid(state, open_stateid, &deleg_cur->stateid, fmode); - ret = 1; -no_delegation_unlock: - spin_unlock(&deleg_cur->lock); -no_delegation: - rcu_read_unlock(); - - if (!ret && open_stateid != NULL) { - __update_open_stateid(state, open_stateid, NULL, fmode); - ret = 1; - } - - return ret; -} - - -static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmode) -{ - struct nfs_delegation *delegation; - - rcu_read_lock(); - delegation = rcu_dereference(NFS_I(inode)->delegation); - if (delegation == NULL || (delegation->type & fmode) == fmode) { - rcu_read_unlock(); - return; - } - rcu_read_unlock(); - nfs_inode_return_delegation(inode); -} - -static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata) -{ - struct nfs4_state *state = opendata->state; - struct nfs_inode *nfsi = NFS_I(state->inode); - struct nfs_delegation *delegation; - int open_mode = opendata->o_arg.open_flags & (O_EXCL|O_TRUNC); - fmode_t fmode = opendata->o_arg.fmode; - nfs4_stateid stateid; - int ret = -EAGAIN; - - for (;;) { - if (can_open_cached(state, fmode, open_mode)) { - spin_lock(&state->owner->so_lock); - if (can_open_cached(state, fmode, open_mode)) { - update_open_stateflags(state, fmode); - spin_unlock(&state->owner->so_lock); - goto out_return_state; - } - spin_unlock(&state->owner->so_lock); - } - rcu_read_lock(); - delegation = rcu_dereference(nfsi->delegation); - if (!can_open_delegated(delegation, fmode)) { - rcu_read_unlock(); - break; - } - /* Save the delegation */ - nfs4_stateid_copy(&stateid, &delegation->stateid); - rcu_read_unlock(); - ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode); - if (ret != 0) - goto out; - ret = -EAGAIN; - - /* Try to update the stateid using the delegation */ - if (update_open_stateid(state, NULL, &stateid, fmode)) - goto out_return_state; - } -out: - return ERR_PTR(ret); -out_return_state: - atomic_inc(&state->count); - return state; -} - -static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data) -{ - struct inode *inode; - struct nfs4_state *state = NULL; - struct nfs_delegation *delegation; - int ret; - - if (!data->rpc_done) { - state = nfs4_try_open_cached(data); - goto out; - } - - ret = -EAGAIN; - if (!(data->f_attr.valid & NFS_ATTR_FATTR)) - goto err; - inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr); - ret = PTR_ERR(inode); - if (IS_ERR(inode)) - goto err; - ret = -ENOMEM; - state = nfs4_get_open_state(inode, data->owner); - if (state == NULL) - goto err_put_inode; - if (data->o_res.delegation_type != 0) { - struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; - int delegation_flags = 0; - - rcu_read_lock(); - delegation = rcu_dereference(NFS_I(inode)->delegation); - if (delegation) - delegation_flags = delegation->flags; - rcu_read_unlock(); - if (data->o_arg.claim == NFS4_OPEN_CLAIM_DELEGATE_CUR) { - pr_err_ratelimited("NFS: Broken NFSv4 server %s is " - "returning a delegation for " - "OPEN(CLAIM_DELEGATE_CUR)\n", - clp->cl_hostname); - } else if ((delegation_flags & 1UL<<NFS_DELEGATION_NEED_RECLAIM) == 0) - nfs_inode_set_delegation(state->inode, - data->owner->so_cred, - &data->o_res); - else - nfs_inode_reclaim_delegation(state->inode, - data->owner->so_cred, - &data->o_res); - } - - update_open_stateid(state, &data->o_res.stateid, NULL, - data->o_arg.fmode); - iput(inode); -out: - return state; -err_put_inode: - iput(inode); -err: - return ERR_PTR(ret); -} - -static struct nfs_open_context *nfs4_state_find_open_context(struct nfs4_state *state) -{ - struct nfs_inode *nfsi = NFS_I(state->inode); - struct nfs_open_context *ctx; - - spin_lock(&state->inode->i_lock); - list_for_each_entry(ctx, &nfsi->open_files, list) { - if (ctx->state != state) - continue; - get_nfs_open_context(ctx); - spin_unlock(&state->inode->i_lock); - return ctx; - } - spin_unlock(&state->inode->i_lock); - return ERR_PTR(-ENOENT); -} - -static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context *ctx, struct nfs4_state *state) -{ - struct nfs4_opendata *opendata; - - opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0, NULL, GFP_NOFS); - if (opendata == NULL) - return ERR_PTR(-ENOMEM); - opendata->state = state; - atomic_inc(&state->count); - return opendata; -} - -static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmode, struct nfs4_state **res) -{ - struct nfs4_state *newstate; - int ret; - - opendata->o_arg.open_flags = 0; - opendata->o_arg.fmode = fmode; - memset(&opendata->o_res, 0, sizeof(opendata->o_res)); - memset(&opendata->c_res, 0, sizeof(opendata->c_res)); - nfs4_init_opendata_res(opendata); - ret = _nfs4_recover_proc_open(opendata); - if (ret != 0) - return ret; - newstate = nfs4_opendata_to_nfs4_state(opendata); - if (IS_ERR(newstate)) - return PTR_ERR(newstate); - nfs4_close_state(newstate, fmode); - *res = newstate; - return 0; -} - -static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *state) -{ - struct nfs4_state *newstate; - int ret; - - /* memory barrier prior to reading state->n_* */ - clear_bit(NFS_DELEGATED_STATE, &state->flags); - smp_rmb(); - if (state->n_rdwr != 0) { - clear_bit(NFS_O_RDWR_STATE, &state->flags); - ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate); - if (ret != 0) - return ret; - if (newstate != state) - return -ESTALE; - } - if (state->n_wronly != 0) { - clear_bit(NFS_O_WRONLY_STATE, &state->flags); - ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate); - if (ret != 0) - return ret; - if (newstate != state) - return -ESTALE; - } - if (state->n_rdonly != 0) { - clear_bit(NFS_O_RDONLY_STATE, &state->flags); - ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate); - if (ret != 0) - return ret; - if (newstate != state) - return -ESTALE; - } - /* - * We may have performed cached opens for all three recoveries. - * Check if we need to update the current stateid. - */ - if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0 && - !nfs4_stateid_match(&state->stateid, &state->open_stateid)) { - write_seqlock(&state->seqlock); - if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0) - nfs4_stateid_copy(&state->stateid, &state->open_stateid); - write_sequnlock(&state->seqlock); - } - return 0; -} - -/* - * OPEN_RECLAIM: - * reclaim state on the server after a reboot. - */ -static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state *state) -{ - struct nfs_delegation *delegation; - struct nfs4_opendata *opendata; - fmode_t delegation_type = 0; - int status; - - opendata = nfs4_open_recoverdata_alloc(ctx, state); - if (IS_ERR(opendata)) - return PTR_ERR(opendata); - opendata->o_arg.claim = NFS4_OPEN_CLAIM_PREVIOUS; - opendata->o_arg.fh = NFS_FH(state->inode); - rcu_read_lock(); - delegation = rcu_dereference(NFS_I(state->inode)->delegation); - if (delegation != NULL && test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) != 0) - delegation_type = delegation->type; - rcu_read_unlock(); - opendata->o_arg.u.delegation_type = delegation_type; - status = nfs4_open_recover(opendata, state); - nfs4_opendata_put(opendata); - return status; -} - -static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state *state) -{ - struct nfs_server *server = NFS_SERVER(state->inode); - struct nfs4_exception exception = { }; - int err; - do { - err = _nfs4_do_open_reclaim(ctx, state); - if (err != -NFS4ERR_DELAY) - break; - nfs4_handle_exception(server, err, &exception); - } while (exception.retry); - return err; -} - -static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) -{ - struct nfs_open_context *ctx; - int ret; - - ctx = nfs4_state_find_open_context(state); - if (IS_ERR(ctx)) - return PTR_ERR(ctx); - ret = nfs4_do_open_reclaim(ctx, state); - put_nfs_open_context(ctx); - return ret; -} - -static int _nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid) -{ - struct nfs4_opendata *opendata; - int ret; - - opendata = nfs4_open_recoverdata_alloc(ctx, state); - if (IS_ERR(opendata)) - return PTR_ERR(opendata); - opendata->o_arg.claim = NFS4_OPEN_CLAIM_DELEGATE_CUR; - nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid); - ret = nfs4_open_recover(opendata, state); - nfs4_opendata_put(opendata); - return ret; -} - -int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid) -{ - struct nfs4_exception exception = { }; - struct nfs_server *server = NFS_SERVER(state->inode); - int err; - do { - err = _nfs4_open_delegation_recall(ctx, state, stateid); - switch (err) { - case 0: - case -ENOENT: - case -ESTALE: - goto out; - case -NFS4ERR_BADSESSION: - case -NFS4ERR_BADSLOT: - case -NFS4ERR_BAD_HIGH_SLOT: - case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: - case -NFS4ERR_DEADSESSION: - nfs4_schedule_session_recovery(server->nfs_client->cl_session); - goto out; - case -NFS4ERR_STALE_CLIENTID: - case -NFS4ERR_STALE_STATEID: - case -NFS4ERR_EXPIRED: - /* Don't recall a delegation if it was lost */ - nfs4_schedule_lease_recovery(server->nfs_client); - goto out; - case -ERESTARTSYS: - /* - * The show must go on: exit, but mark the - * stateid as needing recovery. - */ - case -NFS4ERR_DELEG_REVOKED: - case -NFS4ERR_ADMIN_REVOKED: - case -NFS4ERR_BAD_STATEID: - nfs_inode_find_state_and_recover(state->inode, - stateid); - nfs4_schedule_stateid_recovery(server, state); - case -EKEYEXPIRED: - /* - * User RPCSEC_GSS context has expired. - * We cannot recover this stateid now, so - * skip it and allow recovery thread to - * proceed. - */ - case -ENOMEM: - err = 0; - goto out; - } - err = nfs4_handle_exception(server, err, &exception); - } while (exception.retry); -out: - return err; -} - -static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata) -{ - struct nfs4_opendata *data = calldata; - - data->rpc_status = task->tk_status; - if (data->rpc_status == 0) { - nfs4_stateid_copy(&data->o_res.stateid, &data->c_res.stateid); - nfs_confirm_seqid(&data->owner->so_seqid, 0); - renew_lease(data->o_res.server, data->timestamp); - data->rpc_done = 1; - } -} - -static void nfs4_open_confirm_release(void *calldata) -{ - struct nfs4_opendata *data = calldata; - struct nfs4_state *state = NULL; - - /* If this request hasn't been cancelled, do nothing */ - if (data->cancelled == 0) - goto out_free; - /* In case of error, no cleanup! */ - if (!data->rpc_done) - goto out_free; - state = nfs4_opendata_to_nfs4_state(data); - if (!IS_ERR(state)) - nfs4_close_state(state, data->o_arg.fmode); -out_free: - nfs4_opendata_put(data); -} - -static const struct rpc_call_ops nfs4_open_confirm_ops = { - .rpc_call_done = nfs4_open_confirm_done, - .rpc_release = nfs4_open_confirm_release, -}; - -/* - * Note: On error, nfs4_proc_open_confirm will free the struct nfs4_opendata - */ -static int _nfs4_proc_open_confirm(struct nfs4_opendata *data) -{ - struct nfs_server *server = NFS_SERVER(data->dir->d_inode); - struct rpc_task *task; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM], - .rpc_argp = &data->c_arg, - .rpc_resp = &data->c_res, - .rpc_cred = data->owner->so_cred, - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = server->client, - .rpc_message = &msg, - .callback_ops = &nfs4_open_confirm_ops, - .callback_data = data, - .workqueue = nfsiod_workqueue, - .flags = RPC_TASK_ASYNC, - }; - int status; - - kref_get(&data->kref); - data->rpc_done = 0; - data->rpc_status = 0; - data->timestamp = jiffies; - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) - return PTR_ERR(task); - status = nfs4_wait_for_completion_rpc_task(task); - if (status != 0) { - data->cancelled = 1; - smp_wmb(); - } else - status = data->rpc_status; - rpc_put_task(task); - return status; -} - -static void nfs4_open_prepare(struct rpc_task *task, void *calldata) -{ - struct nfs4_opendata *data = calldata; - struct nfs4_state_owner *sp = data->owner; - - if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0) - return; - /* - * Check if we still need to send an OPEN call, or if we can use - * a delegation instead. - */ - if (data->state != NULL) { - struct nfs_delegation *delegation; - - if (can_open_cached(data->state, data->o_arg.fmode, data->o_arg.open_flags)) - goto out_no_action; - rcu_read_lock(); - delegation = rcu_dereference(NFS_I(data->state->inode)->delegation); - if (data->o_arg.claim != NFS4_OPEN_CLAIM_DELEGATE_CUR && - can_open_delegated(delegation, data->o_arg.fmode)) - goto unlock_no_action; - rcu_read_unlock(); - } - /* Update client id. */ - data->o_arg.clientid = sp->so_server->nfs_client->cl_clientid; - if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) { - task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR]; - nfs_copy_fh(&data->o_res.fh, data->o_arg.fh); - } - data->timestamp = jiffies; - if (nfs4_setup_sequence(data->o_arg.server, - &data->o_arg.seq_args, - &data->o_res.seq_res, task)) - return; - rpc_call_start(task); - return; -unlock_no_action: - rcu_read_unlock(); -out_no_action: - task->tk_action = NULL; - -} - -static void nfs4_recover_open_prepare(struct rpc_task *task, void *calldata) -{ - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - nfs4_open_prepare(task, calldata); -} - -static void nfs4_open_done(struct rpc_task *task, void *calldata) -{ - struct nfs4_opendata *data = calldata; - - data->rpc_status = task->tk_status; - - if (!nfs4_sequence_done(task, &data->o_res.seq_res)) - return; - - if (task->tk_status == 0) { - switch (data->o_res.f_attr->mode & S_IFMT) { - case S_IFREG: - break; - case S_IFLNK: - data->rpc_status = -ELOOP; - break; - case S_IFDIR: - data->rpc_status = -EISDIR; - break; - default: - data->rpc_status = -ENOTDIR; - } - renew_lease(data->o_res.server, data->timestamp); - if (!(data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM)) - nfs_confirm_seqid(&data->owner->so_seqid, 0); - } - data->rpc_done = 1; -} - -static void nfs4_open_release(void *calldata) -{ - struct nfs4_opendata *data = calldata; - struct nfs4_state *state = NULL; - - /* If this request hasn't been cancelled, do nothing */ - if (data->cancelled == 0) - goto out_free; - /* In case of error, no cleanup! */ - if (data->rpc_status != 0 || !data->rpc_done) - goto out_free; - /* In case we need an open_confirm, no cleanup! */ - if (data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM) - goto out_free; - state = nfs4_opendata_to_nfs4_state(data); - if (!IS_ERR(state)) - nfs4_close_state(state, data->o_arg.fmode); -out_free: - nfs4_opendata_put(data); -} - -static const struct rpc_call_ops nfs4_open_ops = { - .rpc_call_prepare = nfs4_open_prepare, - .rpc_call_done = nfs4_open_done, - .rpc_release = nfs4_open_release, -}; - -static const struct rpc_call_ops nfs4_recover_open_ops = { - .rpc_call_prepare = nfs4_recover_open_prepare, - .rpc_call_done = nfs4_open_done, - .rpc_release = nfs4_open_release, -}; - -static int nfs4_run_open_task(struct nfs4_opendata *data, int isrecover) -{ - struct inode *dir = data->dir->d_inode; - struct nfs_server *server = NFS_SERVER(dir); - struct nfs_openargs *o_arg = &data->o_arg; - struct nfs_openres *o_res = &data->o_res; - struct rpc_task *task; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN], - .rpc_argp = o_arg, - .rpc_resp = o_res, - .rpc_cred = data->owner->so_cred, - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = server->client, - .rpc_message = &msg, - .callback_ops = &nfs4_open_ops, - .callback_data = data, - .workqueue = nfsiod_workqueue, - .flags = RPC_TASK_ASYNC, - }; - int status; - - nfs41_init_sequence(&o_arg->seq_args, &o_res->seq_res, 1); - kref_get(&data->kref); - data->rpc_done = 0; - data->rpc_status = 0; - data->cancelled = 0; - if (isrecover) - task_setup_data.callback_ops = &nfs4_recover_open_ops; - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) - return PTR_ERR(task); - status = nfs4_wait_for_completion_rpc_task(task); - if (status != 0) { - data->cancelled = 1; - smp_wmb(); - } else - status = data->rpc_status; - rpc_put_task(task); - - return status; -} - -static int _nfs4_recover_proc_open(struct nfs4_opendata *data) -{ - struct inode *dir = data->dir->d_inode; - struct nfs_openres *o_res = &data->o_res; - int status; - - status = nfs4_run_open_task(data, 1); - if (status != 0 || !data->rpc_done) - return status; - - nfs_fattr_map_and_free_names(NFS_SERVER(dir), &data->f_attr); - - nfs_refresh_inode(dir, o_res->dir_attr); - - if (o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) { - status = _nfs4_proc_open_confirm(data); - if (status != 0) - return status; - } - - return status; -} - -/* - * Note: On error, nfs4_proc_open will free the struct nfs4_opendata - */ -static int _nfs4_proc_open(struct nfs4_opendata *data) -{ - struct inode *dir = data->dir->d_inode; - struct nfs_server *server = NFS_SERVER(dir); - struct nfs_openargs *o_arg = &data->o_arg; - struct nfs_openres *o_res = &data->o_res; - int status; - - status = nfs4_run_open_task(data, 0); - if (!data->rpc_done) - return status; - if (status != 0) { - if (status == -NFS4ERR_BADNAME && - !(o_arg->open_flags & O_CREAT)) - return -ENOENT; - return status; - } - - nfs_fattr_map_and_free_names(server, &data->f_attr); - - if (o_arg->open_flags & O_CREAT) { - update_changeattr(dir, &o_res->cinfo); - nfs_post_op_update_inode(dir, o_res->dir_attr); - } else - nfs_refresh_inode(dir, o_res->dir_attr); - if ((o_res->rflags & NFS4_OPEN_RESULT_LOCKTYPE_POSIX) == 0) - server->caps &= ~NFS_CAP_POSIX_LOCK; - if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) { - status = _nfs4_proc_open_confirm(data); - if (status != 0) - return status; - } - if (!(o_res->f_attr->valid & NFS_ATTR_FATTR)) - _nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr); - return 0; -} - -static int nfs4_client_recover_expired_lease(struct nfs_client *clp) -{ - unsigned int loop; - int ret; - - for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) { - ret = nfs4_wait_clnt_recover(clp); - if (ret != 0) - break; - if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) && - !test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state)) - break; - nfs4_schedule_state_manager(clp); - ret = -EIO; - } - return ret; -} - -static int nfs4_recover_expired_lease(struct nfs_server *server) -{ - return nfs4_client_recover_expired_lease(server->nfs_client); -} - -/* - * OPEN_EXPIRED: - * reclaim state on the server after a network partition. - * Assumes caller holds the appropriate lock - */ -static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *state) -{ - struct nfs4_opendata *opendata; - int ret; - - opendata = nfs4_open_recoverdata_alloc(ctx, state); - if (IS_ERR(opendata)) - return PTR_ERR(opendata); - ret = nfs4_open_recover(opendata, state); - if (ret == -ESTALE) - d_drop(ctx->dentry); - nfs4_opendata_put(opendata); - return ret; -} - -static int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state *state) -{ - struct nfs_server *server = NFS_SERVER(state->inode); - struct nfs4_exception exception = { }; - int err; - - do { - err = _nfs4_open_expired(ctx, state); - switch (err) { - default: - goto out; - case -NFS4ERR_GRACE: - case -NFS4ERR_DELAY: - nfs4_handle_exception(server, err, &exception); - err = 0; - } - } while (exception.retry); -out: - return err; -} - -static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state) -{ - struct nfs_open_context *ctx; - int ret; - - ctx = nfs4_state_find_open_context(state); - if (IS_ERR(ctx)) - return PTR_ERR(ctx); - ret = nfs4_do_open_expired(ctx, state); - put_nfs_open_context(ctx); - return ret; -} - -#if defined(CONFIG_NFS_V4_1) -static int nfs41_check_expired_stateid(struct nfs4_state *state, nfs4_stateid *stateid, unsigned int flags) -{ - int status = NFS_OK; - struct nfs_server *server = NFS_SERVER(state->inode); - - if (state->flags & flags) { - status = nfs41_test_stateid(server, stateid); - if (status != NFS_OK) { - nfs41_free_stateid(server, stateid); - state->flags &= ~flags; - } - } - return status; -} - -static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state) -{ - int deleg_status, open_status; - int deleg_flags = 1 << NFS_DELEGATED_STATE; - int open_flags = (1 << NFS_O_RDONLY_STATE) | (1 << NFS_O_WRONLY_STATE) | (1 << NFS_O_RDWR_STATE); - - deleg_status = nfs41_check_expired_stateid(state, &state->stateid, deleg_flags); - open_status = nfs41_check_expired_stateid(state, &state->open_stateid, open_flags); - - if ((deleg_status == NFS_OK) && (open_status == NFS_OK)) - return NFS_OK; - return nfs4_open_expired(sp, state); -} -#endif - -/* - * on an EXCLUSIVE create, the server should send back a bitmask with FATTR4-* - * fields corresponding to attributes that were used to store the verifier. - * Make sure we clobber those fields in the later setattr call - */ -static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct iattr *sattr) -{ - if ((opendata->o_res.attrset[1] & FATTR4_WORD1_TIME_ACCESS) && - !(sattr->ia_valid & ATTR_ATIME_SET)) - sattr->ia_valid |= ATTR_ATIME; - - if ((opendata->o_res.attrset[1] & FATTR4_WORD1_TIME_MODIFY) && - !(sattr->ia_valid & ATTR_MTIME_SET)) - sattr->ia_valid |= ATTR_MTIME; -} - -/* - * Returns a referenced nfs4_state - */ -static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res) -{ - struct nfs4_state_owner *sp; - struct nfs4_state *state = NULL; - struct nfs_server *server = NFS_SERVER(dir); - struct nfs4_opendata *opendata; - int status; - - /* Protect against reboot recovery conflicts */ - status = -ENOMEM; - sp = nfs4_get_state_owner(server, cred, GFP_KERNEL); - if (sp == NULL) { - dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n"); - goto out_err; - } - status = nfs4_recover_expired_lease(server); - if (status != 0) - goto err_put_state_owner; - if (dentry->d_inode != NULL) - nfs4_return_incompatible_delegation(dentry->d_inode, fmode); - status = -ENOMEM; - opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr, GFP_KERNEL); - if (opendata == NULL) - goto err_put_state_owner; - - if (dentry->d_inode != NULL) - opendata->state = nfs4_get_open_state(dentry->d_inode, sp); - - status = _nfs4_proc_open(opendata); - if (status != 0) - goto err_opendata_put; - - state = nfs4_opendata_to_nfs4_state(opendata); - status = PTR_ERR(state); - if (IS_ERR(state)) - goto err_opendata_put; - if (server->caps & NFS_CAP_POSIX_LOCK) - set_bit(NFS_STATE_POSIX_LOCKS, &state->flags); - - if (opendata->o_arg.open_flags & O_EXCL) { - nfs4_exclusive_attrset(opendata, sattr); - - nfs_fattr_init(opendata->o_res.f_attr); - status = nfs4_do_setattr(state->inode, cred, - opendata->o_res.f_attr, sattr, - state); - if (status == 0) - nfs_setattr_update_inode(state->inode, sattr); - nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr); - } - nfs4_opendata_put(opendata); - nfs4_put_state_owner(sp); - *res = state; - return 0; -err_opendata_put: - nfs4_opendata_put(opendata); -err_put_state_owner: - nfs4_put_state_owner(sp); -out_err: - *res = NULL; - return status; -} - - -static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred) -{ - struct nfs4_exception exception = { }; - struct nfs4_state *res; - int status; - - fmode &= FMODE_READ|FMODE_WRITE; - do { - status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, cred, &res); - if (status == 0) - break; - /* NOTE: BAD_SEQID means the server and client disagree about the - * book-keeping w.r.t. state-changing operations - * (OPEN/CLOSE/LOCK/LOCKU...) - * It is actually a sign of a bug on the client or on the server. - * - * If we receive a BAD_SEQID error in the particular case of - * doing an OPEN, we assume that nfs_increment_open_seqid() will - * have unhashed the old state_owner for us, and that we can - * therefore safely retry using a new one. We should still warn - * the user though... - */ - if (status == -NFS4ERR_BAD_SEQID) { - pr_warn_ratelimited("NFS: v4 server %s " - " returned a bad sequence-id error!\n", - NFS_SERVER(dir)->nfs_client->cl_hostname); - exception.retry = 1; - continue; - } - /* - * BAD_STATEID on OPEN means that the server cancelled our - * state before it received the OPEN_CONFIRM. - * Recover by retrying the request as per the discussion - * on Page 181 of RFC3530. - */ - if (status == -NFS4ERR_BAD_STATEID) { - exception.retry = 1; - continue; - } - if (status == -EAGAIN) { - /* We must have found a delegation */ - exception.retry = 1; - continue; - } - res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir), - status, &exception)); - } while (exception.retry); - return res; -} - -static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, - struct nfs_fattr *fattr, struct iattr *sattr, - struct nfs4_state *state) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct nfs_setattrargs arg = { - .fh = NFS_FH(inode), - .iap = sattr, - .server = server, - .bitmask = server->attr_bitmask, - }; - struct nfs_setattrres res = { - .fattr = fattr, - .server = server, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR], - .rpc_argp = &arg, - .rpc_resp = &res, - .rpc_cred = cred, - }; - unsigned long timestamp = jiffies; - int status; - - nfs_fattr_init(fattr); - - if (state != NULL) { - nfs4_select_rw_stateid(&arg.stateid, state, FMODE_WRITE, - current->files, current->tgid); - } else if (nfs4_copy_delegation_stateid(&arg.stateid, inode, - FMODE_WRITE)) { - /* Use that stateid */ - } else - nfs4_stateid_copy(&arg.stateid, &zero_stateid); - - status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); - if (status == 0 && state != NULL) - renew_lease(server, timestamp); - return status; -} - -static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, - struct nfs_fattr *fattr, struct iattr *sattr, - struct nfs4_state *state) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct nfs4_exception exception = { - .state = state, - .inode = inode, - }; - int err; - do { - err = _nfs4_do_setattr(inode, cred, fattr, sattr, state); - switch (err) { - case -NFS4ERR_OPENMODE: - if (state && !(state->state & FMODE_WRITE)) { - err = -EBADF; - if (sattr->ia_valid & ATTR_OPEN) - err = -EACCES; - goto out; - } - } - err = nfs4_handle_exception(server, err, &exception); - } while (exception.retry); -out: - return err; -} - -struct nfs4_closedata { - struct inode *inode; - struct nfs4_state *state; - struct nfs_closeargs arg; - struct nfs_closeres res; - struct nfs_fattr fattr; - unsigned long timestamp; - bool roc; - u32 roc_barrier; -}; - -static void nfs4_free_closedata(void *data) -{ - struct nfs4_closedata *calldata = data; - struct nfs4_state_owner *sp = calldata->state->owner; - struct super_block *sb = calldata->state->inode->i_sb; - - if (calldata->roc) - pnfs_roc_release(calldata->state->inode); - nfs4_put_open_state(calldata->state); - nfs_free_seqid(calldata->arg.seqid); - nfs4_put_state_owner(sp); - nfs_sb_deactive(sb); - kfree(calldata); -} - -static void nfs4_close_clear_stateid_flags(struct nfs4_state *state, - fmode_t fmode) -{ - spin_lock(&state->owner->so_lock); - if (!(fmode & FMODE_READ)) - clear_bit(NFS_O_RDONLY_STATE, &state->flags); - if (!(fmode & FMODE_WRITE)) - clear_bit(NFS_O_WRONLY_STATE, &state->flags); - clear_bit(NFS_O_RDWR_STATE, &state->flags); - spin_unlock(&state->owner->so_lock); -} - -static void nfs4_close_done(struct rpc_task *task, void *data) -{ - struct nfs4_closedata *calldata = data; - struct nfs4_state *state = calldata->state; - struct nfs_server *server = NFS_SERVER(calldata->inode); - - dprintk("%s: begin!\n", __func__); - if (!nfs4_sequence_done(task, &calldata->res.seq_res)) - return; - /* hmm. we are done with the inode, and in the process of freeing - * the state_owner. we keep this around to process errors - */ - switch (task->tk_status) { - case 0: - if (calldata->roc) - pnfs_roc_set_barrier(state->inode, - calldata->roc_barrier); - nfs_set_open_stateid(state, &calldata->res.stateid, 0); - renew_lease(server, calldata->timestamp); - nfs4_close_clear_stateid_flags(state, - calldata->arg.fmode); - break; - case -NFS4ERR_STALE_STATEID: - case -NFS4ERR_OLD_STATEID: - case -NFS4ERR_BAD_STATEID: - case -NFS4ERR_EXPIRED: - if (calldata->arg.fmode == 0) - break; - default: - if (nfs4_async_handle_error(task, server, state) == -EAGAIN) - rpc_restart_call_prepare(task); - } - nfs_release_seqid(calldata->arg.seqid); - nfs_refresh_inode(calldata->inode, calldata->res.fattr); - dprintk("%s: done, ret = %d!\n", __func__, task->tk_status); -} - -static void nfs4_close_prepare(struct rpc_task *task, void *data) -{ - struct nfs4_closedata *calldata = data; - struct nfs4_state *state = calldata->state; - int call_close = 0; - - dprintk("%s: begin!\n", __func__); - if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0) - return; - - task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE]; - calldata->arg.fmode = FMODE_READ|FMODE_WRITE; - spin_lock(&state->owner->so_lock); - /* Calculate the change in open mode */ - if (state->n_rdwr == 0) { - if (state->n_rdonly == 0) { - call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags); - call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags); - calldata->arg.fmode &= ~FMODE_READ; - } - if (state->n_wronly == 0) { - call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags); - call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags); - calldata->arg.fmode &= ~FMODE_WRITE; - } - } - spin_unlock(&state->owner->so_lock); - - if (!call_close) { - /* Note: exit _without_ calling nfs4_close_done */ - task->tk_action = NULL; - goto out; - } - - if (calldata->arg.fmode == 0) { - task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE]; - if (calldata->roc && - pnfs_roc_drain(calldata->inode, &calldata->roc_barrier)) { - rpc_sleep_on(&NFS_SERVER(calldata->inode)->roc_rpcwaitq, - task, NULL); - goto out; - } - } - - nfs_fattr_init(calldata->res.fattr); - calldata->timestamp = jiffies; - if (nfs4_setup_sequence(NFS_SERVER(calldata->inode), - &calldata->arg.seq_args, - &calldata->res.seq_res, - task)) - goto out; - rpc_call_start(task); -out: - dprintk("%s: done!\n", __func__); -} - -static const struct rpc_call_ops nfs4_close_ops = { - .rpc_call_prepare = nfs4_close_prepare, - .rpc_call_done = nfs4_close_done, - .rpc_release = nfs4_free_closedata, -}; - -/* - * It is possible for data to be read/written from a mem-mapped file - * after the sys_close call (which hits the vfs layer as a flush). - * This means that we can't safely call nfsv4 close on a file until - * the inode is cleared. This in turn means that we are not good - * NFSv4 citizens - we do not indicate to the server to update the file's - * share state even when we are done with one of the three share - * stateid's in the inode. - * - * NOTE: Caller must be holding the sp->so_owner semaphore! - */ -int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc) -{ - struct nfs_server *server = NFS_SERVER(state->inode); - struct nfs4_closedata *calldata; - struct nfs4_state_owner *sp = state->owner; - struct rpc_task *task; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE], - .rpc_cred = state->owner->so_cred, - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = server->client, - .rpc_message = &msg, - .callback_ops = &nfs4_close_ops, - .workqueue = nfsiod_workqueue, - .flags = RPC_TASK_ASYNC, - }; - int status = -ENOMEM; - - calldata = kzalloc(sizeof(*calldata), gfp_mask); - if (calldata == NULL) - goto out; - nfs41_init_sequence(&calldata->arg.seq_args, &calldata->res.seq_res, 1); - calldata->inode = state->inode; - calldata->state = state; - calldata->arg.fh = NFS_FH(state->inode); - calldata->arg.stateid = &state->open_stateid; - /* Serialization for the sequence id */ - calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid, gfp_mask); - if (calldata->arg.seqid == NULL) - goto out_free_calldata; - calldata->arg.fmode = 0; - calldata->arg.bitmask = server->cache_consistency_bitmask; - calldata->res.fattr = &calldata->fattr; - calldata->res.seqid = calldata->arg.seqid; - calldata->res.server = server; - calldata->roc = roc; - nfs_sb_active(calldata->inode->i_sb); - - msg.rpc_argp = &calldata->arg; - msg.rpc_resp = &calldata->res; - task_setup_data.callback_data = calldata; - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) - return PTR_ERR(task); - status = 0; - if (wait) - status = rpc_wait_for_completion_task(task); - rpc_put_task(task); - return status; -out_free_calldata: - kfree(calldata); -out: - if (roc) - pnfs_roc_release(state->inode); - nfs4_put_open_state(state); - nfs4_put_state_owner(sp); - return status; -} - -static struct inode * -nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr) -{ - struct nfs4_state *state; - - /* Protect against concurrent sillydeletes */ - state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr, ctx->cred); - if (IS_ERR(state)) - return ERR_CAST(state); - ctx->state = state; - return igrab(state->inode); -} - -static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync) -{ - if (ctx->state == NULL) - return; - if (is_sync) - nfs4_close_sync(ctx->state, ctx->mode); - else - nfs4_close_state(ctx->state, ctx->mode); -} - -static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle) -{ - struct nfs4_server_caps_arg args = { - .fhandle = fhandle, - }; - struct nfs4_server_caps_res res = {}; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SERVER_CAPS], - .rpc_argp = &args, - .rpc_resp = &res, - }; - int status; - - status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); - if (status == 0) { - memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask)); - server->caps &= ~(NFS_CAP_ACLS|NFS_CAP_HARDLINKS| - NFS_CAP_SYMLINKS|NFS_CAP_FILEID| - NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER| - NFS_CAP_OWNER_GROUP|NFS_CAP_ATIME| - NFS_CAP_CTIME|NFS_CAP_MTIME); - if (res.attr_bitmask[0] & FATTR4_WORD0_ACL) - server->caps |= NFS_CAP_ACLS; - if (res.has_links != 0) - server->caps |= NFS_CAP_HARDLINKS; - if (res.has_symlinks != 0) - server->caps |= NFS_CAP_SYMLINKS; - if (res.attr_bitmask[0] & FATTR4_WORD0_FILEID) - server->caps |= NFS_CAP_FILEID; - if (res.attr_bitmask[1] & FATTR4_WORD1_MODE) - server->caps |= NFS_CAP_MODE; - if (res.attr_bitmask[1] & FATTR4_WORD1_NUMLINKS) - server->caps |= NFS_CAP_NLINK; - if (res.attr_bitmask[1] & FATTR4_WORD1_OWNER) - server->caps |= NFS_CAP_OWNER; - if (res.attr_bitmask[1] & FATTR4_WORD1_OWNER_GROUP) - server->caps |= NFS_CAP_OWNER_GROUP; - if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_ACCESS) - server->caps |= NFS_CAP_ATIME; - if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_METADATA) - server->caps |= NFS_CAP_CTIME; - if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY) - server->caps |= NFS_CAP_MTIME; - - memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask)); - server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE; - server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY; - server->acl_bitmask = res.acl_bitmask; - server->fh_expire_type = res.fh_expire_type; - } - - return status; -} - -int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(server, - _nfs4_server_capabilities(server, fhandle), - &exception); - } while (exception.retry); - return err; -} - -static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info) -{ - struct nfs4_lookup_root_arg args = { - .bitmask = nfs4_fattr_bitmap, - }; - struct nfs4_lookup_res res = { - .server = server, - .fattr = info->fattr, - .fh = fhandle, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP_ROOT], - .rpc_argp = &args, - .rpc_resp = &res, - }; - - nfs_fattr_init(info->fattr); - return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); -} - -static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = _nfs4_lookup_root(server, fhandle, info); - switch (err) { - case 0: - case -NFS4ERR_WRONGSEC: - goto out; - default: - err = nfs4_handle_exception(server, err, &exception); - } - } while (exception.retry); -out: - return err; -} - -static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info, rpc_authflavor_t flavor) -{ - struct rpc_auth *auth; - int ret; - - auth = rpcauth_create(flavor, server->client); - if (!auth) { - ret = -EIO; - goto out; - } - ret = nfs4_lookup_root(server, fhandle, info); -out: - return ret; -} - -static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info) -{ - int i, len, status = 0; - rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS]; - - len = gss_mech_list_pseudoflavors(&flav_array[0]); - flav_array[len] = RPC_AUTH_NULL; - len += 1; - - for (i = 0; i < len; i++) { - status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]); - if (status == -NFS4ERR_WRONGSEC || status == -EACCES) - continue; - break; - } - /* - * -EACCESS could mean that the user doesn't have correct permissions - * to access the mount. It could also mean that we tried to mount - * with a gss auth flavor, but rpc.gssd isn't running. Either way, - * existing mount programs don't handle -EACCES very well so it should - * be mapped to -EPERM instead. - */ - if (status == -EACCES) - status = -EPERM; - return status; -} - -/* - * get the file handle for the "/" directory on the server - */ -static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info) -{ - int minor_version = server->nfs_client->cl_minorversion; - int status = nfs4_lookup_root(server, fhandle, info); - if ((status == -NFS4ERR_WRONGSEC) && !(server->flags & NFS_MOUNT_SECFLAVOUR)) - /* - * A status of -NFS4ERR_WRONGSEC will be mapped to -EPERM - * by nfs4_map_errors() as this function exits. - */ - status = nfs_v4_minor_ops[minor_version]->find_root_sec(server, fhandle, info); - if (status == 0) - status = nfs4_server_capabilities(server, fhandle); - if (status == 0) - status = nfs4_do_fsinfo(server, fhandle, info); - return nfs4_map_errors(status); -} - -/* - * Get locations and (maybe) other attributes of a referral. - * Note that we'll actually follow the referral later when - * we detect fsid mismatch in inode revalidation - */ -static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir, - const struct qstr *name, struct nfs_fattr *fattr, - struct nfs_fh *fhandle) -{ - int status = -ENOMEM; - struct page *page = NULL; - struct nfs4_fs_locations *locations = NULL; - - page = alloc_page(GFP_KERNEL); - if (page == NULL) - goto out; - locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); - if (locations == NULL) - goto out; - - status = nfs4_proc_fs_locations(client, dir, name, locations, page); - if (status != 0) - goto out; - /* Make sure server returned a different fsid for the referral */ - if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) { - dprintk("%s: server did not return a different fsid for" - " a referral at %s\n", __func__, name->name); - status = -EIO; - goto out; - } - /* Fixup attributes for the nfs_lookup() call to nfs_fhget() */ - nfs_fixup_referral_attributes(&locations->fattr); - - /* replace the lookup nfs_fattr with the locations nfs_fattr */ - memcpy(fattr, &locations->fattr, sizeof(struct nfs_fattr)); - memset(fhandle, 0, sizeof(struct nfs_fh)); -out: - if (page) - __free_page(page); - kfree(locations); - return status; -} - -static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) -{ - struct nfs4_getattr_arg args = { - .fh = fhandle, - .bitmask = server->attr_bitmask, - }; - struct nfs4_getattr_res res = { - .fattr = fattr, - .server = server, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETATTR], - .rpc_argp = &args, - .rpc_resp = &res, - }; - - nfs_fattr_init(fattr); - return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); -} - -static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(server, - _nfs4_proc_getattr(server, fhandle, fattr), - &exception); - } while (exception.retry); - return err; -} - -/* - * The file is not closed if it is opened due to the a request to change - * the size of the file. The open call will not be needed once the - * VFS layer lookup-intents are implemented. - * - * Close is called when the inode is destroyed. - * If we haven't opened the file for O_WRONLY, we - * need to in the size_change case to obtain a stateid. - * - * Got race? - * Because OPEN is always done by name in nfsv4, it is - * possible that we opened a different file by the same - * name. We can recognize this race condition, but we - * can't do anything about it besides returning an error. - * - * This will be fixed with VFS changes (lookup-intent). - */ -static int -nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, - struct iattr *sattr) -{ - struct inode *inode = dentry->d_inode; - struct rpc_cred *cred = NULL; - struct nfs4_state *state = NULL; - int status; - - if (pnfs_ld_layoutret_on_setattr(inode)) - pnfs_return_layout(inode); - - nfs_fattr_init(fattr); - - /* Search for an existing open(O_WRITE) file */ - if (sattr->ia_valid & ATTR_FILE) { - struct nfs_open_context *ctx; - - ctx = nfs_file_open_context(sattr->ia_file); - if (ctx) { - cred = ctx->cred; - state = ctx->state; - } - } - - /* Deal with open(O_TRUNC) */ - if (sattr->ia_valid & ATTR_OPEN) - sattr->ia_valid &= ~(ATTR_MTIME|ATTR_CTIME|ATTR_OPEN); - - status = nfs4_do_setattr(inode, cred, fattr, sattr, state); - if (status == 0) - nfs_setattr_update_inode(inode, sattr); - return status; -} - -static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, - const struct qstr *name, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) -{ - struct nfs_server *server = NFS_SERVER(dir); - int status; - struct nfs4_lookup_arg args = { - .bitmask = server->attr_bitmask, - .dir_fh = NFS_FH(dir), - .name = name, - }; - struct nfs4_lookup_res res = { - .server = server, - .fattr = fattr, - .fh = fhandle, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP], - .rpc_argp = &args, - .rpc_resp = &res, - }; - - nfs_fattr_init(fattr); - - dprintk("NFS call lookup %s\n", name->name); - status = nfs4_call_sync(clnt, server, &msg, &args.seq_args, &res.seq_res, 0); - dprintk("NFS reply lookup: %d\n", status); - return status; -} - -static void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr) -{ - fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE | - NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_MOUNTPOINT; - fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO; - fattr->nlink = 2; -} - -static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir, - struct qstr *name, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) -{ - struct nfs4_exception exception = { }; - struct rpc_clnt *client = *clnt; - int err; - do { - err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr); - switch (err) { - case -NFS4ERR_BADNAME: - err = -ENOENT; - goto out; - case -NFS4ERR_MOVED: - err = nfs4_get_referral(client, dir, name, fattr, fhandle); - goto out; - case -NFS4ERR_WRONGSEC: - err = -EPERM; - if (client != *clnt) - goto out; - - client = nfs4_create_sec_client(client, dir, name); - if (IS_ERR(client)) - return PTR_ERR(client); - - exception.retry = 1; - break; - default: - err = nfs4_handle_exception(NFS_SERVER(dir), err, &exception); - } - } while (exception.retry); - -out: - if (err == 0) - *clnt = client; - else if (client != *clnt) - rpc_shutdown_client(client); - - return err; -} - -static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) -{ - int status; - struct rpc_clnt *client = NFS_CLIENT(dir); - - status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr); - if (client != NFS_CLIENT(dir)) { - rpc_shutdown_client(client); - nfs_fixup_secinfo_attributes(fattr); - } - return status; -} - -struct rpc_clnt * -nfs4_proc_lookup_mountpoint(struct inode *dir, struct qstr *name, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) -{ - int status; - struct rpc_clnt *client = rpc_clone_client(NFS_CLIENT(dir)); - - status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr); - if (status < 0) { - rpc_shutdown_client(client); - return ERR_PTR(status); - } - return client; -} - -static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct nfs4_accessargs args = { - .fh = NFS_FH(inode), - .bitmask = server->cache_consistency_bitmask, - }; - struct nfs4_accessres res = { - .server = server, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ACCESS], - .rpc_argp = &args, - .rpc_resp = &res, - .rpc_cred = entry->cred, - }; - int mode = entry->mask; - int status; - - /* - * Determine which access bits we want to ask for... - */ - if (mode & MAY_READ) - args.access |= NFS4_ACCESS_READ; - if (S_ISDIR(inode->i_mode)) { - if (mode & MAY_WRITE) - args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE; - if (mode & MAY_EXEC) - args.access |= NFS4_ACCESS_LOOKUP; - } else { - if (mode & MAY_WRITE) - args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND; - if (mode & MAY_EXEC) - args.access |= NFS4_ACCESS_EXECUTE; - } - - res.fattr = nfs_alloc_fattr(); - if (res.fattr == NULL) - return -ENOMEM; - - status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); - if (!status) { - entry->mask = 0; - if (res.access & NFS4_ACCESS_READ) - entry->mask |= MAY_READ; - if (res.access & (NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE)) - entry->mask |= MAY_WRITE; - if (res.access & (NFS4_ACCESS_LOOKUP|NFS4_ACCESS_EXECUTE)) - entry->mask |= MAY_EXEC; - nfs_refresh_inode(inode, res.fattr); - } - nfs_free_fattr(res.fattr); - return status; -} - -static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(NFS_SERVER(inode), - _nfs4_proc_access(inode, entry), - &exception); - } while (exception.retry); - return err; -} - -/* - * TODO: For the time being, we don't try to get any attributes - * along with any of the zero-copy operations READ, READDIR, - * READLINK, WRITE. - * - * In the case of the first three, we want to put the GETATTR - * after the read-type operation -- this is because it is hard - * to predict the length of a GETATTR response in v4, and thus - * align the READ data correctly. This means that the GETATTR - * may end up partially falling into the page cache, and we should - * shift it into the 'tail' of the xdr_buf before processing. - * To do this efficiently, we need to know the total length - * of data received, which doesn't seem to be available outside - * of the RPC layer. - * - * In the case of WRITE, we also want to put the GETATTR after - * the operation -- in this case because we want to make sure - * we get the post-operation mtime and size. This means that - * we can't use xdr_encode_pages() as written: we need a variant - * of it which would leave room in the 'tail' iovec. - * - * Both of these changes to the XDR layer would in fact be quite - * minor, but I decided to leave them for a subsequent patch. - */ -static int _nfs4_proc_readlink(struct inode *inode, struct page *page, - unsigned int pgbase, unsigned int pglen) -{ - struct nfs4_readlink args = { - .fh = NFS_FH(inode), - .pgbase = pgbase, - .pglen = pglen, - .pages = &page, - }; - struct nfs4_readlink_res res; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READLINK], - .rpc_argp = &args, - .rpc_resp = &res, - }; - - return nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), &msg, &args.seq_args, &res.seq_res, 0); -} - -static int nfs4_proc_readlink(struct inode *inode, struct page *page, - unsigned int pgbase, unsigned int pglen) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(NFS_SERVER(inode), - _nfs4_proc_readlink(inode, page, pgbase, pglen), - &exception); - } while (exception.retry); - return err; -} - -/* - * Got race? - * We will need to arrange for the VFS layer to provide an atomic open. - * Until then, this create/open method is prone to inefficiency and race - * conditions due to the lookup, create, and open VFS calls from sys_open() - * placed on the wire. - * - * Given the above sorry state of affairs, I'm simply sending an OPEN. - * The file will be opened again in the subsequent VFS open call - * (nfs4_proc_file_open). - * - * The open for read will just hang around to be used by any process that - * opens the file O_RDONLY. This will all be resolved with the VFS changes. - */ - -static int -nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, - int flags, struct nfs_open_context *ctx) -{ - struct dentry *de = dentry; - struct nfs4_state *state; - struct rpc_cred *cred = NULL; - fmode_t fmode = 0; - int status = 0; - - if (ctx != NULL) { - cred = ctx->cred; - de = ctx->dentry; - fmode = ctx->mode; - } - sattr->ia_mode &= ~current_umask(); - state = nfs4_do_open(dir, de, fmode, flags, sattr, cred); - d_drop(dentry); - if (IS_ERR(state)) { - status = PTR_ERR(state); - goto out; - } - d_add(dentry, igrab(state->inode)); - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); - if (ctx != NULL) - ctx->state = state; - else - nfs4_close_sync(state, fmode); -out: - return status; -} - -static int _nfs4_proc_remove(struct inode *dir, struct qstr *name) -{ - struct nfs_server *server = NFS_SERVER(dir); - struct nfs_removeargs args = { - .fh = NFS_FH(dir), - .name.len = name->len, - .name.name = name->name, - .bitmask = server->attr_bitmask, - }; - struct nfs_removeres res = { - .server = server, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE], - .rpc_argp = &args, - .rpc_resp = &res, - }; - int status = -ENOMEM; - - res.dir_attr = nfs_alloc_fattr(); - if (res.dir_attr == NULL) - goto out; - - status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 1); - if (status == 0) { - update_changeattr(dir, &res.cinfo); - nfs_post_op_update_inode(dir, res.dir_attr); - } - nfs_free_fattr(res.dir_attr); -out: - return status; -} - -static int nfs4_proc_remove(struct inode *dir, struct qstr *name) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(NFS_SERVER(dir), - _nfs4_proc_remove(dir, name), - &exception); - } while (exception.retry); - return err; -} - -static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) -{ - struct nfs_server *server = NFS_SERVER(dir); - struct nfs_removeargs *args = msg->rpc_argp; - struct nfs_removeres *res = msg->rpc_resp; - - args->bitmask = server->cache_consistency_bitmask; - res->server = server; - msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE]; - nfs41_init_sequence(&args->seq_args, &res->seq_res, 1); -} - -static void nfs4_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data) -{ - if (nfs4_setup_sequence(NFS_SERVER(data->dir), - &data->args.seq_args, - &data->res.seq_res, - task)) - return; - rpc_call_start(task); -} - -static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir) -{ - struct nfs_removeres *res = task->tk_msg.rpc_resp; - - if (!nfs4_sequence_done(task, &res->seq_res)) - return 0; - if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN) - return 0; - update_changeattr(dir, &res->cinfo); - nfs_post_op_update_inode(dir, res->dir_attr); - return 1; -} - -static void nfs4_proc_rename_setup(struct rpc_message *msg, struct inode *dir) -{ - struct nfs_server *server = NFS_SERVER(dir); - struct nfs_renameargs *arg = msg->rpc_argp; - struct nfs_renameres *res = msg->rpc_resp; - - msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME]; - arg->bitmask = server->attr_bitmask; - res->server = server; - nfs41_init_sequence(&arg->seq_args, &res->seq_res, 1); -} - -static void nfs4_proc_rename_rpc_prepare(struct rpc_task *task, struct nfs_renamedata *data) -{ - if (nfs4_setup_sequence(NFS_SERVER(data->old_dir), - &data->args.seq_args, - &data->res.seq_res, - task)) - return; - rpc_call_start(task); -} - -static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir, - struct inode *new_dir) -{ - struct nfs_renameres *res = task->tk_msg.rpc_resp; - - if (!nfs4_sequence_done(task, &res->seq_res)) - return 0; - if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN) - return 0; - - update_changeattr(old_dir, &res->old_cinfo); - nfs_post_op_update_inode(old_dir, res->old_fattr); - update_changeattr(new_dir, &res->new_cinfo); - nfs_post_op_update_inode(new_dir, res->new_fattr); - return 1; -} - -static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, - struct inode *new_dir, struct qstr *new_name) -{ - struct nfs_server *server = NFS_SERVER(old_dir); - struct nfs_renameargs arg = { - .old_dir = NFS_FH(old_dir), - .new_dir = NFS_FH(new_dir), - .old_name = old_name, - .new_name = new_name, - .bitmask = server->attr_bitmask, - }; - struct nfs_renameres res = { - .server = server, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME], - .rpc_argp = &arg, - .rpc_resp = &res, - }; - int status = -ENOMEM; - - res.old_fattr = nfs_alloc_fattr(); - res.new_fattr = nfs_alloc_fattr(); - if (res.old_fattr == NULL || res.new_fattr == NULL) - goto out; - - status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); - if (!status) { - update_changeattr(old_dir, &res.old_cinfo); - nfs_post_op_update_inode(old_dir, res.old_fattr); - update_changeattr(new_dir, &res.new_cinfo); - nfs_post_op_update_inode(new_dir, res.new_fattr); - } -out: - nfs_free_fattr(res.new_fattr); - nfs_free_fattr(res.old_fattr); - return status; -} - -static int nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, - struct inode *new_dir, struct qstr *new_name) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(NFS_SERVER(old_dir), - _nfs4_proc_rename(old_dir, old_name, - new_dir, new_name), - &exception); - } while (exception.retry); - return err; -} - -static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct nfs4_link_arg arg = { - .fh = NFS_FH(inode), - .dir_fh = NFS_FH(dir), - .name = name, - .bitmask = server->attr_bitmask, - }; - struct nfs4_link_res res = { - .server = server, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LINK], - .rpc_argp = &arg, - .rpc_resp = &res, - }; - int status = -ENOMEM; - - res.fattr = nfs_alloc_fattr(); - res.dir_attr = nfs_alloc_fattr(); - if (res.fattr == NULL || res.dir_attr == NULL) - goto out; - - status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); - if (!status) { - update_changeattr(dir, &res.cinfo); - nfs_post_op_update_inode(dir, res.dir_attr); - nfs_post_op_update_inode(inode, res.fattr); - } -out: - nfs_free_fattr(res.dir_attr); - nfs_free_fattr(res.fattr); - return status; -} - -static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(NFS_SERVER(inode), - _nfs4_proc_link(inode, dir, name), - &exception); - } while (exception.retry); - return err; -} - -struct nfs4_createdata { - struct rpc_message msg; - struct nfs4_create_arg arg; - struct nfs4_create_res res; - struct nfs_fh fh; - struct nfs_fattr fattr; - struct nfs_fattr dir_fattr; -}; - -static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir, - struct qstr *name, struct iattr *sattr, u32 ftype) -{ - struct nfs4_createdata *data; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (data != NULL) { - struct nfs_server *server = NFS_SERVER(dir); - - data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE]; - data->msg.rpc_argp = &data->arg; - data->msg.rpc_resp = &data->res; - data->arg.dir_fh = NFS_FH(dir); - data->arg.server = server; - data->arg.name = name; - data->arg.attrs = sattr; - data->arg.ftype = ftype; - data->arg.bitmask = server->attr_bitmask; - data->res.server = server; - data->res.fh = &data->fh; - data->res.fattr = &data->fattr; - data->res.dir_fattr = &data->dir_fattr; - nfs_fattr_init(data->res.fattr); - nfs_fattr_init(data->res.dir_fattr); - } - return data; -} - -static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data) -{ - int status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &data->msg, - &data->arg.seq_args, &data->res.seq_res, 1); - if (status == 0) { - update_changeattr(dir, &data->res.dir_cinfo); - nfs_post_op_update_inode(dir, data->res.dir_fattr); - status = nfs_instantiate(dentry, data->res.fh, data->res.fattr); - } - return status; -} - -static void nfs4_free_createdata(struct nfs4_createdata *data) -{ - kfree(data); -} - -static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, - struct page *page, unsigned int len, struct iattr *sattr) -{ - struct nfs4_createdata *data; - int status = -ENAMETOOLONG; - - if (len > NFS4_MAXPATHLEN) - goto out; - - status = -ENOMEM; - data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4LNK); - if (data == NULL) - goto out; - - data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK]; - data->arg.u.symlink.pages = &page; - data->arg.u.symlink.len = len; - - status = nfs4_do_create(dir, dentry, data); - - nfs4_free_createdata(data); -out: - return status; -} - -static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, - struct page *page, unsigned int len, struct iattr *sattr) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(NFS_SERVER(dir), - _nfs4_proc_symlink(dir, dentry, page, - len, sattr), - &exception); - } while (exception.retry); - return err; -} - -static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, - struct iattr *sattr) -{ - struct nfs4_createdata *data; - int status = -ENOMEM; - - data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4DIR); - if (data == NULL) - goto out; - - status = nfs4_do_create(dir, dentry, data); - - nfs4_free_createdata(data); -out: - return status; -} - -static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, - struct iattr *sattr) -{ - struct nfs4_exception exception = { }; - int err; - - sattr->ia_mode &= ~current_umask(); - do { - err = nfs4_handle_exception(NFS_SERVER(dir), - _nfs4_proc_mkdir(dir, dentry, sattr), - &exception); - } while (exception.retry); - return err; -} - -static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, - u64 cookie, struct page **pages, unsigned int count, int plus) -{ - struct inode *dir = dentry->d_inode; - struct nfs4_readdir_arg args = { - .fh = NFS_FH(dir), - .pages = pages, - .pgbase = 0, - .count = count, - .bitmask = NFS_SERVER(dentry->d_inode)->attr_bitmask, - .plus = plus, - }; - struct nfs4_readdir_res res; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READDIR], - .rpc_argp = &args, - .rpc_resp = &res, - .rpc_cred = cred, - }; - int status; - - dprintk("%s: dentry = %s/%s, cookie = %Lu\n", __func__, - dentry->d_parent->d_name.name, - dentry->d_name.name, - (unsigned long long)cookie); - nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args); - res.pgbase = args.pgbase; - status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0); - if (status >= 0) { - memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE); - status += args.pgbase; - } - - nfs_invalidate_atime(dir); - - dprintk("%s: returns %d\n", __func__, status); - return status; -} - -static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, - u64 cookie, struct page **pages, unsigned int count, int plus) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(NFS_SERVER(dentry->d_inode), - _nfs4_proc_readdir(dentry, cred, cookie, - pages, count, plus), - &exception); - } while (exception.retry); - return err; -} - -static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry, - struct iattr *sattr, dev_t rdev) -{ - struct nfs4_createdata *data; - int mode = sattr->ia_mode; - int status = -ENOMEM; - - BUG_ON(!(sattr->ia_valid & ATTR_MODE)); - BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode)); - - data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4SOCK); - if (data == NULL) - goto out; - - if (S_ISFIFO(mode)) - data->arg.ftype = NF4FIFO; - else if (S_ISBLK(mode)) { - data->arg.ftype = NF4BLK; - data->arg.u.device.specdata1 = MAJOR(rdev); - data->arg.u.device.specdata2 = MINOR(rdev); - } - else if (S_ISCHR(mode)) { - data->arg.ftype = NF4CHR; - data->arg.u.device.specdata1 = MAJOR(rdev); - data->arg.u.device.specdata2 = MINOR(rdev); - } - - status = nfs4_do_create(dir, dentry, data); - - nfs4_free_createdata(data); -out: - return status; -} - -static int nfs4_proc_mknod(struct inode *dir, struct dentry *dentry, - struct iattr *sattr, dev_t rdev) -{ - struct nfs4_exception exception = { }; - int err; - - sattr->ia_mode &= ~current_umask(); - do { - err = nfs4_handle_exception(NFS_SERVER(dir), - _nfs4_proc_mknod(dir, dentry, sattr, rdev), - &exception); - } while (exception.retry); - return err; -} - -static int _nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsstat *fsstat) -{ - struct nfs4_statfs_arg args = { - .fh = fhandle, - .bitmask = server->attr_bitmask, - }; - struct nfs4_statfs_res res = { - .fsstat = fsstat, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_STATFS], - .rpc_argp = &args, - .rpc_resp = &res, - }; - - nfs_fattr_init(fsstat->fattr); - return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); -} - -static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsstat *fsstat) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(server, - _nfs4_proc_statfs(server, fhandle, fsstat), - &exception); - } while (exception.retry); - return err; -} - -static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *fsinfo) -{ - struct nfs4_fsinfo_arg args = { - .fh = fhandle, - .bitmask = server->attr_bitmask, - }; - struct nfs4_fsinfo_res res = { - .fsinfo = fsinfo, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSINFO], - .rpc_argp = &args, - .rpc_resp = &res, - }; - - return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); -} - -static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo) -{ - struct nfs4_exception exception = { }; - int err; - - do { - err = nfs4_handle_exception(server, - _nfs4_do_fsinfo(server, fhandle, fsinfo), - &exception); - } while (exception.retry); - return err; -} - -static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo) -{ - nfs_fattr_init(fsinfo->fattr); - return nfs4_do_fsinfo(server, fhandle, fsinfo); -} - -static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_pathconf *pathconf) -{ - struct nfs4_pathconf_arg args = { - .fh = fhandle, - .bitmask = server->attr_bitmask, - }; - struct nfs4_pathconf_res res = { - .pathconf = pathconf, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_PATHCONF], - .rpc_argp = &args, - .rpc_resp = &res, - }; - - /* None of the pathconf attributes are mandatory to implement */ - if ((args.bitmask[0] & nfs4_pathconf_bitmap[0]) == 0) { - memset(pathconf, 0, sizeof(*pathconf)); - return 0; - } - - nfs_fattr_init(pathconf->fattr); - return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); -} - -static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_pathconf *pathconf) -{ - struct nfs4_exception exception = { }; - int err; - - do { - err = nfs4_handle_exception(server, - _nfs4_proc_pathconf(server, fhandle, pathconf), - &exception); - } while (exception.retry); - return err; -} - -void __nfs4_read_done_cb(struct nfs_read_data *data) -{ - nfs_invalidate_atime(data->inode); -} - -static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_read_data *data) -{ - struct nfs_server *server = NFS_SERVER(data->inode); - - if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) { - rpc_restart_call_prepare(task); - return -EAGAIN; - } - - __nfs4_read_done_cb(data); - if (task->tk_status > 0) - renew_lease(server, data->timestamp); - return 0; -} - -static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data) -{ - - dprintk("--> %s\n", __func__); - - if (!nfs4_sequence_done(task, &data->res.seq_res)) - return -EAGAIN; - - return data->read_done_cb ? data->read_done_cb(task, data) : - nfs4_read_done_cb(task, data); -} - -static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message *msg) -{ - data->timestamp = jiffies; - data->read_done_cb = nfs4_read_done_cb; - msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ]; - nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 0); -} - -static void nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) -{ - if (nfs4_setup_sequence(NFS_SERVER(data->inode), - &data->args.seq_args, - &data->res.seq_res, - task)) - return; - rpc_call_start(task); -} - -/* Reset the the nfs_read_data to send the read to the MDS. */ -void nfs4_reset_read(struct rpc_task *task, struct nfs_read_data *data) -{ - dprintk("%s Reset task for i/o through\n", __func__); - put_lseg(data->lseg); - data->lseg = NULL; - /* offsets will differ in the dense stripe case */ - data->args.offset = data->mds_offset; - data->ds_clp = NULL; - data->args.fh = NFS_FH(data->inode); - data->read_done_cb = nfs4_read_done_cb; - task->tk_ops = data->mds_ops; - rpc_task_reset_client(task, NFS_CLIENT(data->inode)); -} -EXPORT_SYMBOL_GPL(nfs4_reset_read); - -static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data) -{ - struct inode *inode = data->inode; - - if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) { - rpc_restart_call_prepare(task); - return -EAGAIN; - } - if (task->tk_status >= 0) { - renew_lease(NFS_SERVER(inode), data->timestamp); - nfs_post_op_update_inode_force_wcc(inode, data->res.fattr); - } - return 0; -} - -static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data) -{ - if (!nfs4_sequence_done(task, &data->res.seq_res)) - return -EAGAIN; - return data->write_done_cb ? data->write_done_cb(task, data) : - nfs4_write_done_cb(task, data); -} - -/* Reset the the nfs_write_data to send the write to the MDS. */ -void nfs4_reset_write(struct rpc_task *task, struct nfs_write_data *data) -{ - dprintk("%s Reset task for i/o through\n", __func__); - put_lseg(data->lseg); - data->lseg = NULL; - data->ds_clp = NULL; - data->write_done_cb = nfs4_write_done_cb; - data->args.fh = NFS_FH(data->inode); - data->args.bitmask = data->res.server->cache_consistency_bitmask; - data->args.offset = data->mds_offset; - data->res.fattr = &data->fattr; - task->tk_ops = data->mds_ops; - rpc_task_reset_client(task, NFS_CLIENT(data->inode)); -} -EXPORT_SYMBOL_GPL(nfs4_reset_write); - -static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_message *msg) -{ - struct nfs_server *server = NFS_SERVER(data->inode); - - if (data->lseg) { - data->args.bitmask = NULL; - data->res.fattr = NULL; - } else - data->args.bitmask = server->cache_consistency_bitmask; - if (!data->write_done_cb) - data->write_done_cb = nfs4_write_done_cb; - data->res.server = server; - data->timestamp = jiffies; - - msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE]; - nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 1); -} - -static void nfs4_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) -{ - if (nfs4_setup_sequence(NFS_SERVER(data->inode), - &data->args.seq_args, - &data->res.seq_res, - task)) - return; - rpc_call_start(task); -} - -static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_write_data *data) -{ - struct inode *inode = data->inode; - - if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) { - rpc_restart_call_prepare(task); - return -EAGAIN; - } - nfs_refresh_inode(inode, data->res.fattr); - return 0; -} - -static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data) -{ - if (!nfs4_sequence_done(task, &data->res.seq_res)) - return -EAGAIN; - return data->write_done_cb(task, data); -} - -static void nfs4_proc_commit_setup(struct nfs_write_data *data, struct rpc_message *msg) -{ - struct nfs_server *server = NFS_SERVER(data->inode); - - if (data->lseg) { - data->args.bitmask = NULL; - data->res.fattr = NULL; - } else - data->args.bitmask = server->cache_consistency_bitmask; - if (!data->write_done_cb) - data->write_done_cb = nfs4_commit_done_cb; - data->res.server = server; - msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT]; - nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 1); -} - -struct nfs4_renewdata { - struct nfs_client *client; - unsigned long timestamp; -}; - -/* - * nfs4_proc_async_renew(): This is not one of the nfs_rpc_ops; it is a special - * standalone procedure for queueing an asynchronous RENEW. - */ -static void nfs4_renew_release(void *calldata) -{ - struct nfs4_renewdata *data = calldata; - struct nfs_client *clp = data->client; - - if (atomic_read(&clp->cl_count) > 1) - nfs4_schedule_state_renewal(clp); - nfs_put_client(clp); - kfree(data); -} - -static void nfs4_renew_done(struct rpc_task *task, void *calldata) -{ - struct nfs4_renewdata *data = calldata; - struct nfs_client *clp = data->client; - unsigned long timestamp = data->timestamp; - - if (task->tk_status < 0) { - /* Unless we're shutting down, schedule state recovery! */ - if (test_bit(NFS_CS_RENEWD, &clp->cl_res_state) == 0) - return; - if (task->tk_status != NFS4ERR_CB_PATH_DOWN) { - nfs4_schedule_lease_recovery(clp); - return; - } - nfs4_schedule_path_down_recovery(clp); - } - do_renew_lease(clp, timestamp); -} - -static const struct rpc_call_ops nfs4_renew_ops = { - .rpc_call_done = nfs4_renew_done, - .rpc_release = nfs4_renew_release, -}; - -static int nfs4_proc_async_renew(struct nfs_client *clp, struct rpc_cred *cred, unsigned renew_flags) -{ - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], - .rpc_argp = clp, - .rpc_cred = cred, - }; - struct nfs4_renewdata *data; - - if (renew_flags == 0) - return 0; - if (!atomic_inc_not_zero(&clp->cl_count)) - return -EIO; - data = kmalloc(sizeof(*data), GFP_NOFS); - if (data == NULL) - return -ENOMEM; - data->client = clp; - data->timestamp = jiffies; - return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT, - &nfs4_renew_ops, data); -} - -static int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred) -{ - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], - .rpc_argp = clp, - .rpc_cred = cred, - }; - unsigned long now = jiffies; - int status; - - status = rpc_call_sync(clp->cl_rpcclient, &msg, 0); - if (status < 0) - return status; - do_renew_lease(clp, now); - return 0; -} - -static inline int nfs4_server_supports_acls(struct nfs_server *server) -{ - return (server->caps & NFS_CAP_ACLS) - && (server->acl_bitmask & ACL4_SUPPORT_ALLOW_ACL) - && (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL); -} - -/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_CACHE_SIZE, and that - * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_CACHE_SIZE) bytes on - * the stack. - */ -#define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT) - -static int buf_to_pages_noslab(const void *buf, size_t buflen, - struct page **pages, unsigned int *pgbase) -{ - struct page *newpage, **spages; - int rc = 0; - size_t len; - spages = pages; - - do { - len = min_t(size_t, PAGE_CACHE_SIZE, buflen); - newpage = alloc_page(GFP_KERNEL); - - if (newpage == NULL) - goto unwind; - memcpy(page_address(newpage), buf, len); - buf += len; - buflen -= len; - *pages++ = newpage; - rc++; - } while (buflen != 0); - - return rc; - -unwind: - for(; rc > 0; rc--) - __free_page(spages[rc-1]); - return -ENOMEM; -} - -struct nfs4_cached_acl { - int cached; - size_t len; - char data[0]; -}; - -static void nfs4_set_cached_acl(struct inode *inode, struct nfs4_cached_acl *acl) -{ - struct nfs_inode *nfsi = NFS_I(inode); - - spin_lock(&inode->i_lock); - kfree(nfsi->nfs4_acl); - nfsi->nfs4_acl = acl; - spin_unlock(&inode->i_lock); -} - -static void nfs4_zap_acl_attr(struct inode *inode) -{ - nfs4_set_cached_acl(inode, NULL); -} - -static inline ssize_t nfs4_read_cached_acl(struct inode *inode, char *buf, size_t buflen) -{ - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs4_cached_acl *acl; - int ret = -ENOENT; - - spin_lock(&inode->i_lock); - acl = nfsi->nfs4_acl; - if (acl == NULL) - goto out; - if (buf == NULL) /* user is just asking for length */ - goto out_len; - if (acl->cached == 0) - goto out; - ret = -ERANGE; /* see getxattr(2) man page */ - if (acl->len > buflen) - goto out; - memcpy(buf, acl->data, acl->len); -out_len: - ret = acl->len; -out: - spin_unlock(&inode->i_lock); - return ret; -} - -static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size_t pgbase, size_t acl_len) -{ - struct nfs4_cached_acl *acl; - - if (pages && acl_len <= PAGE_SIZE) { - acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL); - if (acl == NULL) - goto out; - acl->cached = 1; - _copy_from_pages(acl->data, pages, pgbase, acl_len); - } else { - acl = kmalloc(sizeof(*acl), GFP_KERNEL); - if (acl == NULL) - goto out; - acl->cached = 0; - } - acl->len = acl_len; -out: - nfs4_set_cached_acl(inode, acl); -} - -/* - * The getxattr API returns the required buffer length when called with a - * NULL buf. The NFSv4 acl tool then calls getxattr again after allocating - * the required buf. On a NULL buf, we send a page of data to the server - * guessing that the ACL request can be serviced by a page. If so, we cache - * up to the page of ACL data, and the 2nd call to getxattr is serviced by - * the cache. If not so, we throw away the page, and cache the required - * length. The next getxattr call will then produce another round trip to - * the server, this time with the input buf of the required size. - */ -static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen) -{ - struct page *pages[NFS4ACL_MAXPAGES] = {NULL, }; - struct nfs_getaclargs args = { - .fh = NFS_FH(inode), - .acl_pages = pages, - .acl_len = buflen, - }; - struct nfs_getaclres res = { - .acl_len = buflen, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL], - .rpc_argp = &args, - .rpc_resp = &res, - }; - int ret = -ENOMEM, npages, i, acl_len = 0; - - npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT; - /* As long as we're doing a round trip to the server anyway, - * let's be prepared for a page of acl data. */ - if (npages == 0) - npages = 1; - - /* Add an extra page to handle the bitmap returned */ - npages++; - - for (i = 0; i < npages; i++) { - pages[i] = alloc_page(GFP_KERNEL); - if (!pages[i]) - goto out_free; - } - - /* for decoding across pages */ - res.acl_scratch = alloc_page(GFP_KERNEL); - if (!res.acl_scratch) - goto out_free; - - args.acl_len = npages * PAGE_SIZE; - args.acl_pgbase = 0; - - /* Let decode_getfacl know not to fail if the ACL data is larger than - * the page we send as a guess */ - if (buf == NULL) - res.acl_flags |= NFS4_ACL_LEN_REQUEST; - - dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n", - __func__, buf, buflen, npages, args.acl_len); - ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), - &msg, &args.seq_args, &res.seq_res, 0); - if (ret) - goto out_free; - - acl_len = res.acl_len - res.acl_data_offset; - if (acl_len > args.acl_len) - nfs4_write_cached_acl(inode, NULL, 0, acl_len); - else - nfs4_write_cached_acl(inode, pages, res.acl_data_offset, - acl_len); - if (buf) { - ret = -ERANGE; - if (acl_len > buflen) - goto out_free; - _copy_from_pages(buf, pages, res.acl_data_offset, - acl_len); - } - ret = acl_len; -out_free: - for (i = 0; i < npages; i++) - if (pages[i]) - __free_page(pages[i]); - if (res.acl_scratch) - __free_page(res.acl_scratch); - return ret; -} - -static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen) -{ - struct nfs4_exception exception = { }; - ssize_t ret; - do { - ret = __nfs4_get_acl_uncached(inode, buf, buflen); - if (ret >= 0) - break; - ret = nfs4_handle_exception(NFS_SERVER(inode), ret, &exception); - } while (exception.retry); - return ret; -} - -static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen) -{ - struct nfs_server *server = NFS_SERVER(inode); - int ret; - - if (!nfs4_server_supports_acls(server)) - return -EOPNOTSUPP; - ret = nfs_revalidate_inode(server, inode); - if (ret < 0) - return ret; - if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL) - nfs_zap_acl_cache(inode); - ret = nfs4_read_cached_acl(inode, buf, buflen); - if (ret != -ENOENT) - /* -ENOENT is returned if there is no ACL or if there is an ACL - * but no cached acl data, just the acl length */ - return ret; - return nfs4_get_acl_uncached(inode, buf, buflen); -} - -static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct page *pages[NFS4ACL_MAXPAGES]; - struct nfs_setaclargs arg = { - .fh = NFS_FH(inode), - .acl_pages = pages, - .acl_len = buflen, - }; - struct nfs_setaclres res; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETACL], - .rpc_argp = &arg, - .rpc_resp = &res, - }; - int ret, i; - - if (!nfs4_server_supports_acls(server)) - return -EOPNOTSUPP; - i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase); - if (i < 0) - return i; - nfs_inode_return_delegation(inode); - ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); - - /* - * Free each page after tx, so the only ref left is - * held by the network stack - */ - for (; i > 0; i--) - put_page(pages[i-1]); - - /* - * Acl update can result in inode attribute update. - * so mark the attribute cache invalid. - */ - spin_lock(&inode->i_lock); - NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATTR; - spin_unlock(&inode->i_lock); - nfs_access_zap_cache(inode); - nfs_zap_acl_cache(inode); - return ret; -} - -static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(NFS_SERVER(inode), - __nfs4_proc_set_acl(inode, buf, buflen), - &exception); - } while (exception.retry); - return err; -} - -static int -nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state) -{ - struct nfs_client *clp = server->nfs_client; - - if (task->tk_status >= 0) - return 0; - switch(task->tk_status) { - case -NFS4ERR_DELEG_REVOKED: - case -NFS4ERR_ADMIN_REVOKED: - case -NFS4ERR_BAD_STATEID: - if (state == NULL) - break; - nfs_remove_bad_delegation(state->inode); - case -NFS4ERR_OPENMODE: - if (state == NULL) - break; - nfs4_schedule_stateid_recovery(server, state); - goto wait_on_recovery; - case -NFS4ERR_EXPIRED: - if (state != NULL) - nfs4_schedule_stateid_recovery(server, state); - case -NFS4ERR_STALE_STATEID: - case -NFS4ERR_STALE_CLIENTID: - nfs4_schedule_lease_recovery(clp); - goto wait_on_recovery; -#if defined(CONFIG_NFS_V4_1) - case -NFS4ERR_BADSESSION: - case -NFS4ERR_BADSLOT: - case -NFS4ERR_BAD_HIGH_SLOT: - case -NFS4ERR_DEADSESSION: - case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: - case -NFS4ERR_SEQ_FALSE_RETRY: - case -NFS4ERR_SEQ_MISORDERED: - dprintk("%s ERROR %d, Reset session\n", __func__, - task->tk_status); - nfs4_schedule_session_recovery(clp->cl_session); - task->tk_status = 0; - return -EAGAIN; -#endif /* CONFIG_NFS_V4_1 */ - case -NFS4ERR_DELAY: - nfs_inc_server_stats(server, NFSIOS_DELAY); - case -NFS4ERR_GRACE: - case -EKEYEXPIRED: - rpc_delay(task, NFS4_POLL_RETRY_MAX); - task->tk_status = 0; - return -EAGAIN; - case -NFS4ERR_RETRY_UNCACHED_REP: - case -NFS4ERR_OLD_STATEID: - task->tk_status = 0; - return -EAGAIN; - } - task->tk_status = nfs4_map_errors(task->tk_status); - return 0; -wait_on_recovery: - rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL); - if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0) - rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task); - task->tk_status = 0; - return -EAGAIN; -} - -static void nfs4_construct_boot_verifier(struct nfs_client *clp, - nfs4_verifier *bootverf) -{ - __be32 verf[2]; - - verf[0] = htonl((u32)clp->cl_boot_time.tv_sec); - verf[1] = htonl((u32)clp->cl_boot_time.tv_nsec); - memcpy(bootverf->data, verf, sizeof(bootverf->data)); -} - -int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, - unsigned short port, struct rpc_cred *cred, - struct nfs4_setclientid_res *res) -{ - nfs4_verifier sc_verifier; - struct nfs4_setclientid setclientid = { - .sc_verifier = &sc_verifier, - .sc_prog = program, - .sc_cb_ident = clp->cl_cb_ident, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID], - .rpc_argp = &setclientid, - .rpc_resp = res, - .rpc_cred = cred, - }; - int loop = 0; - int status; - - nfs4_construct_boot_verifier(clp, &sc_verifier); - - for(;;) { - rcu_read_lock(); - setclientid.sc_name_len = scnprintf(setclientid.sc_name, - sizeof(setclientid.sc_name), "%s/%s %s %s %u", - clp->cl_ipaddr, - rpc_peeraddr2str(clp->cl_rpcclient, - RPC_DISPLAY_ADDR), - rpc_peeraddr2str(clp->cl_rpcclient, - RPC_DISPLAY_PROTO), - clp->cl_rpcclient->cl_auth->au_ops->au_name, - clp->cl_id_uniquifier); - setclientid.sc_netid_len = scnprintf(setclientid.sc_netid, - sizeof(setclientid.sc_netid), - rpc_peeraddr2str(clp->cl_rpcclient, - RPC_DISPLAY_NETID)); - setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr, - sizeof(setclientid.sc_uaddr), "%s.%u.%u", - clp->cl_ipaddr, port >> 8, port & 255); - rcu_read_unlock(); - - status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); - if (status != -NFS4ERR_CLID_INUSE) - break; - if (loop != 0) { - ++clp->cl_id_uniquifier; - break; - } - ++loop; - ssleep(clp->cl_lease_time / HZ + 1); - } - return status; -} - -int nfs4_proc_setclientid_confirm(struct nfs_client *clp, - struct nfs4_setclientid_res *arg, - struct rpc_cred *cred) -{ - struct nfs_fsinfo fsinfo; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM], - .rpc_argp = arg, - .rpc_resp = &fsinfo, - .rpc_cred = cred, - }; - unsigned long now; - int status; - - now = jiffies; - status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); - if (status == 0) { - spin_lock(&clp->cl_lock); - clp->cl_lease_time = fsinfo.lease_time * HZ; - clp->cl_last_renewal = now; - spin_unlock(&clp->cl_lock); - } - return status; -} - -struct nfs4_delegreturndata { - struct nfs4_delegreturnargs args; - struct nfs4_delegreturnres res; - struct nfs_fh fh; - nfs4_stateid stateid; - unsigned long timestamp; - struct nfs_fattr fattr; - int rpc_status; -}; - -static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata) -{ - struct nfs4_delegreturndata *data = calldata; - - if (!nfs4_sequence_done(task, &data->res.seq_res)) - return; - - switch (task->tk_status) { - case -NFS4ERR_STALE_STATEID: - case -NFS4ERR_EXPIRED: - case 0: - renew_lease(data->res.server, data->timestamp); - break; - default: - if (nfs4_async_handle_error(task, data->res.server, NULL) == - -EAGAIN) { - rpc_restart_call_prepare(task); - return; - } - } - data->rpc_status = task->tk_status; -} - -static void nfs4_delegreturn_release(void *calldata) -{ - kfree(calldata); -} - -#if defined(CONFIG_NFS_V4_1) -static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data) -{ - struct nfs4_delegreturndata *d_data; - - d_data = (struct nfs4_delegreturndata *)data; - - if (nfs4_setup_sequence(d_data->res.server, - &d_data->args.seq_args, - &d_data->res.seq_res, task)) - return; - rpc_call_start(task); -} -#endif /* CONFIG_NFS_V4_1 */ - -static const struct rpc_call_ops nfs4_delegreturn_ops = { -#if defined(CONFIG_NFS_V4_1) - .rpc_call_prepare = nfs4_delegreturn_prepare, -#endif /* CONFIG_NFS_V4_1 */ - .rpc_call_done = nfs4_delegreturn_done, - .rpc_release = nfs4_delegreturn_release, -}; - -static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync) -{ - struct nfs4_delegreturndata *data; - struct nfs_server *server = NFS_SERVER(inode); - struct rpc_task *task; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DELEGRETURN], - .rpc_cred = cred, - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = server->client, - .rpc_message = &msg, - .callback_ops = &nfs4_delegreturn_ops, - .flags = RPC_TASK_ASYNC, - }; - int status = 0; - - data = kzalloc(sizeof(*data), GFP_NOFS); - if (data == NULL) - return -ENOMEM; - nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 1); - data->args.fhandle = &data->fh; - data->args.stateid = &data->stateid; - data->args.bitmask = server->attr_bitmask; - nfs_copy_fh(&data->fh, NFS_FH(inode)); - nfs4_stateid_copy(&data->stateid, stateid); - data->res.fattr = &data->fattr; - data->res.server = server; - nfs_fattr_init(data->res.fattr); - data->timestamp = jiffies; - data->rpc_status = 0; - - task_setup_data.callback_data = data; - msg.rpc_argp = &data->args; - msg.rpc_resp = &data->res; - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) - return PTR_ERR(task); - if (!issync) - goto out; - status = nfs4_wait_for_completion_rpc_task(task); - if (status != 0) - goto out; - status = data->rpc_status; - if (status != 0) - goto out; - nfs_refresh_inode(inode, &data->fattr); -out: - rpc_put_task(task); - return status; -} - -int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct nfs4_exception exception = { }; - int err; - do { - err = _nfs4_proc_delegreturn(inode, cred, stateid, issync); - switch (err) { - case -NFS4ERR_STALE_STATEID: - case -NFS4ERR_EXPIRED: - case 0: - return 0; - } - err = nfs4_handle_exception(server, err, &exception); - } while (exception.retry); - return err; -} - -#define NFS4_LOCK_MINTIMEOUT (1 * HZ) -#define NFS4_LOCK_MAXTIMEOUT (30 * HZ) - -/* - * sleep, with exponential backoff, and retry the LOCK operation. - */ -static unsigned long -nfs4_set_lock_task_retry(unsigned long timeout) -{ - freezable_schedule_timeout_killable(timeout); - timeout <<= 1; - if (timeout > NFS4_LOCK_MAXTIMEOUT) - return NFS4_LOCK_MAXTIMEOUT; - return timeout; -} - -static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request) -{ - struct inode *inode = state->inode; - struct nfs_server *server = NFS_SERVER(inode); - struct nfs_client *clp = server->nfs_client; - struct nfs_lockt_args arg = { - .fh = NFS_FH(inode), - .fl = request, - }; - struct nfs_lockt_res res = { - .denied = request, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKT], - .rpc_argp = &arg, - .rpc_resp = &res, - .rpc_cred = state->owner->so_cred, - }; - struct nfs4_lock_state *lsp; - int status; - - arg.lock_owner.clientid = clp->cl_clientid; - status = nfs4_set_lock_state(state, request); - if (status != 0) - goto out; - lsp = request->fl_u.nfs4_fl.owner; - arg.lock_owner.id = lsp->ls_seqid.owner_id; - arg.lock_owner.s_dev = server->s_dev; - status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); - switch (status) { - case 0: - request->fl_type = F_UNLCK; - break; - case -NFS4ERR_DENIED: - status = 0; - } - request->fl_ops->fl_release_private(request); -out: - return status; -} - -static int nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request) -{ - struct nfs4_exception exception = { }; - int err; - - do { - err = nfs4_handle_exception(NFS_SERVER(state->inode), - _nfs4_proc_getlk(state, cmd, request), - &exception); - } while (exception.retry); - return err; -} - -static int do_vfs_lock(struct file *file, struct file_lock *fl) -{ - int res = 0; - switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) { - case FL_POSIX: - res = posix_lock_file_wait(file, fl); - break; - case FL_FLOCK: - res = flock_lock_file_wait(file, fl); - break; - default: - BUG(); - } - return res; -} - -struct nfs4_unlockdata { - struct nfs_locku_args arg; - struct nfs_locku_res res; - struct nfs4_lock_state *lsp; - struct nfs_open_context *ctx; - struct file_lock fl; - const struct nfs_server *server; - unsigned long timestamp; -}; - -static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl, - struct nfs_open_context *ctx, - struct nfs4_lock_state *lsp, - struct nfs_seqid *seqid) -{ - struct nfs4_unlockdata *p; - struct inode *inode = lsp->ls_state->inode; - - p = kzalloc(sizeof(*p), GFP_NOFS); - if (p == NULL) - return NULL; - p->arg.fh = NFS_FH(inode); - p->arg.fl = &p->fl; - p->arg.seqid = seqid; - p->res.seqid = seqid; - p->arg.stateid = &lsp->ls_stateid; - p->lsp = lsp; - atomic_inc(&lsp->ls_count); - /* Ensure we don't close file until we're done freeing locks! */ - p->ctx = get_nfs_open_context(ctx); - memcpy(&p->fl, fl, sizeof(p->fl)); - p->server = NFS_SERVER(inode); - return p; -} - -static void nfs4_locku_release_calldata(void *data) -{ - struct nfs4_unlockdata *calldata = data; - nfs_free_seqid(calldata->arg.seqid); - nfs4_put_lock_state(calldata->lsp); - put_nfs_open_context(calldata->ctx); - kfree(calldata); -} - -static void nfs4_locku_done(struct rpc_task *task, void *data) -{ - struct nfs4_unlockdata *calldata = data; - - if (!nfs4_sequence_done(task, &calldata->res.seq_res)) - return; - switch (task->tk_status) { - case 0: - nfs4_stateid_copy(&calldata->lsp->ls_stateid, - &calldata->res.stateid); - renew_lease(calldata->server, calldata->timestamp); - break; - case -NFS4ERR_BAD_STATEID: - case -NFS4ERR_OLD_STATEID: - case -NFS4ERR_STALE_STATEID: - case -NFS4ERR_EXPIRED: - break; - default: - if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN) - rpc_restart_call_prepare(task); - } -} - -static void nfs4_locku_prepare(struct rpc_task *task, void *data) -{ - struct nfs4_unlockdata *calldata = data; - - if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0) - return; - if ((calldata->lsp->ls_flags & NFS_LOCK_INITIALIZED) == 0) { - /* Note: exit _without_ running nfs4_locku_done */ - task->tk_action = NULL; - return; - } - calldata->timestamp = jiffies; - if (nfs4_setup_sequence(calldata->server, - &calldata->arg.seq_args, - &calldata->res.seq_res, task)) - return; - rpc_call_start(task); -} - -static const struct rpc_call_ops nfs4_locku_ops = { - .rpc_call_prepare = nfs4_locku_prepare, - .rpc_call_done = nfs4_locku_done, - .rpc_release = nfs4_locku_release_calldata, -}; - -static struct rpc_task *nfs4_do_unlck(struct file_lock *fl, - struct nfs_open_context *ctx, - struct nfs4_lock_state *lsp, - struct nfs_seqid *seqid) -{ - struct nfs4_unlockdata *data; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU], - .rpc_cred = ctx->cred, - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = NFS_CLIENT(lsp->ls_state->inode), - .rpc_message = &msg, - .callback_ops = &nfs4_locku_ops, - .workqueue = nfsiod_workqueue, - .flags = RPC_TASK_ASYNC, - }; - - /* Ensure this is an unlock - when canceling a lock, the - * canceled lock is passed in, and it won't be an unlock. - */ - fl->fl_type = F_UNLCK; - - data = nfs4_alloc_unlockdata(fl, ctx, lsp, seqid); - if (data == NULL) { - nfs_free_seqid(seqid); - return ERR_PTR(-ENOMEM); - } - - nfs41_init_sequence(&data->arg.seq_args, &data->res.seq_res, 1); - msg.rpc_argp = &data->arg; - msg.rpc_resp = &data->res; - task_setup_data.callback_data = data; - return rpc_run_task(&task_setup_data); -} - -static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) -{ - struct nfs_inode *nfsi = NFS_I(state->inode); - struct nfs_seqid *seqid; - struct nfs4_lock_state *lsp; - struct rpc_task *task; - int status = 0; - unsigned char fl_flags = request->fl_flags; - - status = nfs4_set_lock_state(state, request); - /* Unlock _before_ we do the RPC call */ - request->fl_flags |= FL_EXISTS; - down_read(&nfsi->rwsem); - if (do_vfs_lock(request->fl_file, request) == -ENOENT) { - up_read(&nfsi->rwsem); - goto out; - } - up_read(&nfsi->rwsem); - if (status != 0) - goto out; - /* Is this a delegated lock? */ - if (test_bit(NFS_DELEGATED_STATE, &state->flags)) - goto out; - lsp = request->fl_u.nfs4_fl.owner; - seqid = nfs_alloc_seqid(&lsp->ls_seqid, GFP_KERNEL); - status = -ENOMEM; - if (seqid == NULL) - goto out; - task = nfs4_do_unlck(request, nfs_file_open_context(request->fl_file), lsp, seqid); - status = PTR_ERR(task); - if (IS_ERR(task)) - goto out; - status = nfs4_wait_for_completion_rpc_task(task); - rpc_put_task(task); -out: - request->fl_flags = fl_flags; - return status; -} - -struct nfs4_lockdata { - struct nfs_lock_args arg; - struct nfs_lock_res res; - struct nfs4_lock_state *lsp; - struct nfs_open_context *ctx; - struct file_lock fl; - unsigned long timestamp; - int rpc_status; - int cancelled; - struct nfs_server *server; -}; - -static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl, - struct nfs_open_context *ctx, struct nfs4_lock_state *lsp, - gfp_t gfp_mask) -{ - struct nfs4_lockdata *p; - struct inode *inode = lsp->ls_state->inode; - struct nfs_server *server = NFS_SERVER(inode); - - p = kzalloc(sizeof(*p), gfp_mask); - if (p == NULL) - return NULL; - - p->arg.fh = NFS_FH(inode); - p->arg.fl = &p->fl; - p->arg.open_seqid = nfs_alloc_seqid(&lsp->ls_state->owner->so_seqid, gfp_mask); - if (p->arg.open_seqid == NULL) - goto out_free; - p->arg.lock_seqid = nfs_alloc_seqid(&lsp->ls_seqid, gfp_mask); - if (p->arg.lock_seqid == NULL) - goto out_free_seqid; - p->arg.lock_stateid = &lsp->ls_stateid; - p->arg.lock_owner.clientid = server->nfs_client->cl_clientid; - p->arg.lock_owner.id = lsp->ls_seqid.owner_id; - p->arg.lock_owner.s_dev = server->s_dev; - p->res.lock_seqid = p->arg.lock_seqid; - p->lsp = lsp; - p->server = server; - atomic_inc(&lsp->ls_count); - p->ctx = get_nfs_open_context(ctx); - memcpy(&p->fl, fl, sizeof(p->fl)); - return p; -out_free_seqid: - nfs_free_seqid(p->arg.open_seqid); -out_free: - kfree(p); - return NULL; -} - -static void nfs4_lock_prepare(struct rpc_task *task, void *calldata) -{ - struct nfs4_lockdata *data = calldata; - struct nfs4_state *state = data->lsp->ls_state; - - dprintk("%s: begin!\n", __func__); - if (nfs_wait_on_sequence(data->arg.lock_seqid, task) != 0) - return; - /* Do we need to do an open_to_lock_owner? */ - if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) { - if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0) - return; - data->arg.open_stateid = &state->stateid; - data->arg.new_lock_owner = 1; - data->res.open_seqid = data->arg.open_seqid; - } else - data->arg.new_lock_owner = 0; - data->timestamp = jiffies; - if (nfs4_setup_sequence(data->server, - &data->arg.seq_args, - &data->res.seq_res, task)) - return; - rpc_call_start(task); - dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status); -} - -static void nfs4_recover_lock_prepare(struct rpc_task *task, void *calldata) -{ - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - nfs4_lock_prepare(task, calldata); -} - -static void nfs4_lock_done(struct rpc_task *task, void *calldata) -{ - struct nfs4_lockdata *data = calldata; - - dprintk("%s: begin!\n", __func__); - - if (!nfs4_sequence_done(task, &data->res.seq_res)) - return; - - data->rpc_status = task->tk_status; - if (data->arg.new_lock_owner != 0) { - if (data->rpc_status == 0) - nfs_confirm_seqid(&data->lsp->ls_seqid, 0); - else - goto out; - } - if (data->rpc_status == 0) { - nfs4_stateid_copy(&data->lsp->ls_stateid, &data->res.stateid); - data->lsp->ls_flags |= NFS_LOCK_INITIALIZED; - renew_lease(NFS_SERVER(data->ctx->dentry->d_inode), data->timestamp); - } -out: - dprintk("%s: done, ret = %d!\n", __func__, data->rpc_status); -} - -static void nfs4_lock_release(void *calldata) -{ - struct nfs4_lockdata *data = calldata; - - dprintk("%s: begin!\n", __func__); - nfs_free_seqid(data->arg.open_seqid); - if (data->cancelled != 0) { - struct rpc_task *task; - task = nfs4_do_unlck(&data->fl, data->ctx, data->lsp, - data->arg.lock_seqid); - if (!IS_ERR(task)) - rpc_put_task_async(task); - dprintk("%s: cancelling lock!\n", __func__); - } else - nfs_free_seqid(data->arg.lock_seqid); - nfs4_put_lock_state(data->lsp); - put_nfs_open_context(data->ctx); - kfree(data); - dprintk("%s: done!\n", __func__); -} - -static const struct rpc_call_ops nfs4_lock_ops = { - .rpc_call_prepare = nfs4_lock_prepare, - .rpc_call_done = nfs4_lock_done, - .rpc_release = nfs4_lock_release, -}; - -static const struct rpc_call_ops nfs4_recover_lock_ops = { - .rpc_call_prepare = nfs4_recover_lock_prepare, - .rpc_call_done = nfs4_lock_done, - .rpc_release = nfs4_lock_release, -}; - -static void nfs4_handle_setlk_error(struct nfs_server *server, struct nfs4_lock_state *lsp, int new_lock_owner, int error) -{ - switch (error) { - case -NFS4ERR_ADMIN_REVOKED: - case -NFS4ERR_BAD_STATEID: - lsp->ls_seqid.flags &= ~NFS_SEQID_CONFIRMED; - if (new_lock_owner != 0 || - (lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0) - nfs4_schedule_stateid_recovery(server, lsp->ls_state); - break; - case -NFS4ERR_STALE_STATEID: - lsp->ls_seqid.flags &= ~NFS_SEQID_CONFIRMED; - case -NFS4ERR_EXPIRED: - nfs4_schedule_lease_recovery(server->nfs_client); - }; -} - -static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int recovery_type) -{ - struct nfs4_lockdata *data; - struct rpc_task *task; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCK], - .rpc_cred = state->owner->so_cred, - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = NFS_CLIENT(state->inode), - .rpc_message = &msg, - .callback_ops = &nfs4_lock_ops, - .workqueue = nfsiod_workqueue, - .flags = RPC_TASK_ASYNC, - }; - int ret; - - dprintk("%s: begin!\n", __func__); - data = nfs4_alloc_lockdata(fl, nfs_file_open_context(fl->fl_file), - fl->fl_u.nfs4_fl.owner, - recovery_type == NFS_LOCK_NEW ? GFP_KERNEL : GFP_NOFS); - if (data == NULL) - return -ENOMEM; - if (IS_SETLKW(cmd)) - data->arg.block = 1; - if (recovery_type > NFS_LOCK_NEW) { - if (recovery_type == NFS_LOCK_RECLAIM) - data->arg.reclaim = NFS_LOCK_RECLAIM; - task_setup_data.callback_ops = &nfs4_recover_lock_ops; - } - nfs41_init_sequence(&data->arg.seq_args, &data->res.seq_res, 1); - msg.rpc_argp = &data->arg; - msg.rpc_resp = &data->res; - task_setup_data.callback_data = data; - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) - return PTR_ERR(task); - ret = nfs4_wait_for_completion_rpc_task(task); - if (ret == 0) { - ret = data->rpc_status; - if (ret) - nfs4_handle_setlk_error(data->server, data->lsp, - data->arg.new_lock_owner, ret); - } else - data->cancelled = 1; - rpc_put_task(task); - dprintk("%s: done, ret = %d!\n", __func__, ret); - return ret; -} - -static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request) -{ - struct nfs_server *server = NFS_SERVER(state->inode); - struct nfs4_exception exception = { - .inode = state->inode, - }; - int err; - - do { - /* Cache the lock if possible... */ - if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) - return 0; - err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_RECLAIM); - if (err != -NFS4ERR_DELAY) - break; - nfs4_handle_exception(server, err, &exception); - } while (exception.retry); - return err; -} - -static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request) -{ - struct nfs_server *server = NFS_SERVER(state->inode); - struct nfs4_exception exception = { - .inode = state->inode, - }; - int err; - - err = nfs4_set_lock_state(state, request); - if (err != 0) - return err; - do { - if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) - return 0; - err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_EXPIRED); - switch (err) { - default: - goto out; - case -NFS4ERR_GRACE: - case -NFS4ERR_DELAY: - nfs4_handle_exception(server, err, &exception); - err = 0; - } - } while (exception.retry); -out: - return err; -} - -#if defined(CONFIG_NFS_V4_1) -static int nfs41_check_expired_locks(struct nfs4_state *state) -{ - int status, ret = NFS_OK; - struct nfs4_lock_state *lsp; - struct nfs_server *server = NFS_SERVER(state->inode); - - list_for_each_entry(lsp, &state->lock_states, ls_locks) { - if (lsp->ls_flags & NFS_LOCK_INITIALIZED) { - status = nfs41_test_stateid(server, &lsp->ls_stateid); - if (status != NFS_OK) { - nfs41_free_stateid(server, &lsp->ls_stateid); - lsp->ls_flags &= ~NFS_LOCK_INITIALIZED; - ret = status; - } - } - }; - - return ret; -} - -static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *request) -{ - int status = NFS_OK; - - if (test_bit(LK_STATE_IN_USE, &state->flags)) - status = nfs41_check_expired_locks(state); - if (status == NFS_OK) - return status; - return nfs4_lock_expired(state, request); -} -#endif - -static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) -{ - struct nfs_inode *nfsi = NFS_I(state->inode); - unsigned char fl_flags = request->fl_flags; - int status = -ENOLCK; - - if ((fl_flags & FL_POSIX) && - !test_bit(NFS_STATE_POSIX_LOCKS, &state->flags)) - goto out; - /* Is this a delegated open? */ - status = nfs4_set_lock_state(state, request); - if (status != 0) - goto out; - request->fl_flags |= FL_ACCESS; - status = do_vfs_lock(request->fl_file, request); - if (status < 0) - goto out; - down_read(&nfsi->rwsem); - if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { - /* Yes: cache locks! */ - /* ...but avoid races with delegation recall... */ - request->fl_flags = fl_flags & ~FL_SLEEP; - status = do_vfs_lock(request->fl_file, request); - goto out_unlock; - } - status = _nfs4_do_setlk(state, cmd, request, NFS_LOCK_NEW); - if (status != 0) - goto out_unlock; - /* Note: we always want to sleep here! */ - request->fl_flags = fl_flags | FL_SLEEP; - if (do_vfs_lock(request->fl_file, request) < 0) - printk(KERN_WARNING "NFS: %s: VFS is out of sync with lock " - "manager!\n", __func__); -out_unlock: - up_read(&nfsi->rwsem); -out: - request->fl_flags = fl_flags; - return status; -} - -static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) -{ - struct nfs4_exception exception = { - .state = state, - .inode = state->inode, - }; - int err; - - do { - err = _nfs4_proc_setlk(state, cmd, request); - if (err == -NFS4ERR_DENIED) - err = -EAGAIN; - err = nfs4_handle_exception(NFS_SERVER(state->inode), - err, &exception); - } while (exception.retry); - return err; -} - -static int -nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request) -{ - struct nfs_open_context *ctx; - struct nfs4_state *state; - unsigned long timeout = NFS4_LOCK_MINTIMEOUT; - int status; - - /* verify open state */ - ctx = nfs_file_open_context(filp); - state = ctx->state; - - if (request->fl_start < 0 || request->fl_end < 0) - return -EINVAL; - - if (IS_GETLK(cmd)) { - if (state != NULL) - return nfs4_proc_getlk(state, F_GETLK, request); - return 0; - } - - if (!(IS_SETLK(cmd) || IS_SETLKW(cmd))) - return -EINVAL; - - if (request->fl_type == F_UNLCK) { - if (state != NULL) - return nfs4_proc_unlck(state, cmd, request); - return 0; - } - - if (state == NULL) - return -ENOLCK; - /* - * Don't rely on the VFS having checked the file open mode, - * since it won't do this for flock() locks. - */ - switch (request->fl_type & (F_RDLCK|F_WRLCK|F_UNLCK)) { - case F_RDLCK: - if (!(filp->f_mode & FMODE_READ)) - return -EBADF; - break; - case F_WRLCK: - if (!(filp->f_mode & FMODE_WRITE)) - return -EBADF; - } - - do { - status = nfs4_proc_setlk(state, cmd, request); - if ((status != -EAGAIN) || IS_SETLK(cmd)) - break; - timeout = nfs4_set_lock_task_retry(timeout); - status = -ERESTARTSYS; - if (signalled()) - break; - } while(status < 0); - return status; -} - -int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl) -{ - struct nfs_server *server = NFS_SERVER(state->inode); - struct nfs4_exception exception = { }; - int err; - - err = nfs4_set_lock_state(state, fl); - if (err != 0) - goto out; - do { - err = _nfs4_do_setlk(state, F_SETLK, fl, NFS_LOCK_NEW); - switch (err) { - default: - printk(KERN_ERR "NFS: %s: unhandled error " - "%d.\n", __func__, err); - case 0: - case -ESTALE: - goto out; - case -NFS4ERR_EXPIRED: - nfs4_schedule_stateid_recovery(server, state); - case -NFS4ERR_STALE_CLIENTID: - case -NFS4ERR_STALE_STATEID: - nfs4_schedule_lease_recovery(server->nfs_client); - goto out; - case -NFS4ERR_BADSESSION: - case -NFS4ERR_BADSLOT: - case -NFS4ERR_BAD_HIGH_SLOT: - case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: - case -NFS4ERR_DEADSESSION: - nfs4_schedule_session_recovery(server->nfs_client->cl_session); - goto out; - case -ERESTARTSYS: - /* - * The show must go on: exit, but mark the - * stateid as needing recovery. - */ - case -NFS4ERR_DELEG_REVOKED: - case -NFS4ERR_ADMIN_REVOKED: - case -NFS4ERR_BAD_STATEID: - case -NFS4ERR_OPENMODE: - nfs4_schedule_stateid_recovery(server, state); - err = 0; - goto out; - case -EKEYEXPIRED: - /* - * User RPCSEC_GSS context has expired. - * We cannot recover this stateid now, so - * skip it and allow recovery thread to - * proceed. - */ - err = 0; - goto out; - case -ENOMEM: - case -NFS4ERR_DENIED: - /* kill_proc(fl->fl_pid, SIGLOST, 1); */ - err = 0; - goto out; - case -NFS4ERR_DELAY: - break; - } - err = nfs4_handle_exception(server, err, &exception); - } while (exception.retry); -out: - return err; -} - -struct nfs_release_lockowner_data { - struct nfs4_lock_state *lsp; - struct nfs_server *server; - struct nfs_release_lockowner_args args; -}; - -static void nfs4_release_lockowner_release(void *calldata) -{ - struct nfs_release_lockowner_data *data = calldata; - nfs4_free_lock_state(data->server, data->lsp); - kfree(calldata); -} - -static const struct rpc_call_ops nfs4_release_lockowner_ops = { - .rpc_release = nfs4_release_lockowner_release, -}; - -int nfs4_release_lockowner(struct nfs4_lock_state *lsp) -{ - struct nfs_server *server = lsp->ls_state->owner->so_server; - struct nfs_release_lockowner_data *data; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER], - }; - - if (server->nfs_client->cl_mvops->minor_version != 0) - return -EINVAL; - data = kmalloc(sizeof(*data), GFP_NOFS); - if (!data) - return -ENOMEM; - data->lsp = lsp; - data->server = server; - data->args.lock_owner.clientid = server->nfs_client->cl_clientid; - data->args.lock_owner.id = lsp->ls_seqid.owner_id; - data->args.lock_owner.s_dev = server->s_dev; - msg.rpc_argp = &data->args; - rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, data); - return 0; -} - -#define XATTR_NAME_NFSV4_ACL "system.nfs4_acl" - -static int nfs4_xattr_set_nfs4_acl(struct dentry *dentry, const char *key, - const void *buf, size_t buflen, - int flags, int type) -{ - if (strcmp(key, "") != 0) - return -EINVAL; - - return nfs4_proc_set_acl(dentry->d_inode, buf, buflen); -} - -static int nfs4_xattr_get_nfs4_acl(struct dentry *dentry, const char *key, - void *buf, size_t buflen, int type) -{ - if (strcmp(key, "") != 0) - return -EINVAL; - - return nfs4_proc_get_acl(dentry->d_inode, buf, buflen); -} - -static size_t nfs4_xattr_list_nfs4_acl(struct dentry *dentry, char *list, - size_t list_len, const char *name, - size_t name_len, int type) -{ - size_t len = sizeof(XATTR_NAME_NFSV4_ACL); - - if (!nfs4_server_supports_acls(NFS_SERVER(dentry->d_inode))) - return 0; - - if (list && len <= list_len) - memcpy(list, XATTR_NAME_NFSV4_ACL, len); - return len; -} - -/* - * nfs_fhget will use either the mounted_on_fileid or the fileid - */ -static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr) -{ - if (!(((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) || - (fattr->valid & NFS_ATTR_FATTR_FILEID)) && - (fattr->valid & NFS_ATTR_FATTR_FSID) && - (fattr->valid & NFS_ATTR_FATTR_V4_LOCATIONS))) - return; - - fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE | - NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_V4_REFERRAL; - fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO; - fattr->nlink = 2; -} - -static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, - const struct qstr *name, - struct nfs4_fs_locations *fs_locations, - struct page *page) -{ - struct nfs_server *server = NFS_SERVER(dir); - u32 bitmask[2] = { - [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS, - }; - struct nfs4_fs_locations_arg args = { - .dir_fh = NFS_FH(dir), - .name = name, - .page = page, - .bitmask = bitmask, - }; - struct nfs4_fs_locations_res res = { - .fs_locations = fs_locations, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS], - .rpc_argp = &args, - .rpc_resp = &res, - }; - int status; - - dprintk("%s: start\n", __func__); - - /* Ask for the fileid of the absent filesystem if mounted_on_fileid - * is not supported */ - if (NFS_SERVER(dir)->attr_bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID) - bitmask[1] |= FATTR4_WORD1_MOUNTED_ON_FILEID; - else - bitmask[0] |= FATTR4_WORD0_FILEID; - - nfs_fattr_init(&fs_locations->fattr); - fs_locations->server = server; - fs_locations->nlocations = 0; - status = nfs4_call_sync(client, server, &msg, &args.seq_args, &res.seq_res, 0); - dprintk("%s: returned status = %d\n", __func__, status); - return status; -} - -int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, - const struct qstr *name, - struct nfs4_fs_locations *fs_locations, - struct page *page) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(NFS_SERVER(dir), - _nfs4_proc_fs_locations(client, dir, name, fs_locations, page), - &exception); - } while (exception.retry); - return err; -} - -static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct nfs4_secinfo_flavors *flavors) -{ - int status; - struct nfs4_secinfo_arg args = { - .dir_fh = NFS_FH(dir), - .name = name, - }; - struct nfs4_secinfo_res res = { - .flavors = flavors, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO], - .rpc_argp = &args, - .rpc_resp = &res, - }; - - dprintk("NFS call secinfo %s\n", name->name); - status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0); - dprintk("NFS reply secinfo: %d\n", status); - return status; -} - -int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, - struct nfs4_secinfo_flavors *flavors) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(NFS_SERVER(dir), - _nfs4_proc_secinfo(dir, name, flavors), - &exception); - } while (exception.retry); - return err; -} - -#ifdef CONFIG_NFS_V4_1 -/* - * Check the exchange flags returned by the server for invalid flags, having - * both PNFS and NON_PNFS flags set, and not having one of NON_PNFS, PNFS, or - * DS flags set. - */ -static int nfs4_check_cl_exchange_flags(u32 flags) -{ - if (flags & ~EXCHGID4_FLAG_MASK_R) - goto out_inval; - if ((flags & EXCHGID4_FLAG_USE_PNFS_MDS) && - (flags & EXCHGID4_FLAG_USE_NON_PNFS)) - goto out_inval; - if (!(flags & (EXCHGID4_FLAG_MASK_PNFS))) - goto out_inval; - return NFS_OK; -out_inval: - return -NFS4ERR_INVAL; -} - -static bool -nfs41_same_server_scope(struct server_scope *a, struct server_scope *b) -{ - if (a->server_scope_sz == b->server_scope_sz && - memcmp(a->server_scope, b->server_scope, a->server_scope_sz) == 0) - return true; - - return false; -} - -/* - * nfs4_proc_exchange_id() - * - * Since the clientid has expired, all compounds using sessions - * associated with the stale clientid will be returning - * NFS4ERR_BADSESSION in the sequence operation, and will therefore - * be in some phase of session reset. - */ -int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred) -{ - nfs4_verifier verifier; - struct nfs41_exchange_id_args args = { - .verifier = &verifier, - .client = clp, - .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER, - }; - struct nfs41_exchange_id_res res = { - .client = clp, - }; - int status; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_EXCHANGE_ID], - .rpc_argp = &args, - .rpc_resp = &res, - .rpc_cred = cred, - }; - - dprintk("--> %s\n", __func__); - BUG_ON(clp == NULL); - - nfs4_construct_boot_verifier(clp, &verifier); - - args.id_len = scnprintf(args.id, sizeof(args.id), - "%s/%s/%u", - clp->cl_ipaddr, - clp->cl_rpcclient->cl_nodename, - clp->cl_rpcclient->cl_auth->au_flavor); - - res.server_scope = kzalloc(sizeof(struct server_scope), GFP_KERNEL); - if (unlikely(!res.server_scope)) { - status = -ENOMEM; - goto out; - } - - res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_KERNEL); - if (unlikely(!res.impl_id)) { - status = -ENOMEM; - goto out_server_scope; - } - - status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); - if (!status) - status = nfs4_check_cl_exchange_flags(clp->cl_exchange_flags); - - if (!status) { - /* use the most recent implementation id */ - kfree(clp->impl_id); - clp->impl_id = res.impl_id; - } else - kfree(res.impl_id); - - if (!status) { - if (clp->server_scope && - !nfs41_same_server_scope(clp->server_scope, - res.server_scope)) { - dprintk("%s: server_scope mismatch detected\n", - __func__); - set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state); - kfree(clp->server_scope); - clp->server_scope = NULL; - } - - if (!clp->server_scope) { - clp->server_scope = res.server_scope; - goto out; - } - } - -out_server_scope: - kfree(res.server_scope); -out: - if (clp->impl_id) - dprintk("%s: Server Implementation ID: " - "domain: %s, name: %s, date: %llu,%u\n", - __func__, clp->impl_id->domain, clp->impl_id->name, - clp->impl_id->date.seconds, - clp->impl_id->date.nseconds); - dprintk("<-- %s status= %d\n", __func__, status); - return status; -} - -struct nfs4_get_lease_time_data { - struct nfs4_get_lease_time_args *args; - struct nfs4_get_lease_time_res *res; - struct nfs_client *clp; -}; - -static void nfs4_get_lease_time_prepare(struct rpc_task *task, - void *calldata) -{ - int ret; - struct nfs4_get_lease_time_data *data = - (struct nfs4_get_lease_time_data *)calldata; - - dprintk("--> %s\n", __func__); - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - /* just setup sequence, do not trigger session recovery - since we're invoked within one */ - ret = nfs41_setup_sequence(data->clp->cl_session, - &data->args->la_seq_args, - &data->res->lr_seq_res, task); - - BUG_ON(ret == -EAGAIN); - rpc_call_start(task); - dprintk("<-- %s\n", __func__); -} - -/* - * Called from nfs4_state_manager thread for session setup, so don't recover - * from sequence operation or clientid errors. - */ -static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata) -{ - struct nfs4_get_lease_time_data *data = - (struct nfs4_get_lease_time_data *)calldata; - - dprintk("--> %s\n", __func__); - if (!nfs41_sequence_done(task, &data->res->lr_seq_res)) - return; - switch (task->tk_status) { - case -NFS4ERR_DELAY: - case -NFS4ERR_GRACE: - dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status); - rpc_delay(task, NFS4_POLL_RETRY_MIN); - task->tk_status = 0; - /* fall through */ - case -NFS4ERR_RETRY_UNCACHED_REP: - rpc_restart_call_prepare(task); - return; - } - dprintk("<-- %s\n", __func__); -} - -static const struct rpc_call_ops nfs4_get_lease_time_ops = { - .rpc_call_prepare = nfs4_get_lease_time_prepare, - .rpc_call_done = nfs4_get_lease_time_done, -}; - -int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) -{ - struct rpc_task *task; - struct nfs4_get_lease_time_args args; - struct nfs4_get_lease_time_res res = { - .lr_fsinfo = fsinfo, - }; - struct nfs4_get_lease_time_data data = { - .args = &args, - .res = &res, - .clp = clp, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GET_LEASE_TIME], - .rpc_argp = &args, - .rpc_resp = &res, - }; - struct rpc_task_setup task_setup = { - .rpc_client = clp->cl_rpcclient, - .rpc_message = &msg, - .callback_ops = &nfs4_get_lease_time_ops, - .callback_data = &data, - .flags = RPC_TASK_TIMEOUT, - }; - int status; - - nfs41_init_sequence(&args.la_seq_args, &res.lr_seq_res, 0); - dprintk("--> %s\n", __func__); - task = rpc_run_task(&task_setup); - - if (IS_ERR(task)) - status = PTR_ERR(task); - else { - status = task->tk_status; - rpc_put_task(task); - } - dprintk("<-- %s return %d\n", __func__, status); - - return status; -} - -static struct nfs4_slot *nfs4_alloc_slots(u32 max_slots, gfp_t gfp_flags) -{ - return kcalloc(max_slots, sizeof(struct nfs4_slot), gfp_flags); -} - -static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl, - struct nfs4_slot *new, - u32 max_slots, - u32 ivalue) -{ - struct nfs4_slot *old = NULL; - u32 i; - - spin_lock(&tbl->slot_tbl_lock); - if (new) { - old = tbl->slots; - tbl->slots = new; - tbl->max_slots = max_slots; - } - tbl->highest_used_slotid = -1; /* no slot is currently used */ - for (i = 0; i < tbl->max_slots; i++) - tbl->slots[i].seq_nr = ivalue; - spin_unlock(&tbl->slot_tbl_lock); - kfree(old); -} - -/* - * (re)Initialise a slot table - */ -static int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl, u32 max_reqs, - u32 ivalue) -{ - struct nfs4_slot *new = NULL; - int ret = -ENOMEM; - - dprintk("--> %s: max_reqs=%u, tbl->max_slots %d\n", __func__, - max_reqs, tbl->max_slots); - - /* Does the newly negotiated max_reqs match the existing slot table? */ - if (max_reqs != tbl->max_slots) { - new = nfs4_alloc_slots(max_reqs, GFP_NOFS); - if (!new) - goto out; - } - ret = 0; - - nfs4_add_and_init_slots(tbl, new, max_reqs, ivalue); - dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__, - tbl, tbl->slots, tbl->max_slots); -out: - dprintk("<-- %s: return %d\n", __func__, ret); - return ret; -} - -/* Destroy the slot table */ -static void nfs4_destroy_slot_tables(struct nfs4_session *session) -{ - if (session->fc_slot_table.slots != NULL) { - kfree(session->fc_slot_table.slots); - session->fc_slot_table.slots = NULL; - } - if (session->bc_slot_table.slots != NULL) { - kfree(session->bc_slot_table.slots); - session->bc_slot_table.slots = NULL; - } - return; -} - -/* - * Initialize or reset the forechannel and backchannel tables - */ -static int nfs4_setup_session_slot_tables(struct nfs4_session *ses) -{ - struct nfs4_slot_table *tbl; - int status; - - dprintk("--> %s\n", __func__); - /* Fore channel */ - tbl = &ses->fc_slot_table; - status = nfs4_realloc_slot_table(tbl, ses->fc_attrs.max_reqs, 1); - if (status) /* -ENOMEM */ - return status; - /* Back channel */ - tbl = &ses->bc_slot_table; - status = nfs4_realloc_slot_table(tbl, ses->bc_attrs.max_reqs, 0); - if (status && tbl->slots == NULL) - /* Fore and back channel share a connection so get - * both slot tables or neither */ - nfs4_destroy_slot_tables(ses); - return status; -} - -struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) -{ - struct nfs4_session *session; - struct nfs4_slot_table *tbl; - - session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS); - if (!session) - return NULL; - - tbl = &session->fc_slot_table; - tbl->highest_used_slotid = NFS4_NO_SLOT; - spin_lock_init(&tbl->slot_tbl_lock); - rpc_init_priority_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table"); - init_completion(&tbl->complete); - - tbl = &session->bc_slot_table; - tbl->highest_used_slotid = NFS4_NO_SLOT; - spin_lock_init(&tbl->slot_tbl_lock); - rpc_init_wait_queue(&tbl->slot_tbl_waitq, "BackChannel Slot table"); - init_completion(&tbl->complete); - - session->session_state = 1<<NFS4_SESSION_INITING; - - session->clp = clp; - return session; -} - -void nfs4_destroy_session(struct nfs4_session *session) -{ - struct rpc_xprt *xprt; - - nfs4_proc_destroy_session(session); - - rcu_read_lock(); - xprt = rcu_dereference(session->clp->cl_rpcclient->cl_xprt); - rcu_read_unlock(); - dprintk("%s Destroy backchannel for xprt %p\n", - __func__, xprt); - xprt_destroy_backchannel(xprt, NFS41_BC_MIN_CALLBACKS); - nfs4_destroy_slot_tables(session); - kfree(session); -} - -/* - * Initialize the values to be used by the client in CREATE_SESSION - * If nfs4_init_session set the fore channel request and response sizes, - * use them. - * - * Set the back channel max_resp_sz_cached to zero to force the client to - * always set csa_cachethis to FALSE because the current implementation - * of the back channel DRC only supports caching the CB_SEQUENCE operation. - */ -static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args) -{ - struct nfs4_session *session = args->client->cl_session; - unsigned int mxrqst_sz = session->fc_attrs.max_rqst_sz, - mxresp_sz = session->fc_attrs.max_resp_sz; - - if (mxrqst_sz == 0) - mxrqst_sz = NFS_MAX_FILE_IO_SIZE; - if (mxresp_sz == 0) - mxresp_sz = NFS_MAX_FILE_IO_SIZE; - /* Fore channel attributes */ - args->fc_attrs.max_rqst_sz = mxrqst_sz; - args->fc_attrs.max_resp_sz = mxresp_sz; - args->fc_attrs.max_ops = NFS4_MAX_OPS; - args->fc_attrs.max_reqs = max_session_slots; - - dprintk("%s: Fore Channel : max_rqst_sz=%u max_resp_sz=%u " - "max_ops=%u max_reqs=%u\n", - __func__, - args->fc_attrs.max_rqst_sz, args->fc_attrs.max_resp_sz, - args->fc_attrs.max_ops, args->fc_attrs.max_reqs); - - /* Back channel attributes */ - args->bc_attrs.max_rqst_sz = PAGE_SIZE; - args->bc_attrs.max_resp_sz = PAGE_SIZE; - args->bc_attrs.max_resp_sz_cached = 0; - args->bc_attrs.max_ops = NFS4_MAX_BACK_CHANNEL_OPS; - args->bc_attrs.max_reqs = 1; - - dprintk("%s: Back Channel : max_rqst_sz=%u max_resp_sz=%u " - "max_resp_sz_cached=%u max_ops=%u max_reqs=%u\n", - __func__, - args->bc_attrs.max_rqst_sz, args->bc_attrs.max_resp_sz, - args->bc_attrs.max_resp_sz_cached, args->bc_attrs.max_ops, - args->bc_attrs.max_reqs); -} - -static int nfs4_verify_fore_channel_attrs(struct nfs41_create_session_args *args, struct nfs4_session *session) -{ - struct nfs4_channel_attrs *sent = &args->fc_attrs; - struct nfs4_channel_attrs *rcvd = &session->fc_attrs; - - if (rcvd->max_resp_sz > sent->max_resp_sz) - return -EINVAL; - /* - * Our requested max_ops is the minimum we need; we're not - * prepared to break up compounds into smaller pieces than that. - * So, no point even trying to continue if the server won't - * cooperate: - */ - if (rcvd->max_ops < sent->max_ops) - return -EINVAL; - if (rcvd->max_reqs == 0) - return -EINVAL; - if (rcvd->max_reqs > NFS4_MAX_SLOT_TABLE) - rcvd->max_reqs = NFS4_MAX_SLOT_TABLE; - return 0; -} - -static int nfs4_verify_back_channel_attrs(struct nfs41_create_session_args *args, struct nfs4_session *session) -{ - struct nfs4_channel_attrs *sent = &args->bc_attrs; - struct nfs4_channel_attrs *rcvd = &session->bc_attrs; - - if (rcvd->max_rqst_sz > sent->max_rqst_sz) - return -EINVAL; - if (rcvd->max_resp_sz < sent->max_resp_sz) - return -EINVAL; - if (rcvd->max_resp_sz_cached > sent->max_resp_sz_cached) - return -EINVAL; - /* These would render the backchannel useless: */ - if (rcvd->max_ops != sent->max_ops) - return -EINVAL; - if (rcvd->max_reqs != sent->max_reqs) - return -EINVAL; - return 0; -} - -static int nfs4_verify_channel_attrs(struct nfs41_create_session_args *args, - struct nfs4_session *session) -{ - int ret; - - ret = nfs4_verify_fore_channel_attrs(args, session); - if (ret) - return ret; - return nfs4_verify_back_channel_attrs(args, session); -} - -static int _nfs4_proc_create_session(struct nfs_client *clp) -{ - struct nfs4_session *session = clp->cl_session; - struct nfs41_create_session_args args = { - .client = clp, - .cb_program = NFS4_CALLBACK, - }; - struct nfs41_create_session_res res = { - .client = clp, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE_SESSION], - .rpc_argp = &args, - .rpc_resp = &res, - }; - int status; - - nfs4_init_channel_attrs(&args); - args.flags = (SESSION4_PERSIST | SESSION4_BACK_CHAN); - - status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); - - if (!status) - /* Verify the session's negotiated channel_attrs values */ - status = nfs4_verify_channel_attrs(&args, session); - if (!status) { - /* Increment the clientid slot sequence id */ - clp->cl_seqid++; - } - - return status; -} - -/* - * Issues a CREATE_SESSION operation to the server. - * It is the responsibility of the caller to verify the session is - * expired before calling this routine. - */ -int nfs4_proc_create_session(struct nfs_client *clp) -{ - int status; - unsigned *ptr; - struct nfs4_session *session = clp->cl_session; - - dprintk("--> %s clp=%p session=%p\n", __func__, clp, session); - - status = _nfs4_proc_create_session(clp); - if (status) - goto out; - - /* Init or reset the session slot tables */ - status = nfs4_setup_session_slot_tables(session); - dprintk("slot table setup returned %d\n", status); - if (status) - goto out; - - ptr = (unsigned *)&session->sess_id.data[0]; - dprintk("%s client>seqid %d sessionid %u:%u:%u:%u\n", __func__, - clp->cl_seqid, ptr[0], ptr[1], ptr[2], ptr[3]); -out: - dprintk("<-- %s\n", __func__); - return status; -} - -/* - * Issue the over-the-wire RPC DESTROY_SESSION. - * The caller must serialize access to this routine. - */ -int nfs4_proc_destroy_session(struct nfs4_session *session) -{ - int status = 0; - struct rpc_message msg; - - dprintk("--> nfs4_proc_destroy_session\n"); - - /* session is still being setup */ - if (session->clp->cl_cons_state != NFS_CS_READY) - return status; - - msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_SESSION]; - msg.rpc_argp = session; - msg.rpc_resp = NULL; - msg.rpc_cred = NULL; - status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); - - if (status) - printk(KERN_WARNING - "NFS: Got error %d from the server on DESTROY_SESSION. " - "Session has been destroyed regardless...\n", status); - - dprintk("<-- nfs4_proc_destroy_session\n"); - return status; -} - -int nfs4_init_session(struct nfs_server *server) -{ - struct nfs_client *clp = server->nfs_client; - struct nfs4_session *session; - unsigned int rsize, wsize; - int ret; - - if (!nfs4_has_session(clp)) - return 0; - - session = clp->cl_session; - if (!test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) - return 0; - - rsize = server->rsize; - if (rsize == 0) - rsize = NFS_MAX_FILE_IO_SIZE; - wsize = server->wsize; - if (wsize == 0) - wsize = NFS_MAX_FILE_IO_SIZE; - - session->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead; - session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead; - - ret = nfs4_recover_expired_lease(server); - if (!ret) - ret = nfs4_check_client_ready(clp); - return ret; -} - -int nfs4_init_ds_session(struct nfs_client *clp) -{ - struct nfs4_session *session = clp->cl_session; - int ret; - - if (!test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) - return 0; - - ret = nfs4_client_recover_expired_lease(clp); - if (!ret) - /* Test for the DS role */ - if (!is_ds_client(clp)) - ret = -ENODEV; - if (!ret) - ret = nfs4_check_client_ready(clp); - return ret; - -} -EXPORT_SYMBOL_GPL(nfs4_init_ds_session); - - -/* - * Renew the cl_session lease. - */ -struct nfs4_sequence_data { - struct nfs_client *clp; - struct nfs4_sequence_args args; - struct nfs4_sequence_res res; -}; - -static void nfs41_sequence_release(void *data) -{ - struct nfs4_sequence_data *calldata = data; - struct nfs_client *clp = calldata->clp; - - if (atomic_read(&clp->cl_count) > 1) - nfs4_schedule_state_renewal(clp); - nfs_put_client(clp); - kfree(calldata); -} - -static int nfs41_sequence_handle_errors(struct rpc_task *task, struct nfs_client *clp) -{ - switch(task->tk_status) { - case -NFS4ERR_DELAY: - rpc_delay(task, NFS4_POLL_RETRY_MAX); - return -EAGAIN; - default: - nfs4_schedule_lease_recovery(clp); - } - return 0; -} - -static void nfs41_sequence_call_done(struct rpc_task *task, void *data) -{ - struct nfs4_sequence_data *calldata = data; - struct nfs_client *clp = calldata->clp; - - if (!nfs41_sequence_done(task, task->tk_msg.rpc_resp)) - return; - - if (task->tk_status < 0) { - dprintk("%s ERROR %d\n", __func__, task->tk_status); - if (atomic_read(&clp->cl_count) == 1) - goto out; - - if (nfs41_sequence_handle_errors(task, clp) == -EAGAIN) { - rpc_restart_call_prepare(task); - return; - } - } - dprintk("%s rpc_cred %p\n", __func__, task->tk_msg.rpc_cred); -out: - dprintk("<-- %s\n", __func__); -} - -static void nfs41_sequence_prepare(struct rpc_task *task, void *data) -{ - struct nfs4_sequence_data *calldata = data; - struct nfs_client *clp = calldata->clp; - struct nfs4_sequence_args *args; - struct nfs4_sequence_res *res; - - args = task->tk_msg.rpc_argp; - res = task->tk_msg.rpc_resp; - - if (nfs41_setup_sequence(clp->cl_session, args, res, task)) - return; - rpc_call_start(task); -} - -static const struct rpc_call_ops nfs41_sequence_ops = { - .rpc_call_done = nfs41_sequence_call_done, - .rpc_call_prepare = nfs41_sequence_prepare, - .rpc_release = nfs41_sequence_release, -}; - -static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred) -{ - struct nfs4_sequence_data *calldata; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE], - .rpc_cred = cred, - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = clp->cl_rpcclient, - .rpc_message = &msg, - .callback_ops = &nfs41_sequence_ops, - .flags = RPC_TASK_ASYNC | RPC_TASK_SOFT, - }; - - if (!atomic_inc_not_zero(&clp->cl_count)) - return ERR_PTR(-EIO); - calldata = kzalloc(sizeof(*calldata), GFP_NOFS); - if (calldata == NULL) { - nfs_put_client(clp); - return ERR_PTR(-ENOMEM); - } - nfs41_init_sequence(&calldata->args, &calldata->res, 0); - msg.rpc_argp = &calldata->args; - msg.rpc_resp = &calldata->res; - calldata->clp = clp; - task_setup_data.callback_data = calldata; - - return rpc_run_task(&task_setup_data); -} - -static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cred, unsigned renew_flags) -{ - struct rpc_task *task; - int ret = 0; - - if ((renew_flags & NFS4_RENEW_TIMEOUT) == 0) - return 0; - task = _nfs41_proc_sequence(clp, cred); - if (IS_ERR(task)) - ret = PTR_ERR(task); - else - rpc_put_task_async(task); - dprintk("<-- %s status=%d\n", __func__, ret); - return ret; -} - -static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred) -{ - struct rpc_task *task; - int ret; - - task = _nfs41_proc_sequence(clp, cred); - if (IS_ERR(task)) { - ret = PTR_ERR(task); - goto out; - } - ret = rpc_wait_for_completion_task(task); - if (!ret) { - struct nfs4_sequence_res *res = task->tk_msg.rpc_resp; - - if (task->tk_status == 0) - nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags); - ret = task->tk_status; - } - rpc_put_task(task); -out: - dprintk("<-- %s status=%d\n", __func__, ret); - return ret; -} - -struct nfs4_reclaim_complete_data { - struct nfs_client *clp; - struct nfs41_reclaim_complete_args arg; - struct nfs41_reclaim_complete_res res; -}; - -static void nfs4_reclaim_complete_prepare(struct rpc_task *task, void *data) -{ - struct nfs4_reclaim_complete_data *calldata = data; - - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - if (nfs41_setup_sequence(calldata->clp->cl_session, - &calldata->arg.seq_args, - &calldata->res.seq_res, task)) - return; - - rpc_call_start(task); -} - -static int nfs41_reclaim_complete_handle_errors(struct rpc_task *task, struct nfs_client *clp) -{ - switch(task->tk_status) { - case 0: - case -NFS4ERR_COMPLETE_ALREADY: - case -NFS4ERR_WRONG_CRED: /* What to do here? */ - break; - case -NFS4ERR_DELAY: - rpc_delay(task, NFS4_POLL_RETRY_MAX); - /* fall through */ - case -NFS4ERR_RETRY_UNCACHED_REP: - return -EAGAIN; - default: - nfs4_schedule_lease_recovery(clp); - } - return 0; -} - -static void nfs4_reclaim_complete_done(struct rpc_task *task, void *data) -{ - struct nfs4_reclaim_complete_data *calldata = data; - struct nfs_client *clp = calldata->clp; - struct nfs4_sequence_res *res = &calldata->res.seq_res; - - dprintk("--> %s\n", __func__); - if (!nfs41_sequence_done(task, res)) - return; - - if (nfs41_reclaim_complete_handle_errors(task, clp) == -EAGAIN) { - rpc_restart_call_prepare(task); - return; - } - dprintk("<-- %s\n", __func__); -} - -static void nfs4_free_reclaim_complete_data(void *data) -{ - struct nfs4_reclaim_complete_data *calldata = data; - - kfree(calldata); -} - -static const struct rpc_call_ops nfs4_reclaim_complete_call_ops = { - .rpc_call_prepare = nfs4_reclaim_complete_prepare, - .rpc_call_done = nfs4_reclaim_complete_done, - .rpc_release = nfs4_free_reclaim_complete_data, -}; - -/* - * Issue a global reclaim complete. - */ -static int nfs41_proc_reclaim_complete(struct nfs_client *clp) -{ - struct nfs4_reclaim_complete_data *calldata; - struct rpc_task *task; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RECLAIM_COMPLETE], - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = clp->cl_rpcclient, - .rpc_message = &msg, - .callback_ops = &nfs4_reclaim_complete_call_ops, - .flags = RPC_TASK_ASYNC, - }; - int status = -ENOMEM; - - dprintk("--> %s\n", __func__); - calldata = kzalloc(sizeof(*calldata), GFP_NOFS); - if (calldata == NULL) - goto out; - calldata->clp = clp; - calldata->arg.one_fs = 0; - - nfs41_init_sequence(&calldata->arg.seq_args, &calldata->res.seq_res, 0); - msg.rpc_argp = &calldata->arg; - msg.rpc_resp = &calldata->res; - task_setup_data.callback_data = calldata; - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) { - status = PTR_ERR(task); - goto out; - } - status = nfs4_wait_for_completion_rpc_task(task); - if (status == 0) - status = task->tk_status; - rpc_put_task(task); - return 0; -out: - dprintk("<-- %s status=%d\n", __func__, status); - return status; -} - -static void -nfs4_layoutget_prepare(struct rpc_task *task, void *calldata) -{ - struct nfs4_layoutget *lgp = calldata; - struct nfs_server *server = NFS_SERVER(lgp->args.inode); - - dprintk("--> %s\n", __func__); - /* Note the is a race here, where a CB_LAYOUTRECALL can come in - * right now covering the LAYOUTGET we are about to send. - * However, that is not so catastrophic, and there seems - * to be no way to prevent it completely. - */ - if (nfs4_setup_sequence(server, &lgp->args.seq_args, - &lgp->res.seq_res, task)) - return; - if (pnfs_choose_layoutget_stateid(&lgp->args.stateid, - NFS_I(lgp->args.inode)->layout, - lgp->args.ctx->state)) { - rpc_exit(task, NFS4_OK); - return; - } - rpc_call_start(task); -} - -static void nfs4_layoutget_done(struct rpc_task *task, void *calldata) -{ - struct nfs4_layoutget *lgp = calldata; - struct nfs_server *server = NFS_SERVER(lgp->args.inode); - - dprintk("--> %s\n", __func__); - - if (!nfs4_sequence_done(task, &lgp->res.seq_res)) - return; - - switch (task->tk_status) { - case 0: - break; - case -NFS4ERR_LAYOUTTRYLATER: - case -NFS4ERR_RECALLCONFLICT: - task->tk_status = -NFS4ERR_DELAY; - /* Fall through */ - default: - if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) { - rpc_restart_call_prepare(task); - return; - } - } - dprintk("<-- %s\n", __func__); -} - -static void nfs4_layoutget_release(void *calldata) -{ - struct nfs4_layoutget *lgp = calldata; - - dprintk("--> %s\n", __func__); - put_nfs_open_context(lgp->args.ctx); - kfree(calldata); - dprintk("<-- %s\n", __func__); -} - -static const struct rpc_call_ops nfs4_layoutget_call_ops = { - .rpc_call_prepare = nfs4_layoutget_prepare, - .rpc_call_done = nfs4_layoutget_done, - .rpc_release = nfs4_layoutget_release, -}; - -int nfs4_proc_layoutget(struct nfs4_layoutget *lgp) -{ - struct nfs_server *server = NFS_SERVER(lgp->args.inode); - struct rpc_task *task; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET], - .rpc_argp = &lgp->args, - .rpc_resp = &lgp->res, - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = server->client, - .rpc_message = &msg, - .callback_ops = &nfs4_layoutget_call_ops, - .callback_data = lgp, - .flags = RPC_TASK_ASYNC, - }; - int status = 0; - - dprintk("--> %s\n", __func__); - - lgp->res.layoutp = &lgp->args.layout; - lgp->res.seq_res.sr_slot = NULL; - nfs41_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0); - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) - return PTR_ERR(task); - status = nfs4_wait_for_completion_rpc_task(task); - if (status == 0) - status = task->tk_status; - if (status == 0) - status = pnfs_layout_process(lgp); - rpc_put_task(task); - dprintk("<-- %s status=%d\n", __func__, status); - return status; -} - -static void -nfs4_layoutreturn_prepare(struct rpc_task *task, void *calldata) -{ - struct nfs4_layoutreturn *lrp = calldata; - - dprintk("--> %s\n", __func__); - if (nfs41_setup_sequence(lrp->clp->cl_session, &lrp->args.seq_args, - &lrp->res.seq_res, task)) - return; - rpc_call_start(task); -} - -static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata) -{ - struct nfs4_layoutreturn *lrp = calldata; - struct nfs_server *server; - struct pnfs_layout_hdr *lo = lrp->args.layout; - - dprintk("--> %s\n", __func__); - - if (!nfs4_sequence_done(task, &lrp->res.seq_res)) - return; - - server = NFS_SERVER(lrp->args.inode); - if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) { - rpc_restart_call_prepare(task); - return; - } - spin_lock(&lo->plh_inode->i_lock); - if (task->tk_status == 0) { - if (lrp->res.lrs_present) { - pnfs_set_layout_stateid(lo, &lrp->res.stateid, true); - } else - BUG_ON(!list_empty(&lo->plh_segs)); - } - lo->plh_block_lgets--; - spin_unlock(&lo->plh_inode->i_lock); - dprintk("<-- %s\n", __func__); -} - -static void nfs4_layoutreturn_release(void *calldata) -{ - struct nfs4_layoutreturn *lrp = calldata; - - dprintk("--> %s\n", __func__); - put_layout_hdr(lrp->args.layout); - kfree(calldata); - dprintk("<-- %s\n", __func__); -} - -static const struct rpc_call_ops nfs4_layoutreturn_call_ops = { - .rpc_call_prepare = nfs4_layoutreturn_prepare, - .rpc_call_done = nfs4_layoutreturn_done, - .rpc_release = nfs4_layoutreturn_release, -}; - -int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp) -{ - struct rpc_task *task; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTRETURN], - .rpc_argp = &lrp->args, - .rpc_resp = &lrp->res, - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = lrp->clp->cl_rpcclient, - .rpc_message = &msg, - .callback_ops = &nfs4_layoutreturn_call_ops, - .callback_data = lrp, - }; - int status; - - dprintk("--> %s\n", __func__); - nfs41_init_sequence(&lrp->args.seq_args, &lrp->res.seq_res, 1); - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) - return PTR_ERR(task); - status = task->tk_status; - dprintk("<-- %s status=%d\n", __func__, status); - rpc_put_task(task); - return status; -} - -/* - * Retrieve the list of Data Server devices from the MDS. - */ -static int _nfs4_getdevicelist(struct nfs_server *server, - const struct nfs_fh *fh, - struct pnfs_devicelist *devlist) -{ - struct nfs4_getdevicelist_args args = { - .fh = fh, - .layoutclass = server->pnfs_curr_ld->id, - }; - struct nfs4_getdevicelist_res res = { - .devlist = devlist, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICELIST], - .rpc_argp = &args, - .rpc_resp = &res, - }; - int status; - - dprintk("--> %s\n", __func__); - status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, - &res.seq_res, 0); - dprintk("<-- %s status=%d\n", __func__, status); - return status; -} - -int nfs4_proc_getdevicelist(struct nfs_server *server, - const struct nfs_fh *fh, - struct pnfs_devicelist *devlist) -{ - struct nfs4_exception exception = { }; - int err; - - do { - err = nfs4_handle_exception(server, - _nfs4_getdevicelist(server, fh, devlist), - &exception); - } while (exception.retry); - - dprintk("%s: err=%d, num_devs=%u\n", __func__, - err, devlist->num_devs); - - return err; -} -EXPORT_SYMBOL_GPL(nfs4_proc_getdevicelist); - -static int -_nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev) -{ - struct nfs4_getdeviceinfo_args args = { - .pdev = pdev, - }; - struct nfs4_getdeviceinfo_res res = { - .pdev = pdev, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICEINFO], - .rpc_argp = &args, - .rpc_resp = &res, - }; - int status; - - dprintk("--> %s\n", __func__); - status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); - dprintk("<-- %s status=%d\n", __func__, status); - - return status; -} - -int nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev) -{ - struct nfs4_exception exception = { }; - int err; - - do { - err = nfs4_handle_exception(server, - _nfs4_proc_getdeviceinfo(server, pdev), - &exception); - } while (exception.retry); - return err; -} -EXPORT_SYMBOL_GPL(nfs4_proc_getdeviceinfo); - -static void nfs4_layoutcommit_prepare(struct rpc_task *task, void *calldata) -{ - struct nfs4_layoutcommit_data *data = calldata; - struct nfs_server *server = NFS_SERVER(data->args.inode); - - if (nfs4_setup_sequence(server, &data->args.seq_args, - &data->res.seq_res, task)) - return; - rpc_call_start(task); -} - -static void -nfs4_layoutcommit_done(struct rpc_task *task, void *calldata) -{ - struct nfs4_layoutcommit_data *data = calldata; - struct nfs_server *server = NFS_SERVER(data->args.inode); - - if (!nfs4_sequence_done(task, &data->res.seq_res)) - return; - - switch (task->tk_status) { /* Just ignore these failures */ - case -NFS4ERR_DELEG_REVOKED: /* layout was recalled */ - case -NFS4ERR_BADIOMODE: /* no IOMODE_RW layout for range */ - case -NFS4ERR_BADLAYOUT: /* no layout */ - case -NFS4ERR_GRACE: /* loca_recalim always false */ - task->tk_status = 0; - break; - case 0: - nfs_post_op_update_inode_force_wcc(data->args.inode, - data->res.fattr); - break; - default: - if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) { - rpc_restart_call_prepare(task); - return; - } - } -} - -static void nfs4_layoutcommit_release(void *calldata) -{ - struct nfs4_layoutcommit_data *data = calldata; - struct pnfs_layout_segment *lseg, *tmp; - unsigned long *bitlock = &NFS_I(data->args.inode)->flags; - - pnfs_cleanup_layoutcommit(data); - /* Matched by references in pnfs_set_layoutcommit */ - list_for_each_entry_safe(lseg, tmp, &data->lseg_list, pls_lc_list) { - list_del_init(&lseg->pls_lc_list); - if (test_and_clear_bit(NFS_LSEG_LAYOUTCOMMIT, - &lseg->pls_flags)) - put_lseg(lseg); - } - - clear_bit_unlock(NFS_INO_LAYOUTCOMMITTING, bitlock); - smp_mb__after_clear_bit(); - wake_up_bit(bitlock, NFS_INO_LAYOUTCOMMITTING); - - put_rpccred(data->cred); - kfree(data); -} - -static const struct rpc_call_ops nfs4_layoutcommit_ops = { - .rpc_call_prepare = nfs4_layoutcommit_prepare, - .rpc_call_done = nfs4_layoutcommit_done, - .rpc_release = nfs4_layoutcommit_release, -}; - -int -nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync) -{ - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTCOMMIT], - .rpc_argp = &data->args, - .rpc_resp = &data->res, - .rpc_cred = data->cred, - }; - struct rpc_task_setup task_setup_data = { - .task = &data->task, - .rpc_client = NFS_CLIENT(data->args.inode), - .rpc_message = &msg, - .callback_ops = &nfs4_layoutcommit_ops, - .callback_data = data, - .flags = RPC_TASK_ASYNC, - }; - struct rpc_task *task; - int status = 0; - - dprintk("NFS: %4d initiating layoutcommit call. sync %d " - "lbw: %llu inode %lu\n", - data->task.tk_pid, sync, - data->args.lastbytewritten, - data->args.inode->i_ino); - - nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 1); - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) - return PTR_ERR(task); - if (sync == false) - goto out; - status = nfs4_wait_for_completion_rpc_task(task); - if (status != 0) - goto out; - status = task->tk_status; -out: - dprintk("%s: status %d\n", __func__, status); - rpc_put_task(task); - return status; -} - -static int -_nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors) -{ - struct nfs41_secinfo_no_name_args args = { - .style = SECINFO_STYLE_CURRENT_FH, - }; - struct nfs4_secinfo_res res = { - .flavors = flavors, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO_NO_NAME], - .rpc_argp = &args, - .rpc_resp = &res, - }; - return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); -} - -static int -nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = _nfs41_proc_secinfo_no_name(server, fhandle, info, flavors); - switch (err) { - case 0: - case -NFS4ERR_WRONGSEC: - case -NFS4ERR_NOTSUPP: - goto out; - default: - err = nfs4_handle_exception(server, err, &exception); - } - } while (exception.retry); -out: - return err; -} - -static int -nfs41_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info) -{ - int err; - struct page *page; - rpc_authflavor_t flavor; - struct nfs4_secinfo_flavors *flavors; - - page = alloc_page(GFP_KERNEL); - if (!page) { - err = -ENOMEM; - goto out; - } - - flavors = page_address(page); - err = nfs41_proc_secinfo_no_name(server, fhandle, info, flavors); - - /* - * Fall back on "guess and check" method if - * the server doesn't support SECINFO_NO_NAME - */ - if (err == -NFS4ERR_WRONGSEC || err == -NFS4ERR_NOTSUPP) { - err = nfs4_find_root_sec(server, fhandle, info); - goto out_freepage; - } - if (err) - goto out_freepage; - - flavor = nfs_find_best_sec(flavors); - if (err == 0) - err = nfs4_lookup_root_sec(server, fhandle, info, flavor); - -out_freepage: - put_page(page); - if (err == -EACCES) - return -EPERM; -out: - return err; -} - -static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid) -{ - int status; - struct nfs41_test_stateid_args args = { - .stateid = stateid, - }; - struct nfs41_test_stateid_res res; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_TEST_STATEID], - .rpc_argp = &args, - .rpc_resp = &res, - }; - - nfs41_init_sequence(&args.seq_args, &res.seq_res, 0); - status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 1); - - if (status == NFS_OK) - return res.status; - return status; -} - -static int nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(server, - _nfs41_test_stateid(server, stateid), - &exception); - } while (exception.retry); - return err; -} - -static int _nfs4_free_stateid(struct nfs_server *server, nfs4_stateid *stateid) -{ - struct nfs41_free_stateid_args args = { - .stateid = stateid, - }; - struct nfs41_free_stateid_res res; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FREE_STATEID], - .rpc_argp = &args, - .rpc_resp = &res, - }; - - nfs41_init_sequence(&args.seq_args, &res.seq_res, 0); - return nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 1); -} - -static int nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(server, - _nfs4_free_stateid(server, stateid), - &exception); - } while (exception.retry); - return err; -} - -static bool nfs41_match_stateid(const nfs4_stateid *s1, - const nfs4_stateid *s2) -{ - if (memcmp(s1->other, s2->other, sizeof(s1->other)) != 0) - return false; - - if (s1->seqid == s2->seqid) - return true; - if (s1->seqid == 0 || s2->seqid == 0) - return true; - - return false; -} - -#endif /* CONFIG_NFS_V4_1 */ - -static bool nfs4_match_stateid(const nfs4_stateid *s1, - const nfs4_stateid *s2) -{ - return nfs4_stateid_match(s1, s2); -} - - -static const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { - .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, - .state_flag_bit = NFS_STATE_RECLAIM_REBOOT, - .recover_open = nfs4_open_reclaim, - .recover_lock = nfs4_lock_reclaim, - .establish_clid = nfs4_init_clientid, - .get_clid_cred = nfs4_get_setclientid_cred, -}; - -#if defined(CONFIG_NFS_V4_1) -static const struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = { - .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, - .state_flag_bit = NFS_STATE_RECLAIM_REBOOT, - .recover_open = nfs4_open_reclaim, - .recover_lock = nfs4_lock_reclaim, - .establish_clid = nfs41_init_clientid, - .get_clid_cred = nfs4_get_exchange_id_cred, - .reclaim_complete = nfs41_proc_reclaim_complete, -}; -#endif /* CONFIG_NFS_V4_1 */ - -static const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = { - .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE, - .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE, - .recover_open = nfs4_open_expired, - .recover_lock = nfs4_lock_expired, - .establish_clid = nfs4_init_clientid, - .get_clid_cred = nfs4_get_setclientid_cred, -}; - -#if defined(CONFIG_NFS_V4_1) -static const struct nfs4_state_recovery_ops nfs41_nograce_recovery_ops = { - .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE, - .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE, - .recover_open = nfs41_open_expired, - .recover_lock = nfs41_lock_expired, - .establish_clid = nfs41_init_clientid, - .get_clid_cred = nfs4_get_exchange_id_cred, -}; -#endif /* CONFIG_NFS_V4_1 */ - -static const struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = { - .sched_state_renewal = nfs4_proc_async_renew, - .get_state_renewal_cred_locked = nfs4_get_renew_cred_locked, - .renew_lease = nfs4_proc_renew, -}; - -#if defined(CONFIG_NFS_V4_1) -static const struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = { - .sched_state_renewal = nfs41_proc_async_sequence, - .get_state_renewal_cred_locked = nfs4_get_machine_cred_locked, - .renew_lease = nfs4_proc_sequence, -}; -#endif - -static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = { - .minor_version = 0, - .call_sync = _nfs4_call_sync, - .match_stateid = nfs4_match_stateid, - .find_root_sec = nfs4_find_root_sec, - .reboot_recovery_ops = &nfs40_reboot_recovery_ops, - .nograce_recovery_ops = &nfs40_nograce_recovery_ops, - .state_renewal_ops = &nfs40_state_renewal_ops, -}; - -#if defined(CONFIG_NFS_V4_1) -static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = { - .minor_version = 1, - .call_sync = _nfs4_call_sync_session, - .match_stateid = nfs41_match_stateid, - .find_root_sec = nfs41_find_root_sec, - .reboot_recovery_ops = &nfs41_reboot_recovery_ops, - .nograce_recovery_ops = &nfs41_nograce_recovery_ops, - .state_renewal_ops = &nfs41_state_renewal_ops, -}; -#endif - -const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = { - [0] = &nfs_v4_0_minor_ops, -#if defined(CONFIG_NFS_V4_1) - [1] = &nfs_v4_1_minor_ops, -#endif -}; - -static const struct inode_operations nfs4_file_inode_operations = { - .permission = nfs_permission, - .getattr = nfs_getattr, - .setattr = nfs_setattr, - .getxattr = generic_getxattr, - .setxattr = generic_setxattr, - .listxattr = generic_listxattr, - .removexattr = generic_removexattr, -}; - -const struct nfs_rpc_ops nfs_v4_clientops = { - .version = 4, /* protocol version */ - .dentry_ops = &nfs4_dentry_operations, - .dir_inode_ops = &nfs4_dir_inode_operations, - .file_inode_ops = &nfs4_file_inode_operations, - .file_ops = &nfs4_file_operations, - .getroot = nfs4_proc_get_root, - .getattr = nfs4_proc_getattr, - .setattr = nfs4_proc_setattr, - .lookup = nfs4_proc_lookup, - .access = nfs4_proc_access, - .readlink = nfs4_proc_readlink, - .create = nfs4_proc_create, - .remove = nfs4_proc_remove, - .unlink_setup = nfs4_proc_unlink_setup, - .unlink_rpc_prepare = nfs4_proc_unlink_rpc_prepare, - .unlink_done = nfs4_proc_unlink_done, - .rename = nfs4_proc_rename, - .rename_setup = nfs4_proc_rename_setup, - .rename_rpc_prepare = nfs4_proc_rename_rpc_prepare, - .rename_done = nfs4_proc_rename_done, - .link = nfs4_proc_link, - .symlink = nfs4_proc_symlink, - .mkdir = nfs4_proc_mkdir, - .rmdir = nfs4_proc_remove, - .readdir = nfs4_proc_readdir, - .mknod = nfs4_proc_mknod, - .statfs = nfs4_proc_statfs, - .fsinfo = nfs4_proc_fsinfo, - .pathconf = nfs4_proc_pathconf, - .set_capabilities = nfs4_server_capabilities, - .decode_dirent = nfs4_decode_dirent, - .read_setup = nfs4_proc_read_setup, - .read_rpc_prepare = nfs4_proc_read_rpc_prepare, - .read_done = nfs4_read_done, - .write_setup = nfs4_proc_write_setup, - .write_rpc_prepare = nfs4_proc_write_rpc_prepare, - .write_done = nfs4_write_done, - .commit_setup = nfs4_proc_commit_setup, - .commit_done = nfs4_commit_done, - .lock = nfs4_proc_lock, - .clear_acl_cache = nfs4_zap_acl_attr, - .close_context = nfs4_close_context, - .open_context = nfs4_atomic_open, - .init_client = nfs4_init_client, - .secinfo = nfs4_proc_secinfo, -}; - -static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = { - .prefix = XATTR_NAME_NFSV4_ACL, - .list = nfs4_xattr_list_nfs4_acl, - .get = nfs4_xattr_get_nfs4_acl, - .set = nfs4_xattr_set_nfs4_acl, -}; - -const struct xattr_handler *nfs4_xattr_handlers[] = { - &nfs4_xattr_nfs4_acl_handler, - NULL -}; - -module_param(max_session_slots, ushort, 0644); -MODULE_PARM_DESC(max_session_slots, "Maximum number of outstanding NFSv4.1 " - "requests the client will negotiate"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/ANDROID_3.4.5/fs/nfs/nfs4renewd.c b/ANDROID_3.4.5/fs/nfs/nfs4renewd.c deleted file mode 100644 index dc484c0e..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfs4renewd.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * fs/nfs/nfs4renewd.c - * - * Copyright (c) 2002 The Regents of the University of Michigan. - * All rights reserved. - * - * Kendrick Smith <kmsmith@umich.edu> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Implementation of the NFSv4 "renew daemon", which wakes up periodically to - * send a RENEW, to keep state alive on the server. The daemon is implemented - * as an rpc_task, not a real kernel thread, so it always runs in rpciod's - * context. There is one renewd per nfs_server. - * - */ - -#include <linux/mm.h> -#include <linux/pagemap.h> -#include <linux/sunrpc/sched.h> -#include <linux/sunrpc/clnt.h> - -#include <linux/nfs.h> -#include <linux/nfs4.h> -#include <linux/nfs_fs.h> -#include "nfs4_fs.h" -#include "delegation.h" - -#define NFSDBG_FACILITY NFSDBG_PROC - -void -nfs4_renew_state(struct work_struct *work) -{ - const struct nfs4_state_maintenance_ops *ops; - struct nfs_client *clp = - container_of(work, struct nfs_client, cl_renewd.work); - struct rpc_cred *cred; - long lease; - unsigned long last, now; - unsigned renew_flags = 0; - - ops = clp->cl_mvops->state_renewal_ops; - dprintk("%s: start\n", __func__); - - if (test_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state)) - goto out; - - spin_lock(&clp->cl_lock); - lease = clp->cl_lease_time; - last = clp->cl_last_renewal; - now = jiffies; - /* Are we close to a lease timeout? */ - if (time_after(now, last + lease/3)) - renew_flags |= NFS4_RENEW_TIMEOUT; - if (nfs_delegations_present(clp)) - renew_flags |= NFS4_RENEW_DELEGATION_CB; - - if (renew_flags != 0) { - cred = ops->get_state_renewal_cred_locked(clp); - spin_unlock(&clp->cl_lock); - if (cred == NULL) { - if (!(renew_flags & NFS4_RENEW_DELEGATION_CB)) { - set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); - goto out; - } - nfs_expire_all_delegations(clp); - } else { - /* Queue an asynchronous RENEW. */ - ops->sched_state_renewal(clp, cred, renew_flags); - put_rpccred(cred); - goto out_exp; - } - } else { - dprintk("%s: failed to call renewd. Reason: lease not expired \n", - __func__); - spin_unlock(&clp->cl_lock); - } - nfs4_schedule_state_renewal(clp); -out_exp: - nfs_expire_unreferenced_delegations(clp); -out: - dprintk("%s: done\n", __func__); -} - -void -nfs4_schedule_state_renewal(struct nfs_client *clp) -{ - long timeout; - - spin_lock(&clp->cl_lock); - timeout = (2 * clp->cl_lease_time) / 3 + (long)clp->cl_last_renewal - - (long)jiffies; - if (timeout < 5 * HZ) - timeout = 5 * HZ; - dprintk("%s: requeueing work. Lease period = %ld\n", - __func__, (timeout + HZ - 1) / HZ); - cancel_delayed_work(&clp->cl_renewd); - schedule_delayed_work(&clp->cl_renewd, timeout); - set_bit(NFS_CS_RENEWD, &clp->cl_res_state); - spin_unlock(&clp->cl_lock); -} - -void -nfs4_kill_renewd(struct nfs_client *clp) -{ - cancel_delayed_work_sync(&clp->cl_renewd); -} - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/ANDROID_3.4.5/fs/nfs/nfs4state.c b/ANDROID_3.4.5/fs/nfs/nfs4state.c deleted file mode 100644 index 7f0fcfc1..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfs4state.c +++ /dev/null @@ -1,1877 +0,0 @@ -/* - * fs/nfs/nfs4state.c - * - * Client-side XDR for NFSv4. - * - * Copyright (c) 2002 The Regents of the University of Michigan. - * All rights reserved. - * - * Kendrick Smith <kmsmith@umich.edu> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Implementation of the NFSv4 state model. For the time being, - * this is minimal, but will be made much more complex in a - * subsequent patch. - */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/fs.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_idmap.h> -#include <linux/kthread.h> -#include <linux/module.h> -#include <linux/random.h> -#include <linux/ratelimit.h> -#include <linux/workqueue.h> -#include <linux/bitops.h> -#include <linux/jiffies.h> - -#include "nfs4_fs.h" -#include "callback.h" -#include "delegation.h" -#include "internal.h" -#include "pnfs.h" - -#define OPENOWNER_POOL_SIZE 8 - -const nfs4_stateid zero_stateid; - -static LIST_HEAD(nfs4_clientid_list); - -int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred) -{ - struct nfs4_setclientid_res clid = { - .clientid = clp->cl_clientid, - .confirm = clp->cl_confirm, - }; - unsigned short port; - int status; - - if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state)) - goto do_confirm; - port = nfs_callback_tcpport; - if (clp->cl_addr.ss_family == AF_INET6) - port = nfs_callback_tcpport6; - - status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid); - if (status != 0) - goto out; - clp->cl_clientid = clid.clientid; - clp->cl_confirm = clid.confirm; - set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); -do_confirm: - status = nfs4_proc_setclientid_confirm(clp, &clid, cred); - if (status != 0) - goto out; - clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); - nfs4_schedule_state_renewal(clp); -out: - return status; -} - -struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp) -{ - struct rpc_cred *cred = NULL; - - if (clp->cl_machine_cred != NULL) - cred = get_rpccred(clp->cl_machine_cred); - return cred; -} - -static void nfs4_clear_machine_cred(struct nfs_client *clp) -{ - struct rpc_cred *cred; - - spin_lock(&clp->cl_lock); - cred = clp->cl_machine_cred; - clp->cl_machine_cred = NULL; - spin_unlock(&clp->cl_lock); - if (cred != NULL) - put_rpccred(cred); -} - -static struct rpc_cred * -nfs4_get_renew_cred_server_locked(struct nfs_server *server) -{ - struct rpc_cred *cred = NULL; - struct nfs4_state_owner *sp; - struct rb_node *pos; - - for (pos = rb_first(&server->state_owners); - pos != NULL; - pos = rb_next(pos)) { - sp = rb_entry(pos, struct nfs4_state_owner, so_server_node); - if (list_empty(&sp->so_states)) - continue; - cred = get_rpccred(sp->so_cred); - break; - } - return cred; -} - -/** - * nfs4_get_renew_cred_locked - Acquire credential for a renew operation - * @clp: client state handle - * - * Returns an rpc_cred with reference count bumped, or NULL. - * Caller must hold clp->cl_lock. - */ -struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp) -{ - struct rpc_cred *cred = NULL; - struct nfs_server *server; - - /* Use machine credentials if available */ - cred = nfs4_get_machine_cred_locked(clp); - if (cred != NULL) - goto out; - - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { - cred = nfs4_get_renew_cred_server_locked(server); - if (cred != NULL) - break; - } - rcu_read_unlock(); - -out: - return cred; -} - -#if defined(CONFIG_NFS_V4_1) - -static int nfs41_setup_state_renewal(struct nfs_client *clp) -{ - int status; - struct nfs_fsinfo fsinfo; - - if (!test_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state)) { - nfs4_schedule_state_renewal(clp); - return 0; - } - - status = nfs4_proc_get_lease_time(clp, &fsinfo); - if (status == 0) { - /* Update lease time and schedule renewal */ - spin_lock(&clp->cl_lock); - clp->cl_lease_time = fsinfo.lease_time * HZ; - clp->cl_last_renewal = jiffies; - spin_unlock(&clp->cl_lock); - - nfs4_schedule_state_renewal(clp); - } - - return status; -} - -/* - * Back channel returns NFS4ERR_DELAY for new requests when - * NFS4_SESSION_DRAINING is set so there is no work to be done when draining - * is ended. - */ -static void nfs4_end_drain_session(struct nfs_client *clp) -{ - struct nfs4_session *ses = clp->cl_session; - struct nfs4_slot_table *tbl; - int max_slots; - - if (ses == NULL) - return; - tbl = &ses->fc_slot_table; - if (test_and_clear_bit(NFS4_SESSION_DRAINING, &ses->session_state)) { - spin_lock(&tbl->slot_tbl_lock); - max_slots = tbl->max_slots; - while (max_slots--) { - if (rpc_wake_up_first(&tbl->slot_tbl_waitq, - nfs4_set_task_privileged, - NULL) == NULL) - break; - } - spin_unlock(&tbl->slot_tbl_lock); - } -} - -static int nfs4_wait_on_slot_tbl(struct nfs4_slot_table *tbl) -{ - spin_lock(&tbl->slot_tbl_lock); - if (tbl->highest_used_slotid != NFS4_NO_SLOT) { - INIT_COMPLETION(tbl->complete); - spin_unlock(&tbl->slot_tbl_lock); - return wait_for_completion_interruptible(&tbl->complete); - } - spin_unlock(&tbl->slot_tbl_lock); - return 0; -} - -static int nfs4_begin_drain_session(struct nfs_client *clp) -{ - struct nfs4_session *ses = clp->cl_session; - int ret = 0; - - set_bit(NFS4_SESSION_DRAINING, &ses->session_state); - /* back channel */ - ret = nfs4_wait_on_slot_tbl(&ses->bc_slot_table); - if (ret) - return ret; - /* fore channel */ - return nfs4_wait_on_slot_tbl(&ses->fc_slot_table); -} - -int nfs41_init_clientid(struct nfs_client *clp, struct rpc_cred *cred) -{ - int status; - - if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state)) - goto do_confirm; - nfs4_begin_drain_session(clp); - status = nfs4_proc_exchange_id(clp, cred); - if (status != 0) - goto out; - set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); -do_confirm: - status = nfs4_proc_create_session(clp); - if (status != 0) - goto out; - clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); - nfs41_setup_state_renewal(clp); - nfs_mark_client_ready(clp, NFS_CS_READY); -out: - return status; -} - -struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp) -{ - struct rpc_cred *cred; - - spin_lock(&clp->cl_lock); - cred = nfs4_get_machine_cred_locked(clp); - spin_unlock(&clp->cl_lock); - return cred; -} - -#endif /* CONFIG_NFS_V4_1 */ - -static struct rpc_cred * -nfs4_get_setclientid_cred_server(struct nfs_server *server) -{ - struct nfs_client *clp = server->nfs_client; - struct rpc_cred *cred = NULL; - struct nfs4_state_owner *sp; - struct rb_node *pos; - - spin_lock(&clp->cl_lock); - pos = rb_first(&server->state_owners); - if (pos != NULL) { - sp = rb_entry(pos, struct nfs4_state_owner, so_server_node); - cred = get_rpccred(sp->so_cred); - } - spin_unlock(&clp->cl_lock); - return cred; -} - -/** - * nfs4_get_setclientid_cred - Acquire credential for a setclientid operation - * @clp: client state handle - * - * Returns an rpc_cred with reference count bumped, or NULL. - */ -struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp) -{ - struct nfs_server *server; - struct rpc_cred *cred; - - spin_lock(&clp->cl_lock); - cred = nfs4_get_machine_cred_locked(clp); - spin_unlock(&clp->cl_lock); - if (cred != NULL) - goto out; - - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { - cred = nfs4_get_setclientid_cred_server(server); - if (cred != NULL) - break; - } - rcu_read_unlock(); - -out: - return cred; -} - -static struct nfs4_state_owner * -nfs4_find_state_owner_locked(struct nfs_server *server, struct rpc_cred *cred) -{ - struct rb_node **p = &server->state_owners.rb_node, - *parent = NULL; - struct nfs4_state_owner *sp; - - while (*p != NULL) { - parent = *p; - sp = rb_entry(parent, struct nfs4_state_owner, so_server_node); - - if (cred < sp->so_cred) - p = &parent->rb_left; - else if (cred > sp->so_cred) - p = &parent->rb_right; - else { - if (!list_empty(&sp->so_lru)) - list_del_init(&sp->so_lru); - atomic_inc(&sp->so_count); - return sp; - } - } - return NULL; -} - -static struct nfs4_state_owner * -nfs4_insert_state_owner_locked(struct nfs4_state_owner *new) -{ - struct nfs_server *server = new->so_server; - struct rb_node **p = &server->state_owners.rb_node, - *parent = NULL; - struct nfs4_state_owner *sp; - int err; - - while (*p != NULL) { - parent = *p; - sp = rb_entry(parent, struct nfs4_state_owner, so_server_node); - - if (new->so_cred < sp->so_cred) - p = &parent->rb_left; - else if (new->so_cred > sp->so_cred) - p = &parent->rb_right; - else { - if (!list_empty(&sp->so_lru)) - list_del_init(&sp->so_lru); - atomic_inc(&sp->so_count); - return sp; - } - } - err = ida_get_new(&server->openowner_id, &new->so_seqid.owner_id); - if (err) - return ERR_PTR(err); - rb_link_node(&new->so_server_node, parent, p); - rb_insert_color(&new->so_server_node, &server->state_owners); - return new; -} - -static void -nfs4_remove_state_owner_locked(struct nfs4_state_owner *sp) -{ - struct nfs_server *server = sp->so_server; - - if (!RB_EMPTY_NODE(&sp->so_server_node)) - rb_erase(&sp->so_server_node, &server->state_owners); - ida_remove(&server->openowner_id, sp->so_seqid.owner_id); -} - -static void -nfs4_init_seqid_counter(struct nfs_seqid_counter *sc) -{ - sc->create_time = ktime_get(); - sc->flags = 0; - sc->counter = 0; - spin_lock_init(&sc->lock); - INIT_LIST_HEAD(&sc->list); - rpc_init_wait_queue(&sc->wait, "Seqid_waitqueue"); -} - -static void -nfs4_destroy_seqid_counter(struct nfs_seqid_counter *sc) -{ - rpc_destroy_wait_queue(&sc->wait); -} - -/* - * nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to - * create a new state_owner. - * - */ -static struct nfs4_state_owner * -nfs4_alloc_state_owner(struct nfs_server *server, - struct rpc_cred *cred, - gfp_t gfp_flags) -{ - struct nfs4_state_owner *sp; - - sp = kzalloc(sizeof(*sp), gfp_flags); - if (!sp) - return NULL; - sp->so_server = server; - sp->so_cred = get_rpccred(cred); - spin_lock_init(&sp->so_lock); - INIT_LIST_HEAD(&sp->so_states); - nfs4_init_seqid_counter(&sp->so_seqid); - atomic_set(&sp->so_count, 1); - INIT_LIST_HEAD(&sp->so_lru); - return sp; -} - -static void -nfs4_drop_state_owner(struct nfs4_state_owner *sp) -{ - struct rb_node *rb_node = &sp->so_server_node; - - if (!RB_EMPTY_NODE(rb_node)) { - struct nfs_server *server = sp->so_server; - struct nfs_client *clp = server->nfs_client; - - spin_lock(&clp->cl_lock); - if (!RB_EMPTY_NODE(rb_node)) { - rb_erase(rb_node, &server->state_owners); - RB_CLEAR_NODE(rb_node); - } - spin_unlock(&clp->cl_lock); - } -} - -static void nfs4_free_state_owner(struct nfs4_state_owner *sp) -{ - nfs4_destroy_seqid_counter(&sp->so_seqid); - put_rpccred(sp->so_cred); - kfree(sp); -} - -static void nfs4_gc_state_owners(struct nfs_server *server) -{ - struct nfs_client *clp = server->nfs_client; - struct nfs4_state_owner *sp, *tmp; - unsigned long time_min, time_max; - LIST_HEAD(doomed); - - spin_lock(&clp->cl_lock); - time_max = jiffies; - time_min = (long)time_max - (long)clp->cl_lease_time; - list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) { - /* NB: LRU is sorted so that oldest is at the head */ - if (time_in_range(sp->so_expires, time_min, time_max)) - break; - list_move(&sp->so_lru, &doomed); - nfs4_remove_state_owner_locked(sp); - } - spin_unlock(&clp->cl_lock); - - list_for_each_entry_safe(sp, tmp, &doomed, so_lru) { - list_del(&sp->so_lru); - nfs4_free_state_owner(sp); - } -} - -/** - * nfs4_get_state_owner - Look up a state owner given a credential - * @server: nfs_server to search - * @cred: RPC credential to match - * - * Returns a pointer to an instantiated nfs4_state_owner struct, or NULL. - */ -struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, - struct rpc_cred *cred, - gfp_t gfp_flags) -{ - struct nfs_client *clp = server->nfs_client; - struct nfs4_state_owner *sp, *new; - - spin_lock(&clp->cl_lock); - sp = nfs4_find_state_owner_locked(server, cred); - spin_unlock(&clp->cl_lock); - if (sp != NULL) - goto out; - new = nfs4_alloc_state_owner(server, cred, gfp_flags); - if (new == NULL) - goto out; - do { - if (ida_pre_get(&server->openowner_id, gfp_flags) == 0) - break; - spin_lock(&clp->cl_lock); - sp = nfs4_insert_state_owner_locked(new); - spin_unlock(&clp->cl_lock); - } while (sp == ERR_PTR(-EAGAIN)); - if (sp != new) - nfs4_free_state_owner(new); -out: - nfs4_gc_state_owners(server); - return sp; -} - -/** - * nfs4_put_state_owner - Release a nfs4_state_owner - * @sp: state owner data to release - * - * Note that we keep released state owners on an LRU - * list. - * This caches valid state owners so that they can be - * reused, to avoid the OPEN_CONFIRM on minor version 0. - * It also pins the uniquifier of dropped state owners for - * a while, to ensure that those state owner names are - * never reused. - */ -void nfs4_put_state_owner(struct nfs4_state_owner *sp) -{ - struct nfs_server *server = sp->so_server; - struct nfs_client *clp = server->nfs_client; - - if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock)) - return; - - sp->so_expires = jiffies; - list_add_tail(&sp->so_lru, &server->state_owners_lru); - spin_unlock(&clp->cl_lock); -} - -/** - * nfs4_purge_state_owners - Release all cached state owners - * @server: nfs_server with cached state owners to release - * - * Called at umount time. Remaining state owners will be on - * the LRU with ref count of zero. - */ -void nfs4_purge_state_owners(struct nfs_server *server) -{ - struct nfs_client *clp = server->nfs_client; - struct nfs4_state_owner *sp, *tmp; - LIST_HEAD(doomed); - - spin_lock(&clp->cl_lock); - list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) { - list_move(&sp->so_lru, &doomed); - nfs4_remove_state_owner_locked(sp); - } - spin_unlock(&clp->cl_lock); - - list_for_each_entry_safe(sp, tmp, &doomed, so_lru) { - list_del(&sp->so_lru); - nfs4_free_state_owner(sp); - } -} - -static struct nfs4_state * -nfs4_alloc_open_state(void) -{ - struct nfs4_state *state; - - state = kzalloc(sizeof(*state), GFP_NOFS); - if (!state) - return NULL; - atomic_set(&state->count, 1); - INIT_LIST_HEAD(&state->lock_states); - spin_lock_init(&state->state_lock); - seqlock_init(&state->seqlock); - return state; -} - -void -nfs4_state_set_mode_locked(struct nfs4_state *state, fmode_t fmode) -{ - if (state->state == fmode) - return; - /* NB! List reordering - see the reclaim code for why. */ - if ((fmode & FMODE_WRITE) != (state->state & FMODE_WRITE)) { - if (fmode & FMODE_WRITE) - list_move(&state->open_states, &state->owner->so_states); - else - list_move_tail(&state->open_states, &state->owner->so_states); - } - state->state = fmode; -} - -static struct nfs4_state * -__nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) -{ - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs4_state *state; - - list_for_each_entry(state, &nfsi->open_states, inode_states) { - if (state->owner != owner) - continue; - if (atomic_inc_not_zero(&state->count)) - return state; - } - return NULL; -} - -static void -nfs4_free_open_state(struct nfs4_state *state) -{ - kfree(state); -} - -struct nfs4_state * -nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner) -{ - struct nfs4_state *state, *new; - struct nfs_inode *nfsi = NFS_I(inode); - - spin_lock(&inode->i_lock); - state = __nfs4_find_state_byowner(inode, owner); - spin_unlock(&inode->i_lock); - if (state) - goto out; - new = nfs4_alloc_open_state(); - spin_lock(&owner->so_lock); - spin_lock(&inode->i_lock); - state = __nfs4_find_state_byowner(inode, owner); - if (state == NULL && new != NULL) { - state = new; - state->owner = owner; - atomic_inc(&owner->so_count); - list_add(&state->inode_states, &nfsi->open_states); - ihold(inode); - state->inode = inode; - spin_unlock(&inode->i_lock); - /* Note: The reclaim code dictates that we add stateless - * and read-only stateids to the end of the list */ - list_add_tail(&state->open_states, &owner->so_states); - spin_unlock(&owner->so_lock); - } else { - spin_unlock(&inode->i_lock); - spin_unlock(&owner->so_lock); - if (new) - nfs4_free_open_state(new); - } -out: - return state; -} - -void nfs4_put_open_state(struct nfs4_state *state) -{ - struct inode *inode = state->inode; - struct nfs4_state_owner *owner = state->owner; - - if (!atomic_dec_and_lock(&state->count, &owner->so_lock)) - return; - spin_lock(&inode->i_lock); - list_del(&state->inode_states); - list_del(&state->open_states); - spin_unlock(&inode->i_lock); - spin_unlock(&owner->so_lock); - iput(inode); - nfs4_free_open_state(state); - nfs4_put_state_owner(owner); -} - -/* - * Close the current file. - */ -static void __nfs4_close(struct nfs4_state *state, - fmode_t fmode, gfp_t gfp_mask, int wait) -{ - struct nfs4_state_owner *owner = state->owner; - int call_close = 0; - fmode_t newstate; - - atomic_inc(&owner->so_count); - /* Protect against nfs4_find_state() */ - spin_lock(&owner->so_lock); - switch (fmode & (FMODE_READ | FMODE_WRITE)) { - case FMODE_READ: - state->n_rdonly--; - break; - case FMODE_WRITE: - state->n_wronly--; - break; - case FMODE_READ|FMODE_WRITE: - state->n_rdwr--; - } - newstate = FMODE_READ|FMODE_WRITE; - if (state->n_rdwr == 0) { - if (state->n_rdonly == 0) { - newstate &= ~FMODE_READ; - call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags); - call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags); - } - if (state->n_wronly == 0) { - newstate &= ~FMODE_WRITE; - call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags); - call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags); - } - if (newstate == 0) - clear_bit(NFS_DELEGATED_STATE, &state->flags); - } - nfs4_state_set_mode_locked(state, newstate); - spin_unlock(&owner->so_lock); - - if (!call_close) { - nfs4_put_open_state(state); - nfs4_put_state_owner(owner); - } else { - bool roc = pnfs_roc(state->inode); - - nfs4_do_close(state, gfp_mask, wait, roc); - } -} - -void nfs4_close_state(struct nfs4_state *state, fmode_t fmode) -{ - __nfs4_close(state, fmode, GFP_NOFS, 0); -} - -void nfs4_close_sync(struct nfs4_state *state, fmode_t fmode) -{ - __nfs4_close(state, fmode, GFP_KERNEL, 1); -} - -/* - * Search the state->lock_states for an existing lock_owner - * that is compatible with current->files - */ -static struct nfs4_lock_state * -__nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner, pid_t fl_pid, unsigned int type) -{ - struct nfs4_lock_state *pos; - list_for_each_entry(pos, &state->lock_states, ls_locks) { - if (type != NFS4_ANY_LOCK_TYPE && pos->ls_owner.lo_type != type) - continue; - switch (pos->ls_owner.lo_type) { - case NFS4_POSIX_LOCK_TYPE: - if (pos->ls_owner.lo_u.posix_owner != fl_owner) - continue; - break; - case NFS4_FLOCK_LOCK_TYPE: - if (pos->ls_owner.lo_u.flock_owner != fl_pid) - continue; - } - atomic_inc(&pos->ls_count); - return pos; - } - return NULL; -} - -/* - * Return a compatible lock_state. If no initialized lock_state structure - * exists, return an uninitialized one. - * - */ -static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner, pid_t fl_pid, unsigned int type) -{ - struct nfs4_lock_state *lsp; - struct nfs_server *server = state->owner->so_server; - - lsp = kzalloc(sizeof(*lsp), GFP_NOFS); - if (lsp == NULL) - return NULL; - nfs4_init_seqid_counter(&lsp->ls_seqid); - atomic_set(&lsp->ls_count, 1); - lsp->ls_state = state; - lsp->ls_owner.lo_type = type; - switch (lsp->ls_owner.lo_type) { - case NFS4_FLOCK_LOCK_TYPE: - lsp->ls_owner.lo_u.flock_owner = fl_pid; - break; - case NFS4_POSIX_LOCK_TYPE: - lsp->ls_owner.lo_u.posix_owner = fl_owner; - break; - default: - goto out_free; - } - lsp->ls_seqid.owner_id = ida_simple_get(&server->lockowner_id, 0, 0, GFP_NOFS); - if (lsp->ls_seqid.owner_id < 0) - goto out_free; - INIT_LIST_HEAD(&lsp->ls_locks); - return lsp; -out_free: - kfree(lsp); - return NULL; -} - -void nfs4_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp) -{ - ida_simple_remove(&server->lockowner_id, lsp->ls_seqid.owner_id); - nfs4_destroy_seqid_counter(&lsp->ls_seqid); - kfree(lsp); -} - -/* - * Return a compatible lock_state. If no initialized lock_state structure - * exists, return an uninitialized one. - * - */ -static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner, pid_t pid, unsigned int type) -{ - struct nfs4_lock_state *lsp, *new = NULL; - - for(;;) { - spin_lock(&state->state_lock); - lsp = __nfs4_find_lock_state(state, owner, pid, type); - if (lsp != NULL) - break; - if (new != NULL) { - list_add(&new->ls_locks, &state->lock_states); - set_bit(LK_STATE_IN_USE, &state->flags); - lsp = new; - new = NULL; - break; - } - spin_unlock(&state->state_lock); - new = nfs4_alloc_lock_state(state, owner, pid, type); - if (new == NULL) - return NULL; - } - spin_unlock(&state->state_lock); - if (new != NULL) - nfs4_free_lock_state(state->owner->so_server, new); - return lsp; -} - -/* - * Release reference to lock_state, and free it if we see that - * it is no longer in use - */ -void nfs4_put_lock_state(struct nfs4_lock_state *lsp) -{ - struct nfs4_state *state; - - if (lsp == NULL) - return; - state = lsp->ls_state; - if (!atomic_dec_and_lock(&lsp->ls_count, &state->state_lock)) - return; - list_del(&lsp->ls_locks); - if (list_empty(&state->lock_states)) - clear_bit(LK_STATE_IN_USE, &state->flags); - spin_unlock(&state->state_lock); - if (lsp->ls_flags & NFS_LOCK_INITIALIZED) { - if (nfs4_release_lockowner(lsp) == 0) - return; - } - nfs4_free_lock_state(lsp->ls_state->owner->so_server, lsp); -} - -static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src) -{ - struct nfs4_lock_state *lsp = src->fl_u.nfs4_fl.owner; - - dst->fl_u.nfs4_fl.owner = lsp; - atomic_inc(&lsp->ls_count); -} - -static void nfs4_fl_release_lock(struct file_lock *fl) -{ - nfs4_put_lock_state(fl->fl_u.nfs4_fl.owner); -} - -static const struct file_lock_operations nfs4_fl_lock_ops = { - .fl_copy_lock = nfs4_fl_copy_lock, - .fl_release_private = nfs4_fl_release_lock, -}; - -int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl) -{ - struct nfs4_lock_state *lsp; - - if (fl->fl_ops != NULL) - return 0; - if (fl->fl_flags & FL_POSIX) - lsp = nfs4_get_lock_state(state, fl->fl_owner, 0, NFS4_POSIX_LOCK_TYPE); - else if (fl->fl_flags & FL_FLOCK) - lsp = nfs4_get_lock_state(state, NULL, fl->fl_pid, - NFS4_FLOCK_LOCK_TYPE); - else - return -EINVAL; - if (lsp == NULL) - return -ENOMEM; - fl->fl_u.nfs4_fl.owner = lsp; - fl->fl_ops = &nfs4_fl_lock_ops; - return 0; -} - -static bool nfs4_copy_lock_stateid(nfs4_stateid *dst, struct nfs4_state *state, - fl_owner_t fl_owner, pid_t fl_pid) -{ - struct nfs4_lock_state *lsp; - bool ret = false; - - if (test_bit(LK_STATE_IN_USE, &state->flags) == 0) - goto out; - - spin_lock(&state->state_lock); - lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE); - if (lsp != NULL && (lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0) { - nfs4_stateid_copy(dst, &lsp->ls_stateid); - ret = true; - } - spin_unlock(&state->state_lock); - nfs4_put_lock_state(lsp); -out: - return ret; -} - -static void nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state) -{ - int seq; - - do { - seq = read_seqbegin(&state->seqlock); - nfs4_stateid_copy(dst, &state->stateid); - } while (read_seqretry(&state->seqlock, seq)); -} - -/* - * Byte-range lock aware utility to initialize the stateid of read/write - * requests. - */ -void nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state, - fmode_t fmode, fl_owner_t fl_owner, pid_t fl_pid) -{ - if (nfs4_copy_delegation_stateid(dst, state->inode, fmode)) - return; - if (nfs4_copy_lock_stateid(dst, state, fl_owner, fl_pid)) - return; - nfs4_copy_open_stateid(dst, state); -} - -struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask) -{ - struct nfs_seqid *new; - - new = kmalloc(sizeof(*new), gfp_mask); - if (new != NULL) { - new->sequence = counter; - INIT_LIST_HEAD(&new->list); - new->task = NULL; - } - return new; -} - -void nfs_release_seqid(struct nfs_seqid *seqid) -{ - struct nfs_seqid_counter *sequence; - - if (list_empty(&seqid->list)) - return; - sequence = seqid->sequence; - spin_lock(&sequence->lock); - list_del_init(&seqid->list); - if (!list_empty(&sequence->list)) { - struct nfs_seqid *next; - - next = list_first_entry(&sequence->list, - struct nfs_seqid, list); - rpc_wake_up_queued_task(&sequence->wait, next->task); - } - spin_unlock(&sequence->lock); -} - -void nfs_free_seqid(struct nfs_seqid *seqid) -{ - nfs_release_seqid(seqid); - kfree(seqid); -} - -/* - * Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or - * failed with a seqid incrementing error - - * see comments nfs_fs.h:seqid_mutating_error() - */ -static void nfs_increment_seqid(int status, struct nfs_seqid *seqid) -{ - BUG_ON(list_first_entry(&seqid->sequence->list, struct nfs_seqid, list) != seqid); - switch (status) { - case 0: - break; - case -NFS4ERR_BAD_SEQID: - if (seqid->sequence->flags & NFS_SEQID_CONFIRMED) - return; - pr_warn_ratelimited("NFS: v4 server returned a bad" - " sequence-id error on an" - " unconfirmed sequence %p!\n", - seqid->sequence); - case -NFS4ERR_STALE_CLIENTID: - case -NFS4ERR_STALE_STATEID: - case -NFS4ERR_BAD_STATEID: - case -NFS4ERR_BADXDR: - case -NFS4ERR_RESOURCE: - case -NFS4ERR_NOFILEHANDLE: - /* Non-seqid mutating errors */ - return; - }; - /* - * Note: no locking needed as we are guaranteed to be first - * on the sequence list - */ - seqid->sequence->counter++; -} - -void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid) -{ - struct nfs4_state_owner *sp = container_of(seqid->sequence, - struct nfs4_state_owner, so_seqid); - struct nfs_server *server = sp->so_server; - - if (status == -NFS4ERR_BAD_SEQID) - nfs4_drop_state_owner(sp); - if (!nfs4_has_session(server->nfs_client)) - nfs_increment_seqid(status, seqid); -} - -/* - * Increment the seqid if the LOCK/LOCKU succeeded, or - * failed with a seqid incrementing error - - * see comments nfs_fs.h:seqid_mutating_error() - */ -void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid) -{ - nfs_increment_seqid(status, seqid); -} - -int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task) -{ - struct nfs_seqid_counter *sequence = seqid->sequence; - int status = 0; - - spin_lock(&sequence->lock); - seqid->task = task; - if (list_empty(&seqid->list)) - list_add_tail(&seqid->list, &sequence->list); - if (list_first_entry(&sequence->list, struct nfs_seqid, list) == seqid) - goto unlock; - rpc_sleep_on(&sequence->wait, task, NULL); - status = -EAGAIN; -unlock: - spin_unlock(&sequence->lock); - return status; -} - -static int nfs4_run_state_manager(void *); - -static void nfs4_clear_state_manager_bit(struct nfs_client *clp) -{ - smp_mb__before_clear_bit(); - clear_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state); - smp_mb__after_clear_bit(); - wake_up_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING); - rpc_wake_up(&clp->cl_rpcwaitq); -} - -/* - * Schedule the nfs_client asynchronous state management routine - */ -void nfs4_schedule_state_manager(struct nfs_client *clp) -{ - struct task_struct *task; - char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1]; - - if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0) - return; - __module_get(THIS_MODULE); - atomic_inc(&clp->cl_count); - - /* The rcu_read_lock() is not strictly necessary, as the state - * manager is the only thread that ever changes the rpc_xprt - * after it's initialized. At this point, we're single threaded. */ - rcu_read_lock(); - snprintf(buf, sizeof(buf), "%s-manager", - rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); - rcu_read_unlock(); - task = kthread_run(nfs4_run_state_manager, clp, buf); - if (IS_ERR(task)) { - printk(KERN_ERR "%s: kthread_run: %ld\n", - __func__, PTR_ERR(task)); - nfs4_clear_state_manager_bit(clp); - nfs_put_client(clp); - module_put(THIS_MODULE); - } -} - -/* - * Schedule a lease recovery attempt - */ -void nfs4_schedule_lease_recovery(struct nfs_client *clp) -{ - if (!clp) - return; - if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) - set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state); - nfs4_schedule_state_manager(clp); -} -EXPORT_SYMBOL_GPL(nfs4_schedule_lease_recovery); - -/* - * nfs40_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN - * @clp: client to process - * - * Set the NFS4CLNT_LEASE_EXPIRED state in order to force a - * resend of the SETCLIENTID and hence re-establish the - * callback channel. Then return all existing delegations. - */ -static void nfs40_handle_cb_pathdown(struct nfs_client *clp) -{ - set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); - nfs_expire_all_delegations(clp); -} - -void nfs4_schedule_path_down_recovery(struct nfs_client *clp) -{ - nfs40_handle_cb_pathdown(clp); - nfs4_schedule_state_manager(clp); -} - -static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state) -{ - - set_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags); - /* Don't recover state that expired before the reboot */ - if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) { - clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags); - return 0; - } - set_bit(NFS_OWNER_RECLAIM_REBOOT, &state->owner->so_flags); - set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state); - return 1; -} - -static int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state) -{ - set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags); - clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags); - set_bit(NFS_OWNER_RECLAIM_NOGRACE, &state->owner->so_flags); - set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state); - return 1; -} - -void nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_state *state) -{ - struct nfs_client *clp = server->nfs_client; - - nfs4_state_mark_reclaim_nograce(clp, state); - nfs4_schedule_state_manager(clp); -} -EXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery); - -void nfs_inode_find_state_and_recover(struct inode *inode, - const nfs4_stateid *stateid) -{ - struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs_open_context *ctx; - struct nfs4_state *state; - bool found = false; - - spin_lock(&inode->i_lock); - list_for_each_entry(ctx, &nfsi->open_files, list) { - state = ctx->state; - if (state == NULL) - continue; - if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) - continue; - if (!nfs4_stateid_match(&state->stateid, stateid)) - continue; - nfs4_state_mark_reclaim_nograce(clp, state); - found = true; - } - spin_unlock(&inode->i_lock); - if (found) - nfs4_schedule_state_manager(clp); -} - - -static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops) -{ - struct inode *inode = state->inode; - struct nfs_inode *nfsi = NFS_I(inode); - struct file_lock *fl; - int status = 0; - - if (inode->i_flock == NULL) - return 0; - - /* Guard against delegation returns and new lock/unlock calls */ - down_write(&nfsi->rwsem); - /* Protect inode->i_flock using the BKL */ - lock_flocks(); - for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { - if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) - continue; - if (nfs_file_open_context(fl->fl_file)->state != state) - continue; - unlock_flocks(); - status = ops->recover_lock(state, fl); - switch (status) { - case 0: - break; - case -ESTALE: - case -NFS4ERR_ADMIN_REVOKED: - case -NFS4ERR_STALE_STATEID: - case -NFS4ERR_BAD_STATEID: - case -NFS4ERR_EXPIRED: - case -NFS4ERR_NO_GRACE: - case -NFS4ERR_STALE_CLIENTID: - case -NFS4ERR_BADSESSION: - case -NFS4ERR_BADSLOT: - case -NFS4ERR_BAD_HIGH_SLOT: - case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: - goto out; - default: - printk(KERN_ERR "NFS: %s: unhandled error %d. " - "Zeroing state\n", __func__, status); - case -ENOMEM: - case -NFS4ERR_DENIED: - case -NFS4ERR_RECLAIM_BAD: - case -NFS4ERR_RECLAIM_CONFLICT: - /* kill_proc(fl->fl_pid, SIGLOST, 1); */ - status = 0; - } - lock_flocks(); - } - unlock_flocks(); -out: - up_write(&nfsi->rwsem); - return status; -} - -static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs4_state_recovery_ops *ops) -{ - struct nfs4_state *state; - struct nfs4_lock_state *lock; - int status = 0; - - /* Note: we rely on the sp->so_states list being ordered - * so that we always reclaim open(O_RDWR) and/or open(O_WRITE) - * states first. - * This is needed to ensure that the server won't give us any - * read delegations that we have to return if, say, we are - * recovering after a network partition or a reboot from a - * server that doesn't support a grace period. - */ -restart: - spin_lock(&sp->so_lock); - list_for_each_entry(state, &sp->so_states, open_states) { - if (!test_and_clear_bit(ops->state_flag_bit, &state->flags)) - continue; - if (state->state == 0) - continue; - atomic_inc(&state->count); - spin_unlock(&sp->so_lock); - status = ops->recover_open(sp, state); - if (status >= 0) { - status = nfs4_reclaim_locks(state, ops); - if (status >= 0) { - spin_lock(&state->state_lock); - list_for_each_entry(lock, &state->lock_states, ls_locks) { - if (!(lock->ls_flags & NFS_LOCK_INITIALIZED)) - pr_warn_ratelimited("NFS: " - "%s: Lock reclaim " - "failed!\n", __func__); - } - spin_unlock(&state->state_lock); - nfs4_put_open_state(state); - goto restart; - } - } - switch (status) { - default: - printk(KERN_ERR "NFS: %s: unhandled error %d. " - "Zeroing state\n", __func__, status); - case -ENOENT: - case -ENOMEM: - case -ESTALE: - /* - * Open state on this file cannot be recovered - * All we can do is revert to using the zero stateid. - */ - memset(&state->stateid, 0, - sizeof(state->stateid)); - /* Mark the file as being 'closed' */ - state->state = 0; - break; - case -EKEYEXPIRED: - /* - * User RPCSEC_GSS context has expired. - * We cannot recover this stateid now, so - * skip it and allow recovery thread to - * proceed. - */ - break; - case -NFS4ERR_ADMIN_REVOKED: - case -NFS4ERR_STALE_STATEID: - case -NFS4ERR_BAD_STATEID: - case -NFS4ERR_RECLAIM_BAD: - case -NFS4ERR_RECLAIM_CONFLICT: - nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state); - break; - case -NFS4ERR_EXPIRED: - case -NFS4ERR_NO_GRACE: - nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state); - case -NFS4ERR_STALE_CLIENTID: - case -NFS4ERR_BADSESSION: - case -NFS4ERR_BADSLOT: - case -NFS4ERR_BAD_HIGH_SLOT: - case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: - goto out_err; - } - nfs4_put_open_state(state); - goto restart; - } - spin_unlock(&sp->so_lock); - return 0; -out_err: - nfs4_put_open_state(state); - return status; -} - -static void nfs4_clear_open_state(struct nfs4_state *state) -{ - struct nfs4_lock_state *lock; - - clear_bit(NFS_DELEGATED_STATE, &state->flags); - clear_bit(NFS_O_RDONLY_STATE, &state->flags); - clear_bit(NFS_O_WRONLY_STATE, &state->flags); - clear_bit(NFS_O_RDWR_STATE, &state->flags); - spin_lock(&state->state_lock); - list_for_each_entry(lock, &state->lock_states, ls_locks) { - lock->ls_seqid.flags = 0; - lock->ls_flags &= ~NFS_LOCK_INITIALIZED; - } - spin_unlock(&state->state_lock); -} - -static void nfs4_reset_seqids(struct nfs_server *server, - int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state)) -{ - struct nfs_client *clp = server->nfs_client; - struct nfs4_state_owner *sp; - struct rb_node *pos; - struct nfs4_state *state; - - spin_lock(&clp->cl_lock); - for (pos = rb_first(&server->state_owners); - pos != NULL; - pos = rb_next(pos)) { - sp = rb_entry(pos, struct nfs4_state_owner, so_server_node); - sp->so_seqid.flags = 0; - spin_lock(&sp->so_lock); - list_for_each_entry(state, &sp->so_states, open_states) { - if (mark_reclaim(clp, state)) - nfs4_clear_open_state(state); - } - spin_unlock(&sp->so_lock); - } - spin_unlock(&clp->cl_lock); -} - -static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp, - int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state)) -{ - struct nfs_server *server; - - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) - nfs4_reset_seqids(server, mark_reclaim); - rcu_read_unlock(); -} - -static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp) -{ - /* Mark all delegations for reclaim */ - nfs_delegation_mark_reclaim(clp); - nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot); -} - -static void nfs4_reclaim_complete(struct nfs_client *clp, - const struct nfs4_state_recovery_ops *ops) -{ - /* Notify the server we're done reclaiming our state */ - if (ops->reclaim_complete) - (void)ops->reclaim_complete(clp); -} - -static void nfs4_clear_reclaim_server(struct nfs_server *server) -{ - struct nfs_client *clp = server->nfs_client; - struct nfs4_state_owner *sp; - struct rb_node *pos; - struct nfs4_state *state; - - spin_lock(&clp->cl_lock); - for (pos = rb_first(&server->state_owners); - pos != NULL; - pos = rb_next(pos)) { - sp = rb_entry(pos, struct nfs4_state_owner, so_server_node); - spin_lock(&sp->so_lock); - list_for_each_entry(state, &sp->so_states, open_states) { - if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT, - &state->flags)) - continue; - nfs4_state_mark_reclaim_nograce(clp, state); - } - spin_unlock(&sp->so_lock); - } - spin_unlock(&clp->cl_lock); -} - -static int nfs4_state_clear_reclaim_reboot(struct nfs_client *clp) -{ - struct nfs_server *server; - - if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) - return 0; - - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) - nfs4_clear_reclaim_server(server); - rcu_read_unlock(); - - nfs_delegation_reap_unclaimed(clp); - return 1; -} - -static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp) -{ - if (!nfs4_state_clear_reclaim_reboot(clp)) - return; - nfs4_reclaim_complete(clp, clp->cl_mvops->reboot_recovery_ops); -} - -static void nfs_delegation_clear_all(struct nfs_client *clp) -{ - nfs_delegation_mark_reclaim(clp); - nfs_delegation_reap_unclaimed(clp); -} - -static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp) -{ - nfs_delegation_clear_all(clp); - nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce); -} - -static void nfs4_warn_keyexpired(const char *s) -{ - printk_ratelimited(KERN_WARNING "Error: state manager" - " encountered RPCSEC_GSS session" - " expired against NFSv4 server %s.\n", - s); -} - -static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) -{ - switch (error) { - case 0: - break; - case -NFS4ERR_CB_PATH_DOWN: - nfs40_handle_cb_pathdown(clp); - break; - case -NFS4ERR_NO_GRACE: - nfs4_state_end_reclaim_reboot(clp); - break; - case -NFS4ERR_STALE_CLIENTID: - case -NFS4ERR_LEASE_MOVED: - set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); - nfs4_state_clear_reclaim_reboot(clp); - nfs4_state_start_reclaim_reboot(clp); - break; - case -NFS4ERR_EXPIRED: - set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); - nfs4_state_start_reclaim_nograce(clp); - break; - case -NFS4ERR_BADSESSION: - case -NFS4ERR_BADSLOT: - case -NFS4ERR_BAD_HIGH_SLOT: - case -NFS4ERR_DEADSESSION: - case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: - case -NFS4ERR_SEQ_FALSE_RETRY: - case -NFS4ERR_SEQ_MISORDERED: - set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); - /* Zero session reset errors */ - break; - case -EKEYEXPIRED: - /* Nothing we can do */ - nfs4_warn_keyexpired(clp->cl_hostname); - break; - default: - return error; - } - return 0; -} - -static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops) -{ - struct nfs4_state_owner *sp; - struct nfs_server *server; - struct rb_node *pos; - int status = 0; - -restart: - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { - nfs4_purge_state_owners(server); - spin_lock(&clp->cl_lock); - for (pos = rb_first(&server->state_owners); - pos != NULL; - pos = rb_next(pos)) { - sp = rb_entry(pos, - struct nfs4_state_owner, so_server_node); - if (!test_and_clear_bit(ops->owner_flag_bit, - &sp->so_flags)) - continue; - atomic_inc(&sp->so_count); - spin_unlock(&clp->cl_lock); - rcu_read_unlock(); - - status = nfs4_reclaim_open_state(sp, ops); - if (status < 0) { - set_bit(ops->owner_flag_bit, &sp->so_flags); - nfs4_put_state_owner(sp); - return nfs4_recovery_handle_error(clp, status); - } - - nfs4_put_state_owner(sp); - goto restart; - } - spin_unlock(&clp->cl_lock); - } - rcu_read_unlock(); - return status; -} - -static int nfs4_check_lease(struct nfs_client *clp) -{ - struct rpc_cred *cred; - const struct nfs4_state_maintenance_ops *ops = - clp->cl_mvops->state_renewal_ops; - int status; - - /* Is the client already known to have an expired lease? */ - if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) - return 0; - spin_lock(&clp->cl_lock); - cred = ops->get_state_renewal_cred_locked(clp); - spin_unlock(&clp->cl_lock); - if (cred == NULL) { - cred = nfs4_get_setclientid_cred(clp); - status = -ENOKEY; - if (cred == NULL) - goto out; - } - status = ops->renew_lease(clp, cred); - put_rpccred(cred); -out: - return nfs4_recovery_handle_error(clp, status); -} - -static int nfs4_reclaim_lease(struct nfs_client *clp) -{ - struct rpc_cred *cred; - const struct nfs4_state_recovery_ops *ops = - clp->cl_mvops->reboot_recovery_ops; - int status = -ENOENT; - - cred = ops->get_clid_cred(clp); - if (cred != NULL) { - status = ops->establish_clid(clp, cred); - put_rpccred(cred); - /* Handle case where the user hasn't set up machine creds */ - if (status == -EACCES && cred == clp->cl_machine_cred) { - nfs4_clear_machine_cred(clp); - status = -EAGAIN; - } - if (status == -NFS4ERR_MINOR_VERS_MISMATCH) - status = -EPROTONOSUPPORT; - } - return status; -} - -#ifdef CONFIG_NFS_V4_1 -void nfs4_schedule_session_recovery(struct nfs4_session *session) -{ - struct nfs_client *clp = session->clp; - - set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); - nfs4_schedule_lease_recovery(clp); -} -EXPORT_SYMBOL_GPL(nfs4_schedule_session_recovery); - -void nfs41_handle_recall_slot(struct nfs_client *clp) -{ - set_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state); - nfs4_schedule_state_manager(clp); -} - -static void nfs4_reset_all_state(struct nfs_client *clp) -{ - if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) { - clp->cl_boot_time = CURRENT_TIME; - nfs4_state_start_reclaim_nograce(clp); - nfs4_schedule_state_manager(clp); - } -} - -static void nfs41_handle_server_reboot(struct nfs_client *clp) -{ - if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) { - nfs4_state_start_reclaim_reboot(clp); - nfs4_schedule_state_manager(clp); - } -} - -static void nfs41_handle_state_revoked(struct nfs_client *clp) -{ - /* Temporary */ - nfs4_reset_all_state(clp); -} - -static void nfs41_handle_recallable_state_revoked(struct nfs_client *clp) -{ - /* This will need to handle layouts too */ - nfs_expire_all_delegations(clp); -} - -static void nfs41_handle_cb_path_down(struct nfs_client *clp) -{ - nfs_expire_all_delegations(clp); - if (test_and_set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) == 0) - nfs4_schedule_state_manager(clp); -} - -void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags) -{ - if (!flags) - return; - if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED) - nfs41_handle_server_reboot(clp); - if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED | - SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED | - SEQ4_STATUS_ADMIN_STATE_REVOKED | - SEQ4_STATUS_LEASE_MOVED)) - nfs41_handle_state_revoked(clp); - if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED) - nfs41_handle_recallable_state_revoked(clp); - if (flags & (SEQ4_STATUS_CB_PATH_DOWN | - SEQ4_STATUS_BACKCHANNEL_FAULT | - SEQ4_STATUS_CB_PATH_DOWN_SESSION)) - nfs41_handle_cb_path_down(clp); -} - -static int nfs4_reset_session(struct nfs_client *clp) -{ - int status; - - nfs4_begin_drain_session(clp); - status = nfs4_proc_destroy_session(clp->cl_session); - if (status && status != -NFS4ERR_BADSESSION && - status != -NFS4ERR_DEADSESSION) { - status = nfs4_recovery_handle_error(clp, status); - goto out; - } - - memset(clp->cl_session->sess_id.data, 0, NFS4_MAX_SESSIONID_LEN); - status = nfs4_proc_create_session(clp); - if (status) { - status = nfs4_recovery_handle_error(clp, status); - goto out; - } - clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); - /* create_session negotiated new slot table */ - clear_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state); - - /* Let the state manager reestablish state */ - if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) - nfs41_setup_state_renewal(clp); -out: - return status; -} - -static int nfs4_recall_slot(struct nfs_client *clp) -{ - struct nfs4_slot_table *fc_tbl = &clp->cl_session->fc_slot_table; - struct nfs4_channel_attrs *fc_attrs = &clp->cl_session->fc_attrs; - struct nfs4_slot *new, *old; - int i; - - nfs4_begin_drain_session(clp); - new = kmalloc(fc_tbl->target_max_slots * sizeof(struct nfs4_slot), - GFP_NOFS); - if (!new) - return -ENOMEM; - - spin_lock(&fc_tbl->slot_tbl_lock); - for (i = 0; i < fc_tbl->target_max_slots; i++) - new[i].seq_nr = fc_tbl->slots[i].seq_nr; - old = fc_tbl->slots; - fc_tbl->slots = new; - fc_tbl->max_slots = fc_tbl->target_max_slots; - fc_tbl->target_max_slots = 0; - fc_attrs->max_reqs = fc_tbl->max_slots; - spin_unlock(&fc_tbl->slot_tbl_lock); - - kfree(old); - nfs4_end_drain_session(clp); - return 0; -} - -#else /* CONFIG_NFS_V4_1 */ -static int nfs4_reset_session(struct nfs_client *clp) { return 0; } -static int nfs4_end_drain_session(struct nfs_client *clp) { return 0; } -static int nfs4_recall_slot(struct nfs_client *clp) { return 0; } -#endif /* CONFIG_NFS_V4_1 */ - -/* Set NFS4CLNT_LEASE_EXPIRED for all v4.0 errors and for recoverable errors - * on EXCHANGE_ID for v4.1 - */ -static void nfs4_set_lease_expired(struct nfs_client *clp, int status) -{ - switch (status) { - case -NFS4ERR_CLID_INUSE: - case -NFS4ERR_STALE_CLIENTID: - clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); - break; - case -NFS4ERR_DELAY: - case -ETIMEDOUT: - case -EAGAIN: - ssleep(1); - break; - - case -EKEYEXPIRED: - nfs4_warn_keyexpired(clp->cl_hostname); - case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery - * in nfs4_exchange_id */ - default: - return; - } - set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); -} - -static void nfs4_state_manager(struct nfs_client *clp) -{ - int status = 0; - - /* Ensure exclusive access to NFSv4 state */ - do { - if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) { - /* We're going to have to re-establish a clientid */ - status = nfs4_reclaim_lease(clp); - if (status) { - nfs4_set_lease_expired(clp, status); - if (test_bit(NFS4CLNT_LEASE_EXPIRED, - &clp->cl_state)) - continue; - if (clp->cl_cons_state == - NFS_CS_SESSION_INITING) - nfs_mark_client_ready(clp, status); - goto out_error; - } - clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state); - - if (test_and_clear_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, - &clp->cl_state)) - nfs4_state_start_reclaim_nograce(clp); - else - set_bit(NFS4CLNT_RECLAIM_REBOOT, - &clp->cl_state); - - pnfs_destroy_all_layouts(clp); - } - - if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) { - status = nfs4_check_lease(clp); - if (status < 0) - goto out_error; - if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) - continue; - } - - /* Initialize or reset the session */ - if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) - && nfs4_has_session(clp)) { - status = nfs4_reset_session(clp); - if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) - continue; - if (status < 0) - goto out_error; - } - - /* First recover reboot state... */ - if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) { - status = nfs4_do_reclaim(clp, - clp->cl_mvops->reboot_recovery_ops); - if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) || - test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) - continue; - nfs4_state_end_reclaim_reboot(clp); - if (test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) - continue; - if (status < 0) - goto out_error; - } - - /* Now recover expired state... */ - if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) { - status = nfs4_do_reclaim(clp, - clp->cl_mvops->nograce_recovery_ops); - if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) || - test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) || - test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) - continue; - if (status < 0) - goto out_error; - } - - nfs4_end_drain_session(clp); - if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) { - nfs_client_return_marked_delegations(clp); - continue; - } - /* Recall session slots */ - if (test_and_clear_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state) - && nfs4_has_session(clp)) { - status = nfs4_recall_slot(clp); - if (status < 0) - goto out_error; - continue; - } - - - nfs4_clear_state_manager_bit(clp); - /* Did we race with an attempt to give us more work? */ - if (clp->cl_state == 0) - break; - if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0) - break; - } while (atomic_read(&clp->cl_count) > 1); - return; -out_error: - pr_warn_ratelimited("NFS: state manager failed on NFSv4 server %s" - " with error %d\n", clp->cl_hostname, -status); - nfs4_end_drain_session(clp); - nfs4_clear_state_manager_bit(clp); -} - -static int nfs4_run_state_manager(void *ptr) -{ - struct nfs_client *clp = ptr; - - allow_signal(SIGKILL); - nfs4_state_manager(clp); - nfs_put_client(clp); - module_put_and_exit(0); - return 0; -} - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/ANDROID_3.4.5/fs/nfs/nfs4xdr.c b/ANDROID_3.4.5/fs/nfs/nfs4xdr.c deleted file mode 100644 index c54aae36..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfs4xdr.c +++ /dev/null @@ -1,7101 +0,0 @@ -/* - * fs/nfs/nfs4xdr.c - * - * Client-side XDR for NFSv4. - * - * Copyright (c) 2002 The Regents of the University of Michigan. - * All rights reserved. - * - * Kendrick Smith <kmsmith@umich.edu> - * Andy Adamson <andros@umich.edu> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <linux/param.h> -#include <linux/time.h> -#include <linux/mm.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/module.h> -#include <linux/utsname.h> -#include <linux/sunrpc/clnt.h> -#include <linux/sunrpc/msg_prot.h> -#include <linux/sunrpc/gss_api.h> -#include <linux/nfs.h> -#include <linux/nfs4.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_idmap.h> -#include "nfs4_fs.h" -#include "internal.h" -#include "pnfs.h" - -#define NFSDBG_FACILITY NFSDBG_XDR - -/* Mapping from NFS error code to "errno" error code. */ -#define errno_NFSERR_IO EIO - -static int nfs4_stat_to_errno(int); - -/* NFSv4 COMPOUND tags are only wanted for debugging purposes */ -#ifdef DEBUG -#define NFS4_MAXTAGLEN 20 -#else -#define NFS4_MAXTAGLEN 0 -#endif - -/* lock,open owner id: - * we currently use size 2 (u64) out of (NFS4_OPAQUE_LIMIT >> 2) - */ -#define open_owner_id_maxsz (1 + 2 + 1 + 1 + 2) -#define lock_owner_id_maxsz (1 + 1 + 4) -#define decode_lockowner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) -#define compound_encode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2)) -#define compound_decode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2)) -#define op_encode_hdr_maxsz (1) -#define op_decode_hdr_maxsz (2) -#define encode_stateid_maxsz (XDR_QUADLEN(NFS4_STATEID_SIZE)) -#define decode_stateid_maxsz (XDR_QUADLEN(NFS4_STATEID_SIZE)) -#define encode_verifier_maxsz (XDR_QUADLEN(NFS4_VERIFIER_SIZE)) -#define decode_verifier_maxsz (XDR_QUADLEN(NFS4_VERIFIER_SIZE)) -#define encode_putfh_maxsz (op_encode_hdr_maxsz + 1 + \ - (NFS4_FHSIZE >> 2)) -#define decode_putfh_maxsz (op_decode_hdr_maxsz) -#define encode_putrootfh_maxsz (op_encode_hdr_maxsz) -#define decode_putrootfh_maxsz (op_decode_hdr_maxsz) -#define encode_getfh_maxsz (op_encode_hdr_maxsz) -#define decode_getfh_maxsz (op_decode_hdr_maxsz + 1 + \ - ((3+NFS4_FHSIZE) >> 2)) -#define nfs4_fattr_bitmap_maxsz 4 -#define encode_getattr_maxsz (op_encode_hdr_maxsz + nfs4_fattr_bitmap_maxsz) -#define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2)) -#define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2)) -#define nfs4_owner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) -#define nfs4_group_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) -/* This is based on getfattr, which uses the most attributes: */ -#define nfs4_fattr_value_maxsz (1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \ - 3 + 3 + 3 + nfs4_owner_maxsz + nfs4_group_maxsz)) -#define nfs4_fattr_maxsz (nfs4_fattr_bitmap_maxsz + \ - nfs4_fattr_value_maxsz) -#define decode_getattr_maxsz (op_decode_hdr_maxsz + nfs4_fattr_maxsz) -#define encode_attrs_maxsz (nfs4_fattr_bitmap_maxsz + \ - 1 + 2 + 1 + \ - nfs4_owner_maxsz + \ - nfs4_group_maxsz + \ - 4 + 4) -#define encode_savefh_maxsz (op_encode_hdr_maxsz) -#define decode_savefh_maxsz (op_decode_hdr_maxsz) -#define encode_restorefh_maxsz (op_encode_hdr_maxsz) -#define decode_restorefh_maxsz (op_decode_hdr_maxsz) -#define encode_fsinfo_maxsz (encode_getattr_maxsz) -/* The 5 accounts for the PNFS attributes, and assumes that at most three - * layout types will be returned. - */ -#define decode_fsinfo_maxsz (op_decode_hdr_maxsz + \ - nfs4_fattr_bitmap_maxsz + 4 + 8 + 5) -#define encode_renew_maxsz (op_encode_hdr_maxsz + 3) -#define decode_renew_maxsz (op_decode_hdr_maxsz) -#define encode_setclientid_maxsz \ - (op_encode_hdr_maxsz + \ - XDR_QUADLEN(NFS4_VERIFIER_SIZE) + \ - XDR_QUADLEN(NFS4_SETCLIENTID_NAMELEN) + \ - 1 /* sc_prog */ + \ - XDR_QUADLEN(RPCBIND_MAXNETIDLEN) + \ - XDR_QUADLEN(RPCBIND_MAXUADDRLEN) + \ - 1) /* sc_cb_ident */ -#define decode_setclientid_maxsz \ - (op_decode_hdr_maxsz + \ - 2 + \ - 1024) /* large value for CLID_INUSE */ -#define encode_setclientid_confirm_maxsz \ - (op_encode_hdr_maxsz + \ - 3 + (NFS4_VERIFIER_SIZE >> 2)) -#define decode_setclientid_confirm_maxsz \ - (op_decode_hdr_maxsz) -#define encode_lookup_maxsz (op_encode_hdr_maxsz + nfs4_name_maxsz) -#define decode_lookup_maxsz (op_decode_hdr_maxsz) -#define encode_share_access_maxsz \ - (2) -#define encode_createmode_maxsz (1 + encode_attrs_maxsz + encode_verifier_maxsz) -#define encode_opentype_maxsz (1 + encode_createmode_maxsz) -#define encode_claim_null_maxsz (1 + nfs4_name_maxsz) -#define encode_open_maxsz (op_encode_hdr_maxsz + \ - 2 + encode_share_access_maxsz + 2 + \ - open_owner_id_maxsz + \ - encode_opentype_maxsz + \ - encode_claim_null_maxsz) -#define decode_ace_maxsz (3 + nfs4_owner_maxsz) -#define decode_delegation_maxsz (1 + decode_stateid_maxsz + 1 + \ - decode_ace_maxsz) -#define decode_change_info_maxsz (5) -#define decode_open_maxsz (op_decode_hdr_maxsz + \ - decode_stateid_maxsz + \ - decode_change_info_maxsz + 1 + \ - nfs4_fattr_bitmap_maxsz + \ - decode_delegation_maxsz) -#define encode_open_confirm_maxsz \ - (op_encode_hdr_maxsz + \ - encode_stateid_maxsz + 1) -#define decode_open_confirm_maxsz \ - (op_decode_hdr_maxsz + \ - decode_stateid_maxsz) -#define encode_open_downgrade_maxsz \ - (op_encode_hdr_maxsz + \ - encode_stateid_maxsz + 1 + \ - encode_share_access_maxsz) -#define decode_open_downgrade_maxsz \ - (op_decode_hdr_maxsz + \ - decode_stateid_maxsz) -#define encode_close_maxsz (op_encode_hdr_maxsz + \ - 1 + encode_stateid_maxsz) -#define decode_close_maxsz (op_decode_hdr_maxsz + \ - decode_stateid_maxsz) -#define encode_setattr_maxsz (op_encode_hdr_maxsz + \ - encode_stateid_maxsz + \ - encode_attrs_maxsz) -#define decode_setattr_maxsz (op_decode_hdr_maxsz + \ - nfs4_fattr_bitmap_maxsz) -#define encode_read_maxsz (op_encode_hdr_maxsz + \ - encode_stateid_maxsz + 3) -#define decode_read_maxsz (op_decode_hdr_maxsz + 2) -#define encode_readdir_maxsz (op_encode_hdr_maxsz + \ - 2 + encode_verifier_maxsz + 5) -#define decode_readdir_maxsz (op_decode_hdr_maxsz + \ - decode_verifier_maxsz) -#define encode_readlink_maxsz (op_encode_hdr_maxsz) -#define decode_readlink_maxsz (op_decode_hdr_maxsz + 1) -#define encode_write_maxsz (op_encode_hdr_maxsz + \ - encode_stateid_maxsz + 4) -#define decode_write_maxsz (op_decode_hdr_maxsz + \ - 2 + decode_verifier_maxsz) -#define encode_commit_maxsz (op_encode_hdr_maxsz + 3) -#define decode_commit_maxsz (op_decode_hdr_maxsz + \ - decode_verifier_maxsz) -#define encode_remove_maxsz (op_encode_hdr_maxsz + \ - nfs4_name_maxsz) -#define decode_remove_maxsz (op_decode_hdr_maxsz + \ - decode_change_info_maxsz) -#define encode_rename_maxsz (op_encode_hdr_maxsz + \ - 2 * nfs4_name_maxsz) -#define decode_rename_maxsz (op_decode_hdr_maxsz + \ - decode_change_info_maxsz + \ - decode_change_info_maxsz) -#define encode_link_maxsz (op_encode_hdr_maxsz + \ - nfs4_name_maxsz) -#define decode_link_maxsz (op_decode_hdr_maxsz + decode_change_info_maxsz) -#define encode_lockowner_maxsz (7) -#define encode_lock_maxsz (op_encode_hdr_maxsz + \ - 7 + \ - 1 + encode_stateid_maxsz + 1 + \ - encode_lockowner_maxsz) -#define decode_lock_denied_maxsz \ - (8 + decode_lockowner_maxsz) -#define decode_lock_maxsz (op_decode_hdr_maxsz + \ - decode_lock_denied_maxsz) -#define encode_lockt_maxsz (op_encode_hdr_maxsz + 5 + \ - encode_lockowner_maxsz) -#define decode_lockt_maxsz (op_decode_hdr_maxsz + \ - decode_lock_denied_maxsz) -#define encode_locku_maxsz (op_encode_hdr_maxsz + 3 + \ - encode_stateid_maxsz + \ - 4) -#define decode_locku_maxsz (op_decode_hdr_maxsz + \ - decode_stateid_maxsz) -#define encode_release_lockowner_maxsz \ - (op_encode_hdr_maxsz + \ - encode_lockowner_maxsz) -#define decode_release_lockowner_maxsz \ - (op_decode_hdr_maxsz) -#define encode_access_maxsz (op_encode_hdr_maxsz + 1) -#define decode_access_maxsz (op_decode_hdr_maxsz + 2) -#define encode_symlink_maxsz (op_encode_hdr_maxsz + \ - 1 + nfs4_name_maxsz + \ - 1 + \ - nfs4_fattr_maxsz) -#define decode_symlink_maxsz (op_decode_hdr_maxsz + 8) -#define encode_create_maxsz (op_encode_hdr_maxsz + \ - 1 + 2 + nfs4_name_maxsz + \ - encode_attrs_maxsz) -#define decode_create_maxsz (op_decode_hdr_maxsz + \ - decode_change_info_maxsz + \ - nfs4_fattr_bitmap_maxsz) -#define encode_statfs_maxsz (encode_getattr_maxsz) -#define decode_statfs_maxsz (decode_getattr_maxsz) -#define encode_delegreturn_maxsz (op_encode_hdr_maxsz + 4) -#define decode_delegreturn_maxsz (op_decode_hdr_maxsz) -#define encode_getacl_maxsz (encode_getattr_maxsz) -#define decode_getacl_maxsz (op_decode_hdr_maxsz + \ - nfs4_fattr_bitmap_maxsz + 1) -#define encode_setacl_maxsz (op_encode_hdr_maxsz + \ - encode_stateid_maxsz + 3) -#define decode_setacl_maxsz (decode_setattr_maxsz) -#define encode_fs_locations_maxsz \ - (encode_getattr_maxsz) -#define decode_fs_locations_maxsz \ - (0) -#define encode_secinfo_maxsz (op_encode_hdr_maxsz + nfs4_name_maxsz) -#define decode_secinfo_maxsz (op_decode_hdr_maxsz + 1 + ((NFS_MAX_SECFLAVORS * (16 + GSS_OID_MAX_LEN)) / 4)) - -#if defined(CONFIG_NFS_V4_1) -#define NFS4_MAX_MACHINE_NAME_LEN (64) - -#define encode_exchange_id_maxsz (op_encode_hdr_maxsz + \ - encode_verifier_maxsz + \ - 1 /* co_ownerid.len */ + \ - XDR_QUADLEN(NFS4_EXCHANGE_ID_LEN) + \ - 1 /* flags */ + \ - 1 /* spa_how */ + \ - 0 /* SP4_NONE (for now) */ + \ - 1 /* implementation id array of size 1 */ + \ - 1 /* nii_domain */ + \ - XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \ - 1 /* nii_name */ + \ - XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \ - 3 /* nii_date */) -#define decode_exchange_id_maxsz (op_decode_hdr_maxsz + \ - 2 /* eir_clientid */ + \ - 1 /* eir_sequenceid */ + \ - 1 /* eir_flags */ + \ - 1 /* spr_how */ + \ - 0 /* SP4_NONE (for now) */ + \ - 2 /* eir_server_owner.so_minor_id */ + \ - /* eir_server_owner.so_major_id<> */ \ - XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 + \ - /* eir_server_scope<> */ \ - XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 + \ - 1 /* eir_server_impl_id array length */ + \ - 1 /* nii_domain */ + \ - XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \ - 1 /* nii_name */ + \ - XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \ - 3 /* nii_date */) -#define encode_channel_attrs_maxsz (6 + 1 /* ca_rdma_ird.len (0) */) -#define decode_channel_attrs_maxsz (6 + \ - 1 /* ca_rdma_ird.len */ + \ - 1 /* ca_rdma_ird */) -#define encode_create_session_maxsz (op_encode_hdr_maxsz + \ - 2 /* csa_clientid */ + \ - 1 /* csa_sequence */ + \ - 1 /* csa_flags */ + \ - encode_channel_attrs_maxsz + \ - encode_channel_attrs_maxsz + \ - 1 /* csa_cb_program */ + \ - 1 /* csa_sec_parms.len (1) */ + \ - 1 /* cb_secflavor (AUTH_SYS) */ + \ - 1 /* stamp */ + \ - 1 /* machinename.len */ + \ - XDR_QUADLEN(NFS4_MAX_MACHINE_NAME_LEN) + \ - 1 /* uid */ + \ - 1 /* gid */ + \ - 1 /* gids.len (0) */) -#define decode_create_session_maxsz (op_decode_hdr_maxsz + \ - XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + \ - 1 /* csr_sequence */ + \ - 1 /* csr_flags */ + \ - decode_channel_attrs_maxsz + \ - decode_channel_attrs_maxsz) -#define encode_destroy_session_maxsz (op_encode_hdr_maxsz + 4) -#define decode_destroy_session_maxsz (op_decode_hdr_maxsz) -#define encode_sequence_maxsz (op_encode_hdr_maxsz + \ - XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 4) -#define decode_sequence_maxsz (op_decode_hdr_maxsz + \ - XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 5) -#define encode_reclaim_complete_maxsz (op_encode_hdr_maxsz + 4) -#define decode_reclaim_complete_maxsz (op_decode_hdr_maxsz + 4) -#define encode_getdevicelist_maxsz (op_encode_hdr_maxsz + 4 + \ - encode_verifier_maxsz) -#define decode_getdevicelist_maxsz (op_decode_hdr_maxsz + \ - 2 /* nfs_cookie4 gdlr_cookie */ + \ - decode_verifier_maxsz \ - /* verifier4 gdlr_verifier */ + \ - 1 /* gdlr_deviceid_list count */ + \ - XDR_QUADLEN(NFS4_PNFS_GETDEVLIST_MAXNUM * \ - NFS4_DEVICEID4_SIZE) \ - /* gdlr_deviceid_list */ + \ - 1 /* bool gdlr_eof */) -#define encode_getdeviceinfo_maxsz (op_encode_hdr_maxsz + 4 + \ - XDR_QUADLEN(NFS4_DEVICEID4_SIZE)) -#define decode_getdeviceinfo_maxsz (op_decode_hdr_maxsz + \ - 1 /* layout type */ + \ - 1 /* opaque devaddr4 length */ + \ - /* devaddr4 payload is read into page */ \ - 1 /* notification bitmap length */ + \ - 1 /* notification bitmap */) -#define encode_layoutget_maxsz (op_encode_hdr_maxsz + 10 + \ - encode_stateid_maxsz) -#define decode_layoutget_maxsz (op_decode_hdr_maxsz + 8 + \ - decode_stateid_maxsz + \ - XDR_QUADLEN(PNFS_LAYOUT_MAXSIZE)) -#define encode_layoutcommit_maxsz (op_encode_hdr_maxsz + \ - 2 /* offset */ + \ - 2 /* length */ + \ - 1 /* reclaim */ + \ - encode_stateid_maxsz + \ - 1 /* new offset (true) */ + \ - 2 /* last byte written */ + \ - 1 /* nt_timechanged (false) */ + \ - 1 /* layoutupdate4 layout type */ + \ - 1 /* NULL filelayout layoutupdate4 payload */) -#define decode_layoutcommit_maxsz (op_decode_hdr_maxsz + 3) -#define encode_layoutreturn_maxsz (8 + op_encode_hdr_maxsz + \ - encode_stateid_maxsz + \ - 1 /* FIXME: opaque lrf_body always empty at the moment */) -#define decode_layoutreturn_maxsz (op_decode_hdr_maxsz + \ - 1 + decode_stateid_maxsz) -#define encode_secinfo_no_name_maxsz (op_encode_hdr_maxsz + 1) -#define decode_secinfo_no_name_maxsz decode_secinfo_maxsz -#define encode_test_stateid_maxsz (op_encode_hdr_maxsz + 2 + \ - XDR_QUADLEN(NFS4_STATEID_SIZE)) -#define decode_test_stateid_maxsz (op_decode_hdr_maxsz + 2 + 1) -#define encode_free_stateid_maxsz (op_encode_hdr_maxsz + 1 + \ - XDR_QUADLEN(NFS4_STATEID_SIZE)) -#define decode_free_stateid_maxsz (op_decode_hdr_maxsz + 1) -#else /* CONFIG_NFS_V4_1 */ -#define encode_sequence_maxsz 0 -#define decode_sequence_maxsz 0 -#endif /* CONFIG_NFS_V4_1 */ - -#define NFS4_enc_compound_sz (1024) /* XXX: large enough? */ -#define NFS4_dec_compound_sz (1024) /* XXX: large enough? */ -#define NFS4_enc_read_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_read_maxsz) -#define NFS4_dec_read_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_read_maxsz) -#define NFS4_enc_readlink_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_readlink_maxsz) -#define NFS4_dec_readlink_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_readlink_maxsz) -#define NFS4_enc_readdir_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_readdir_maxsz) -#define NFS4_dec_readdir_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_readdir_maxsz) -#define NFS4_enc_write_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_write_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_write_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_write_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_commit_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_commit_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_commit_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_commit_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_open_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_savefh_maxsz + \ - encode_open_maxsz + \ - encode_getfh_maxsz + \ - encode_getattr_maxsz + \ - encode_restorefh_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_open_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_savefh_maxsz + \ - decode_open_maxsz + \ - decode_getfh_maxsz + \ - decode_getattr_maxsz + \ - decode_restorefh_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_open_confirm_sz \ - (compound_encode_hdr_maxsz + \ - encode_putfh_maxsz + \ - encode_open_confirm_maxsz) -#define NFS4_dec_open_confirm_sz \ - (compound_decode_hdr_maxsz + \ - decode_putfh_maxsz + \ - decode_open_confirm_maxsz) -#define NFS4_enc_open_noattr_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_open_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_open_noattr_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_open_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_open_downgrade_sz \ - (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_open_downgrade_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_open_downgrade_sz \ - (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_open_downgrade_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_close_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_close_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_close_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_close_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_setattr_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_setattr_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_setattr_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_setattr_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_fsinfo_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_fsinfo_maxsz) -#define NFS4_dec_fsinfo_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_fsinfo_maxsz) -#define NFS4_enc_renew_sz (compound_encode_hdr_maxsz + \ - encode_renew_maxsz) -#define NFS4_dec_renew_sz (compound_decode_hdr_maxsz + \ - decode_renew_maxsz) -#define NFS4_enc_setclientid_sz (compound_encode_hdr_maxsz + \ - encode_setclientid_maxsz) -#define NFS4_dec_setclientid_sz (compound_decode_hdr_maxsz + \ - decode_setclientid_maxsz) -#define NFS4_enc_setclientid_confirm_sz \ - (compound_encode_hdr_maxsz + \ - encode_setclientid_confirm_maxsz + \ - encode_putrootfh_maxsz + \ - encode_fsinfo_maxsz) -#define NFS4_dec_setclientid_confirm_sz \ - (compound_decode_hdr_maxsz + \ - decode_setclientid_confirm_maxsz + \ - decode_putrootfh_maxsz + \ - decode_fsinfo_maxsz) -#define NFS4_enc_lock_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_lock_maxsz) -#define NFS4_dec_lock_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_lock_maxsz) -#define NFS4_enc_lockt_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_lockt_maxsz) -#define NFS4_dec_lockt_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_lockt_maxsz) -#define NFS4_enc_locku_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_locku_maxsz) -#define NFS4_dec_locku_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_locku_maxsz) -#define NFS4_enc_release_lockowner_sz \ - (compound_encode_hdr_maxsz + \ - encode_lockowner_maxsz) -#define NFS4_dec_release_lockowner_sz \ - (compound_decode_hdr_maxsz + \ - decode_lockowner_maxsz) -#define NFS4_enc_access_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_access_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_access_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_access_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_getattr_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_getattr_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_lookup_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_lookup_maxsz + \ - encode_getattr_maxsz + \ - encode_getfh_maxsz) -#define NFS4_dec_lookup_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_lookup_maxsz + \ - decode_getattr_maxsz + \ - decode_getfh_maxsz) -#define NFS4_enc_lookup_root_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putrootfh_maxsz + \ - encode_getattr_maxsz + \ - encode_getfh_maxsz) -#define NFS4_dec_lookup_root_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putrootfh_maxsz + \ - decode_getattr_maxsz + \ - decode_getfh_maxsz) -#define NFS4_enc_remove_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_remove_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_remove_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_remove_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_rename_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_savefh_maxsz + \ - encode_putfh_maxsz + \ - encode_rename_maxsz + \ - encode_getattr_maxsz + \ - encode_restorefh_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_rename_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_savefh_maxsz + \ - decode_putfh_maxsz + \ - decode_rename_maxsz + \ - decode_getattr_maxsz + \ - decode_restorefh_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_link_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_savefh_maxsz + \ - encode_putfh_maxsz + \ - encode_link_maxsz + \ - decode_getattr_maxsz + \ - encode_restorefh_maxsz + \ - decode_getattr_maxsz) -#define NFS4_dec_link_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_savefh_maxsz + \ - decode_putfh_maxsz + \ - decode_link_maxsz + \ - decode_getattr_maxsz + \ - decode_restorefh_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_symlink_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_symlink_maxsz + \ - encode_getattr_maxsz + \ - encode_getfh_maxsz) -#define NFS4_dec_symlink_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_symlink_maxsz + \ - decode_getattr_maxsz + \ - decode_getfh_maxsz) -#define NFS4_enc_create_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_savefh_maxsz + \ - encode_create_maxsz + \ - encode_getfh_maxsz + \ - encode_getattr_maxsz + \ - encode_restorefh_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_create_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_savefh_maxsz + \ - decode_create_maxsz + \ - decode_getfh_maxsz + \ - decode_getattr_maxsz + \ - decode_restorefh_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_pathconf_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_pathconf_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_statfs_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_statfs_maxsz) -#define NFS4_dec_statfs_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_statfs_maxsz) -#define NFS4_enc_server_caps_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_server_caps_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_delegreturn_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_delegreturn_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_delegreturn_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_delegreturn_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_getacl_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_getacl_maxsz) -#define NFS4_dec_getacl_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_getacl_maxsz) -#define NFS4_enc_setacl_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_setacl_maxsz) -#define NFS4_dec_setacl_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_setacl_maxsz) -#define NFS4_enc_fs_locations_sz \ - (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_lookup_maxsz + \ - encode_fs_locations_maxsz) -#define NFS4_dec_fs_locations_sz \ - (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_lookup_maxsz + \ - decode_fs_locations_maxsz) -#define NFS4_enc_secinfo_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_secinfo_maxsz) -#define NFS4_dec_secinfo_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_secinfo_maxsz) -#if defined(CONFIG_NFS_V4_1) -#define NFS4_enc_exchange_id_sz \ - (compound_encode_hdr_maxsz + \ - encode_exchange_id_maxsz) -#define NFS4_dec_exchange_id_sz \ - (compound_decode_hdr_maxsz + \ - decode_exchange_id_maxsz) -#define NFS4_enc_create_session_sz \ - (compound_encode_hdr_maxsz + \ - encode_create_session_maxsz) -#define NFS4_dec_create_session_sz \ - (compound_decode_hdr_maxsz + \ - decode_create_session_maxsz) -#define NFS4_enc_destroy_session_sz (compound_encode_hdr_maxsz + \ - encode_destroy_session_maxsz) -#define NFS4_dec_destroy_session_sz (compound_decode_hdr_maxsz + \ - decode_destroy_session_maxsz) -#define NFS4_enc_sequence_sz \ - (compound_decode_hdr_maxsz + \ - encode_sequence_maxsz) -#define NFS4_dec_sequence_sz \ - (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz) -#define NFS4_enc_get_lease_time_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putrootfh_maxsz + \ - encode_fsinfo_maxsz) -#define NFS4_dec_get_lease_time_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putrootfh_maxsz + \ - decode_fsinfo_maxsz) -#define NFS4_enc_reclaim_complete_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_reclaim_complete_maxsz) -#define NFS4_dec_reclaim_complete_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_reclaim_complete_maxsz) -#define NFS4_enc_getdevicelist_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_getdevicelist_maxsz) -#define NFS4_dec_getdevicelist_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_getdevicelist_maxsz) -#define NFS4_enc_getdeviceinfo_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz +\ - encode_getdeviceinfo_maxsz) -#define NFS4_dec_getdeviceinfo_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_getdeviceinfo_maxsz) -#define NFS4_enc_layoutget_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_layoutget_maxsz) -#define NFS4_dec_layoutget_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_layoutget_maxsz) -#define NFS4_enc_layoutcommit_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz +\ - encode_putfh_maxsz + \ - encode_layoutcommit_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_layoutcommit_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_layoutcommit_maxsz + \ - decode_getattr_maxsz) -#define NFS4_enc_layoutreturn_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putfh_maxsz + \ - encode_layoutreturn_maxsz) -#define NFS4_dec_layoutreturn_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putfh_maxsz + \ - decode_layoutreturn_maxsz) -#define NFS4_enc_secinfo_no_name_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_putrootfh_maxsz +\ - encode_secinfo_no_name_maxsz) -#define NFS4_dec_secinfo_no_name_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_putrootfh_maxsz + \ - decode_secinfo_no_name_maxsz) -#define NFS4_enc_test_stateid_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_test_stateid_maxsz) -#define NFS4_dec_test_stateid_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_test_stateid_maxsz) -#define NFS4_enc_free_stateid_sz (compound_encode_hdr_maxsz + \ - encode_sequence_maxsz + \ - encode_free_stateid_maxsz) -#define NFS4_dec_free_stateid_sz (compound_decode_hdr_maxsz + \ - decode_sequence_maxsz + \ - decode_free_stateid_maxsz) - -const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH + - compound_encode_hdr_maxsz + - encode_sequence_maxsz + - encode_putfh_maxsz + - encode_getattr_maxsz) * - XDR_UNIT); - -const u32 nfs41_maxread_overhead = ((RPC_MAX_HEADER_WITH_AUTH + - compound_decode_hdr_maxsz + - decode_sequence_maxsz + - decode_putfh_maxsz) * - XDR_UNIT); -#endif /* CONFIG_NFS_V4_1 */ - -static unsigned short send_implementation_id = 1; - -module_param(send_implementation_id, ushort, 0644); -MODULE_PARM_DESC(send_implementation_id, - "Send implementation ID with NFSv4.1 exchange_id"); - -static const umode_t nfs_type2fmt[] = { - [NF4BAD] = 0, - [NF4REG] = S_IFREG, - [NF4DIR] = S_IFDIR, - [NF4BLK] = S_IFBLK, - [NF4CHR] = S_IFCHR, - [NF4LNK] = S_IFLNK, - [NF4SOCK] = S_IFSOCK, - [NF4FIFO] = S_IFIFO, - [NF4ATTRDIR] = 0, - [NF4NAMEDATTR] = 0, -}; - -struct compound_hdr { - int32_t status; - uint32_t nops; - __be32 * nops_p; - uint32_t taglen; - char * tag; - uint32_t replen; /* expected reply words */ - u32 minorversion; -}; - -static __be32 *reserve_space(struct xdr_stream *xdr, size_t nbytes) -{ - __be32 *p = xdr_reserve_space(xdr, nbytes); - BUG_ON(!p); - return p; -} - -static void encode_opaque_fixed(struct xdr_stream *xdr, const void *buf, size_t len) -{ - __be32 *p; - - p = xdr_reserve_space(xdr, len); - xdr_encode_opaque_fixed(p, buf, len); -} - -static void encode_string(struct xdr_stream *xdr, unsigned int len, const char *str) -{ - __be32 *p; - - p = reserve_space(xdr, 4 + len); - xdr_encode_opaque(p, str, len); -} - -static void encode_uint32(struct xdr_stream *xdr, u32 n) -{ - __be32 *p; - - p = reserve_space(xdr, 4); - *p = cpu_to_be32(n); -} - -static void encode_uint64(struct xdr_stream *xdr, u64 n) -{ - __be32 *p; - - p = reserve_space(xdr, 8); - xdr_encode_hyper(p, n); -} - -static void encode_nfs4_seqid(struct xdr_stream *xdr, - const struct nfs_seqid *seqid) -{ - encode_uint32(xdr, seqid->sequence->counter); -} - -static void encode_compound_hdr(struct xdr_stream *xdr, - struct rpc_rqst *req, - struct compound_hdr *hdr) -{ - __be32 *p; - struct rpc_auth *auth = req->rq_cred->cr_auth; - - /* initialize running count of expected bytes in reply. - * NOTE: the replied tag SHOULD be the same is the one sent, - * but this is not required as a MUST for the server to do so. */ - hdr->replen = RPC_REPHDRSIZE + auth->au_rslack + 3 + hdr->taglen; - - BUG_ON(hdr->taglen > NFS4_MAXTAGLEN); - encode_string(xdr, hdr->taglen, hdr->tag); - p = reserve_space(xdr, 8); - *p++ = cpu_to_be32(hdr->minorversion); - hdr->nops_p = p; - *p = cpu_to_be32(hdr->nops); -} - -static void encode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 op, - uint32_t replen, - struct compound_hdr *hdr) -{ - encode_uint32(xdr, op); - hdr->nops++; - hdr->replen += replen; -} - -static void encode_nops(struct compound_hdr *hdr) -{ - BUG_ON(hdr->nops > NFS4_MAX_OPS); - *hdr->nops_p = htonl(hdr->nops); -} - -static void encode_nfs4_stateid(struct xdr_stream *xdr, const nfs4_stateid *stateid) -{ - encode_opaque_fixed(xdr, stateid, NFS4_STATEID_SIZE); -} - -static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *verf) -{ - encode_opaque_fixed(xdr, verf->data, NFS4_VERIFIER_SIZE); -} - -static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server) -{ - char owner_name[IDMAP_NAMESZ]; - char owner_group[IDMAP_NAMESZ]; - int owner_namelen = 0; - int owner_grouplen = 0; - __be32 *p; - __be32 *q; - int len; - uint32_t bmval0 = 0; - uint32_t bmval1 = 0; - - /* - * We reserve enough space to write the entire attribute buffer at once. - * In the worst-case, this would be - * 12(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime) - * = 36 bytes, plus any contribution from variable-length fields - * such as owner/group. - */ - len = 16; - - /* Sigh */ - if (iap->ia_valid & ATTR_SIZE) - len += 8; - if (iap->ia_valid & ATTR_MODE) - len += 4; - if (iap->ia_valid & ATTR_UID) { - owner_namelen = nfs_map_uid_to_name(server, iap->ia_uid, owner_name, IDMAP_NAMESZ); - if (owner_namelen < 0) { - dprintk("nfs: couldn't resolve uid %d to string\n", - iap->ia_uid); - /* XXX */ - strcpy(owner_name, "nobody"); - owner_namelen = sizeof("nobody") - 1; - /* goto out; */ - } - len += 4 + (XDR_QUADLEN(owner_namelen) << 2); - } - if (iap->ia_valid & ATTR_GID) { - owner_grouplen = nfs_map_gid_to_group(server, iap->ia_gid, owner_group, IDMAP_NAMESZ); - if (owner_grouplen < 0) { - dprintk("nfs: couldn't resolve gid %d to string\n", - iap->ia_gid); - strcpy(owner_group, "nobody"); - owner_grouplen = sizeof("nobody") - 1; - /* goto out; */ - } - len += 4 + (XDR_QUADLEN(owner_grouplen) << 2); - } - if (iap->ia_valid & ATTR_ATIME_SET) - len += 16; - else if (iap->ia_valid & ATTR_ATIME) - len += 4; - if (iap->ia_valid & ATTR_MTIME_SET) - len += 16; - else if (iap->ia_valid & ATTR_MTIME) - len += 4; - p = reserve_space(xdr, len); - - /* - * We write the bitmap length now, but leave the bitmap and the attribute - * buffer length to be backfilled at the end of this routine. - */ - *p++ = cpu_to_be32(2); - q = p; - p += 3; - - if (iap->ia_valid & ATTR_SIZE) { - bmval0 |= FATTR4_WORD0_SIZE; - p = xdr_encode_hyper(p, iap->ia_size); - } - if (iap->ia_valid & ATTR_MODE) { - bmval1 |= FATTR4_WORD1_MODE; - *p++ = cpu_to_be32(iap->ia_mode & S_IALLUGO); - } - if (iap->ia_valid & ATTR_UID) { - bmval1 |= FATTR4_WORD1_OWNER; - p = xdr_encode_opaque(p, owner_name, owner_namelen); - } - if (iap->ia_valid & ATTR_GID) { - bmval1 |= FATTR4_WORD1_OWNER_GROUP; - p = xdr_encode_opaque(p, owner_group, owner_grouplen); - } - if (iap->ia_valid & ATTR_ATIME_SET) { - bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET; - *p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME); - *p++ = cpu_to_be32(0); - *p++ = cpu_to_be32(iap->ia_atime.tv_sec); - *p++ = cpu_to_be32(iap->ia_atime.tv_nsec); - } - else if (iap->ia_valid & ATTR_ATIME) { - bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET; - *p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME); - } - if (iap->ia_valid & ATTR_MTIME_SET) { - bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET; - *p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME); - *p++ = cpu_to_be32(0); - *p++ = cpu_to_be32(iap->ia_mtime.tv_sec); - *p++ = cpu_to_be32(iap->ia_mtime.tv_nsec); - } - else if (iap->ia_valid & ATTR_MTIME) { - bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET; - *p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME); - } - - /* - * Now we backfill the bitmap and the attribute buffer length. - */ - if (len != ((char *)p - (char *)q) + 4) { - printk(KERN_ERR "NFS: Attr length error, %u != %Zu\n", - len, ((char *)p - (char *)q) + 4); - BUG(); - } - len = (char *)p - (char *)q - 12; - *q++ = htonl(bmval0); - *q++ = htonl(bmval1); - *q = htonl(len); - -/* out: */ -} - -static void encode_access(struct xdr_stream *xdr, u32 access, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_ACCESS, decode_access_maxsz, hdr); - encode_uint32(xdr, access); -} - -static void encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_CLOSE, decode_close_maxsz, hdr); - encode_nfs4_seqid(xdr, arg->seqid); - encode_nfs4_stateid(xdr, arg->stateid); -} - -static void encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *args, struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_COMMIT, decode_commit_maxsz, hdr); - p = reserve_space(xdr, 12); - p = xdr_encode_hyper(p, args->offset); - *p = cpu_to_be32(args->count); -} - -static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *create, struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_CREATE, decode_create_maxsz, hdr); - encode_uint32(xdr, create->ftype); - - switch (create->ftype) { - case NF4LNK: - p = reserve_space(xdr, 4); - *p = cpu_to_be32(create->u.symlink.len); - xdr_write_pages(xdr, create->u.symlink.pages, 0, create->u.symlink.len); - break; - - case NF4BLK: case NF4CHR: - p = reserve_space(xdr, 8); - *p++ = cpu_to_be32(create->u.device.specdata1); - *p = cpu_to_be32(create->u.device.specdata2); - break; - - default: - break; - } - - encode_string(xdr, create->name->len, create->name->name); - encode_attrs(xdr, create->attrs, create->server); -} - -static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_GETATTR, decode_getattr_maxsz, hdr); - p = reserve_space(xdr, 8); - *p++ = cpu_to_be32(1); - *p = cpu_to_be32(bitmap); -} - -static void encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1, struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_GETATTR, decode_getattr_maxsz, hdr); - p = reserve_space(xdr, 12); - *p++ = cpu_to_be32(2); - *p++ = cpu_to_be32(bm0); - *p = cpu_to_be32(bm1); -} - -static void -encode_getattr_three(struct xdr_stream *xdr, - uint32_t bm0, uint32_t bm1, uint32_t bm2, - struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_GETATTR, decode_getattr_maxsz, hdr); - if (bm2) { - p = reserve_space(xdr, 16); - *p++ = cpu_to_be32(3); - *p++ = cpu_to_be32(bm0); - *p++ = cpu_to_be32(bm1); - *p = cpu_to_be32(bm2); - } else if (bm1) { - p = reserve_space(xdr, 12); - *p++ = cpu_to_be32(2); - *p++ = cpu_to_be32(bm0); - *p = cpu_to_be32(bm1); - } else { - p = reserve_space(xdr, 8); - *p++ = cpu_to_be32(1); - *p = cpu_to_be32(bm0); - } -} - -static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr) -{ - encode_getattr_two(xdr, bitmask[0] & nfs4_fattr_bitmap[0], - bitmask[1] & nfs4_fattr_bitmap[1], hdr); -} - -static void encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr) -{ - encode_getattr_three(xdr, - bitmask[0] & nfs4_fsinfo_bitmap[0], - bitmask[1] & nfs4_fsinfo_bitmap[1], - bitmask[2] & nfs4_fsinfo_bitmap[2], - hdr); -} - -static void encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr) -{ - encode_getattr_two(xdr, bitmask[0] & nfs4_fs_locations_bitmap[0], - bitmask[1] & nfs4_fs_locations_bitmap[1], hdr); -} - -static void encode_getfh(struct xdr_stream *xdr, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_GETFH, decode_getfh_maxsz, hdr); -} - -static void encode_link(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_LINK, decode_link_maxsz, hdr); - encode_string(xdr, name->len, name->name); -} - -static inline int nfs4_lock_type(struct file_lock *fl, int block) -{ - if ((fl->fl_type & (F_RDLCK|F_WRLCK|F_UNLCK)) == F_RDLCK) - return block ? NFS4_READW_LT : NFS4_READ_LT; - return block ? NFS4_WRITEW_LT : NFS4_WRITE_LT; -} - -static inline uint64_t nfs4_lock_length(struct file_lock *fl) -{ - if (fl->fl_end == OFFSET_MAX) - return ~(uint64_t)0; - return fl->fl_end - fl->fl_start + 1; -} - -static void encode_lockowner(struct xdr_stream *xdr, const struct nfs_lowner *lowner) -{ - __be32 *p; - - p = reserve_space(xdr, 32); - p = xdr_encode_hyper(p, lowner->clientid); - *p++ = cpu_to_be32(20); - p = xdr_encode_opaque_fixed(p, "lock id:", 8); - *p++ = cpu_to_be32(lowner->s_dev); - xdr_encode_hyper(p, lowner->id); -} - -/* - * opcode,type,reclaim,offset,length,new_lock_owner = 32 - * open_seqid,open_stateid,lock_seqid,lock_owner.clientid, lock_owner.id = 40 - */ -static void encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args, struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_LOCK, decode_lock_maxsz, hdr); - p = reserve_space(xdr, 28); - *p++ = cpu_to_be32(nfs4_lock_type(args->fl, args->block)); - *p++ = cpu_to_be32(args->reclaim); - p = xdr_encode_hyper(p, args->fl->fl_start); - p = xdr_encode_hyper(p, nfs4_lock_length(args->fl)); - *p = cpu_to_be32(args->new_lock_owner); - if (args->new_lock_owner){ - encode_nfs4_seqid(xdr, args->open_seqid); - encode_nfs4_stateid(xdr, args->open_stateid); - encode_nfs4_seqid(xdr, args->lock_seqid); - encode_lockowner(xdr, &args->lock_owner); - } - else { - encode_nfs4_stateid(xdr, args->lock_stateid); - encode_nfs4_seqid(xdr, args->lock_seqid); - } -} - -static void encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *args, struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_LOCKT, decode_lockt_maxsz, hdr); - p = reserve_space(xdr, 20); - *p++ = cpu_to_be32(nfs4_lock_type(args->fl, 0)); - p = xdr_encode_hyper(p, args->fl->fl_start); - p = xdr_encode_hyper(p, nfs4_lock_length(args->fl)); - encode_lockowner(xdr, &args->lock_owner); -} - -static void encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *args, struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_LOCKU, decode_locku_maxsz, hdr); - encode_uint32(xdr, nfs4_lock_type(args->fl, 0)); - encode_nfs4_seqid(xdr, args->seqid); - encode_nfs4_stateid(xdr, args->stateid); - p = reserve_space(xdr, 16); - p = xdr_encode_hyper(p, args->fl->fl_start); - xdr_encode_hyper(p, nfs4_lock_length(args->fl)); -} - -static void encode_release_lockowner(struct xdr_stream *xdr, const struct nfs_lowner *lowner, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_RELEASE_LOCKOWNER, decode_release_lockowner_maxsz, hdr); - encode_lockowner(xdr, lowner); -} - -static void encode_lookup(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_LOOKUP, decode_lookup_maxsz, hdr); - encode_string(xdr, name->len, name->name); -} - -static void encode_share_access(struct xdr_stream *xdr, fmode_t fmode) -{ - __be32 *p; - - p = reserve_space(xdr, 8); - switch (fmode & (FMODE_READ|FMODE_WRITE)) { - case FMODE_READ: - *p++ = cpu_to_be32(NFS4_SHARE_ACCESS_READ); - break; - case FMODE_WRITE: - *p++ = cpu_to_be32(NFS4_SHARE_ACCESS_WRITE); - break; - case FMODE_READ|FMODE_WRITE: - *p++ = cpu_to_be32(NFS4_SHARE_ACCESS_BOTH); - break; - default: - *p++ = cpu_to_be32(0); - } - *p = cpu_to_be32(0); /* for linux, share_deny = 0 always */ -} - -static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_openargs *arg) -{ - __be32 *p; - /* - * opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4, - * owner 4 = 32 - */ - encode_nfs4_seqid(xdr, arg->seqid); - encode_share_access(xdr, arg->fmode); - p = reserve_space(xdr, 36); - p = xdr_encode_hyper(p, arg->clientid); - *p++ = cpu_to_be32(24); - p = xdr_encode_opaque_fixed(p, "open id:", 8); - *p++ = cpu_to_be32(arg->server->s_dev); - *p++ = cpu_to_be32(arg->id.uniquifier); - xdr_encode_hyper(p, arg->id.create_time); -} - -static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_openargs *arg) -{ - __be32 *p; - struct nfs_client *clp; - - p = reserve_space(xdr, 4); - switch(arg->open_flags & O_EXCL) { - case 0: - *p = cpu_to_be32(NFS4_CREATE_UNCHECKED); - encode_attrs(xdr, arg->u.attrs, arg->server); - break; - default: - clp = arg->server->nfs_client; - if (clp->cl_mvops->minor_version > 0) { - if (nfs4_has_persistent_session(clp)) { - *p = cpu_to_be32(NFS4_CREATE_GUARDED); - encode_attrs(xdr, arg->u.attrs, arg->server); - } else { - struct iattr dummy; - - *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1); - encode_nfs4_verifier(xdr, &arg->u.verifier); - dummy.ia_valid = 0; - encode_attrs(xdr, &dummy, arg->server); - } - } else { - *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE); - encode_nfs4_verifier(xdr, &arg->u.verifier); - } - } -} - -static void encode_opentype(struct xdr_stream *xdr, const struct nfs_openargs *arg) -{ - __be32 *p; - - p = reserve_space(xdr, 4); - switch (arg->open_flags & O_CREAT) { - case 0: - *p = cpu_to_be32(NFS4_OPEN_NOCREATE); - break; - default: - BUG_ON(arg->claim != NFS4_OPEN_CLAIM_NULL); - *p = cpu_to_be32(NFS4_OPEN_CREATE); - encode_createmode(xdr, arg); - } -} - -static inline void encode_delegation_type(struct xdr_stream *xdr, fmode_t delegation_type) -{ - __be32 *p; - - p = reserve_space(xdr, 4); - switch (delegation_type) { - case 0: - *p = cpu_to_be32(NFS4_OPEN_DELEGATE_NONE); - break; - case FMODE_READ: - *p = cpu_to_be32(NFS4_OPEN_DELEGATE_READ); - break; - case FMODE_WRITE|FMODE_READ: - *p = cpu_to_be32(NFS4_OPEN_DELEGATE_WRITE); - break; - default: - BUG(); - } -} - -static inline void encode_claim_null(struct xdr_stream *xdr, const struct qstr *name) -{ - __be32 *p; - - p = reserve_space(xdr, 4); - *p = cpu_to_be32(NFS4_OPEN_CLAIM_NULL); - encode_string(xdr, name->len, name->name); -} - -static inline void encode_claim_previous(struct xdr_stream *xdr, fmode_t type) -{ - __be32 *p; - - p = reserve_space(xdr, 4); - *p = cpu_to_be32(NFS4_OPEN_CLAIM_PREVIOUS); - encode_delegation_type(xdr, type); -} - -static inline void encode_claim_delegate_cur(struct xdr_stream *xdr, const struct qstr *name, const nfs4_stateid *stateid) -{ - __be32 *p; - - p = reserve_space(xdr, 4); - *p = cpu_to_be32(NFS4_OPEN_CLAIM_DELEGATE_CUR); - encode_nfs4_stateid(xdr, stateid); - encode_string(xdr, name->len, name->name); -} - -static void encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_OPEN, decode_open_maxsz, hdr); - encode_openhdr(xdr, arg); - encode_opentype(xdr, arg); - switch (arg->claim) { - case NFS4_OPEN_CLAIM_NULL: - encode_claim_null(xdr, arg->name); - break; - case NFS4_OPEN_CLAIM_PREVIOUS: - encode_claim_previous(xdr, arg->u.delegation_type); - break; - case NFS4_OPEN_CLAIM_DELEGATE_CUR: - encode_claim_delegate_cur(xdr, arg->name, &arg->u.delegation); - break; - default: - BUG(); - } -} - -static void encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_OPEN_CONFIRM, decode_open_confirm_maxsz, hdr); - encode_nfs4_stateid(xdr, arg->stateid); - encode_nfs4_seqid(xdr, arg->seqid); -} - -static void encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_OPEN_DOWNGRADE, decode_open_downgrade_maxsz, hdr); - encode_nfs4_stateid(xdr, arg->stateid); - encode_nfs4_seqid(xdr, arg->seqid); - encode_share_access(xdr, arg->fmode); -} - -static void -encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_PUTFH, decode_putfh_maxsz, hdr); - encode_string(xdr, fh->size, fh->data); -} - -static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_PUTROOTFH, decode_putrootfh_maxsz, hdr); -} - -static void encode_open_stateid(struct xdr_stream *xdr, - const struct nfs_open_context *ctx, - const struct nfs_lock_context *l_ctx, - fmode_t fmode, - int zero_seqid) -{ - nfs4_stateid stateid; - - if (ctx->state != NULL) { - nfs4_select_rw_stateid(&stateid, ctx->state, - fmode, l_ctx->lockowner, l_ctx->pid); - if (zero_seqid) - stateid.seqid = 0; - encode_nfs4_stateid(xdr, &stateid); - } else - encode_nfs4_stateid(xdr, &zero_stateid); -} - -static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args, struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_READ, decode_read_maxsz, hdr); - encode_open_stateid(xdr, args->context, args->lock_context, - FMODE_READ, hdr->minorversion); - - p = reserve_space(xdr, 12); - p = xdr_encode_hyper(p, args->offset); - *p = cpu_to_be32(args->count); -} - -static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr) -{ - uint32_t attrs[2] = { - FATTR4_WORD0_RDATTR_ERROR, - FATTR4_WORD1_MOUNTED_ON_FILEID, - }; - uint32_t dircount = readdir->count >> 1; - __be32 *p, verf[2]; - - if (readdir->plus) { - attrs[0] |= FATTR4_WORD0_TYPE|FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE| - FATTR4_WORD0_FSID|FATTR4_WORD0_FILEHANDLE|FATTR4_WORD0_FILEID; - attrs[1] |= FATTR4_WORD1_MODE|FATTR4_WORD1_NUMLINKS|FATTR4_WORD1_OWNER| - FATTR4_WORD1_OWNER_GROUP|FATTR4_WORD1_RAWDEV| - FATTR4_WORD1_SPACE_USED|FATTR4_WORD1_TIME_ACCESS| - FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY; - dircount >>= 1; - } - /* Use mounted_on_fileid only if the server supports it */ - if (!(readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)) - attrs[0] |= FATTR4_WORD0_FILEID; - - encode_op_hdr(xdr, OP_READDIR, decode_readdir_maxsz, hdr); - encode_uint64(xdr, readdir->cookie); - encode_nfs4_verifier(xdr, &readdir->verifier); - p = reserve_space(xdr, 20); - *p++ = cpu_to_be32(dircount); - *p++ = cpu_to_be32(readdir->count); - *p++ = cpu_to_be32(2); - - *p++ = cpu_to_be32(attrs[0] & readdir->bitmask[0]); - *p = cpu_to_be32(attrs[1] & readdir->bitmask[1]); - memcpy(verf, readdir->verifier.data, sizeof(verf)); - dprintk("%s: cookie = %Lu, verifier = %08x:%08x, bitmap = %08x:%08x\n", - __func__, - (unsigned long long)readdir->cookie, - verf[0], verf[1], - attrs[0] & readdir->bitmask[0], - attrs[1] & readdir->bitmask[1]); -} - -static void encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_READLINK, decode_readlink_maxsz, hdr); -} - -static void encode_remove(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_REMOVE, decode_remove_maxsz, hdr); - encode_string(xdr, name->len, name->name); -} - -static void encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, const struct qstr *newname, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_RENAME, decode_rename_maxsz, hdr); - encode_string(xdr, oldname->len, oldname->name); - encode_string(xdr, newname->len, newname->name); -} - -static void encode_renew(struct xdr_stream *xdr, clientid4 clid, - struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_RENEW, decode_renew_maxsz, hdr); - encode_uint64(xdr, clid); -} - -static void -encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_RESTOREFH, decode_restorefh_maxsz, hdr); -} - -static void -encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_SETATTR, decode_setacl_maxsz, hdr); - encode_nfs4_stateid(xdr, &zero_stateid); - p = reserve_space(xdr, 2*4); - *p++ = cpu_to_be32(1); - *p = cpu_to_be32(FATTR4_WORD0_ACL); - BUG_ON(arg->acl_len % 4); - p = reserve_space(xdr, 4); - *p = cpu_to_be32(arg->acl_len); - xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len); -} - -static void -encode_savefh(struct xdr_stream *xdr, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_SAVEFH, decode_savefh_maxsz, hdr); -} - -static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs *arg, const struct nfs_server *server, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_SETATTR, decode_setattr_maxsz, hdr); - encode_nfs4_stateid(xdr, &arg->stateid); - encode_attrs(xdr, arg->iap, server); -} - -static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid, struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_SETCLIENTID, decode_setclientid_maxsz, hdr); - encode_nfs4_verifier(xdr, setclientid->sc_verifier); - - encode_string(xdr, setclientid->sc_name_len, setclientid->sc_name); - p = reserve_space(xdr, 4); - *p = cpu_to_be32(setclientid->sc_prog); - encode_string(xdr, setclientid->sc_netid_len, setclientid->sc_netid); - encode_string(xdr, setclientid->sc_uaddr_len, setclientid->sc_uaddr); - p = reserve_space(xdr, 4); - *p = cpu_to_be32(setclientid->sc_cb_ident); -} - -static void encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs4_setclientid_res *arg, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_SETCLIENTID_CONFIRM, - decode_setclientid_confirm_maxsz, hdr); - encode_uint64(xdr, arg->clientid); - encode_nfs4_verifier(xdr, &arg->confirm); -} - -static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args, struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_WRITE, decode_write_maxsz, hdr); - encode_open_stateid(xdr, args->context, args->lock_context, - FMODE_WRITE, hdr->minorversion); - - p = reserve_space(xdr, 16); - p = xdr_encode_hyper(p, args->offset); - *p++ = cpu_to_be32(args->stable); - *p = cpu_to_be32(args->count); - - xdr_write_pages(xdr, args->pages, args->pgbase, args->count); -} - -static void encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *stateid, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_DELEGRETURN, decode_delegreturn_maxsz, hdr); - encode_nfs4_stateid(xdr, stateid); -} - -static void encode_secinfo(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_SECINFO, decode_secinfo_maxsz, hdr); - encode_string(xdr, name->len, name->name); -} - -#if defined(CONFIG_NFS_V4_1) -/* NFSv4.1 operations */ -static void encode_exchange_id(struct xdr_stream *xdr, - struct nfs41_exchange_id_args *args, - struct compound_hdr *hdr) -{ - __be32 *p; - char impl_name[NFS4_OPAQUE_LIMIT]; - int len = 0; - - encode_op_hdr(xdr, OP_EXCHANGE_ID, decode_exchange_id_maxsz, hdr); - encode_nfs4_verifier(xdr, args->verifier); - - encode_string(xdr, args->id_len, args->id); - - p = reserve_space(xdr, 12); - *p++ = cpu_to_be32(args->flags); - *p++ = cpu_to_be32(0); /* zero length state_protect4_a */ - - if (send_implementation_id && - sizeof(CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN) > 1 && - sizeof(CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN) - <= NFS4_OPAQUE_LIMIT + 1) - len = snprintf(impl_name, sizeof(impl_name), "%s %s %s %s", - utsname()->sysname, utsname()->release, - utsname()->version, utsname()->machine); - - if (len > 0) { - *p = cpu_to_be32(1); /* implementation id array length=1 */ - - encode_string(xdr, - sizeof(CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN) - 1, - CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN); - encode_string(xdr, len, impl_name); - /* just send zeros for nii_date - the date is in nii_name */ - p = reserve_space(xdr, 12); - p = xdr_encode_hyper(p, 0); - *p = cpu_to_be32(0); - } else - *p = cpu_to_be32(0); /* implementation id array length=0 */ -} - -static void encode_create_session(struct xdr_stream *xdr, - struct nfs41_create_session_args *args, - struct compound_hdr *hdr) -{ - __be32 *p; - char machine_name[NFS4_MAX_MACHINE_NAME_LEN]; - uint32_t len; - struct nfs_client *clp = args->client; - u32 max_resp_sz_cached; - - /* - * Assumes OPEN is the biggest non-idempotent compound. - * 2 is the verifier. - */ - max_resp_sz_cached = (NFS4_dec_open_sz + RPC_REPHDRSIZE + - RPC_MAX_AUTH_SIZE + 2) * XDR_UNIT; - - len = scnprintf(machine_name, sizeof(machine_name), "%s", - clp->cl_ipaddr); - - encode_op_hdr(xdr, OP_CREATE_SESSION, decode_create_session_maxsz, hdr); - p = reserve_space(xdr, 16 + 2*28 + 20 + len + 12); - p = xdr_encode_hyper(p, clp->cl_clientid); - *p++ = cpu_to_be32(clp->cl_seqid); /*Sequence id */ - *p++ = cpu_to_be32(args->flags); /*flags */ - - /* Fore Channel */ - *p++ = cpu_to_be32(0); /* header padding size */ - *p++ = cpu_to_be32(args->fc_attrs.max_rqst_sz); /* max req size */ - *p++ = cpu_to_be32(args->fc_attrs.max_resp_sz); /* max resp size */ - *p++ = cpu_to_be32(max_resp_sz_cached); /* Max resp sz cached */ - *p++ = cpu_to_be32(args->fc_attrs.max_ops); /* max operations */ - *p++ = cpu_to_be32(args->fc_attrs.max_reqs); /* max requests */ - *p++ = cpu_to_be32(0); /* rdmachannel_attrs */ - - /* Back Channel */ - *p++ = cpu_to_be32(0); /* header padding size */ - *p++ = cpu_to_be32(args->bc_attrs.max_rqst_sz); /* max req size */ - *p++ = cpu_to_be32(args->bc_attrs.max_resp_sz); /* max resp size */ - *p++ = cpu_to_be32(args->bc_attrs.max_resp_sz_cached); /* Max resp sz cached */ - *p++ = cpu_to_be32(args->bc_attrs.max_ops); /* max operations */ - *p++ = cpu_to_be32(args->bc_attrs.max_reqs); /* max requests */ - *p++ = cpu_to_be32(0); /* rdmachannel_attrs */ - - *p++ = cpu_to_be32(args->cb_program); /* cb_program */ - *p++ = cpu_to_be32(1); - *p++ = cpu_to_be32(RPC_AUTH_UNIX); /* auth_sys */ - - /* authsys_parms rfc1831 */ - *p++ = cpu_to_be32((u32)clp->cl_boot_time.tv_nsec); /* stamp */ - p = xdr_encode_opaque(p, machine_name, len); - *p++ = cpu_to_be32(0); /* UID */ - *p++ = cpu_to_be32(0); /* GID */ - *p = cpu_to_be32(0); /* No more gids */ -} - -static void encode_destroy_session(struct xdr_stream *xdr, - struct nfs4_session *session, - struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_DESTROY_SESSION, decode_destroy_session_maxsz, hdr); - encode_opaque_fixed(xdr, session->sess_id.data, NFS4_MAX_SESSIONID_LEN); -} - -static void encode_reclaim_complete(struct xdr_stream *xdr, - struct nfs41_reclaim_complete_args *args, - struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_RECLAIM_COMPLETE, decode_reclaim_complete_maxsz, hdr); - encode_uint32(xdr, args->one_fs); -} -#endif /* CONFIG_NFS_V4_1 */ - -static void encode_sequence(struct xdr_stream *xdr, - const struct nfs4_sequence_args *args, - struct compound_hdr *hdr) -{ -#if defined(CONFIG_NFS_V4_1) - struct nfs4_session *session = args->sa_session; - struct nfs4_slot_table *tp; - struct nfs4_slot *slot; - __be32 *p; - - if (!session) - return; - - tp = &session->fc_slot_table; - - WARN_ON(args->sa_slotid == NFS4_MAX_SLOT_TABLE); - slot = tp->slots + args->sa_slotid; - - encode_op_hdr(xdr, OP_SEQUENCE, decode_sequence_maxsz, hdr); - - /* - * Sessionid + seqid + slotid + max slotid + cache_this - */ - dprintk("%s: sessionid=%u:%u:%u:%u seqid=%d slotid=%d " - "max_slotid=%d cache_this=%d\n", - __func__, - ((u32 *)session->sess_id.data)[0], - ((u32 *)session->sess_id.data)[1], - ((u32 *)session->sess_id.data)[2], - ((u32 *)session->sess_id.data)[3], - slot->seq_nr, args->sa_slotid, - tp->highest_used_slotid, args->sa_cache_this); - p = reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 16); - p = xdr_encode_opaque_fixed(p, session->sess_id.data, NFS4_MAX_SESSIONID_LEN); - *p++ = cpu_to_be32(slot->seq_nr); - *p++ = cpu_to_be32(args->sa_slotid); - *p++ = cpu_to_be32(tp->highest_used_slotid); - *p = cpu_to_be32(args->sa_cache_this); -#endif /* CONFIG_NFS_V4_1 */ -} - -#ifdef CONFIG_NFS_V4_1 -static void -encode_getdevicelist(struct xdr_stream *xdr, - const struct nfs4_getdevicelist_args *args, - struct compound_hdr *hdr) -{ - __be32 *p; - nfs4_verifier dummy = { - .data = "dummmmmy", - }; - - encode_op_hdr(xdr, OP_GETDEVICELIST, decode_getdevicelist_maxsz, hdr); - p = reserve_space(xdr, 16); - *p++ = cpu_to_be32(args->layoutclass); - *p++ = cpu_to_be32(NFS4_PNFS_GETDEVLIST_MAXNUM); - xdr_encode_hyper(p, 0ULL); /* cookie */ - encode_nfs4_verifier(xdr, &dummy); -} - -static void -encode_getdeviceinfo(struct xdr_stream *xdr, - const struct nfs4_getdeviceinfo_args *args, - struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_GETDEVICEINFO, decode_getdeviceinfo_maxsz, hdr); - p = reserve_space(xdr, 12 + NFS4_DEVICEID4_SIZE); - p = xdr_encode_opaque_fixed(p, args->pdev->dev_id.data, - NFS4_DEVICEID4_SIZE); - *p++ = cpu_to_be32(args->pdev->layout_type); - *p++ = cpu_to_be32(args->pdev->pglen); /* gdia_maxcount */ - *p++ = cpu_to_be32(0); /* bitmap length 0 */ -} - -static void -encode_layoutget(struct xdr_stream *xdr, - const struct nfs4_layoutget_args *args, - struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_LAYOUTGET, decode_layoutget_maxsz, hdr); - p = reserve_space(xdr, 36); - *p++ = cpu_to_be32(0); /* Signal layout available */ - *p++ = cpu_to_be32(args->type); - *p++ = cpu_to_be32(args->range.iomode); - p = xdr_encode_hyper(p, args->range.offset); - p = xdr_encode_hyper(p, args->range.length); - p = xdr_encode_hyper(p, args->minlength); - encode_nfs4_stateid(xdr, &args->stateid); - encode_uint32(xdr, args->maxcount); - - dprintk("%s: 1st type:0x%x iomode:%d off:%lu len:%lu mc:%d\n", - __func__, - args->type, - args->range.iomode, - (unsigned long)args->range.offset, - (unsigned long)args->range.length, - args->maxcount); -} - -static int -encode_layoutcommit(struct xdr_stream *xdr, - struct inode *inode, - const struct nfs4_layoutcommit_args *args, - struct compound_hdr *hdr) -{ - __be32 *p; - - dprintk("%s: lbw: %llu type: %d\n", __func__, args->lastbytewritten, - NFS_SERVER(args->inode)->pnfs_curr_ld->id); - - encode_op_hdr(xdr, OP_LAYOUTCOMMIT, decode_layoutcommit_maxsz, hdr); - p = reserve_space(xdr, 20); - /* Only whole file layouts */ - p = xdr_encode_hyper(p, 0); /* offset */ - p = xdr_encode_hyper(p, args->lastbytewritten + 1); /* length */ - *p = cpu_to_be32(0); /* reclaim */ - encode_nfs4_stateid(xdr, &args->stateid); - p = reserve_space(xdr, 20); - *p++ = cpu_to_be32(1); /* newoffset = TRUE */ - p = xdr_encode_hyper(p, args->lastbytewritten); - *p++ = cpu_to_be32(0); /* Never send time_modify_changed */ - *p++ = cpu_to_be32(NFS_SERVER(args->inode)->pnfs_curr_ld->id);/* type */ - - if (NFS_SERVER(inode)->pnfs_curr_ld->encode_layoutcommit) - NFS_SERVER(inode)->pnfs_curr_ld->encode_layoutcommit( - NFS_I(inode)->layout, xdr, args); - else - encode_uint32(xdr, 0); /* no layout-type payload */ - - return 0; -} - -static void -encode_layoutreturn(struct xdr_stream *xdr, - const struct nfs4_layoutreturn_args *args, - struct compound_hdr *hdr) -{ - __be32 *p; - - encode_op_hdr(xdr, OP_LAYOUTRETURN, decode_layoutreturn_maxsz, hdr); - p = reserve_space(xdr, 16); - *p++ = cpu_to_be32(0); /* reclaim. always 0 for now */ - *p++ = cpu_to_be32(args->layout_type); - *p++ = cpu_to_be32(IOMODE_ANY); - *p = cpu_to_be32(RETURN_FILE); - p = reserve_space(xdr, 16); - p = xdr_encode_hyper(p, 0); - p = xdr_encode_hyper(p, NFS4_MAX_UINT64); - spin_lock(&args->inode->i_lock); - encode_nfs4_stateid(xdr, &args->stateid); - spin_unlock(&args->inode->i_lock); - if (NFS_SERVER(args->inode)->pnfs_curr_ld->encode_layoutreturn) { - NFS_SERVER(args->inode)->pnfs_curr_ld->encode_layoutreturn( - NFS_I(args->inode)->layout, xdr, args); - } else - encode_uint32(xdr, 0); -} - -static int -encode_secinfo_no_name(struct xdr_stream *xdr, - const struct nfs41_secinfo_no_name_args *args, - struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_SECINFO_NO_NAME, decode_secinfo_no_name_maxsz, hdr); - encode_uint32(xdr, args->style); - return 0; -} - -static void encode_test_stateid(struct xdr_stream *xdr, - struct nfs41_test_stateid_args *args, - struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_TEST_STATEID, decode_test_stateid_maxsz, hdr); - encode_uint32(xdr, 1); - encode_nfs4_stateid(xdr, args->stateid); -} - -static void encode_free_stateid(struct xdr_stream *xdr, - struct nfs41_free_stateid_args *args, - struct compound_hdr *hdr) -{ - encode_op_hdr(xdr, OP_FREE_STATEID, decode_free_stateid_maxsz, hdr); - encode_nfs4_stateid(xdr, args->stateid); -} -#endif /* CONFIG_NFS_V4_1 */ - -/* - * END OF "GENERIC" ENCODE ROUTINES. - */ - -static u32 nfs4_xdr_minorversion(const struct nfs4_sequence_args *args) -{ -#if defined(CONFIG_NFS_V4_1) - if (args->sa_session) - return args->sa_session->clp->cl_mvops->minor_version; -#endif /* CONFIG_NFS_V4_1 */ - return 0; -} - -/* - * Encode an ACCESS request - */ -static void nfs4_xdr_enc_access(struct rpc_rqst *req, struct xdr_stream *xdr, - const struct nfs4_accessargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_access(xdr, args->access, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode LOOKUP request - */ -static void nfs4_xdr_enc_lookup(struct rpc_rqst *req, struct xdr_stream *xdr, - const struct nfs4_lookup_arg *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->dir_fh, &hdr); - encode_lookup(xdr, args->name, &hdr); - encode_getfh(xdr, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode LOOKUP_ROOT request - */ -static void nfs4_xdr_enc_lookup_root(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs4_lookup_root_arg *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putrootfh(xdr, &hdr); - encode_getfh(xdr, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode REMOVE request - */ -static void nfs4_xdr_enc_remove(struct rpc_rqst *req, struct xdr_stream *xdr, - const struct nfs_removeargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_remove(xdr, &args->name, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode RENAME request - */ -static void nfs4_xdr_enc_rename(struct rpc_rqst *req, struct xdr_stream *xdr, - const struct nfs_renameargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->old_dir, &hdr); - encode_savefh(xdr, &hdr); - encode_putfh(xdr, args->new_dir, &hdr); - encode_rename(xdr, args->old_name, args->new_name, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_restorefh(xdr, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode LINK request - */ -static void nfs4_xdr_enc_link(struct rpc_rqst *req, struct xdr_stream *xdr, - const struct nfs4_link_arg *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_savefh(xdr, &hdr); - encode_putfh(xdr, args->dir_fh, &hdr); - encode_link(xdr, args->name, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_restorefh(xdr, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode CREATE request - */ -static void nfs4_xdr_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr, - const struct nfs4_create_arg *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->dir_fh, &hdr); - encode_savefh(xdr, &hdr); - encode_create(xdr, args, &hdr); - encode_getfh(xdr, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_restorefh(xdr, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode SYMLINK request - */ -static void nfs4_xdr_enc_symlink(struct rpc_rqst *req, struct xdr_stream *xdr, - const struct nfs4_create_arg *args) -{ - nfs4_xdr_enc_create(req, xdr, args); -} - -/* - * Encode GETATTR request - */ -static void nfs4_xdr_enc_getattr(struct rpc_rqst *req, struct xdr_stream *xdr, - const struct nfs4_getattr_arg *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode a CLOSE request - */ -static void nfs4_xdr_enc_close(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_closeargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_close(xdr, args, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode an OPEN request - */ -static void nfs4_xdr_enc_open(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_openargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_savefh(xdr, &hdr); - encode_open(xdr, args, &hdr); - encode_getfh(xdr, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_restorefh(xdr, &hdr); - encode_getfattr(xdr, args->dir_bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode an OPEN_CONFIRM request - */ -static void nfs4_xdr_enc_open_confirm(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_open_confirmargs *args) -{ - struct compound_hdr hdr = { - .nops = 0, - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_open_confirm(xdr, args, &hdr); - encode_nops(&hdr); -} - -/* - * Encode an OPEN request with no attributes. - */ -static void nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_openargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_open(xdr, args, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode an OPEN_DOWNGRADE request - */ -static void nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_closeargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_open_downgrade(xdr, args, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode a LOCK request - */ -static void nfs4_xdr_enc_lock(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_lock_args *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_lock(xdr, args, &hdr); - encode_nops(&hdr); -} - -/* - * Encode a LOCKT request - */ -static void nfs4_xdr_enc_lockt(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_lockt_args *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_lockt(xdr, args, &hdr); - encode_nops(&hdr); -} - -/* - * Encode a LOCKU request - */ -static void nfs4_xdr_enc_locku(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_locku_args *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_locku(xdr, args, &hdr); - encode_nops(&hdr); -} - -static void nfs4_xdr_enc_release_lockowner(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_release_lockowner_args *args) -{ - struct compound_hdr hdr = { - .minorversion = 0, - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_release_lockowner(xdr, &args->lock_owner, &hdr); - encode_nops(&hdr); -} - -/* - * Encode a READLINK request - */ -static void nfs4_xdr_enc_readlink(struct rpc_rqst *req, struct xdr_stream *xdr, - const struct nfs4_readlink *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_readlink(xdr, args, req, &hdr); - - xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages, - args->pgbase, args->pglen); - encode_nops(&hdr); -} - -/* - * Encode a READDIR request - */ -static void nfs4_xdr_enc_readdir(struct rpc_rqst *req, struct xdr_stream *xdr, - const struct nfs4_readdir_arg *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_readdir(xdr, args, req, &hdr); - - xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages, - args->pgbase, args->count); - dprintk("%s: inlined page args = (%u, %p, %u, %u)\n", - __func__, hdr.replen << 2, args->pages, - args->pgbase, args->count); - encode_nops(&hdr); -} - -/* - * Encode a READ request - */ -static void nfs4_xdr_enc_read(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_readargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_read(xdr, args, &hdr); - - xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, - args->pages, args->pgbase, args->count); - req->rq_rcv_buf.flags |= XDRBUF_READ; - encode_nops(&hdr); -} - -/* - * Encode an SETATTR request - */ -static void nfs4_xdr_enc_setattr(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_setattrargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_setattr(xdr, args, args->server, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode a GETACL request - */ -static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_getaclargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - uint32_t replen; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - replen = hdr.replen + op_decode_hdr_maxsz + 1; - encode_getattr_two(xdr, FATTR4_WORD0_ACL, 0, &hdr); - - xdr_inline_pages(&req->rq_rcv_buf, replen << 2, - args->acl_pages, args->acl_pgbase, args->acl_len); - - encode_nops(&hdr); -} - -/* - * Encode a WRITE request - */ -static void nfs4_xdr_enc_write(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_writeargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_write(xdr, args, &hdr); - req->rq_snd_buf.flags |= XDRBUF_WRITE; - if (args->bitmask) - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * a COMMIT request - */ -static void nfs4_xdr_enc_commit(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_writeargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_commit(xdr, args, &hdr); - if (args->bitmask) - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * FSINFO request - */ -static void nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs4_fsinfo_arg *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_fsinfo(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * a PATHCONF request - */ -static void nfs4_xdr_enc_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr, - const struct nfs4_pathconf_arg *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_getattr_one(xdr, args->bitmask[0] & nfs4_pathconf_bitmap[0], - &hdr); - encode_nops(&hdr); -} - -/* - * a STATFS request - */ -static void nfs4_xdr_enc_statfs(struct rpc_rqst *req, struct xdr_stream *xdr, - const struct nfs4_statfs_arg *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_getattr_two(xdr, args->bitmask[0] & nfs4_statfs_bitmap[0], - args->bitmask[1] & nfs4_statfs_bitmap[1], &hdr); - encode_nops(&hdr); -} - -/* - * GETATTR_BITMAP request - */ -static void nfs4_xdr_enc_server_caps(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_server_caps_arg *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fhandle, &hdr); - encode_getattr_one(xdr, FATTR4_WORD0_SUPPORTED_ATTRS| - FATTR4_WORD0_FH_EXPIRE_TYPE| - FATTR4_WORD0_LINK_SUPPORT| - FATTR4_WORD0_SYMLINK_SUPPORT| - FATTR4_WORD0_ACLSUPPORT, &hdr); - encode_nops(&hdr); -} - -/* - * a RENEW request - */ -static void nfs4_xdr_enc_renew(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_client *clp) -{ - struct compound_hdr hdr = { - .nops = 0, - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_renew(xdr, clp->cl_clientid, &hdr); - encode_nops(&hdr); -} - -/* - * a SETCLIENTID request - */ -static void nfs4_xdr_enc_setclientid(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_setclientid *sc) -{ - struct compound_hdr hdr = { - .nops = 0, - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_setclientid(xdr, sc, &hdr); - encode_nops(&hdr); -} - -/* - * a SETCLIENTID_CONFIRM request - */ -static void nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_setclientid_res *arg) -{ - struct compound_hdr hdr = { - .nops = 0, - }; - const u32 lease_bitmap[3] = { FATTR4_WORD0_LEASE_TIME }; - - encode_compound_hdr(xdr, req, &hdr); - encode_setclientid_confirm(xdr, arg, &hdr); - encode_putrootfh(xdr, &hdr); - encode_fsinfo(xdr, lease_bitmap, &hdr); - encode_nops(&hdr); -} - -/* - * DELEGRETURN request - */ -static void nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, - struct xdr_stream *xdr, - const struct nfs4_delegreturnargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fhandle, &hdr); - encode_delegreturn(xdr, args->stateid, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode FS_LOCATIONS request - */ -static void nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_fs_locations_arg *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - uint32_t replen; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->dir_fh, &hdr); - encode_lookup(xdr, args->name, &hdr); - replen = hdr.replen; /* get the attribute into args->page */ - encode_fs_locations(xdr, args->bitmask, &hdr); - - xdr_inline_pages(&req->rq_rcv_buf, replen << 2, &args->page, - 0, PAGE_SIZE); - encode_nops(&hdr); -} - -/* - * Encode SECINFO request - */ -static void nfs4_xdr_enc_secinfo(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_secinfo_arg *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->dir_fh, &hdr); - encode_secinfo(xdr, args->name, &hdr); - encode_nops(&hdr); -} - -#if defined(CONFIG_NFS_V4_1) -/* - * EXCHANGE_ID request - */ -static void nfs4_xdr_enc_exchange_id(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs41_exchange_id_args *args) -{ - struct compound_hdr hdr = { - .minorversion = args->client->cl_mvops->minor_version, - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_exchange_id(xdr, args, &hdr); - encode_nops(&hdr); -} - -/* - * a CREATE_SESSION request - */ -static void nfs4_xdr_enc_create_session(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs41_create_session_args *args) -{ - struct compound_hdr hdr = { - .minorversion = args->client->cl_mvops->minor_version, - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_create_session(xdr, args, &hdr); - encode_nops(&hdr); -} - -/* - * a DESTROY_SESSION request - */ -static void nfs4_xdr_enc_destroy_session(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_session *session) -{ - struct compound_hdr hdr = { - .minorversion = session->clp->cl_mvops->minor_version, - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_destroy_session(xdr, session, &hdr); - encode_nops(&hdr); -} - -/* - * a SEQUENCE request - */ -static void nfs4_xdr_enc_sequence(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs4_sequence_args *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, args, &hdr); - encode_nops(&hdr); -} - -/* - * a GET_LEASE_TIME request - */ -static void nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_get_lease_time_args *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->la_seq_args), - }; - const u32 lease_bitmap[3] = { FATTR4_WORD0_LEASE_TIME }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->la_seq_args, &hdr); - encode_putrootfh(xdr, &hdr); - encode_fsinfo(xdr, lease_bitmap, &hdr); - encode_nops(&hdr); -} - -/* - * a RECLAIM_COMPLETE request - */ -static void nfs4_xdr_enc_reclaim_complete(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs41_reclaim_complete_args *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args) - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_reclaim_complete(xdr, args, &hdr); - encode_nops(&hdr); -} - -/* - * Encode GETDEVICELIST request - */ -static void nfs4_xdr_enc_getdevicelist(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_getdevicelist_args *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_getdevicelist(xdr, args, &hdr); - encode_nops(&hdr); -} - -/* - * Encode GETDEVICEINFO request - */ -static void nfs4_xdr_enc_getdeviceinfo(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_getdeviceinfo_args *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_getdeviceinfo(xdr, args, &hdr); - - /* set up reply kvec. Subtract notification bitmap max size (2) - * so that notification bitmap is put in xdr_buf tail */ - xdr_inline_pages(&req->rq_rcv_buf, (hdr.replen - 2) << 2, - args->pdev->pages, args->pdev->pgbase, - args->pdev->pglen); - - encode_nops(&hdr); -} - -/* - * Encode LAYOUTGET request - */ -static void nfs4_xdr_enc_layoutget(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_layoutget_args *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, NFS_FH(args->inode), &hdr); - encode_layoutget(xdr, args, &hdr); - - xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, - args->layout.pages, 0, args->layout.pglen); - - encode_nops(&hdr); -} - -/* - * Encode LAYOUTCOMMIT request - */ -static void nfs4_xdr_enc_layoutcommit(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_layoutcommit_args *args) -{ - struct nfs4_layoutcommit_data *data = - container_of(args, struct nfs4_layoutcommit_data, args); - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, NFS_FH(args->inode), &hdr); - encode_layoutcommit(xdr, data->args.inode, args, &hdr); - encode_getfattr(xdr, args->bitmask, &hdr); - encode_nops(&hdr); -} - -/* - * Encode LAYOUTRETURN request - */ -static void nfs4_xdr_enc_layoutreturn(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_layoutreturn_args *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, NFS_FH(args->inode), &hdr); - encode_layoutreturn(xdr, args, &hdr); - encode_nops(&hdr); -} - -/* - * Encode SECINFO_NO_NAME request - */ -static int nfs4_xdr_enc_secinfo_no_name(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs41_secinfo_no_name_args *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putrootfh(xdr, &hdr); - encode_secinfo_no_name(xdr, args, &hdr); - encode_nops(&hdr); - return 0; -} - -/* - * Encode TEST_STATEID request - */ -static void nfs4_xdr_enc_test_stateid(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs41_test_stateid_args *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_test_stateid(xdr, args, &hdr); - encode_nops(&hdr); -} - -/* - * Encode FREE_STATEID request - */ -static void nfs4_xdr_enc_free_stateid(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs41_free_stateid_args *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_free_stateid(xdr, args, &hdr); - encode_nops(&hdr); -} -#endif /* CONFIG_NFS_V4_1 */ - -static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) -{ - dprintk("nfs: %s: prematurely hit end of receive buffer. " - "Remaining buffer length is %tu words.\n", - func, xdr->end - xdr->p); -} - -static int decode_opaque_inline(struct xdr_stream *xdr, unsigned int *len, char **string) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - *len = be32_to_cpup(p); - p = xdr_inline_decode(xdr, *len); - if (unlikely(!p)) - goto out_overflow; - *string = (char *)p; - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - hdr->status = be32_to_cpup(p++); - hdr->taglen = be32_to_cpup(p); - - p = xdr_inline_decode(xdr, hdr->taglen + 4); - if (unlikely(!p)) - goto out_overflow; - hdr->tag = (char *)p; - p += XDR_QUADLEN(hdr->taglen); - hdr->nops = be32_to_cpup(p); - if (unlikely(hdr->nops < 1)) - return nfs4_stat_to_errno(hdr->status); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected) -{ - __be32 *p; - uint32_t opnum; - int32_t nfserr; - - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - opnum = be32_to_cpup(p++); - if (opnum != expected) { - dprintk("nfs: Server returned operation" - " %d but we issued a request for %d\n", - opnum, expected); - return -EIO; - } - nfserr = be32_to_cpup(p); - if (nfserr != NFS_OK) - return nfs4_stat_to_errno(nfserr); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* Dummy routine */ -static int decode_ace(struct xdr_stream *xdr, void *ace, struct nfs_client *clp) -{ - __be32 *p; - unsigned int strlen; - char *str; - - p = xdr_inline_decode(xdr, 12); - if (likely(p)) - return decode_opaque_inline(xdr, &strlen, &str); - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap) -{ - uint32_t bmlen; - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - bmlen = be32_to_cpup(p); - - bitmap[0] = bitmap[1] = bitmap[2] = 0; - p = xdr_inline_decode(xdr, (bmlen << 2)); - if (unlikely(!p)) - goto out_overflow; - if (bmlen > 0) { - bitmap[0] = be32_to_cpup(p++); - if (bmlen > 1) { - bitmap[1] = be32_to_cpup(p++); - if (bmlen > 2) - bitmap[2] = be32_to_cpup(p); - } - } - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static inline int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, __be32 **savep) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - *attrlen = be32_to_cpup(p); - *savep = xdr->p; - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *bitmask) -{ - if (likely(bitmap[0] & FATTR4_WORD0_SUPPORTED_ATTRS)) { - int ret; - ret = decode_attr_bitmap(xdr, bitmask); - if (unlikely(ret < 0)) - return ret; - bitmap[0] &= ~FATTR4_WORD0_SUPPORTED_ATTRS; - } else - bitmask[0] = bitmask[1] = bitmask[2] = 0; - dprintk("%s: bitmask=%08x:%08x:%08x\n", __func__, - bitmask[0], bitmask[1], bitmask[2]); - return 0; -} - -static int decode_attr_type(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *type) -{ - __be32 *p; - int ret = 0; - - *type = 0; - if (unlikely(bitmap[0] & (FATTR4_WORD0_TYPE - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_TYPE)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - *type = be32_to_cpup(p); - if (*type < NF4REG || *type > NF4NAMEDATTR) { - dprintk("%s: bad type %d\n", __func__, *type); - return -EIO; - } - bitmap[0] &= ~FATTR4_WORD0_TYPE; - ret = NFS_ATTR_FATTR_TYPE; - } - dprintk("%s: type=0%o\n", __func__, nfs_type2fmt[*type]); - return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_fh_expire_type(struct xdr_stream *xdr, - uint32_t *bitmap, uint32_t *type) -{ - __be32 *p; - - *type = 0; - if (unlikely(bitmap[0] & (FATTR4_WORD0_FH_EXPIRE_TYPE - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_FH_EXPIRE_TYPE)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - *type = be32_to_cpup(p); - bitmap[0] &= ~FATTR4_WORD0_FH_EXPIRE_TYPE; - } - dprintk("%s: expire type=0x%x\n", __func__, *type); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *change) -{ - __be32 *p; - int ret = 0; - - *change = 0; - if (unlikely(bitmap[0] & (FATTR4_WORD0_CHANGE - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_CHANGE)) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, change); - bitmap[0] &= ~FATTR4_WORD0_CHANGE; - ret = NFS_ATTR_FATTR_CHANGE; - } - dprintk("%s: change attribute=%Lu\n", __func__, - (unsigned long long)*change); - return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_size(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *size) -{ - __be32 *p; - int ret = 0; - - *size = 0; - if (unlikely(bitmap[0] & (FATTR4_WORD0_SIZE - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_SIZE)) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, size); - bitmap[0] &= ~FATTR4_WORD0_SIZE; - ret = NFS_ATTR_FATTR_SIZE; - } - dprintk("%s: file size=%Lu\n", __func__, (unsigned long long)*size); - return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_link_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) -{ - __be32 *p; - - *res = 0; - if (unlikely(bitmap[0] & (FATTR4_WORD0_LINK_SUPPORT - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_LINK_SUPPORT)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - *res = be32_to_cpup(p); - bitmap[0] &= ~FATTR4_WORD0_LINK_SUPPORT; - } - dprintk("%s: link support=%s\n", __func__, *res == 0 ? "false" : "true"); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_symlink_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) -{ - __be32 *p; - - *res = 0; - if (unlikely(bitmap[0] & (FATTR4_WORD0_SYMLINK_SUPPORT - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_SYMLINK_SUPPORT)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - *res = be32_to_cpup(p); - bitmap[0] &= ~FATTR4_WORD0_SYMLINK_SUPPORT; - } - dprintk("%s: symlink support=%s\n", __func__, *res == 0 ? "false" : "true"); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_fsid *fsid) -{ - __be32 *p; - int ret = 0; - - fsid->major = 0; - fsid->minor = 0; - if (unlikely(bitmap[0] & (FATTR4_WORD0_FSID - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_FSID)) { - p = xdr_inline_decode(xdr, 16); - if (unlikely(!p)) - goto out_overflow; - p = xdr_decode_hyper(p, &fsid->major); - xdr_decode_hyper(p, &fsid->minor); - bitmap[0] &= ~FATTR4_WORD0_FSID; - ret = NFS_ATTR_FATTR_FSID; - } - dprintk("%s: fsid=(0x%Lx/0x%Lx)\n", __func__, - (unsigned long long)fsid->major, - (unsigned long long)fsid->minor); - return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_lease_time(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) -{ - __be32 *p; - - *res = 60; - if (unlikely(bitmap[0] & (FATTR4_WORD0_LEASE_TIME - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_LEASE_TIME)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - *res = be32_to_cpup(p); - bitmap[0] &= ~FATTR4_WORD0_LEASE_TIME; - } - dprintk("%s: file size=%u\n", __func__, (unsigned int)*res); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_error(struct xdr_stream *xdr, uint32_t *bitmap, int32_t *res) -{ - __be32 *p; - - if (unlikely(bitmap[0] & (FATTR4_WORD0_RDATTR_ERROR - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_RDATTR_ERROR)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - bitmap[0] &= ~FATTR4_WORD0_RDATTR_ERROR; - *res = -be32_to_cpup(p); - } - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_filehandle(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_fh *fh) -{ - __be32 *p; - int len; - - if (fh != NULL) - memset(fh, 0, sizeof(*fh)); - - if (unlikely(bitmap[0] & (FATTR4_WORD0_FILEHANDLE - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_FILEHANDLE)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - len = be32_to_cpup(p); - if (len > NFS4_FHSIZE) - return -EIO; - p = xdr_inline_decode(xdr, len); - if (unlikely(!p)) - goto out_overflow; - if (fh != NULL) { - memcpy(fh->data, p, len); - fh->size = len; - } - bitmap[0] &= ~FATTR4_WORD0_FILEHANDLE; - } - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) -{ - __be32 *p; - - *res = ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL; - if (unlikely(bitmap[0] & (FATTR4_WORD0_ACLSUPPORT - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_ACLSUPPORT)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - *res = be32_to_cpup(p); - bitmap[0] &= ~FATTR4_WORD0_ACLSUPPORT; - } - dprintk("%s: ACLs supported=%u\n", __func__, (unsigned int)*res); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid) -{ - __be32 *p; - int ret = 0; - - *fileid = 0; - if (unlikely(bitmap[0] & (FATTR4_WORD0_FILEID - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_FILEID)) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, fileid); - bitmap[0] &= ~FATTR4_WORD0_FILEID; - ret = NFS_ATTR_FATTR_FILEID; - } - dprintk("%s: fileid=%Lu\n", __func__, (unsigned long long)*fileid); - return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_mounted_on_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid) -{ - __be32 *p; - int ret = 0; - - *fileid = 0; - if (unlikely(bitmap[1] & (FATTR4_WORD1_MOUNTED_ON_FILEID - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, fileid); - bitmap[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID; - ret = NFS_ATTR_FATTR_MOUNTED_ON_FILEID; - } - dprintk("%s: fileid=%Lu\n", __func__, (unsigned long long)*fileid); - return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) -{ - __be32 *p; - int status = 0; - - *res = 0; - if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_AVAIL - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_FILES_AVAIL)) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, res); - bitmap[0] &= ~FATTR4_WORD0_FILES_AVAIL; - } - dprintk("%s: files avail=%Lu\n", __func__, (unsigned long long)*res); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_files_free(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) -{ - __be32 *p; - int status = 0; - - *res = 0; - if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_FREE - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_FILES_FREE)) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, res); - bitmap[0] &= ~FATTR4_WORD0_FILES_FREE; - } - dprintk("%s: files free=%Lu\n", __func__, (unsigned long long)*res); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_files_total(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) -{ - __be32 *p; - int status = 0; - - *res = 0; - if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_TOTAL - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_FILES_TOTAL)) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, res); - bitmap[0] &= ~FATTR4_WORD0_FILES_TOTAL; - } - dprintk("%s: files total=%Lu\n", __func__, (unsigned long long)*res); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path) -{ - u32 n; - __be32 *p; - int status = 0; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - n = be32_to_cpup(p); - if (n == 0) - goto root_path; - dprintk("pathname4: "); - path->ncomponents = 0; - while (path->ncomponents < n) { - struct nfs4_string *component = &path->components[path->ncomponents]; - status = decode_opaque_inline(xdr, &component->len, &component->data); - if (unlikely(status != 0)) - goto out_eio; - ifdebug (XDR) - pr_cont("%s%.*s ", - (path->ncomponents != n ? "/ " : ""), - component->len, component->data); - if (path->ncomponents < NFS4_PATHNAME_MAXCOMPONENTS) - path->ncomponents++; - else { - dprintk("cannot parse %d components in path\n", n); - goto out_eio; - } - } -out: - return status; -root_path: -/* a root pathname is sent as a zero component4 */ - path->ncomponents = 1; - path->components[0].len=0; - path->components[0].data=NULL; - dprintk("pathname4: /\n"); - goto out; -out_eio: - dprintk(" status %d", status); - status = -EIO; - goto out; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_fs_locations *res) -{ - int n; - __be32 *p; - int status = -EIO; - - if (unlikely(bitmap[0] & (FATTR4_WORD0_FS_LOCATIONS -1U))) - goto out; - status = 0; - if (unlikely(!(bitmap[0] & FATTR4_WORD0_FS_LOCATIONS))) - goto out; - status = -EIO; - /* Ignore borken servers that return unrequested attrs */ - if (unlikely(res == NULL)) - goto out; - dprintk("%s: fsroot:\n", __func__); - status = decode_pathname(xdr, &res->fs_path); - if (unlikely(status != 0)) - goto out; - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - n = be32_to_cpup(p); - if (n <= 0) - goto out_eio; - res->nlocations = 0; - while (res->nlocations < n) { - u32 m; - struct nfs4_fs_location *loc = &res->locations[res->nlocations]; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - m = be32_to_cpup(p); - - loc->nservers = 0; - dprintk("%s: servers:\n", __func__); - while (loc->nservers < m) { - struct nfs4_string *server = &loc->servers[loc->nservers]; - status = decode_opaque_inline(xdr, &server->len, &server->data); - if (unlikely(status != 0)) - goto out_eio; - dprintk("%s ", server->data); - if (loc->nservers < NFS4_FS_LOCATION_MAXSERVERS) - loc->nservers++; - else { - unsigned int i; - dprintk("%s: using first %u of %u servers " - "returned for location %u\n", - __func__, - NFS4_FS_LOCATION_MAXSERVERS, - m, res->nlocations); - for (i = loc->nservers; i < m; i++) { - unsigned int len; - char *data; - status = decode_opaque_inline(xdr, &len, &data); - if (unlikely(status != 0)) - goto out_eio; - } - } - } - status = decode_pathname(xdr, &loc->rootpath); - if (unlikely(status != 0)) - goto out_eio; - if (res->nlocations < NFS4_FS_LOCATIONS_MAXENTRIES) - res->nlocations++; - } - if (res->nlocations != 0) - status = NFS_ATTR_FATTR_V4_LOCATIONS; -out: - dprintk("%s: fs_locations done, error = %d\n", __func__, status); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); -out_eio: - status = -EIO; - goto out; -} - -static int decode_attr_maxfilesize(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) -{ - __be32 *p; - int status = 0; - - *res = 0; - if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXFILESIZE - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_MAXFILESIZE)) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, res); - bitmap[0] &= ~FATTR4_WORD0_MAXFILESIZE; - } - dprintk("%s: maxfilesize=%Lu\n", __func__, (unsigned long long)*res); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_maxlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *maxlink) -{ - __be32 *p; - int status = 0; - - *maxlink = 1; - if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXLINK - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_MAXLINK)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - *maxlink = be32_to_cpup(p); - bitmap[0] &= ~FATTR4_WORD0_MAXLINK; - } - dprintk("%s: maxlink=%u\n", __func__, *maxlink); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_maxname(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *maxname) -{ - __be32 *p; - int status = 0; - - *maxname = 1024; - if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXNAME - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_MAXNAME)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - *maxname = be32_to_cpup(p); - bitmap[0] &= ~FATTR4_WORD0_MAXNAME; - } - dprintk("%s: maxname=%u\n", __func__, *maxname); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) -{ - __be32 *p; - int status = 0; - - *res = 1024; - if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXREAD - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_MAXREAD)) { - uint64_t maxread; - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, &maxread); - if (maxread > 0x7FFFFFFF) - maxread = 0x7FFFFFFF; - *res = (uint32_t)maxread; - bitmap[0] &= ~FATTR4_WORD0_MAXREAD; - } - dprintk("%s: maxread=%lu\n", __func__, (unsigned long)*res); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_maxwrite(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) -{ - __be32 *p; - int status = 0; - - *res = 1024; - if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXWRITE - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_MAXWRITE)) { - uint64_t maxwrite; - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, &maxwrite); - if (maxwrite > 0x7FFFFFFF) - maxwrite = 0x7FFFFFFF; - *res = (uint32_t)maxwrite; - bitmap[0] &= ~FATTR4_WORD0_MAXWRITE; - } - dprintk("%s: maxwrite=%lu\n", __func__, (unsigned long)*res); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_mode(struct xdr_stream *xdr, uint32_t *bitmap, umode_t *mode) -{ - uint32_t tmp; - __be32 *p; - int ret = 0; - - *mode = 0; - if (unlikely(bitmap[1] & (FATTR4_WORD1_MODE - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_MODE)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - tmp = be32_to_cpup(p); - *mode = tmp & ~S_IFMT; - bitmap[1] &= ~FATTR4_WORD1_MODE; - ret = NFS_ATTR_FATTR_MODE; - } - dprintk("%s: file mode=0%o\n", __func__, (unsigned int)*mode); - return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_nlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *nlink) -{ - __be32 *p; - int ret = 0; - - *nlink = 1; - if (unlikely(bitmap[1] & (FATTR4_WORD1_NUMLINKS - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_NUMLINKS)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - *nlink = be32_to_cpup(p); - bitmap[1] &= ~FATTR4_WORD1_NUMLINKS; - ret = NFS_ATTR_FATTR_NLINK; - } - dprintk("%s: nlink=%u\n", __func__, (unsigned int)*nlink); - return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, - const struct nfs_server *server, uint32_t *uid, - struct nfs4_string *owner_name) -{ - uint32_t len; - __be32 *p; - int ret = 0; - - *uid = -2; - if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_OWNER)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - len = be32_to_cpup(p); - p = xdr_inline_decode(xdr, len); - if (unlikely(!p)) - goto out_overflow; - if (owner_name != NULL) { - owner_name->data = kmemdup(p, len, GFP_NOWAIT); - if (owner_name->data != NULL) { - owner_name->len = len; - ret = NFS_ATTR_FATTR_OWNER_NAME; - } - } else if (len < XDR_MAX_NETOBJ) { - if (nfs_map_name_to_uid(server, (char *)p, len, uid) == 0) - ret = NFS_ATTR_FATTR_OWNER; - else - dprintk("%s: nfs_map_name_to_uid failed!\n", - __func__); - } else - dprintk("%s: name too long (%u)!\n", - __func__, len); - bitmap[1] &= ~FATTR4_WORD1_OWNER; - } - dprintk("%s: uid=%d\n", __func__, (int)*uid); - return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, - const struct nfs_server *server, uint32_t *gid, - struct nfs4_string *group_name) -{ - uint32_t len; - __be32 *p; - int ret = 0; - - *gid = -2; - if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER_GROUP - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_OWNER_GROUP)) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - len = be32_to_cpup(p); - p = xdr_inline_decode(xdr, len); - if (unlikely(!p)) - goto out_overflow; - if (group_name != NULL) { - group_name->data = kmemdup(p, len, GFP_NOWAIT); - if (group_name->data != NULL) { - group_name->len = len; - ret = NFS_ATTR_FATTR_GROUP_NAME; - } - } else if (len < XDR_MAX_NETOBJ) { - if (nfs_map_group_to_gid(server, (char *)p, len, gid) == 0) - ret = NFS_ATTR_FATTR_GROUP; - else - dprintk("%s: nfs_map_group_to_gid failed!\n", - __func__); - } else - dprintk("%s: name too long (%u)!\n", - __func__, len); - bitmap[1] &= ~FATTR4_WORD1_OWNER_GROUP; - } - dprintk("%s: gid=%d\n", __func__, (int)*gid); - return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_rdev(struct xdr_stream *xdr, uint32_t *bitmap, dev_t *rdev) -{ - uint32_t major = 0, minor = 0; - __be32 *p; - int ret = 0; - - *rdev = MKDEV(0,0); - if (unlikely(bitmap[1] & (FATTR4_WORD1_RAWDEV - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_RAWDEV)) { - dev_t tmp; - - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - major = be32_to_cpup(p++); - minor = be32_to_cpup(p); - tmp = MKDEV(major, minor); - if (MAJOR(tmp) == major && MINOR(tmp) == minor) - *rdev = tmp; - bitmap[1] &= ~ FATTR4_WORD1_RAWDEV; - ret = NFS_ATTR_FATTR_RDEV; - } - dprintk("%s: rdev=(0x%x:0x%x)\n", __func__, major, minor); - return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_space_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) -{ - __be32 *p; - int status = 0; - - *res = 0; - if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_AVAIL - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_SPACE_AVAIL)) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, res); - bitmap[1] &= ~FATTR4_WORD1_SPACE_AVAIL; - } - dprintk("%s: space avail=%Lu\n", __func__, (unsigned long long)*res); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_space_free(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) -{ - __be32 *p; - int status = 0; - - *res = 0; - if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_FREE - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_SPACE_FREE)) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, res); - bitmap[1] &= ~FATTR4_WORD1_SPACE_FREE; - } - dprintk("%s: space free=%Lu\n", __func__, (unsigned long long)*res); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_space_total(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) -{ - __be32 *p; - int status = 0; - - *res = 0; - if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_TOTAL - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_SPACE_TOTAL)) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, res); - bitmap[1] &= ~FATTR4_WORD1_SPACE_TOTAL; - } - dprintk("%s: space total=%Lu\n", __func__, (unsigned long long)*res); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *used) -{ - __be32 *p; - int ret = 0; - - *used = 0; - if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_USED - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_SPACE_USED)) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, used); - bitmap[1] &= ~FATTR4_WORD1_SPACE_USED; - ret = NFS_ATTR_FATTR_SPACE_USED; - } - dprintk("%s: space used=%Lu\n", __func__, - (unsigned long long)*used); - return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_time(struct xdr_stream *xdr, struct timespec *time) -{ - __be32 *p; - uint64_t sec; - uint32_t nsec; - - p = xdr_inline_decode(xdr, 12); - if (unlikely(!p)) - goto out_overflow; - p = xdr_decode_hyper(p, &sec); - nsec = be32_to_cpup(p); - time->tv_sec = (time_t)sec; - time->tv_nsec = (long)nsec; - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_attr_time_access(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time) -{ - int status = 0; - - time->tv_sec = 0; - time->tv_nsec = 0; - if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_ACCESS - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_TIME_ACCESS)) { - status = decode_attr_time(xdr, time); - if (status == 0) - status = NFS_ATTR_FATTR_ATIME; - bitmap[1] &= ~FATTR4_WORD1_TIME_ACCESS; - } - dprintk("%s: atime=%ld\n", __func__, (long)time->tv_sec); - return status; -} - -static int decode_attr_time_metadata(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time) -{ - int status = 0; - - time->tv_sec = 0; - time->tv_nsec = 0; - if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_METADATA - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_TIME_METADATA)) { - status = decode_attr_time(xdr, time); - if (status == 0) - status = NFS_ATTR_FATTR_CTIME; - bitmap[1] &= ~FATTR4_WORD1_TIME_METADATA; - } - dprintk("%s: ctime=%ld\n", __func__, (long)time->tv_sec); - return status; -} - -static int decode_attr_time_delta(struct xdr_stream *xdr, uint32_t *bitmap, - struct timespec *time) -{ - int status = 0; - - time->tv_sec = 0; - time->tv_nsec = 0; - if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_DELTA - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_TIME_DELTA)) { - status = decode_attr_time(xdr, time); - bitmap[1] &= ~FATTR4_WORD1_TIME_DELTA; - } - dprintk("%s: time_delta=%ld %ld\n", __func__, (long)time->tv_sec, - (long)time->tv_nsec); - return status; -} - -static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time) -{ - int status = 0; - - time->tv_sec = 0; - time->tv_nsec = 0; - if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_MODIFY - 1U))) - return -EIO; - if (likely(bitmap[1] & FATTR4_WORD1_TIME_MODIFY)) { - status = decode_attr_time(xdr, time); - if (status == 0) - status = NFS_ATTR_FATTR_MTIME; - bitmap[1] &= ~FATTR4_WORD1_TIME_MODIFY; - } - dprintk("%s: mtime=%ld\n", __func__, (long)time->tv_sec); - return status; -} - -static int verify_attr_len(struct xdr_stream *xdr, __be32 *savep, uint32_t attrlen) -{ - unsigned int attrwords = XDR_QUADLEN(attrlen); - unsigned int nwords = xdr->p - savep; - - if (unlikely(attrwords != nwords)) { - dprintk("%s: server returned incorrect attribute length: " - "%u %c %u\n", - __func__, - attrwords << 2, - (attrwords < nwords) ? '<' : '>', - nwords << 2); - return -EIO; - } - return 0; -} - -static int decode_change_info(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 20); - if (unlikely(!p)) - goto out_overflow; - cinfo->atomic = be32_to_cpup(p++); - p = xdr_decode_hyper(p, &cinfo->before); - xdr_decode_hyper(p, &cinfo->after); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_access(struct xdr_stream *xdr, struct nfs4_accessres *access) -{ - __be32 *p; - uint32_t supp, acc; - int status; - - status = decode_op_hdr(xdr, OP_ACCESS); - if (status) - return status; - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - supp = be32_to_cpup(p++); - acc = be32_to_cpup(p); - access->supported = supp; - access->access = acc; - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_opaque_fixed(struct xdr_stream *xdr, void *buf, size_t len) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, len); - if (likely(p)) { - memcpy(buf, p, len); - return 0; - } - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid) -{ - return decode_opaque_fixed(xdr, stateid, NFS4_STATEID_SIZE); -} - -static int decode_close(struct xdr_stream *xdr, struct nfs_closeres *res) -{ - int status; - - status = decode_op_hdr(xdr, OP_CLOSE); - if (status != -EIO) - nfs_increment_open_seqid(status, res->seqid); - if (!status) - status = decode_stateid(xdr, &res->stateid); - return status; -} - -static int decode_verifier(struct xdr_stream *xdr, void *verifier) -{ - return decode_opaque_fixed(xdr, verifier, NFS4_VERIFIER_SIZE); -} - -static int decode_commit(struct xdr_stream *xdr, struct nfs_writeres *res) -{ - int status; - - status = decode_op_hdr(xdr, OP_COMMIT); - if (!status) - status = decode_verifier(xdr, res->verf->verifier); - return status; -} - -static int decode_create(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) -{ - __be32 *p; - uint32_t bmlen; - int status; - - status = decode_op_hdr(xdr, OP_CREATE); - if (status) - return status; - if ((status = decode_change_info(xdr, cinfo))) - return status; - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - bmlen = be32_to_cpup(p); - p = xdr_inline_decode(xdr, bmlen << 2); - if (likely(p)) - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res) -{ - __be32 *savep; - uint32_t attrlen, bitmap[3] = {0}; - int status; - - if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) - goto xdr_error; - if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) - goto xdr_error; - if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) - goto xdr_error; - if ((status = decode_attr_supported(xdr, bitmap, res->attr_bitmask)) != 0) - goto xdr_error; - if ((status = decode_attr_fh_expire_type(xdr, bitmap, - &res->fh_expire_type)) != 0) - goto xdr_error; - if ((status = decode_attr_link_support(xdr, bitmap, &res->has_links)) != 0) - goto xdr_error; - if ((status = decode_attr_symlink_support(xdr, bitmap, &res->has_symlinks)) != 0) - goto xdr_error; - if ((status = decode_attr_aclsupport(xdr, bitmap, &res->acl_bitmask)) != 0) - goto xdr_error; - status = verify_attr_len(xdr, savep, attrlen); -xdr_error: - dprintk("%s: xdr returned %d!\n", __func__, -status); - return status; -} - -static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat) -{ - __be32 *savep; - uint32_t attrlen, bitmap[3] = {0}; - int status; - - if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) - goto xdr_error; - if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) - goto xdr_error; - if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) - goto xdr_error; - - if ((status = decode_attr_files_avail(xdr, bitmap, &fsstat->afiles)) != 0) - goto xdr_error; - if ((status = decode_attr_files_free(xdr, bitmap, &fsstat->ffiles)) != 0) - goto xdr_error; - if ((status = decode_attr_files_total(xdr, bitmap, &fsstat->tfiles)) != 0) - goto xdr_error; - if ((status = decode_attr_space_avail(xdr, bitmap, &fsstat->abytes)) != 0) - goto xdr_error; - if ((status = decode_attr_space_free(xdr, bitmap, &fsstat->fbytes)) != 0) - goto xdr_error; - if ((status = decode_attr_space_total(xdr, bitmap, &fsstat->tbytes)) != 0) - goto xdr_error; - - status = verify_attr_len(xdr, savep, attrlen); -xdr_error: - dprintk("%s: xdr returned %d!\n", __func__, -status); - return status; -} - -static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf) -{ - __be32 *savep; - uint32_t attrlen, bitmap[3] = {0}; - int status; - - if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) - goto xdr_error; - if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) - goto xdr_error; - if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) - goto xdr_error; - - if ((status = decode_attr_maxlink(xdr, bitmap, &pathconf->max_link)) != 0) - goto xdr_error; - if ((status = decode_attr_maxname(xdr, bitmap, &pathconf->max_namelen)) != 0) - goto xdr_error; - - status = verify_attr_len(xdr, savep, attrlen); -xdr_error: - dprintk("%s: xdr returned %d!\n", __func__, -status); - return status; -} - -static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap, - struct nfs_fattr *fattr, struct nfs_fh *fh, - struct nfs4_fs_locations *fs_loc, - const struct nfs_server *server) -{ - int status; - umode_t fmode = 0; - uint32_t type; - int32_t err; - - status = decode_attr_type(xdr, bitmap, &type); - if (status < 0) - goto xdr_error; - fattr->mode = 0; - if (status != 0) { - fattr->mode |= nfs_type2fmt[type]; - fattr->valid |= status; - } - - status = decode_attr_change(xdr, bitmap, &fattr->change_attr); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - - status = decode_attr_size(xdr, bitmap, &fattr->size); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - - status = decode_attr_fsid(xdr, bitmap, &fattr->fsid); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - - err = 0; - status = decode_attr_error(xdr, bitmap, &err); - if (status < 0) - goto xdr_error; - - status = decode_attr_filehandle(xdr, bitmap, fh); - if (status < 0) - goto xdr_error; - - status = decode_attr_fileid(xdr, bitmap, &fattr->fileid); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - - status = decode_attr_fs_locations(xdr, bitmap, fs_loc); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - - status = decode_attr_mode(xdr, bitmap, &fmode); - if (status < 0) - goto xdr_error; - if (status != 0) { - fattr->mode |= fmode; - fattr->valid |= status; - } - - status = decode_attr_nlink(xdr, bitmap, &fattr->nlink); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - - status = decode_attr_owner(xdr, bitmap, server, &fattr->uid, fattr->owner_name); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - - status = decode_attr_group(xdr, bitmap, server, &fattr->gid, fattr->group_name); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - - status = decode_attr_rdev(xdr, bitmap, &fattr->rdev); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - - status = decode_attr_space_used(xdr, bitmap, &fattr->du.nfs3.used); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - - status = decode_attr_time_access(xdr, bitmap, &fattr->atime); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - - status = decode_attr_time_metadata(xdr, bitmap, &fattr->ctime); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - - status = decode_attr_time_modify(xdr, bitmap, &fattr->mtime); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - - status = decode_attr_mounted_on_fileid(xdr, bitmap, &fattr->mounted_on_fileid); - if (status < 0) - goto xdr_error; - fattr->valid |= status; - -xdr_error: - dprintk("%s: xdr returned %d\n", __func__, -status); - return status; -} - -static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fattr, - struct nfs_fh *fh, struct nfs4_fs_locations *fs_loc, - const struct nfs_server *server) -{ - __be32 *savep; - uint32_t attrlen, - bitmap[3] = {0}; - int status; - - status = decode_op_hdr(xdr, OP_GETATTR); - if (status < 0) - goto xdr_error; - - status = decode_attr_bitmap(xdr, bitmap); - if (status < 0) - goto xdr_error; - - status = decode_attr_length(xdr, &attrlen, &savep); - if (status < 0) - goto xdr_error; - - status = decode_getfattr_attrs(xdr, bitmap, fattr, fh, fs_loc, server); - if (status < 0) - goto xdr_error; - - status = verify_attr_len(xdr, savep, attrlen); -xdr_error: - dprintk("%s: xdr returned %d\n", __func__, -status); - return status; -} - -static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, - const struct nfs_server *server) -{ - return decode_getfattr_generic(xdr, fattr, NULL, NULL, server); -} - -/* - * Decode potentially multiple layout types. Currently we only support - * one layout driver per file system. - */ -static int decode_first_pnfs_layout_type(struct xdr_stream *xdr, - uint32_t *layouttype) -{ - uint32_t *p; - int num; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - num = be32_to_cpup(p); - - /* pNFS is not supported by the underlying file system */ - if (num == 0) { - *layouttype = 0; - return 0; - } - if (num > 1) - printk(KERN_INFO "NFS: %s: Warning: Multiple pNFS layout " - "drivers per filesystem not supported\n", __func__); - - /* Decode and set first layout type, move xdr->p past unused types */ - p = xdr_inline_decode(xdr, num * 4); - if (unlikely(!p)) - goto out_overflow; - *layouttype = be32_to_cpup(p); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -/* - * The type of file system exported. - * Note we must ensure that layouttype is set in any non-error case. - */ -static int decode_attr_pnfstype(struct xdr_stream *xdr, uint32_t *bitmap, - uint32_t *layouttype) -{ - int status = 0; - - dprintk("%s: bitmap is %x\n", __func__, bitmap[1]); - if (unlikely(bitmap[1] & (FATTR4_WORD1_FS_LAYOUT_TYPES - 1U))) - return -EIO; - if (bitmap[1] & FATTR4_WORD1_FS_LAYOUT_TYPES) { - status = decode_first_pnfs_layout_type(xdr, layouttype); - bitmap[1] &= ~FATTR4_WORD1_FS_LAYOUT_TYPES; - } else - *layouttype = 0; - return status; -} - -/* - * The prefered block size for layout directed io - */ -static int decode_attr_layout_blksize(struct xdr_stream *xdr, uint32_t *bitmap, - uint32_t *res) -{ - __be32 *p; - - dprintk("%s: bitmap is %x\n", __func__, bitmap[2]); - *res = 0; - if (bitmap[2] & FATTR4_WORD2_LAYOUT_BLKSIZE) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) { - print_overflow_msg(__func__, xdr); - return -EIO; - } - *res = be32_to_cpup(p); - bitmap[2] &= ~FATTR4_WORD2_LAYOUT_BLKSIZE; - } - return 0; -} - -static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) -{ - __be32 *savep; - uint32_t attrlen, bitmap[3]; - int status; - - if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) - goto xdr_error; - if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) - goto xdr_error; - if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) - goto xdr_error; - - fsinfo->rtmult = fsinfo->wtmult = 512; /* ??? */ - - if ((status = decode_attr_lease_time(xdr, bitmap, &fsinfo->lease_time)) != 0) - goto xdr_error; - if ((status = decode_attr_maxfilesize(xdr, bitmap, &fsinfo->maxfilesize)) != 0) - goto xdr_error; - if ((status = decode_attr_maxread(xdr, bitmap, &fsinfo->rtmax)) != 0) - goto xdr_error; - fsinfo->rtpref = fsinfo->dtpref = fsinfo->rtmax; - if ((status = decode_attr_maxwrite(xdr, bitmap, &fsinfo->wtmax)) != 0) - goto xdr_error; - fsinfo->wtpref = fsinfo->wtmax; - status = decode_attr_time_delta(xdr, bitmap, &fsinfo->time_delta); - if (status != 0) - goto xdr_error; - status = decode_attr_pnfstype(xdr, bitmap, &fsinfo->layouttype); - if (status != 0) - goto xdr_error; - status = decode_attr_layout_blksize(xdr, bitmap, &fsinfo->blksize); - if (status) - goto xdr_error; - - status = verify_attr_len(xdr, savep, attrlen); -xdr_error: - dprintk("%s: xdr returned %d!\n", __func__, -status); - return status; -} - -static int decode_getfh(struct xdr_stream *xdr, struct nfs_fh *fh) -{ - __be32 *p; - uint32_t len; - int status; - - /* Zero handle first to allow comparisons */ - memset(fh, 0, sizeof(*fh)); - - status = decode_op_hdr(xdr, OP_GETFH); - if (status) - return status; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - len = be32_to_cpup(p); - if (len > NFS4_FHSIZE) - return -EIO; - fh->size = len; - p = xdr_inline_decode(xdr, len); - if (unlikely(!p)) - goto out_overflow; - memcpy(fh->data, p, len); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_link(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) -{ - int status; - - status = decode_op_hdr(xdr, OP_LINK); - if (status) - return status; - return decode_change_info(xdr, cinfo); -} - -/* - * We create the owner, so we know a proper owner.id length is 4. - */ -static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl) -{ - uint64_t offset, length, clientid; - __be32 *p; - uint32_t namelen, type; - - p = xdr_inline_decode(xdr, 32); /* read 32 bytes */ - if (unlikely(!p)) - goto out_overflow; - p = xdr_decode_hyper(p, &offset); /* read 2 8-byte long words */ - p = xdr_decode_hyper(p, &length); - type = be32_to_cpup(p++); /* 4 byte read */ - if (fl != NULL) { /* manipulate file lock */ - fl->fl_start = (loff_t)offset; - fl->fl_end = fl->fl_start + (loff_t)length - 1; - if (length == ~(uint64_t)0) - fl->fl_end = OFFSET_MAX; - fl->fl_type = F_WRLCK; - if (type & 1) - fl->fl_type = F_RDLCK; - fl->fl_pid = 0; - } - p = xdr_decode_hyper(p, &clientid); /* read 8 bytes */ - namelen = be32_to_cpup(p); /* read 4 bytes */ /* have read all 32 bytes now */ - p = xdr_inline_decode(xdr, namelen); /* variable size field */ - if (likely(p)) - return -NFS4ERR_DENIED; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_lock(struct xdr_stream *xdr, struct nfs_lock_res *res) -{ - int status; - - status = decode_op_hdr(xdr, OP_LOCK); - if (status == -EIO) - goto out; - if (status == 0) { - status = decode_stateid(xdr, &res->stateid); - if (unlikely(status)) - goto out; - } else if (status == -NFS4ERR_DENIED) - status = decode_lock_denied(xdr, NULL); - if (res->open_seqid != NULL) - nfs_increment_open_seqid(status, res->open_seqid); - nfs_increment_lock_seqid(status, res->lock_seqid); -out: - return status; -} - -static int decode_lockt(struct xdr_stream *xdr, struct nfs_lockt_res *res) -{ - int status; - status = decode_op_hdr(xdr, OP_LOCKT); - if (status == -NFS4ERR_DENIED) - return decode_lock_denied(xdr, res->denied); - return status; -} - -static int decode_locku(struct xdr_stream *xdr, struct nfs_locku_res *res) -{ - int status; - - status = decode_op_hdr(xdr, OP_LOCKU); - if (status != -EIO) - nfs_increment_lock_seqid(status, res->seqid); - if (status == 0) - status = decode_stateid(xdr, &res->stateid); - return status; -} - -static int decode_release_lockowner(struct xdr_stream *xdr) -{ - return decode_op_hdr(xdr, OP_RELEASE_LOCKOWNER); -} - -static int decode_lookup(struct xdr_stream *xdr) -{ - return decode_op_hdr(xdr, OP_LOOKUP); -} - -/* This is too sick! */ -static int decode_space_limit(struct xdr_stream *xdr, u64 *maxsize) -{ - __be32 *p; - uint32_t limit_type, nblocks, blocksize; - - p = xdr_inline_decode(xdr, 12); - if (unlikely(!p)) - goto out_overflow; - limit_type = be32_to_cpup(p++); - switch (limit_type) { - case 1: - xdr_decode_hyper(p, maxsize); - break; - case 2: - nblocks = be32_to_cpup(p++); - blocksize = be32_to_cpup(p); - *maxsize = (uint64_t)nblocks * (uint64_t)blocksize; - } - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res) -{ - __be32 *p; - uint32_t delegation_type; - int status; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - delegation_type = be32_to_cpup(p); - if (delegation_type == NFS4_OPEN_DELEGATE_NONE) { - res->delegation_type = 0; - return 0; - } - status = decode_stateid(xdr, &res->delegation); - if (unlikely(status)) - return status; - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - res->do_recall = be32_to_cpup(p); - - switch (delegation_type) { - case NFS4_OPEN_DELEGATE_READ: - res->delegation_type = FMODE_READ; - break; - case NFS4_OPEN_DELEGATE_WRITE: - res->delegation_type = FMODE_WRITE|FMODE_READ; - if (decode_space_limit(xdr, &res->maxsize) < 0) - return -EIO; - } - return decode_ace(xdr, NULL, res->server->nfs_client); -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res) -{ - __be32 *p; - uint32_t savewords, bmlen, i; - int status; - - status = decode_op_hdr(xdr, OP_OPEN); - if (status != -EIO) - nfs_increment_open_seqid(status, res->seqid); - if (!status) - status = decode_stateid(xdr, &res->stateid); - if (unlikely(status)) - return status; - - decode_change_info(xdr, &res->cinfo); - - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - res->rflags = be32_to_cpup(p++); - bmlen = be32_to_cpup(p); - if (bmlen > 10) - goto xdr_error; - - p = xdr_inline_decode(xdr, bmlen << 2); - if (unlikely(!p)) - goto out_overflow; - savewords = min_t(uint32_t, bmlen, NFS4_BITMAP_SIZE); - for (i = 0; i < savewords; ++i) - res->attrset[i] = be32_to_cpup(p++); - for (; i < NFS4_BITMAP_SIZE; i++) - res->attrset[i] = 0; - - return decode_delegation(xdr, res); -xdr_error: - dprintk("%s: Bitmap too large! Length = %u\n", __func__, bmlen); - return -EIO; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res) -{ - int status; - - status = decode_op_hdr(xdr, OP_OPEN_CONFIRM); - if (status != -EIO) - nfs_increment_open_seqid(status, res->seqid); - if (!status) - status = decode_stateid(xdr, &res->stateid); - return status; -} - -static int decode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeres *res) -{ - int status; - - status = decode_op_hdr(xdr, OP_OPEN_DOWNGRADE); - if (status != -EIO) - nfs_increment_open_seqid(status, res->seqid); - if (!status) - status = decode_stateid(xdr, &res->stateid); - return status; -} - -static int decode_putfh(struct xdr_stream *xdr) -{ - return decode_op_hdr(xdr, OP_PUTFH); -} - -static int decode_putrootfh(struct xdr_stream *xdr) -{ - return decode_op_hdr(xdr, OP_PUTROOTFH); -} - -static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_readres *res) -{ - struct kvec *iov = req->rq_rcv_buf.head; - __be32 *p; - uint32_t count, eof, recvd, hdrlen; - int status; - - status = decode_op_hdr(xdr, OP_READ); - if (status) - return status; - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - eof = be32_to_cpup(p++); - count = be32_to_cpup(p); - hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base; - recvd = req->rq_rcv_buf.len - hdrlen; - if (count > recvd) { - dprintk("NFS: server cheating in read reply: " - "count %u > recvd %u\n", count, recvd); - count = recvd; - eof = 0; - } - xdr_read_pages(xdr, count); - res->eof = eof; - res->count = count; - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir) -{ - struct xdr_buf *rcvbuf = &req->rq_rcv_buf; - struct kvec *iov = rcvbuf->head; - size_t hdrlen; - u32 recvd, pglen = rcvbuf->page_len; - int status; - __be32 verf[2]; - - status = decode_op_hdr(xdr, OP_READDIR); - if (!status) - status = decode_verifier(xdr, readdir->verifier.data); - if (unlikely(status)) - return status; - memcpy(verf, readdir->verifier.data, sizeof(verf)); - dprintk("%s: verifier = %08x:%08x\n", - __func__, verf[0], verf[1]); - - hdrlen = (char *) xdr->p - (char *) iov->iov_base; - recvd = rcvbuf->len - hdrlen; - if (pglen > recvd) - pglen = recvd; - xdr_read_pages(xdr, pglen); - - - return pglen; -} - -static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) -{ - struct xdr_buf *rcvbuf = &req->rq_rcv_buf; - struct kvec *iov = rcvbuf->head; - size_t hdrlen; - u32 len, recvd; - __be32 *p; - int status; - - status = decode_op_hdr(xdr, OP_READLINK); - if (status) - return status; - - /* Convert length of symlink */ - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - len = be32_to_cpup(p); - if (len >= rcvbuf->page_len || len <= 0) { - dprintk("nfs: server returned giant symlink!\n"); - return -ENAMETOOLONG; - } - hdrlen = (char *) xdr->p - (char *) iov->iov_base; - recvd = req->rq_rcv_buf.len - hdrlen; - if (recvd < len) { - dprintk("NFS: server cheating in readlink reply: " - "count %u > recvd %u\n", len, recvd); - return -EIO; - } - xdr_read_pages(xdr, len); - /* - * The XDR encode routine has set things up so that - * the link text will be copied directly into the - * buffer. We just have to do overflow-checking, - * and and null-terminate the text (the VFS expects - * null-termination). - */ - xdr_terminate_string(rcvbuf, len); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_remove(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) -{ - int status; - - status = decode_op_hdr(xdr, OP_REMOVE); - if (status) - goto out; - status = decode_change_info(xdr, cinfo); -out: - return status; -} - -static int decode_rename(struct xdr_stream *xdr, struct nfs4_change_info *old_cinfo, - struct nfs4_change_info *new_cinfo) -{ - int status; - - status = decode_op_hdr(xdr, OP_RENAME); - if (status) - goto out; - if ((status = decode_change_info(xdr, old_cinfo))) - goto out; - status = decode_change_info(xdr, new_cinfo); -out: - return status; -} - -static int decode_renew(struct xdr_stream *xdr) -{ - return decode_op_hdr(xdr, OP_RENEW); -} - -static int -decode_restorefh(struct xdr_stream *xdr) -{ - return decode_op_hdr(xdr, OP_RESTOREFH); -} - -static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, - struct nfs_getaclres *res) -{ - __be32 *savep, *bm_p; - uint32_t attrlen, - bitmap[3] = {0}; - struct kvec *iov = req->rq_rcv_buf.head; - int status; - size_t page_len = xdr->buf->page_len; - - res->acl_len = 0; - if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) - goto out; - - bm_p = xdr->p; - res->acl_data_offset = be32_to_cpup(bm_p) + 2; - res->acl_data_offset <<= 2; - /* Check if the acl data starts beyond the allocated buffer */ - if (res->acl_data_offset > page_len) - return -ERANGE; - - if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) - goto out; - if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) - goto out; - - if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U))) - return -EIO; - if (likely(bitmap[0] & FATTR4_WORD0_ACL)) { - size_t hdrlen; - - /* The bitmap (xdr len + bitmaps) and the attr xdr len words - * are stored with the acl data to handle the problem of - * variable length bitmaps.*/ - xdr->p = bm_p; - - /* We ignore &savep and don't do consistency checks on - * the attr length. Let userspace figure it out.... */ - hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base; - attrlen += res->acl_data_offset; - if (attrlen > page_len) { - if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { - /* getxattr interface called with a NULL buf */ - res->acl_len = attrlen; - goto out; - } - dprintk("NFS: acl reply: attrlen %u > page_len %zu\n", - attrlen, page_len); - return -EINVAL; - } - xdr_read_pages(xdr, attrlen); - res->acl_len = attrlen; - } else - status = -EOPNOTSUPP; - -out: - return status; -} - -static int -decode_savefh(struct xdr_stream *xdr) -{ - return decode_op_hdr(xdr, OP_SAVEFH); -} - -static int decode_setattr(struct xdr_stream *xdr) -{ - __be32 *p; - uint32_t bmlen; - int status; - - status = decode_op_hdr(xdr, OP_SETATTR); - if (status) - return status; - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - bmlen = be32_to_cpup(p); - p = xdr_inline_decode(xdr, bmlen << 2); - if (likely(p)) - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid_res *res) -{ - __be32 *p; - uint32_t opnum; - int32_t nfserr; - - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - opnum = be32_to_cpup(p++); - if (opnum != OP_SETCLIENTID) { - dprintk("nfs: decode_setclientid: Server returned operation" - " %d\n", opnum); - return -EIO; - } - nfserr = be32_to_cpup(p); - if (nfserr == NFS_OK) { - p = xdr_inline_decode(xdr, 8 + NFS4_VERIFIER_SIZE); - if (unlikely(!p)) - goto out_overflow; - p = xdr_decode_hyper(p, &res->clientid); - memcpy(res->confirm.data, p, NFS4_VERIFIER_SIZE); - } else if (nfserr == NFSERR_CLID_INUSE) { - uint32_t len; - - /* skip netid string */ - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - len = be32_to_cpup(p); - p = xdr_inline_decode(xdr, len); - if (unlikely(!p)) - goto out_overflow; - - /* skip uaddr string */ - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - len = be32_to_cpup(p); - p = xdr_inline_decode(xdr, len); - if (unlikely(!p)) - goto out_overflow; - return -NFSERR_CLID_INUSE; - } else - return nfs4_stat_to_errno(nfserr); - - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_setclientid_confirm(struct xdr_stream *xdr) -{ - return decode_op_hdr(xdr, OP_SETCLIENTID_CONFIRM); -} - -static int decode_write(struct xdr_stream *xdr, struct nfs_writeres *res) -{ - __be32 *p; - int status; - - status = decode_op_hdr(xdr, OP_WRITE); - if (status) - return status; - - p = xdr_inline_decode(xdr, 16); - if (unlikely(!p)) - goto out_overflow; - res->count = be32_to_cpup(p++); - res->verf->committed = be32_to_cpup(p++); - memcpy(res->verf->verifier, p, NFS4_VERIFIER_SIZE); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_delegreturn(struct xdr_stream *xdr) -{ - return decode_op_hdr(xdr, OP_DELEGRETURN); -} - -static int decode_secinfo_gss(struct xdr_stream *xdr, struct nfs4_secinfo_flavor *flavor) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - flavor->gss.sec_oid4.len = be32_to_cpup(p); - if (flavor->gss.sec_oid4.len > GSS_OID_MAX_LEN) - goto out_err; - - p = xdr_inline_decode(xdr, flavor->gss.sec_oid4.len); - if (unlikely(!p)) - goto out_overflow; - memcpy(flavor->gss.sec_oid4.data, p, flavor->gss.sec_oid4.len); - - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - flavor->gss.qop4 = be32_to_cpup(p++); - flavor->gss.service = be32_to_cpup(p); - - return 0; - -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -out_err: - return -EINVAL; -} - -static int decode_secinfo_common(struct xdr_stream *xdr, struct nfs4_secinfo_res *res) -{ - struct nfs4_secinfo_flavor *sec_flavor; - int status; - __be32 *p; - int i, num_flavors; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - - res->flavors->num_flavors = 0; - num_flavors = be32_to_cpup(p); - - for (i = 0; i < num_flavors; i++) { - sec_flavor = &res->flavors->flavors[i]; - if ((char *)&sec_flavor[1] - (char *)res->flavors > PAGE_SIZE) - break; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - sec_flavor->flavor = be32_to_cpup(p); - - if (sec_flavor->flavor == RPC_AUTH_GSS) { - status = decode_secinfo_gss(xdr, sec_flavor); - if (status) - goto out; - } - res->flavors->num_flavors++; - } - - status = 0; -out: - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_secinfo(struct xdr_stream *xdr, struct nfs4_secinfo_res *res) -{ - int status = decode_op_hdr(xdr, OP_SECINFO); - if (status) - return status; - return decode_secinfo_common(xdr, res); -} - -#if defined(CONFIG_NFS_V4_1) -static int decode_secinfo_no_name(struct xdr_stream *xdr, struct nfs4_secinfo_res *res) -{ - int status = decode_op_hdr(xdr, OP_SECINFO_NO_NAME); - if (status) - return status; - return decode_secinfo_common(xdr, res); -} - -static int decode_exchange_id(struct xdr_stream *xdr, - struct nfs41_exchange_id_res *res) -{ - __be32 *p; - uint32_t dummy; - char *dummy_str; - int status; - struct nfs_client *clp = res->client; - uint32_t impl_id_count; - - status = decode_op_hdr(xdr, OP_EXCHANGE_ID); - if (status) - return status; - - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, &clp->cl_clientid); - p = xdr_inline_decode(xdr, 12); - if (unlikely(!p)) - goto out_overflow; - clp->cl_seqid = be32_to_cpup(p++); - clp->cl_exchange_flags = be32_to_cpup(p++); - - /* We ask for SP4_NONE */ - dummy = be32_to_cpup(p); - if (dummy != SP4_NONE) - return -EIO; - - /* Throw away minor_id */ - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - - /* Throw away Major id */ - status = decode_opaque_inline(xdr, &dummy, &dummy_str); - if (unlikely(status)) - return status; - - /* Save server_scope */ - status = decode_opaque_inline(xdr, &dummy, &dummy_str); - if (unlikely(status)) - return status; - - if (unlikely(dummy > NFS4_OPAQUE_LIMIT)) - return -EIO; - - memcpy(res->server_scope->server_scope, dummy_str, dummy); - res->server_scope->server_scope_sz = dummy; - - /* Implementation Id */ - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - impl_id_count = be32_to_cpup(p++); - - if (impl_id_count) { - /* nii_domain */ - status = decode_opaque_inline(xdr, &dummy, &dummy_str); - if (unlikely(status)) - return status; - if (unlikely(dummy > NFS4_OPAQUE_LIMIT)) - return -EIO; - memcpy(res->impl_id->domain, dummy_str, dummy); - - /* nii_name */ - status = decode_opaque_inline(xdr, &dummy, &dummy_str); - if (unlikely(status)) - return status; - if (unlikely(dummy > NFS4_OPAQUE_LIMIT)) - return -EIO; - memcpy(res->impl_id->name, dummy_str, dummy); - - /* nii_date */ - p = xdr_inline_decode(xdr, 12); - if (unlikely(!p)) - goto out_overflow; - p = xdr_decode_hyper(p, &res->impl_id->date.seconds); - res->impl_id->date.nseconds = be32_to_cpup(p); - - /* if there's more than one entry, ignore the rest */ - } - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_chan_attrs(struct xdr_stream *xdr, - struct nfs4_channel_attrs *attrs) -{ - __be32 *p; - u32 nr_attrs, val; - - p = xdr_inline_decode(xdr, 28); - if (unlikely(!p)) - goto out_overflow; - val = be32_to_cpup(p++); /* headerpadsz */ - if (val) - return -EINVAL; /* no support for header padding yet */ - attrs->max_rqst_sz = be32_to_cpup(p++); - attrs->max_resp_sz = be32_to_cpup(p++); - attrs->max_resp_sz_cached = be32_to_cpup(p++); - attrs->max_ops = be32_to_cpup(p++); - attrs->max_reqs = be32_to_cpup(p++); - nr_attrs = be32_to_cpup(p); - if (unlikely(nr_attrs > 1)) { - printk(KERN_WARNING "NFS: %s: Invalid rdma channel attrs " - "count %u\n", __func__, nr_attrs); - return -EINVAL; - } - if (nr_attrs == 1) { - p = xdr_inline_decode(xdr, 4); /* skip rdma_attrs */ - if (unlikely(!p)) - goto out_overflow; - } - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_sessionid(struct xdr_stream *xdr, struct nfs4_sessionid *sid) -{ - return decode_opaque_fixed(xdr, sid->data, NFS4_MAX_SESSIONID_LEN); -} - -static int decode_create_session(struct xdr_stream *xdr, - struct nfs41_create_session_res *res) -{ - __be32 *p; - int status; - struct nfs_client *clp = res->client; - struct nfs4_session *session = clp->cl_session; - - status = decode_op_hdr(xdr, OP_CREATE_SESSION); - if (!status) - status = decode_sessionid(xdr, &session->sess_id); - if (unlikely(status)) - return status; - - /* seqid, flags */ - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - clp->cl_seqid = be32_to_cpup(p++); - session->flags = be32_to_cpup(p); - - /* Channel attributes */ - status = decode_chan_attrs(xdr, &session->fc_attrs); - if (!status) - status = decode_chan_attrs(xdr, &session->bc_attrs); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_destroy_session(struct xdr_stream *xdr, void *dummy) -{ - return decode_op_hdr(xdr, OP_DESTROY_SESSION); -} - -static int decode_reclaim_complete(struct xdr_stream *xdr, void *dummy) -{ - return decode_op_hdr(xdr, OP_RECLAIM_COMPLETE); -} -#endif /* CONFIG_NFS_V4_1 */ - -static int decode_sequence(struct xdr_stream *xdr, - struct nfs4_sequence_res *res, - struct rpc_rqst *rqstp) -{ -#if defined(CONFIG_NFS_V4_1) - struct nfs4_sessionid id; - u32 dummy; - int status; - __be32 *p; - - if (!res->sr_session) - return 0; - - status = decode_op_hdr(xdr, OP_SEQUENCE); - if (!status) - status = decode_sessionid(xdr, &id); - if (unlikely(status)) - goto out_err; - - /* - * If the server returns different values for sessionID, slotID or - * sequence number, the server is looney tunes. - */ - status = -EREMOTEIO; - - if (memcmp(id.data, res->sr_session->sess_id.data, - NFS4_MAX_SESSIONID_LEN)) { - dprintk("%s Invalid session id\n", __func__); - goto out_err; - } - - p = xdr_inline_decode(xdr, 20); - if (unlikely(!p)) - goto out_overflow; - - /* seqid */ - dummy = be32_to_cpup(p++); - if (dummy != res->sr_slot->seq_nr) { - dprintk("%s Invalid sequence number\n", __func__); - goto out_err; - } - /* slot id */ - dummy = be32_to_cpup(p++); - if (dummy != res->sr_slot - res->sr_session->fc_slot_table.slots) { - dprintk("%s Invalid slot id\n", __func__); - goto out_err; - } - /* highest slot id - currently not processed */ - dummy = be32_to_cpup(p++); - /* target highest slot id - currently not processed */ - dummy = be32_to_cpup(p++); - /* result flags */ - res->sr_status_flags = be32_to_cpup(p); - status = 0; -out_err: - res->sr_status = status; - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - status = -EIO; - goto out_err; -#else /* CONFIG_NFS_V4_1 */ - return 0; -#endif /* CONFIG_NFS_V4_1 */ -} - -#if defined(CONFIG_NFS_V4_1) -/* - * TODO: Need to handle case when EOF != true; - */ -static int decode_getdevicelist(struct xdr_stream *xdr, - struct pnfs_devicelist *res) -{ - __be32 *p; - int status, i; - struct nfs_writeverf verftemp; - - status = decode_op_hdr(xdr, OP_GETDEVICELIST); - if (status) - return status; - - p = xdr_inline_decode(xdr, 8 + 8 + 4); - if (unlikely(!p)) - goto out_overflow; - - /* TODO: Skip cookie for now */ - p += 2; - - /* Read verifier */ - p = xdr_decode_opaque_fixed(p, verftemp.verifier, NFS4_VERIFIER_SIZE); - - res->num_devs = be32_to_cpup(p); - - dprintk("%s: num_dev %d\n", __func__, res->num_devs); - - if (res->num_devs > NFS4_PNFS_GETDEVLIST_MAXNUM) { - printk(KERN_ERR "NFS: %s too many result dev_num %u\n", - __func__, res->num_devs); - return -EIO; - } - - p = xdr_inline_decode(xdr, - res->num_devs * NFS4_DEVICEID4_SIZE + 4); - if (unlikely(!p)) - goto out_overflow; - for (i = 0; i < res->num_devs; i++) - p = xdr_decode_opaque_fixed(p, res->dev_id[i].data, - NFS4_DEVICEID4_SIZE); - res->eof = be32_to_cpup(p); - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_getdeviceinfo(struct xdr_stream *xdr, - struct pnfs_device *pdev) -{ - __be32 *p; - uint32_t len, type; - int status; - - status = decode_op_hdr(xdr, OP_GETDEVICEINFO); - if (status) { - if (status == -ETOOSMALL) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - pdev->mincount = be32_to_cpup(p); - dprintk("%s: Min count too small. mincnt = %u\n", - __func__, pdev->mincount); - } - return status; - } - - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - type = be32_to_cpup(p++); - if (type != pdev->layout_type) { - dprintk("%s: layout mismatch req: %u pdev: %u\n", - __func__, pdev->layout_type, type); - return -EINVAL; - } - /* - * Get the length of the opaque device_addr4. xdr_read_pages places - * the opaque device_addr4 in the xdr_buf->pages (pnfs_device->pages) - * and places the remaining xdr data in xdr_buf->tail - */ - pdev->mincount = be32_to_cpup(p); - xdr_read_pages(xdr, pdev->mincount); /* include space for the length */ - - /* Parse notification bitmap, verifying that it is zero. */ - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - len = be32_to_cpup(p); - if (len) { - uint32_t i; - - p = xdr_inline_decode(xdr, 4 * len); - if (unlikely(!p)) - goto out_overflow; - for (i = 0; i < len; i++, p++) { - if (be32_to_cpup(p)) { - dprintk("%s: notifications not supported\n", - __func__); - return -EIO; - } - } - } - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, - struct nfs4_layoutget_res *res) -{ - __be32 *p; - int status; - u32 layout_count; - struct xdr_buf *rcvbuf = &req->rq_rcv_buf; - struct kvec *iov = rcvbuf->head; - u32 hdrlen, recvd; - - status = decode_op_hdr(xdr, OP_LAYOUTGET); - if (status) - return status; - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - res->return_on_close = be32_to_cpup(p); - decode_stateid(xdr, &res->stateid); - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - layout_count = be32_to_cpup(p); - if (!layout_count) { - dprintk("%s: server responded with empty layout array\n", - __func__); - return -EINVAL; - } - - p = xdr_inline_decode(xdr, 28); - if (unlikely(!p)) - goto out_overflow; - p = xdr_decode_hyper(p, &res->range.offset); - p = xdr_decode_hyper(p, &res->range.length); - res->range.iomode = be32_to_cpup(p++); - res->type = be32_to_cpup(p++); - res->layoutp->len = be32_to_cpup(p); - - dprintk("%s roff:%lu rlen:%lu riomode:%d, lo_type:0x%x, lo.len:%d\n", - __func__, - (unsigned long)res->range.offset, - (unsigned long)res->range.length, - res->range.iomode, - res->type, - res->layoutp->len); - - hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base; - recvd = req->rq_rcv_buf.len - hdrlen; - if (res->layoutp->len > recvd) { - dprintk("NFS: server cheating in layoutget reply: " - "layout len %u > recvd %u\n", - res->layoutp->len, recvd); - return -EINVAL; - } - - xdr_read_pages(xdr, res->layoutp->len); - - if (layout_count > 1) { - /* We only handle a length one array at the moment. Any - * further entries are just ignored. Note that this means - * the client may see a response that is less than the - * minimum it requested. - */ - dprintk("%s: server responded with %d layouts, dropping tail\n", - __func__, layout_count); - } - - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_layoutreturn(struct xdr_stream *xdr, - struct nfs4_layoutreturn_res *res) -{ - __be32 *p; - int status; - - status = decode_op_hdr(xdr, OP_LAYOUTRETURN); - if (status) - return status; - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - res->lrs_present = be32_to_cpup(p); - if (res->lrs_present) - status = decode_stateid(xdr, &res->stateid); - return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_layoutcommit(struct xdr_stream *xdr, - struct rpc_rqst *req, - struct nfs4_layoutcommit_res *res) -{ - __be32 *p; - __u32 sizechanged; - int status; - - status = decode_op_hdr(xdr, OP_LAYOUTCOMMIT); - res->status = status; - if (status) - return status; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - sizechanged = be32_to_cpup(p); - - if (sizechanged) { - /* throw away new size */ - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - } - return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} - -static int decode_test_stateid(struct xdr_stream *xdr, - struct nfs41_test_stateid_res *res) -{ - __be32 *p; - int status; - int num_res; - - status = decode_op_hdr(xdr, OP_TEST_STATEID); - if (status) - return status; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - num_res = be32_to_cpup(p++); - if (num_res != 1) - goto out; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - res->status = be32_to_cpup(p++); - - return status; -out_overflow: - print_overflow_msg(__func__, xdr); -out: - return -EIO; -} - -static int decode_free_stateid(struct xdr_stream *xdr, - struct nfs41_free_stateid_res *res) -{ - __be32 *p; - int status; - - status = decode_op_hdr(xdr, OP_FREE_STATEID); - if (status) - return status; - - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - res->status = be32_to_cpup(p++); - return res->status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -} -#endif /* CONFIG_NFS_V4_1 */ - -/* - * END OF "GENERIC" DECODE ROUTINES. - */ - -/* - * Decode OPEN_DOWNGRADE response - */ -static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs_closeres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_open_downgrade(xdr, res); - if (status != 0) - goto out; - decode_getfattr(xdr, res->fattr, res->server); -out: - return status; -} - -/* - * Decode ACCESS response - */ -static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs4_accessres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status != 0) - goto out; - status = decode_access(xdr, res); - if (status != 0) - goto out; - decode_getfattr(xdr, res->fattr, res->server); -out: - return status; -} - -/* - * Decode LOOKUP response - */ -static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs4_lookup_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_lookup(xdr); - if (status) - goto out; - status = decode_getfh(xdr, res->fh); - if (status) - goto out; - status = decode_getfattr(xdr, res->fattr, res->server); -out: - return status; -} - -/* - * Decode LOOKUP_ROOT response - */ -static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs4_lookup_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putrootfh(xdr); - if (status) - goto out; - status = decode_getfh(xdr, res->fh); - if (status == 0) - status = decode_getfattr(xdr, res->fattr, res->server); -out: - return status; -} - -/* - * Decode REMOVE response - */ -static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs_removeres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_remove(xdr, &res->cinfo); - if (status) - goto out; - decode_getfattr(xdr, res->dir_attr, res->server); -out: - return status; -} - -/* - * Decode RENAME response - */ -static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs_renameres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_savefh(xdr); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_rename(xdr, &res->old_cinfo, &res->new_cinfo); - if (status) - goto out; - /* Current FH is target directory */ - if (decode_getfattr(xdr, res->new_fattr, res->server)) - goto out; - status = decode_restorefh(xdr); - if (status) - goto out; - decode_getfattr(xdr, res->old_fattr, res->server); -out: - return status; -} - -/* - * Decode LINK response - */ -static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs4_link_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_savefh(xdr); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_link(xdr, &res->cinfo); - if (status) - goto out; - /* - * Note order: OP_LINK leaves the directory as the current - * filehandle. - */ - if (decode_getfattr(xdr, res->dir_attr, res->server)) - goto out; - status = decode_restorefh(xdr); - if (status) - goto out; - decode_getfattr(xdr, res->fattr, res->server); -out: - return status; -} - -/* - * Decode CREATE response - */ -static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs4_create_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_savefh(xdr); - if (status) - goto out; - status = decode_create(xdr, &res->dir_cinfo); - if (status) - goto out; - status = decode_getfh(xdr, res->fh); - if (status) - goto out; - if (decode_getfattr(xdr, res->fattr, res->server)) - goto out; - status = decode_restorefh(xdr); - if (status) - goto out; - decode_getfattr(xdr, res->dir_fattr, res->server); -out: - return status; -} - -/* - * Decode SYMLINK response - */ -static int nfs4_xdr_dec_symlink(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs4_create_res *res) -{ - return nfs4_xdr_dec_create(rqstp, xdr, res); -} - -/* - * Decode GETATTR response - */ -static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs4_getattr_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_getfattr(xdr, res->fattr, res->server); -out: - return status; -} - -/* - * Encode an SETACL request - */ -static void nfs4_xdr_enc_setacl(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs_setaclargs *args) -{ - struct compound_hdr hdr = { - .minorversion = nfs4_xdr_minorversion(&args->seq_args), - }; - - encode_compound_hdr(xdr, req, &hdr); - encode_sequence(xdr, &args->seq_args, &hdr); - encode_putfh(xdr, args->fh, &hdr); - encode_setacl(xdr, args, &hdr); - encode_nops(&hdr); -} - -/* - * Decode SETACL response - */ -static int -nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs_setaclres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_setattr(xdr); -out: - return status; -} - -/* - * Decode GETACL response - */ -static int -nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs_getaclres *res) -{ - struct compound_hdr hdr; - int status; - - if (res->acl_scratch != NULL) { - void *p = page_address(res->acl_scratch); - xdr_set_scratch_buffer(xdr, p, PAGE_SIZE); - } - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_getacl(xdr, rqstp, res); - -out: - return status; -} - -/* - * Decode CLOSE response - */ -static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs_closeres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_close(xdr, res); - if (status != 0) - goto out; - /* - * Note: Server may do delete on close for this file - * in which case the getattr call will fail with - * an ESTALE error. Shouldn't be a problem, - * though, since fattr->valid will remain unset. - */ - decode_getfattr(xdr, res->fattr, res->server); -out: - return status; -} - -/* - * Decode OPEN response - */ -static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs_openres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_savefh(xdr); - if (status) - goto out; - status = decode_open(xdr, res); - if (status) - goto out; - if (decode_getfh(xdr, &res->fh) != 0) - goto out; - if (decode_getfattr(xdr, res->f_attr, res->server) != 0) - goto out; - if (decode_restorefh(xdr) != 0) - goto out; - decode_getfattr(xdr, res->dir_attr, res->server); -out: - return status; -} - -/* - * Decode OPEN_CONFIRM response - */ -static int nfs4_xdr_dec_open_confirm(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs_open_confirmres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_open_confirm(xdr, res); -out: - return status; -} - -/* - * Decode OPEN response - */ -static int nfs4_xdr_dec_open_noattr(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs_openres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_open(xdr, res); - if (status) - goto out; - decode_getfattr(xdr, res->f_attr, res->server); -out: - return status; -} - -/* - * Decode SETATTR response - */ -static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs_setattrres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_setattr(xdr); - if (status) - goto out; - decode_getfattr(xdr, res->fattr, res->server); -out: - return status; -} - -/* - * Decode LOCK response - */ -static int nfs4_xdr_dec_lock(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs_lock_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_lock(xdr, res); -out: - return status; -} - -/* - * Decode LOCKT response - */ -static int nfs4_xdr_dec_lockt(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs_lockt_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_lockt(xdr, res); -out: - return status; -} - -/* - * Decode LOCKU response - */ -static int nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs_locku_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_locku(xdr, res); -out: - return status; -} - -static int nfs4_xdr_dec_release_lockowner(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, void *dummy) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (!status) - status = decode_release_lockowner(xdr); - return status; -} - -/* - * Decode READLINK response - */ -static int nfs4_xdr_dec_readlink(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs4_readlink_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_readlink(xdr, rqstp); -out: - return status; -} - -/* - * Decode READDIR response - */ -static int nfs4_xdr_dec_readdir(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs4_readdir_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_readdir(xdr, rqstp, res); -out: - return status; -} - -/* - * Decode Read response - */ -static int nfs4_xdr_dec_read(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs_readres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_read(xdr, rqstp, res); - if (!status) - status = res->count; -out: - return status; -} - -/* - * Decode WRITE response - */ -static int nfs4_xdr_dec_write(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs_writeres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_write(xdr, res); - if (status) - goto out; - if (res->fattr) - decode_getfattr(xdr, res->fattr, res->server); - if (!status) - status = res->count; -out: - return status; -} - -/* - * Decode COMMIT response - */ -static int nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - struct nfs_writeres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_commit(xdr, res); - if (status) - goto out; - if (res->fattr) - decode_getfattr(xdr, res->fattr, res->server); -out: - return status; -} - -/* - * Decode FSINFO response - */ -static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs4_fsinfo_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (!status) - status = decode_sequence(xdr, &res->seq_res, req); - if (!status) - status = decode_putfh(xdr); - if (!status) - status = decode_fsinfo(xdr, res->fsinfo); - return status; -} - -/* - * Decode PATHCONF response - */ -static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs4_pathconf_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (!status) - status = decode_sequence(xdr, &res->seq_res, req); - if (!status) - status = decode_putfh(xdr); - if (!status) - status = decode_pathconf(xdr, res->pathconf); - return status; -} - -/* - * Decode STATFS response - */ -static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, struct xdr_stream *xdr, - struct nfs4_statfs_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (!status) - status = decode_sequence(xdr, &res->seq_res, req); - if (!status) - status = decode_putfh(xdr); - if (!status) - status = decode_statfs(xdr, res->fsstat); - return status; -} - -/* - * Decode GETATTR_BITMAP response - */ -static int nfs4_xdr_dec_server_caps(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_server_caps_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, req); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_server_caps(xdr, res); -out: - return status; -} - -/* - * Decode RENEW response - */ -static int nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, struct xdr_stream *xdr, - void *__unused) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (!status) - status = decode_renew(xdr); - return status; -} - -/* - * Decode SETCLIENTID response - */ -static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_setclientid_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (!status) - status = decode_setclientid(xdr, res); - return status; -} - -/* - * Decode SETCLIENTID_CONFIRM response - */ -static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs_fsinfo *fsinfo) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (!status) - status = decode_setclientid_confirm(xdr); - if (!status) - status = decode_putrootfh(xdr); - if (!status) - status = decode_fsinfo(xdr, fsinfo); - return status; -} - -/* - * Decode DELEGRETURN response - */ -static int nfs4_xdr_dec_delegreturn(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs4_delegreturnres *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status != 0) - goto out; - status = decode_delegreturn(xdr); - if (status != 0) - goto out; - decode_getfattr(xdr, res->fattr, res->server); -out: - return status; -} - -/* - * Decode FS_LOCATIONS response - */ -static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req, - struct xdr_stream *xdr, - struct nfs4_fs_locations_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, req); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_lookup(xdr); - if (status) - goto out; - xdr_enter_page(xdr, PAGE_SIZE); - status = decode_getfattr_generic(xdr, &res->fs_locations->fattr, - NULL, res->fs_locations, - res->fs_locations->server); -out: - return status; -} - -/* - * Decode SECINFO response - */ -static int nfs4_xdr_dec_secinfo(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs4_secinfo_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_secinfo(xdr, res); -out: - return status; -} - -#if defined(CONFIG_NFS_V4_1) -/* - * Decode EXCHANGE_ID response - */ -static int nfs4_xdr_dec_exchange_id(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - void *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (!status) - status = decode_exchange_id(xdr, res); - return status; -} - -/* - * Decode CREATE_SESSION response - */ -static int nfs4_xdr_dec_create_session(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs41_create_session_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (!status) - status = decode_create_session(xdr, res); - return status; -} - -/* - * Decode DESTROY_SESSION response - */ -static int nfs4_xdr_dec_destroy_session(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - void *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (!status) - status = decode_destroy_session(xdr, res); - return status; -} - -/* - * Decode SEQUENCE response - */ -static int nfs4_xdr_dec_sequence(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs4_sequence_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (!status) - status = decode_sequence(xdr, res, rqstp); - return status; -} - -/* - * Decode GET_LEASE_TIME response - */ -static int nfs4_xdr_dec_get_lease_time(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs4_get_lease_time_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (!status) - status = decode_sequence(xdr, &res->lr_seq_res, rqstp); - if (!status) - status = decode_putrootfh(xdr); - if (!status) - status = decode_fsinfo(xdr, res->lr_fsinfo); - return status; -} - -/* - * Decode RECLAIM_COMPLETE response - */ -static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs41_reclaim_complete_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (!status) - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (!status) - status = decode_reclaim_complete(xdr, (void *)NULL); - return status; -} - -/* - * Decode GETDEVICELIST response - */ -static int nfs4_xdr_dec_getdevicelist(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs4_getdevicelist_res *res) -{ - struct compound_hdr hdr; - int status; - - dprintk("encoding getdevicelist!\n"); - - status = decode_compound_hdr(xdr, &hdr); - if (status != 0) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status != 0) - goto out; - status = decode_putfh(xdr); - if (status != 0) - goto out; - status = decode_getdevicelist(xdr, res->devlist); -out: - return status; -} - -/* - * Decode GETDEVINFO response - */ -static int nfs4_xdr_dec_getdeviceinfo(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs4_getdeviceinfo_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status != 0) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status != 0) - goto out; - status = decode_getdeviceinfo(xdr, res->pdev); -out: - return status; -} - -/* - * Decode LAYOUTGET response - */ -static int nfs4_xdr_dec_layoutget(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs4_layoutget_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_layoutget(xdr, rqstp, res); -out: - return status; -} - -/* - * Decode LAYOUTRETURN response - */ -static int nfs4_xdr_dec_layoutreturn(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs4_layoutreturn_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_layoutreturn(xdr, res); -out: - return status; -} - -/* - * Decode LAYOUTCOMMIT response - */ -static int nfs4_xdr_dec_layoutcommit(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs4_layoutcommit_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putfh(xdr); - if (status) - goto out; - status = decode_layoutcommit(xdr, rqstp, res); - if (status) - goto out; - decode_getfattr(xdr, res->fattr, res->server); -out: - return status; -} - -/* - * Decode SECINFO_NO_NAME response - */ -static int nfs4_xdr_dec_secinfo_no_name(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs4_secinfo_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_putrootfh(xdr); - if (status) - goto out; - status = decode_secinfo_no_name(xdr, res); -out: - return status; -} - -/* - * Decode TEST_STATEID response - */ -static int nfs4_xdr_dec_test_stateid(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs41_test_stateid_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_test_stateid(xdr, res); -out: - return status; -} - -/* - * Decode FREE_STATEID response - */ -static int nfs4_xdr_dec_free_stateid(struct rpc_rqst *rqstp, - struct xdr_stream *xdr, - struct nfs41_free_stateid_res *res) -{ - struct compound_hdr hdr; - int status; - - status = decode_compound_hdr(xdr, &hdr); - if (status) - goto out; - status = decode_sequence(xdr, &res->seq_res, rqstp); - if (status) - goto out; - status = decode_free_stateid(xdr, res); -out: - return status; -} -#endif /* CONFIG_NFS_V4_1 */ - -/** - * nfs4_decode_dirent - Decode a single NFSv4 directory entry stored in - * the local page cache. - * @xdr: XDR stream where entry resides - * @entry: buffer to fill in with entry data - * @plus: boolean indicating whether this should be a readdirplus entry - * - * Returns zero if successful, otherwise a negative errno value is - * returned. - * - * This function is not invoked during READDIR reply decoding, but - * rather whenever an application invokes the getdents(2) system call - * on a directory already in our cache. - */ -int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, - int plus) -{ - uint32_t bitmap[3] = {0}; - uint32_t len; - __be32 *p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - if (*p == xdr_zero) { - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - if (*p == xdr_zero) - return -EAGAIN; - entry->eof = 1; - return -EBADCOOKIE; - } - - p = xdr_inline_decode(xdr, 12); - if (unlikely(!p)) - goto out_overflow; - entry->prev_cookie = entry->cookie; - p = xdr_decode_hyper(p, &entry->cookie); - entry->len = be32_to_cpup(p); - - p = xdr_inline_decode(xdr, entry->len); - if (unlikely(!p)) - goto out_overflow; - entry->name = (const char *) p; - - /* - * In case the server doesn't return an inode number, - * we fake one here. (We don't use inode number 0, - * since glibc seems to choke on it...) - */ - entry->ino = 1; - entry->fattr->valid = 0; - - if (decode_attr_bitmap(xdr, bitmap) < 0) - goto out_overflow; - - if (decode_attr_length(xdr, &len, &p) < 0) - goto out_overflow; - - if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh, - NULL, entry->server) < 0) - goto out_overflow; - if (entry->fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) - entry->ino = entry->fattr->mounted_on_fileid; - else if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID) - entry->ino = entry->fattr->fileid; - - entry->d_type = DT_UNKNOWN; - if (entry->fattr->valid & NFS_ATTR_FATTR_TYPE) - entry->d_type = nfs_umode_to_dtype(entry->fattr->mode); - - return 0; - -out_overflow: - print_overflow_msg(__func__, xdr); - return -EAGAIN; -} - -/* - * We need to translate between nfs status return values and - * the local errno values which may not be the same. - */ -static struct { - int stat; - int errno; -} nfs_errtbl[] = { - { NFS4_OK, 0 }, - { NFS4ERR_PERM, -EPERM }, - { NFS4ERR_NOENT, -ENOENT }, - { NFS4ERR_IO, -errno_NFSERR_IO}, - { NFS4ERR_NXIO, -ENXIO }, - { NFS4ERR_ACCESS, -EACCES }, - { NFS4ERR_EXIST, -EEXIST }, - { NFS4ERR_XDEV, -EXDEV }, - { NFS4ERR_NOTDIR, -ENOTDIR }, - { NFS4ERR_ISDIR, -EISDIR }, - { NFS4ERR_INVAL, -EINVAL }, - { NFS4ERR_FBIG, -EFBIG }, - { NFS4ERR_NOSPC, -ENOSPC }, - { NFS4ERR_ROFS, -EROFS }, - { NFS4ERR_MLINK, -EMLINK }, - { NFS4ERR_NAMETOOLONG, -ENAMETOOLONG }, - { NFS4ERR_NOTEMPTY, -ENOTEMPTY }, - { NFS4ERR_DQUOT, -EDQUOT }, - { NFS4ERR_STALE, -ESTALE }, - { NFS4ERR_BADHANDLE, -EBADHANDLE }, - { NFS4ERR_BAD_COOKIE, -EBADCOOKIE }, - { NFS4ERR_NOTSUPP, -ENOTSUPP }, - { NFS4ERR_TOOSMALL, -ETOOSMALL }, - { NFS4ERR_SERVERFAULT, -EREMOTEIO }, - { NFS4ERR_BADTYPE, -EBADTYPE }, - { NFS4ERR_LOCKED, -EAGAIN }, - { NFS4ERR_SYMLINK, -ELOOP }, - { NFS4ERR_OP_ILLEGAL, -EOPNOTSUPP }, - { NFS4ERR_DEADLOCK, -EDEADLK }, - { -1, -EIO } -}; - -/* - * Convert an NFS error code to a local one. - * This one is used jointly by NFSv2 and NFSv3. - */ -static int -nfs4_stat_to_errno(int stat) -{ - int i; - for (i = 0; nfs_errtbl[i].stat != -1; i++) { - if (nfs_errtbl[i].stat == stat) - return nfs_errtbl[i].errno; - } - if (stat <= 10000 || stat > 10100) { - /* The server is looney tunes. */ - return -EREMOTEIO; - } - /* If we cannot translate the error, the recovery routines should - * handle it. - * Note: remaining NFSv4 error codes have values > 10000, so should - * not conflict with native Linux error codes. - */ - return -stat; -} - -#define PROC(proc, argtype, restype) \ -[NFSPROC4_CLNT_##proc] = { \ - .p_proc = NFSPROC4_COMPOUND, \ - .p_encode = (kxdreproc_t)nfs4_xdr_##argtype, \ - .p_decode = (kxdrdproc_t)nfs4_xdr_##restype, \ - .p_arglen = NFS4_##argtype##_sz, \ - .p_replen = NFS4_##restype##_sz, \ - .p_statidx = NFSPROC4_CLNT_##proc, \ - .p_name = #proc, \ -} - -struct rpc_procinfo nfs4_procedures[] = { - PROC(READ, enc_read, dec_read), - PROC(WRITE, enc_write, dec_write), - PROC(COMMIT, enc_commit, dec_commit), - PROC(OPEN, enc_open, dec_open), - PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm), - PROC(OPEN_NOATTR, enc_open_noattr, dec_open_noattr), - PROC(OPEN_DOWNGRADE, enc_open_downgrade, dec_open_downgrade), - PROC(CLOSE, enc_close, dec_close), - PROC(SETATTR, enc_setattr, dec_setattr), - PROC(FSINFO, enc_fsinfo, dec_fsinfo), - PROC(RENEW, enc_renew, dec_renew), - PROC(SETCLIENTID, enc_setclientid, dec_setclientid), - PROC(SETCLIENTID_CONFIRM, enc_setclientid_confirm, dec_setclientid_confirm), - PROC(LOCK, enc_lock, dec_lock), - PROC(LOCKT, enc_lockt, dec_lockt), - PROC(LOCKU, enc_locku, dec_locku), - PROC(ACCESS, enc_access, dec_access), - PROC(GETATTR, enc_getattr, dec_getattr), - PROC(LOOKUP, enc_lookup, dec_lookup), - PROC(LOOKUP_ROOT, enc_lookup_root, dec_lookup_root), - PROC(REMOVE, enc_remove, dec_remove), - PROC(RENAME, enc_rename, dec_rename), - PROC(LINK, enc_link, dec_link), - PROC(SYMLINK, enc_symlink, dec_symlink), - PROC(CREATE, enc_create, dec_create), - PROC(PATHCONF, enc_pathconf, dec_pathconf), - PROC(STATFS, enc_statfs, dec_statfs), - PROC(READLINK, enc_readlink, dec_readlink), - PROC(READDIR, enc_readdir, dec_readdir), - PROC(SERVER_CAPS, enc_server_caps, dec_server_caps), - PROC(DELEGRETURN, enc_delegreturn, dec_delegreturn), - PROC(GETACL, enc_getacl, dec_getacl), - PROC(SETACL, enc_setacl, dec_setacl), - PROC(FS_LOCATIONS, enc_fs_locations, dec_fs_locations), - PROC(RELEASE_LOCKOWNER, enc_release_lockowner, dec_release_lockowner), - PROC(SECINFO, enc_secinfo, dec_secinfo), -#if defined(CONFIG_NFS_V4_1) - PROC(EXCHANGE_ID, enc_exchange_id, dec_exchange_id), - PROC(CREATE_SESSION, enc_create_session, dec_create_session), - PROC(DESTROY_SESSION, enc_destroy_session, dec_destroy_session), - PROC(SEQUENCE, enc_sequence, dec_sequence), - PROC(GET_LEASE_TIME, enc_get_lease_time, dec_get_lease_time), - PROC(RECLAIM_COMPLETE, enc_reclaim_complete, dec_reclaim_complete), - PROC(GETDEVICEINFO, enc_getdeviceinfo, dec_getdeviceinfo), - PROC(LAYOUTGET, enc_layoutget, dec_layoutget), - PROC(LAYOUTCOMMIT, enc_layoutcommit, dec_layoutcommit), - PROC(LAYOUTRETURN, enc_layoutreturn, dec_layoutreturn), - PROC(SECINFO_NO_NAME, enc_secinfo_no_name, dec_secinfo_no_name), - PROC(TEST_STATEID, enc_test_stateid, dec_test_stateid), - PROC(FREE_STATEID, enc_free_stateid, dec_free_stateid), - PROC(GETDEVICELIST, enc_getdevicelist, dec_getdevicelist), -#endif /* CONFIG_NFS_V4_1 */ -}; - -const struct rpc_version nfs_version4 = { - .number = 4, - .nrprocs = ARRAY_SIZE(nfs4_procedures), - .procs = nfs4_procedures -}; - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/ANDROID_3.4.5/fs/nfs/nfsroot.c b/ANDROID_3.4.5/fs/nfs/nfsroot.c deleted file mode 100644 index cd3c910d..00000000 --- a/ANDROID_3.4.5/fs/nfs/nfsroot.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de> - * - * Allow an NFS filesystem to be mounted as root. The way this works is: - * (1) Use the IP autoconfig mechanism to set local IP addresses and routes. - * (2) Construct the device string and the options string using DHCP - * option 17 and/or kernel command line options. - * (3) When mount_root() sets up the root file system, pass these strings - * to the NFS client's regular mount interface via sys_mount(). - * - * - * Changes: - * - * Alan Cox : Removed get_address name clash with FPU. - * Alan Cox : Reformatted a bit. - * Gero Kuhlmann : Code cleanup - * Michael Rausch : Fixed recognition of an incoming RARP answer. - * Martin Mares : (2.0) Auto-configuration via BOOTP supported. - * Martin Mares : Manual selection of interface & BOOTP/RARP. - * Martin Mares : Using network routes instead of host routes, - * allowing the default configuration to be used - * for normal operation of the host. - * Martin Mares : Randomized timer with exponential backoff - * installed to minimize network congestion. - * Martin Mares : Code cleanup. - * Martin Mares : (2.1) BOOTP and RARP made configuration options. - * Martin Mares : Server hostname generation fixed. - * Gerd Knorr : Fixed wired inode handling - * Martin Mares : (2.2) "0.0.0.0" addresses from command line ignored. - * Martin Mares : RARP replies not tested for server address. - * Gero Kuhlmann : (2.3) Some bug fixes and code cleanup again (please - * send me your new patches _before_ bothering - * Linus so that I don' always have to cleanup - * _afterwards_ - thanks) - * Gero Kuhlmann : Last changes of Martin Mares undone. - * Gero Kuhlmann : RARP replies are tested for specified server - * again. However, it's now possible to have - * different RARP and NFS servers. - * Gero Kuhlmann : "0.0.0.0" addresses from command line are - * now mapped to INADDR_NONE. - * Gero Kuhlmann : Fixed a bug which prevented BOOTP path name - * from being used (thanks to Leo Spiekman) - * Andy Walker : Allow to specify the NFS server in nfs_root - * without giving a path name - * Swen Thümmler : Allow to specify the NFS options in nfs_root - * without giving a path name. Fix BOOTP request - * for domainname (domainname is NIS domain, not - * DNS domain!). Skip dummy devices for BOOTP. - * Jacek Zapala : Fixed a bug which prevented server-ip address - * from nfsroot parameter from being used. - * Olaf Kirch : Adapted to new NFS code. - * Jakub Jelinek : Free used code segment. - * Marko Kohtala : Fixed some bugs. - * Martin Mares : Debug message cleanup - * Martin Mares : Changed to use the new generic IP layer autoconfig - * code. BOOTP and RARP moved there. - * Martin Mares : Default path now contains host name instead of - * host IP address (but host name defaults to IP - * address anyway). - * 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. - * Hua Qin : Support for mounting root file system via - * NFS over TCP. - * Fabian Frederick: Option parser rebuilt (using parser lib) - * Chuck Lever : Use super.c's text-based mount option parsing - * Chuck Lever : Add "nfsrootdebug". - */ - -#include <linux/types.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/nfs.h> -#include <linux/nfs_fs.h> -#include <linux/utsname.h> -#include <linux/root_dev.h> -#include <net/ipconfig.h> - -#include "internal.h" - -#define NFSDBG_FACILITY NFSDBG_ROOT - -/* Default path we try to mount. "%s" gets replaced by our IP address */ -#define NFS_ROOT "/tftpboot/%s" - -/* Default NFSROOT mount options. */ -#define NFS_DEF_OPTIONS "vers=2,udp,rsize=4096,wsize=4096" - -/* Parameters passed from the kernel command line */ -static char nfs_root_parms[256] __initdata = ""; - -/* Text-based mount options passed to super.c */ -static char nfs_root_options[256] __initdata = NFS_DEF_OPTIONS; - -/* Address of NFS server */ -static __be32 servaddr __initdata = htonl(INADDR_NONE); - -/* Name of directory to mount */ -static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = ""; - -/* server:export path string passed to super.c */ -static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = ""; - -#ifdef NFS_DEBUG -/* - * When the "nfsrootdebug" kernel command line option is specified, - * enable debugging messages for NFSROOT. - */ -static int __init nfs_root_debug(char *__unused) -{ - nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT; - return 1; -} - -__setup("nfsrootdebug", nfs_root_debug); -#endif - -/* - * Parse NFS server and directory information passed on the kernel - * command line. - * - * nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] - * - * If there is a "%s" token in the <root-dir> string, it is replaced - * by the ASCII-representation of the client's IP address. - */ -static int __init nfs_root_setup(char *line) -{ - ROOT_DEV = Root_NFS; - - if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { - strlcpy(nfs_root_parms, line, sizeof(nfs_root_parms)); - } else { - size_t n = strlen(line) + sizeof(NFS_ROOT) - 1; - if (n >= sizeof(nfs_root_parms)) - line[sizeof(nfs_root_parms) - sizeof(NFS_ROOT) - 2] = '\0'; - sprintf(nfs_root_parms, NFS_ROOT, line); - } - - /* - * Extract the IP address of the NFS server containing our - * root file system, if one was specified. - * - * Note: root_nfs_parse_addr() removes the server-ip from - * nfs_root_parms, if it exists. - */ - root_server_addr = root_nfs_parse_addr(nfs_root_parms); - - return 1; -} - -__setup("nfsroot=", nfs_root_setup); - -static int __init root_nfs_copy(char *dest, const char *src, - const size_t destlen) -{ - if (strlcpy(dest, src, destlen) > destlen) - return -1; - return 0; -} - -static int __init root_nfs_cat(char *dest, const char *src, - const size_t destlen) -{ - size_t len = strlen(dest); - - if (len && dest[len - 1] != ',') - if (strlcat(dest, ",", destlen) > destlen) - return -1; - - if (strlcat(dest, src, destlen) > destlen) - return -1; - return 0; -} - -/* - * Parse out root export path and mount options from - * passed-in string @incoming. - * - * Copy the export path into @exppath. - */ -static int __init root_nfs_parse_options(char *incoming, char *exppath, - const size_t exppathlen) -{ - char *p; - - /* - * Set the NFS remote path - */ - p = strsep(&incoming, ","); - if (*p != '\0' && strcmp(p, "default") != 0) - if (root_nfs_copy(exppath, p, exppathlen)) - return -1; - - /* - * @incoming now points to the rest of the string; if it - * contains something, append it to our root options buffer - */ - if (incoming != NULL && *incoming != '\0') - if (root_nfs_cat(nfs_root_options, incoming, - sizeof(nfs_root_options))) - return -1; - return 0; -} - -/* - * Decode the export directory path name and NFS options from - * the kernel command line. This has to be done late in order to - * use a dynamically acquired client IP address for the remote - * root directory path. - * - * Returns zero if successful; otherwise -1 is returned. - */ -static int __init root_nfs_data(char *cmdline) -{ - char mand_options[sizeof("nolock,addr=") + INET_ADDRSTRLEN + 1]; - int len, retval = -1; - char *tmp = NULL; - const size_t tmplen = sizeof(nfs_export_path); - - tmp = kzalloc(tmplen, GFP_KERNEL); - if (tmp == NULL) - goto out_nomem; - strcpy(tmp, NFS_ROOT); - - if (root_server_path[0] != '\0') { - dprintk("Root-NFS: DHCPv4 option 17: %s\n", - root_server_path); - if (root_nfs_parse_options(root_server_path, tmp, tmplen)) - goto out_optionstoolong; - } - - if (cmdline[0] != '\0') { - dprintk("Root-NFS: nfsroot=%s\n", cmdline); - if (root_nfs_parse_options(cmdline, tmp, tmplen)) - goto out_optionstoolong; - } - - /* - * Append mandatory options for nfsroot so they override - * what has come before - */ - snprintf(mand_options, sizeof(mand_options), "nolock,addr=%pI4", - &servaddr); - if (root_nfs_cat(nfs_root_options, mand_options, - sizeof(nfs_root_options))) - goto out_optionstoolong; - - /* - * Set up nfs_root_device. For NFS mounts, this looks like - * - * server:/path - * - * At this point, utsname()->nodename contains our local - * IP address or hostname, set by ipconfig. If "%s" exists - * in tmp, substitute the nodename, then shovel the whole - * mess into nfs_root_device. - */ - len = snprintf(nfs_export_path, sizeof(nfs_export_path), - tmp, utsname()->nodename); - if (len > (int)sizeof(nfs_export_path)) - goto out_devnametoolong; - len = snprintf(nfs_root_device, sizeof(nfs_root_device), - "%pI4:%s", &servaddr, nfs_export_path); - if (len > (int)sizeof(nfs_root_device)) - goto out_devnametoolong; - - retval = 0; - -out: - kfree(tmp); - return retval; -out_nomem: - printk(KERN_ERR "Root-NFS: could not allocate memory\n"); - goto out; -out_optionstoolong: - printk(KERN_ERR "Root-NFS: mount options string too long\n"); - goto out; -out_devnametoolong: - printk(KERN_ERR "Root-NFS: root device name too long.\n"); - goto out; -} - -/** - * nfs_root_data - Return prepared 'data' for NFSROOT mount - * @root_device: OUT: address of string containing NFSROOT device - * @root_data: OUT: address of string containing NFSROOT mount options - * - * Returns zero and sets @root_device and @root_data if successful, - * otherwise -1 is returned. - */ -int __init nfs_root_data(char **root_device, char **root_data) -{ - servaddr = root_server_addr; - if (servaddr == htonl(INADDR_NONE)) { - printk(KERN_ERR "Root-NFS: no NFS server address\n"); - return -1; - } - - if (root_nfs_data(nfs_root_parms) < 0) - return -1; - - *root_device = nfs_root_device; - *root_data = nfs_root_options; - return 0; -} diff --git a/ANDROID_3.4.5/fs/nfs/objlayout/Kbuild b/ANDROID_3.4.5/fs/nfs/objlayout/Kbuild deleted file mode 100644 index ed30ea07..00000000 --- a/ANDROID_3.4.5/fs/nfs/objlayout/Kbuild +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for the pNFS Objects Layout Driver kernel module -# -objlayoutdriver-y := objio_osd.o pnfs_osd_xdr_cli.o objlayout.o -obj-$(CONFIG_PNFS_OBJLAYOUT) += objlayoutdriver.o diff --git a/ANDROID_3.4.5/fs/nfs/objlayout/objio_osd.c b/ANDROID_3.4.5/fs/nfs/objlayout/objio_osd.c deleted file mode 100644 index 4bff4a3d..00000000 --- a/ANDROID_3.4.5/fs/nfs/objlayout/objio_osd.c +++ /dev/null @@ -1,624 +0,0 @@ -/* - * pNFS Objects layout implementation over open-osd initiator library - * - * Copyright (C) 2009 Panasas Inc. [year of first publication] - * All rights reserved. - * - * Benny Halevy <bhalevy@panasas.com> - * Boaz Harrosh <bharrosh@panasas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * See the file COPYING included with this distribution for more details. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Panasas company nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <linux/module.h> -#include <scsi/osd_ore.h> - -#include "objlayout.h" - -#define NFSDBG_FACILITY NFSDBG_PNFS_LD - -struct objio_dev_ent { - struct nfs4_deviceid_node id_node; - struct ore_dev od; -}; - -static void -objio_free_deviceid_node(struct nfs4_deviceid_node *d) -{ - struct objio_dev_ent *de = container_of(d, struct objio_dev_ent, id_node); - - dprintk("%s: free od=%p\n", __func__, de->od.od); - osduld_put_device(de->od.od); - kfree(de); -} - -static struct objio_dev_ent *_dev_list_find(const struct nfs_server *nfss, - const struct nfs4_deviceid *d_id) -{ - struct nfs4_deviceid_node *d; - struct objio_dev_ent *de; - - d = nfs4_find_get_deviceid(nfss->pnfs_curr_ld, nfss->nfs_client, d_id); - if (!d) - return NULL; - - de = container_of(d, struct objio_dev_ent, id_node); - return de; -} - -static struct objio_dev_ent * -_dev_list_add(const struct nfs_server *nfss, - const struct nfs4_deviceid *d_id, struct osd_dev *od, - gfp_t gfp_flags) -{ - struct nfs4_deviceid_node *d; - struct objio_dev_ent *de = kzalloc(sizeof(*de), gfp_flags); - struct objio_dev_ent *n; - - if (!de) { - dprintk("%s: -ENOMEM od=%p\n", __func__, od); - return NULL; - } - - dprintk("%s: Adding od=%p\n", __func__, od); - nfs4_init_deviceid_node(&de->id_node, - nfss->pnfs_curr_ld, - nfss->nfs_client, - d_id); - de->od.od = od; - - d = nfs4_insert_deviceid_node(&de->id_node); - n = container_of(d, struct objio_dev_ent, id_node); - if (n != de) { - dprintk("%s: Race with other n->od=%p\n", __func__, n->od.od); - objio_free_deviceid_node(&de->id_node); - de = n; - } - - return de; -} - -struct objio_segment { - struct pnfs_layout_segment lseg; - - struct ore_layout layout; - struct ore_components oc; -}; - -static inline struct objio_segment * -OBJIO_LSEG(struct pnfs_layout_segment *lseg) -{ - return container_of(lseg, struct objio_segment, lseg); -} - -struct objio_state { - /* Generic layer */ - struct objlayout_io_res oir; - - bool sync; - /*FIXME: Support for extra_bytes at ore_get_rw_state() */ - struct ore_io_state *ios; -}; - -/* Send and wait for a get_device_info of devices in the layout, - then look them up with the osd_initiator library */ -static int objio_devices_lookup(struct pnfs_layout_hdr *pnfslay, - struct objio_segment *objio_seg, unsigned c, struct nfs4_deviceid *d_id, - gfp_t gfp_flags) -{ - struct pnfs_osd_deviceaddr *deviceaddr; - struct objio_dev_ent *ode; - struct osd_dev *od; - struct osd_dev_info odi; - bool retry_flag = true; - int err; - - ode = _dev_list_find(NFS_SERVER(pnfslay->plh_inode), d_id); - if (ode) { - objio_seg->oc.ods[c] = &ode->od; /* must use container_of */ - return 0; - } - - err = objlayout_get_deviceinfo(pnfslay, d_id, &deviceaddr, gfp_flags); - if (unlikely(err)) { - dprintk("%s: objlayout_get_deviceinfo dev(%llx:%llx) =>%d\n", - __func__, _DEVID_LO(d_id), _DEVID_HI(d_id), err); - return err; - } - - odi.systemid_len = deviceaddr->oda_systemid.len; - if (odi.systemid_len > sizeof(odi.systemid)) { - dprintk("%s: odi.systemid_len > sizeof(systemid=%zd)\n", - __func__, sizeof(odi.systemid)); - err = -EINVAL; - goto out; - } else if (odi.systemid_len) - memcpy(odi.systemid, deviceaddr->oda_systemid.data, - odi.systemid_len); - odi.osdname_len = deviceaddr->oda_osdname.len; - odi.osdname = (u8 *)deviceaddr->oda_osdname.data; - - if (!odi.osdname_len && !odi.systemid_len) { - dprintk("%s: !odi.osdname_len && !odi.systemid_len\n", - __func__); - err = -ENODEV; - goto out; - } - -retry_lookup: - od = osduld_info_lookup(&odi); - if (unlikely(IS_ERR(od))) { - err = PTR_ERR(od); - dprintk("%s: osduld_info_lookup => %d\n", __func__, err); - if (err == -ENODEV && retry_flag) { - err = objlayout_autologin(deviceaddr); - if (likely(!err)) { - retry_flag = false; - goto retry_lookup; - } - } - goto out; - } - - ode = _dev_list_add(NFS_SERVER(pnfslay->plh_inode), d_id, od, - gfp_flags); - objio_seg->oc.ods[c] = &ode->od; /* must use container_of */ - dprintk("Adding new dev_id(%llx:%llx)\n", - _DEVID_LO(d_id), _DEVID_HI(d_id)); -out: - objlayout_put_deviceinfo(deviceaddr); - return err; -} - -static void copy_single_comp(struct ore_components *oc, unsigned c, - struct pnfs_osd_object_cred *src_comp) -{ - struct ore_comp *ocomp = &oc->comps[c]; - - WARN_ON(src_comp->oc_cap_key.cred_len > 0); /* libosd is NO_SEC only */ - WARN_ON(src_comp->oc_cap.cred_len > sizeof(ocomp->cred)); - - ocomp->obj.partition = src_comp->oc_object_id.oid_partition_id; - ocomp->obj.id = src_comp->oc_object_id.oid_object_id; - - memcpy(ocomp->cred, src_comp->oc_cap.cred, sizeof(ocomp->cred)); -} - -int __alloc_objio_seg(unsigned numdevs, gfp_t gfp_flags, - struct objio_segment **pseg) -{ -/* This is the in memory structure of the objio_segment - * - * struct __alloc_objio_segment { - * struct objio_segment olseg; - * struct ore_dev *ods[numdevs]; - * struct ore_comp comps[numdevs]; - * } *aolseg; - * NOTE: The code as above compiles and runs perfectly. It is elegant, - * type safe and compact. At some Past time Linus has decided he does not - * like variable length arrays, For the sake of this principal we uglify - * the code as below. - */ - struct objio_segment *lseg; - size_t lseg_size = sizeof(*lseg) + - numdevs * sizeof(lseg->oc.ods[0]) + - numdevs * sizeof(*lseg->oc.comps); - - lseg = kzalloc(lseg_size, gfp_flags); - if (unlikely(!lseg)) { - dprintk("%s: Faild allocation numdevs=%d size=%zd\n", __func__, - numdevs, lseg_size); - return -ENOMEM; - } - - lseg->oc.numdevs = numdevs; - lseg->oc.single_comp = EC_MULTPLE_COMPS; - lseg->oc.ods = (void *)(lseg + 1); - lseg->oc.comps = (void *)(lseg->oc.ods + numdevs); - - *pseg = lseg; - return 0; -} - -int objio_alloc_lseg(struct pnfs_layout_segment **outp, - struct pnfs_layout_hdr *pnfslay, - struct pnfs_layout_range *range, - struct xdr_stream *xdr, - gfp_t gfp_flags) -{ - struct objio_segment *objio_seg; - struct pnfs_osd_xdr_decode_layout_iter iter; - struct pnfs_osd_layout layout; - struct pnfs_osd_object_cred src_comp; - unsigned cur_comp; - int err; - - err = pnfs_osd_xdr_decode_layout_map(&layout, &iter, xdr); - if (unlikely(err)) - return err; - - err = __alloc_objio_seg(layout.olo_num_comps, gfp_flags, &objio_seg); - if (unlikely(err)) - return err; - - objio_seg->layout.stripe_unit = layout.olo_map.odm_stripe_unit; - objio_seg->layout.group_width = layout.olo_map.odm_group_width; - objio_seg->layout.group_depth = layout.olo_map.odm_group_depth; - objio_seg->layout.mirrors_p1 = layout.olo_map.odm_mirror_cnt + 1; - objio_seg->layout.raid_algorithm = layout.olo_map.odm_raid_algorithm; - - err = ore_verify_layout(layout.olo_map.odm_num_comps, - &objio_seg->layout); - if (unlikely(err)) - goto err; - - objio_seg->oc.first_dev = layout.olo_comps_index; - cur_comp = 0; - while (pnfs_osd_xdr_decode_layout_comp(&src_comp, &iter, xdr, &err)) { - copy_single_comp(&objio_seg->oc, cur_comp, &src_comp); - err = objio_devices_lookup(pnfslay, objio_seg, cur_comp, - &src_comp.oc_object_id.oid_device_id, - gfp_flags); - if (err) - goto err; - ++cur_comp; - } - /* pnfs_osd_xdr_decode_layout_comp returns false on error */ - if (unlikely(err)) - goto err; - - *outp = &objio_seg->lseg; - return 0; - -err: - kfree(objio_seg); - dprintk("%s: Error: return %d\n", __func__, err); - *outp = NULL; - return err; -} - -void objio_free_lseg(struct pnfs_layout_segment *lseg) -{ - int i; - struct objio_segment *objio_seg = OBJIO_LSEG(lseg); - - for (i = 0; i < objio_seg->oc.numdevs; i++) { - struct ore_dev *od = objio_seg->oc.ods[i]; - struct objio_dev_ent *ode; - - if (!od) - break; - ode = container_of(od, typeof(*ode), od); - nfs4_put_deviceid_node(&ode->id_node); - } - kfree(objio_seg); -} - -static int -objio_alloc_io_state(struct pnfs_layout_hdr *pnfs_layout_type, bool is_reading, - struct pnfs_layout_segment *lseg, struct page **pages, unsigned pgbase, - loff_t offset, size_t count, void *rpcdata, gfp_t gfp_flags, - struct objio_state **outp) -{ - struct objio_segment *objio_seg = OBJIO_LSEG(lseg); - struct ore_io_state *ios; - int ret; - struct __alloc_objio_state { - struct objio_state objios; - struct pnfs_osd_ioerr ioerrs[objio_seg->oc.numdevs]; - } *aos; - - aos = kzalloc(sizeof(*aos), gfp_flags); - if (unlikely(!aos)) - return -ENOMEM; - - objlayout_init_ioerrs(&aos->objios.oir, objio_seg->oc.numdevs, - aos->ioerrs, rpcdata, pnfs_layout_type); - - ret = ore_get_rw_state(&objio_seg->layout, &objio_seg->oc, is_reading, - offset, count, &ios); - if (unlikely(ret)) { - kfree(aos); - return ret; - } - - ios->pages = pages; - ios->pgbase = pgbase; - ios->private = aos; - BUG_ON(ios->nr_pages > (pgbase + count + PAGE_SIZE - 1) >> PAGE_SHIFT); - - aos->objios.sync = 0; - aos->objios.ios = ios; - *outp = &aos->objios; - return 0; -} - -void objio_free_result(struct objlayout_io_res *oir) -{ - struct objio_state *objios = container_of(oir, struct objio_state, oir); - - ore_put_io_state(objios->ios); - kfree(objios); -} - -enum pnfs_osd_errno osd_pri_2_pnfs_err(enum osd_err_priority oep) -{ - switch (oep) { - case OSD_ERR_PRI_NO_ERROR: - return (enum pnfs_osd_errno)0; - - case OSD_ERR_PRI_CLEAR_PAGES: - BUG_ON(1); - return 0; - - case OSD_ERR_PRI_RESOURCE: - return PNFS_OSD_ERR_RESOURCE; - case OSD_ERR_PRI_BAD_CRED: - return PNFS_OSD_ERR_BAD_CRED; - case OSD_ERR_PRI_NO_ACCESS: - return PNFS_OSD_ERR_NO_ACCESS; - case OSD_ERR_PRI_UNREACHABLE: - return PNFS_OSD_ERR_UNREACHABLE; - case OSD_ERR_PRI_NOT_FOUND: - return PNFS_OSD_ERR_NOT_FOUND; - case OSD_ERR_PRI_NO_SPACE: - return PNFS_OSD_ERR_NO_SPACE; - default: - WARN_ON(1); - /* fallthrough */ - case OSD_ERR_PRI_EIO: - return PNFS_OSD_ERR_EIO; - } -} - -static void __on_dev_error(struct ore_io_state *ios, - struct ore_dev *od, unsigned dev_index, enum osd_err_priority oep, - u64 dev_offset, u64 dev_len) -{ - struct objio_state *objios = ios->private; - struct pnfs_osd_objid pooid; - struct objio_dev_ent *ode = container_of(od, typeof(*ode), od); - /* FIXME: what to do with more-then-one-group layouts. We need to - * translate from ore_io_state index to oc->comps index - */ - unsigned comp = dev_index; - - pooid.oid_device_id = ode->id_node.deviceid; - pooid.oid_partition_id = ios->oc->comps[comp].obj.partition; - pooid.oid_object_id = ios->oc->comps[comp].obj.id; - - objlayout_io_set_result(&objios->oir, comp, - &pooid, osd_pri_2_pnfs_err(oep), - dev_offset, dev_len, !ios->reading); -} - -/* - * read - */ -static void _read_done(struct ore_io_state *ios, void *private) -{ - struct objio_state *objios = private; - ssize_t status; - int ret = ore_check_io(ios, &__on_dev_error); - - /* FIXME: _io_free(ios) can we dealocate the libosd resources; */ - - if (likely(!ret)) - status = ios->length; - else - status = ret; - - objlayout_read_done(&objios->oir, status, objios->sync); -} - -int objio_read_pagelist(struct nfs_read_data *rdata) -{ - struct objio_state *objios; - int ret; - - ret = objio_alloc_io_state(NFS_I(rdata->inode)->layout, true, - rdata->lseg, rdata->args.pages, rdata->args.pgbase, - rdata->args.offset, rdata->args.count, rdata, - GFP_KERNEL, &objios); - if (unlikely(ret)) - return ret; - - objios->ios->done = _read_done; - dprintk("%s: offset=0x%llx length=0x%x\n", __func__, - rdata->args.offset, rdata->args.count); - return ore_read(objios->ios); -} - -/* - * write - */ -static void _write_done(struct ore_io_state *ios, void *private) -{ - struct objio_state *objios = private; - ssize_t status; - int ret = ore_check_io(ios, &__on_dev_error); - - /* FIXME: _io_free(ios) can we dealocate the libosd resources; */ - - if (likely(!ret)) { - /* FIXME: should be based on the OSD's persistence model - * See OSD2r05 Section 4.13 Data persistence model */ - objios->oir.committed = NFS_FILE_SYNC; - status = ios->length; - } else { - status = ret; - } - - objlayout_write_done(&objios->oir, status, objios->sync); -} - -static struct page *__r4w_get_page(void *priv, u64 offset, bool *uptodate) -{ - struct objio_state *objios = priv; - struct nfs_write_data *wdata = objios->oir.rpcdata; - pgoff_t index = offset / PAGE_SIZE; - struct page *page = find_get_page(wdata->inode->i_mapping, index); - - if (!page) { - page = find_or_create_page(wdata->inode->i_mapping, - index, GFP_NOFS); - if (unlikely(!page)) { - dprintk("%s: grab_cache_page Failed index=0x%lx\n", - __func__, index); - return NULL; - } - unlock_page(page); - } - if (PageDirty(page) || PageWriteback(page)) - *uptodate = true; - else - *uptodate = PageUptodate(page); - dprintk("%s: index=0x%lx uptodate=%d\n", __func__, index, *uptodate); - return page; -} - -static void __r4w_put_page(void *priv, struct page *page) -{ - dprintk("%s: index=0x%lx\n", __func__, page->index); - page_cache_release(page); - return; -} - -static const struct _ore_r4w_op _r4w_op = { - .get_page = &__r4w_get_page, - .put_page = &__r4w_put_page, -}; - -int objio_write_pagelist(struct nfs_write_data *wdata, int how) -{ - struct objio_state *objios; - int ret; - - ret = objio_alloc_io_state(NFS_I(wdata->inode)->layout, false, - wdata->lseg, wdata->args.pages, wdata->args.pgbase, - wdata->args.offset, wdata->args.count, wdata, GFP_NOFS, - &objios); - if (unlikely(ret)) - return ret; - - objios->sync = 0 != (how & FLUSH_SYNC); - objios->ios->r4w = &_r4w_op; - - if (!objios->sync) - objios->ios->done = _write_done; - - dprintk("%s: offset=0x%llx length=0x%x\n", __func__, - wdata->args.offset, wdata->args.count); - ret = ore_write(objios->ios); - if (unlikely(ret)) - return ret; - - if (objios->sync) - _write_done(objios->ios, objios); - - return 0; -} - -static bool objio_pg_test(struct nfs_pageio_descriptor *pgio, - struct nfs_page *prev, struct nfs_page *req) -{ - if (!pnfs_generic_pg_test(pgio, prev, req)) - return false; - - return pgio->pg_count + req->wb_bytes <= - OBJIO_LSEG(pgio->pg_lseg)->layout.max_io_length; -} - -static const struct nfs_pageio_ops objio_pg_read_ops = { - .pg_init = pnfs_generic_pg_init_read, - .pg_test = objio_pg_test, - .pg_doio = pnfs_generic_pg_readpages, -}; - -static const struct nfs_pageio_ops objio_pg_write_ops = { - .pg_init = pnfs_generic_pg_init_write, - .pg_test = objio_pg_test, - .pg_doio = pnfs_generic_pg_writepages, -}; - -static struct pnfs_layoutdriver_type objlayout_type = { - .id = LAYOUT_OSD2_OBJECTS, - .name = "LAYOUT_OSD2_OBJECTS", - .flags = PNFS_LAYOUTRET_ON_SETATTR | - PNFS_LAYOUTRET_ON_ERROR, - - .alloc_layout_hdr = objlayout_alloc_layout_hdr, - .free_layout_hdr = objlayout_free_layout_hdr, - - .alloc_lseg = objlayout_alloc_lseg, - .free_lseg = objlayout_free_lseg, - - .read_pagelist = objlayout_read_pagelist, - .write_pagelist = objlayout_write_pagelist, - .pg_read_ops = &objio_pg_read_ops, - .pg_write_ops = &objio_pg_write_ops, - - .free_deviceid_node = objio_free_deviceid_node, - - .encode_layoutcommit = objlayout_encode_layoutcommit, - .encode_layoutreturn = objlayout_encode_layoutreturn, -}; - -MODULE_DESCRIPTION("pNFS Layout Driver for OSD2 objects"); -MODULE_AUTHOR("Benny Halevy <bhalevy@panasas.com>"); -MODULE_LICENSE("GPL"); - -static int __init -objlayout_init(void) -{ - int ret = pnfs_register_layoutdriver(&objlayout_type); - - if (ret) - printk(KERN_INFO - "NFS: %s: Registering OSD pNFS Layout Driver failed: error=%d\n", - __func__, ret); - else - printk(KERN_INFO "NFS: %s: Registered OSD pNFS Layout Driver\n", - __func__); - return ret; -} - -static void __exit -objlayout_exit(void) -{ - pnfs_unregister_layoutdriver(&objlayout_type); - printk(KERN_INFO "NFS: %s: Unregistered OSD pNFS Layout Driver\n", - __func__); -} - -MODULE_ALIAS("nfs-layouttype4-2"); - -module_init(objlayout_init); -module_exit(objlayout_exit); diff --git a/ANDROID_3.4.5/fs/nfs/objlayout/objlayout.c b/ANDROID_3.4.5/fs/nfs/objlayout/objlayout.c deleted file mode 100644 index 595c5fc2..00000000 --- a/ANDROID_3.4.5/fs/nfs/objlayout/objlayout.c +++ /dev/null @@ -1,785 +0,0 @@ -/* - * pNFS Objects layout driver high level definitions - * - * Copyright (C) 2007 Panasas Inc. [year of first publication] - * All rights reserved. - * - * Benny Halevy <bhalevy@panasas.com> - * Boaz Harrosh <bharrosh@panasas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * See the file COPYING included with this distribution for more details. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Panasas company nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <linux/kmod.h> -#include <linux/moduleparam.h> -#include <linux/ratelimit.h> -#include <scsi/osd_initiator.h> -#include "objlayout.h" - -#define NFSDBG_FACILITY NFSDBG_PNFS_LD -/* - * Create a objlayout layout structure for the given inode and return it. - */ -struct pnfs_layout_hdr * -objlayout_alloc_layout_hdr(struct inode *inode, gfp_t gfp_flags) -{ - struct objlayout *objlay; - - objlay = kzalloc(sizeof(struct objlayout), gfp_flags); - if (objlay) { - spin_lock_init(&objlay->lock); - INIT_LIST_HEAD(&objlay->err_list); - } - dprintk("%s: Return %p\n", __func__, objlay); - return &objlay->pnfs_layout; -} - -/* - * Free an objlayout layout structure - */ -void -objlayout_free_layout_hdr(struct pnfs_layout_hdr *lo) -{ - struct objlayout *objlay = OBJLAYOUT(lo); - - dprintk("%s: objlay %p\n", __func__, objlay); - - WARN_ON(!list_empty(&objlay->err_list)); - kfree(objlay); -} - -/* - * Unmarshall layout and store it in pnfslay. - */ -struct pnfs_layout_segment * -objlayout_alloc_lseg(struct pnfs_layout_hdr *pnfslay, - struct nfs4_layoutget_res *lgr, - gfp_t gfp_flags) -{ - int status = -ENOMEM; - struct xdr_stream stream; - struct xdr_buf buf = { - .pages = lgr->layoutp->pages, - .page_len = lgr->layoutp->len, - .buflen = lgr->layoutp->len, - .len = lgr->layoutp->len, - }; - struct page *scratch; - struct pnfs_layout_segment *lseg; - - dprintk("%s: Begin pnfslay %p\n", __func__, pnfslay); - - scratch = alloc_page(gfp_flags); - if (!scratch) - goto err_nofree; - - xdr_init_decode(&stream, &buf, NULL); - xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE); - - status = objio_alloc_lseg(&lseg, pnfslay, &lgr->range, &stream, gfp_flags); - if (unlikely(status)) { - dprintk("%s: objio_alloc_lseg Return err %d\n", __func__, - status); - goto err; - } - - __free_page(scratch); - - dprintk("%s: Return %p\n", __func__, lseg); - return lseg; - -err: - __free_page(scratch); -err_nofree: - dprintk("%s: Err Return=>%d\n", __func__, status); - return ERR_PTR(status); -} - -/* - * Free a layout segement - */ -void -objlayout_free_lseg(struct pnfs_layout_segment *lseg) -{ - dprintk("%s: freeing layout segment %p\n", __func__, lseg); - - if (unlikely(!lseg)) - return; - - objio_free_lseg(lseg); -} - -/* - * I/O Operations - */ -static inline u64 -end_offset(u64 start, u64 len) -{ - u64 end; - - end = start + len; - return end >= start ? end : NFS4_MAX_UINT64; -} - -/* last octet in a range */ -static inline u64 -last_byte_offset(u64 start, u64 len) -{ - u64 end; - - BUG_ON(!len); - end = start + len; - return end > start ? end - 1 : NFS4_MAX_UINT64; -} - -static void _fix_verify_io_params(struct pnfs_layout_segment *lseg, - struct page ***p_pages, unsigned *p_pgbase, - u64 offset, unsigned long count) -{ - u64 lseg_end_offset; - - BUG_ON(offset < lseg->pls_range.offset); - lseg_end_offset = end_offset(lseg->pls_range.offset, - lseg->pls_range.length); - BUG_ON(offset >= lseg_end_offset); - WARN_ON(offset + count > lseg_end_offset); - - if (*p_pgbase > PAGE_SIZE) { - dprintk("%s: pgbase(0x%x) > PAGE_SIZE\n", __func__, *p_pgbase); - *p_pages += *p_pgbase >> PAGE_SHIFT; - *p_pgbase &= ~PAGE_MASK; - } -} - -/* - * I/O done common code - */ -static void -objlayout_iodone(struct objlayout_io_res *oir) -{ - if (likely(oir->status >= 0)) { - objio_free_result(oir); - } else { - struct objlayout *objlay = oir->objlay; - - spin_lock(&objlay->lock); - objlay->delta_space_valid = OBJ_DSU_INVALID; - list_add(&objlay->err_list, &oir->err_list); - spin_unlock(&objlay->lock); - } -} - -/* - * objlayout_io_set_result - Set an osd_error code on a specific osd comp. - * - * The @index component IO failed (error returned from target). Register - * the error for later reporting at layout-return. - */ -void -objlayout_io_set_result(struct objlayout_io_res *oir, unsigned index, - struct pnfs_osd_objid *pooid, int osd_error, - u64 offset, u64 length, bool is_write) -{ - struct pnfs_osd_ioerr *ioerr = &oir->ioerrs[index]; - - BUG_ON(index >= oir->num_comps); - if (osd_error) { - ioerr->oer_component = *pooid; - ioerr->oer_comp_offset = offset; - ioerr->oer_comp_length = length; - ioerr->oer_iswrite = is_write; - ioerr->oer_errno = osd_error; - - dprintk("%s: err[%d]: errno=%d is_write=%d dev(%llx:%llx) " - "par=0x%llx obj=0x%llx offset=0x%llx length=0x%llx\n", - __func__, index, ioerr->oer_errno, - ioerr->oer_iswrite, - _DEVID_LO(&ioerr->oer_component.oid_device_id), - _DEVID_HI(&ioerr->oer_component.oid_device_id), - ioerr->oer_component.oid_partition_id, - ioerr->oer_component.oid_object_id, - ioerr->oer_comp_offset, - ioerr->oer_comp_length); - } else { - /* User need not call if no error is reported */ - ioerr->oer_errno = 0; - } -} - -/* Function scheduled on rpc workqueue to call ->nfs_readlist_complete(). - * This is because the osd completion is called with ints-off from - * the block layer - */ -static void _rpc_read_complete(struct work_struct *work) -{ - struct rpc_task *task; - struct nfs_read_data *rdata; - - dprintk("%s enter\n", __func__); - task = container_of(work, struct rpc_task, u.tk_work); - rdata = container_of(task, struct nfs_read_data, task); - - pnfs_ld_read_done(rdata); -} - -void -objlayout_read_done(struct objlayout_io_res *oir, ssize_t status, bool sync) -{ - struct nfs_read_data *rdata = oir->rpcdata; - - oir->status = rdata->task.tk_status = status; - if (status >= 0) - rdata->res.count = status; - else - rdata->pnfs_error = status; - objlayout_iodone(oir); - /* must not use oir after this point */ - - dprintk("%s: Return status=%zd eof=%d sync=%d\n", __func__, - status, rdata->res.eof, sync); - - if (sync) - pnfs_ld_read_done(rdata); - else { - INIT_WORK(&rdata->task.u.tk_work, _rpc_read_complete); - schedule_work(&rdata->task.u.tk_work); - } -} - -/* - * Perform sync or async reads. - */ -enum pnfs_try_status -objlayout_read_pagelist(struct nfs_read_data *rdata) -{ - loff_t offset = rdata->args.offset; - size_t count = rdata->args.count; - int err; - loff_t eof; - - eof = i_size_read(rdata->inode); - if (unlikely(offset + count > eof)) { - if (offset >= eof) { - err = 0; - rdata->res.count = 0; - rdata->res.eof = 1; - /*FIXME: do we need to call pnfs_ld_read_done() */ - goto out; - } - count = eof - offset; - } - - rdata->res.eof = (offset + count) >= eof; - _fix_verify_io_params(rdata->lseg, &rdata->args.pages, - &rdata->args.pgbase, - rdata->args.offset, rdata->args.count); - - dprintk("%s: inode(%lx) offset 0x%llx count 0x%Zx eof=%d\n", - __func__, rdata->inode->i_ino, offset, count, rdata->res.eof); - - err = objio_read_pagelist(rdata); - out: - if (unlikely(err)) { - rdata->pnfs_error = err; - dprintk("%s: Returned Error %d\n", __func__, err); - return PNFS_NOT_ATTEMPTED; - } - return PNFS_ATTEMPTED; -} - -/* Function scheduled on rpc workqueue to call ->nfs_writelist_complete(). - * This is because the osd completion is called with ints-off from - * the block layer - */ -static void _rpc_write_complete(struct work_struct *work) -{ - struct rpc_task *task; - struct nfs_write_data *wdata; - - dprintk("%s enter\n", __func__); - task = container_of(work, struct rpc_task, u.tk_work); - wdata = container_of(task, struct nfs_write_data, task); - - pnfs_ld_write_done(wdata); -} - -void -objlayout_write_done(struct objlayout_io_res *oir, ssize_t status, bool sync) -{ - struct nfs_write_data *wdata = oir->rpcdata; - - oir->status = wdata->task.tk_status = status; - if (status >= 0) { - wdata->res.count = status; - wdata->verf.committed = oir->committed; - } else { - wdata->pnfs_error = status; - } - objlayout_iodone(oir); - /* must not use oir after this point */ - - dprintk("%s: Return status %zd committed %d sync=%d\n", __func__, - status, wdata->verf.committed, sync); - - if (sync) - pnfs_ld_write_done(wdata); - else { - INIT_WORK(&wdata->task.u.tk_work, _rpc_write_complete); - schedule_work(&wdata->task.u.tk_work); - } -} - -/* - * Perform sync or async writes. - */ -enum pnfs_try_status -objlayout_write_pagelist(struct nfs_write_data *wdata, - int how) -{ - int err; - - _fix_verify_io_params(wdata->lseg, &wdata->args.pages, - &wdata->args.pgbase, - wdata->args.offset, wdata->args.count); - - err = objio_write_pagelist(wdata, how); - if (unlikely(err)) { - wdata->pnfs_error = err; - dprintk("%s: Returned Error %d\n", __func__, err); - return PNFS_NOT_ATTEMPTED; - } - return PNFS_ATTEMPTED; -} - -void -objlayout_encode_layoutcommit(struct pnfs_layout_hdr *pnfslay, - struct xdr_stream *xdr, - const struct nfs4_layoutcommit_args *args) -{ - struct objlayout *objlay = OBJLAYOUT(pnfslay); - struct pnfs_osd_layoutupdate lou; - __be32 *start; - - dprintk("%s: Begin\n", __func__); - - spin_lock(&objlay->lock); - lou.dsu_valid = (objlay->delta_space_valid == OBJ_DSU_VALID); - lou.dsu_delta = objlay->delta_space_used; - objlay->delta_space_used = 0; - objlay->delta_space_valid = OBJ_DSU_INIT; - lou.olu_ioerr_flag = !list_empty(&objlay->err_list); - spin_unlock(&objlay->lock); - - start = xdr_reserve_space(xdr, 4); - - BUG_ON(pnfs_osd_xdr_encode_layoutupdate(xdr, &lou)); - - *start = cpu_to_be32((xdr->p - start - 1) * 4); - - dprintk("%s: Return delta_space_used %lld err %d\n", __func__, - lou.dsu_delta, lou.olu_ioerr_flag); -} - -static int -err_prio(u32 oer_errno) -{ - switch (oer_errno) { - case 0: - return 0; - - case PNFS_OSD_ERR_RESOURCE: - return OSD_ERR_PRI_RESOURCE; - case PNFS_OSD_ERR_BAD_CRED: - return OSD_ERR_PRI_BAD_CRED; - case PNFS_OSD_ERR_NO_ACCESS: - return OSD_ERR_PRI_NO_ACCESS; - case PNFS_OSD_ERR_UNREACHABLE: - return OSD_ERR_PRI_UNREACHABLE; - case PNFS_OSD_ERR_NOT_FOUND: - return OSD_ERR_PRI_NOT_FOUND; - case PNFS_OSD_ERR_NO_SPACE: - return OSD_ERR_PRI_NO_SPACE; - default: - WARN_ON(1); - /* fallthrough */ - case PNFS_OSD_ERR_EIO: - return OSD_ERR_PRI_EIO; - } -} - -static void -merge_ioerr(struct pnfs_osd_ioerr *dest_err, - const struct pnfs_osd_ioerr *src_err) -{ - u64 dest_end, src_end; - - if (!dest_err->oer_errno) { - *dest_err = *src_err; - /* accumulated device must be blank */ - memset(&dest_err->oer_component.oid_device_id, 0, - sizeof(dest_err->oer_component.oid_device_id)); - - return; - } - - if (dest_err->oer_component.oid_partition_id != - src_err->oer_component.oid_partition_id) - dest_err->oer_component.oid_partition_id = 0; - - if (dest_err->oer_component.oid_object_id != - src_err->oer_component.oid_object_id) - dest_err->oer_component.oid_object_id = 0; - - if (dest_err->oer_comp_offset > src_err->oer_comp_offset) - dest_err->oer_comp_offset = src_err->oer_comp_offset; - - dest_end = end_offset(dest_err->oer_comp_offset, - dest_err->oer_comp_length); - src_end = end_offset(src_err->oer_comp_offset, - src_err->oer_comp_length); - if (dest_end < src_end) - dest_end = src_end; - - dest_err->oer_comp_length = dest_end - dest_err->oer_comp_offset; - - if ((src_err->oer_iswrite == dest_err->oer_iswrite) && - (err_prio(src_err->oer_errno) > err_prio(dest_err->oer_errno))) { - dest_err->oer_errno = src_err->oer_errno; - } else if (src_err->oer_iswrite) { - dest_err->oer_iswrite = true; - dest_err->oer_errno = src_err->oer_errno; - } -} - -static void -encode_accumulated_error(struct objlayout *objlay, __be32 *p) -{ - struct objlayout_io_res *oir, *tmp; - struct pnfs_osd_ioerr accumulated_err = {.oer_errno = 0}; - - list_for_each_entry_safe(oir, tmp, &objlay->err_list, err_list) { - unsigned i; - - for (i = 0; i < oir->num_comps; i++) { - struct pnfs_osd_ioerr *ioerr = &oir->ioerrs[i]; - - if (!ioerr->oer_errno) - continue; - - printk(KERN_ERR "NFS: %s: err[%d]: errno=%d " - "is_write=%d dev(%llx:%llx) par=0x%llx " - "obj=0x%llx offset=0x%llx length=0x%llx\n", - __func__, i, ioerr->oer_errno, - ioerr->oer_iswrite, - _DEVID_LO(&ioerr->oer_component.oid_device_id), - _DEVID_HI(&ioerr->oer_component.oid_device_id), - ioerr->oer_component.oid_partition_id, - ioerr->oer_component.oid_object_id, - ioerr->oer_comp_offset, - ioerr->oer_comp_length); - - merge_ioerr(&accumulated_err, ioerr); - } - list_del(&oir->err_list); - objio_free_result(oir); - } - - pnfs_osd_xdr_encode_ioerr(p, &accumulated_err); -} - -void -objlayout_encode_layoutreturn(struct pnfs_layout_hdr *pnfslay, - struct xdr_stream *xdr, - const struct nfs4_layoutreturn_args *args) -{ - struct objlayout *objlay = OBJLAYOUT(pnfslay); - struct objlayout_io_res *oir, *tmp; - __be32 *start; - - dprintk("%s: Begin\n", __func__); - start = xdr_reserve_space(xdr, 4); - BUG_ON(!start); - - spin_lock(&objlay->lock); - - list_for_each_entry_safe(oir, tmp, &objlay->err_list, err_list) { - __be32 *last_xdr = NULL, *p; - unsigned i; - int res = 0; - - for (i = 0; i < oir->num_comps; i++) { - struct pnfs_osd_ioerr *ioerr = &oir->ioerrs[i]; - - if (!ioerr->oer_errno) - continue; - - dprintk("%s: err[%d]: errno=%d is_write=%d " - "dev(%llx:%llx) par=0x%llx obj=0x%llx " - "offset=0x%llx length=0x%llx\n", - __func__, i, ioerr->oer_errno, - ioerr->oer_iswrite, - _DEVID_LO(&ioerr->oer_component.oid_device_id), - _DEVID_HI(&ioerr->oer_component.oid_device_id), - ioerr->oer_component.oid_partition_id, - ioerr->oer_component.oid_object_id, - ioerr->oer_comp_offset, - ioerr->oer_comp_length); - - p = pnfs_osd_xdr_ioerr_reserve_space(xdr); - if (unlikely(!p)) { - res = -E2BIG; - break; /* accumulated_error */ - } - - last_xdr = p; - pnfs_osd_xdr_encode_ioerr(p, &oir->ioerrs[i]); - } - - /* TODO: use xdr_write_pages */ - if (unlikely(res)) { - /* no space for even one error descriptor */ - BUG_ON(!last_xdr); - - /* we've encountered a situation with lots and lots of - * errors and no space to encode them all. Use the last - * available slot to report the union of all the - * remaining errors. - */ - encode_accumulated_error(objlay, last_xdr); - goto loop_done; - } - list_del(&oir->err_list); - objio_free_result(oir); - } -loop_done: - spin_unlock(&objlay->lock); - - *start = cpu_to_be32((xdr->p - start - 1) * 4); - dprintk("%s: Return\n", __func__); -} - - -/* - * Get Device Info API for io engines - */ -struct objlayout_deviceinfo { - struct page *page; - struct pnfs_osd_deviceaddr da; /* This must be last */ -}; - -/* Initialize and call nfs_getdeviceinfo, then decode and return a - * "struct pnfs_osd_deviceaddr *" Eventually objlayout_put_deviceinfo() - * should be called. - */ -int objlayout_get_deviceinfo(struct pnfs_layout_hdr *pnfslay, - struct nfs4_deviceid *d_id, struct pnfs_osd_deviceaddr **deviceaddr, - gfp_t gfp_flags) -{ - struct objlayout_deviceinfo *odi; - struct pnfs_device pd; - struct page *page, **pages; - u32 *p; - int err; - - page = alloc_page(gfp_flags); - if (!page) - return -ENOMEM; - - pages = &page; - pd.pages = pages; - - memcpy(&pd.dev_id, d_id, sizeof(*d_id)); - pd.layout_type = LAYOUT_OSD2_OBJECTS; - pd.pages = &page; - pd.pgbase = 0; - pd.pglen = PAGE_SIZE; - pd.mincount = 0; - - err = nfs4_proc_getdeviceinfo(NFS_SERVER(pnfslay->plh_inode), &pd); - dprintk("%s nfs_getdeviceinfo returned %d\n", __func__, err); - if (err) - goto err_out; - - p = page_address(page); - odi = kzalloc(sizeof(*odi), gfp_flags); - if (!odi) { - err = -ENOMEM; - goto err_out; - } - pnfs_osd_xdr_decode_deviceaddr(&odi->da, p); - odi->page = page; - *deviceaddr = &odi->da; - return 0; - -err_out: - __free_page(page); - return err; -} - -void objlayout_put_deviceinfo(struct pnfs_osd_deviceaddr *deviceaddr) -{ - struct objlayout_deviceinfo *odi = container_of(deviceaddr, - struct objlayout_deviceinfo, - da); - - __free_page(odi->page); - kfree(odi); -} - -enum { - OBJLAYOUT_MAX_URI_LEN = 256, OBJLAYOUT_MAX_OSDNAME_LEN = 64, - OBJLAYOUT_MAX_SYSID_HEX_LEN = OSD_SYSTEMID_LEN * 2 + 1, - OSD_LOGIN_UPCALL_PATHLEN = 256 -}; - -static char osd_login_prog[OSD_LOGIN_UPCALL_PATHLEN] = "/sbin/osd_login"; - -module_param_string(osd_login_prog, osd_login_prog, sizeof(osd_login_prog), - 0600); -MODULE_PARM_DESC(osd_login_prog, "Path to the osd_login upcall program"); - -struct __auto_login { - char uri[OBJLAYOUT_MAX_URI_LEN]; - char osdname[OBJLAYOUT_MAX_OSDNAME_LEN]; - char systemid_hex[OBJLAYOUT_MAX_SYSID_HEX_LEN]; -}; - -static int __objlayout_upcall(struct __auto_login *login) -{ - static char *envp[] = { "HOME=/", - "TERM=linux", - "PATH=/sbin:/usr/sbin:/bin:/usr/bin", - NULL - }; - char *argv[8]; - int ret; - - if (unlikely(!osd_login_prog[0])) { - dprintk("%s: osd_login_prog is disabled\n", __func__); - return -EACCES; - } - - dprintk("%s uri: %s\n", __func__, login->uri); - dprintk("%s osdname %s\n", __func__, login->osdname); - dprintk("%s systemid_hex %s\n", __func__, login->systemid_hex); - - argv[0] = (char *)osd_login_prog; - argv[1] = "-u"; - argv[2] = login->uri; - argv[3] = "-o"; - argv[4] = login->osdname; - argv[5] = "-s"; - argv[6] = login->systemid_hex; - argv[7] = NULL; - - ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); - /* - * Disable the upcall mechanism if we're getting an ENOENT or - * EACCES error. The admin can re-enable it on the fly by using - * sysfs to set the objlayoutdriver.osd_login_prog module parameter once - * the problem has been fixed. - */ - if (ret == -ENOENT || ret == -EACCES) { - printk(KERN_ERR "PNFS-OBJ: %s was not found please set " - "objlayoutdriver.osd_login_prog kernel parameter!\n", - osd_login_prog); - osd_login_prog[0] = '\0'; - } - dprintk("%s %s return value: %d\n", __func__, osd_login_prog, ret); - - return ret; -} - -/* Assume dest is all zeros */ -static void __copy_nfsS_and_zero_terminate(struct nfs4_string s, - char *dest, int max_len, - const char *var_name) -{ - if (!s.len) - return; - - if (s.len >= max_len) { - pr_warn_ratelimited( - "objlayout_autologin: %s: s.len(%d) >= max_len(%d)", - var_name, s.len, max_len); - s.len = max_len - 1; /* space for null terminator */ - } - - memcpy(dest, s.data, s.len); -} - -/* Assume sysid is all zeros */ -static void _sysid_2_hex(struct nfs4_string s, - char sysid[OBJLAYOUT_MAX_SYSID_HEX_LEN]) -{ - int i; - char *cur; - - if (!s.len) - return; - - if (s.len != OSD_SYSTEMID_LEN) { - pr_warn_ratelimited( - "objlayout_autologin: systemid_len(%d) != OSD_SYSTEMID_LEN", - s.len); - if (s.len > OSD_SYSTEMID_LEN) - s.len = OSD_SYSTEMID_LEN; - } - - cur = sysid; - for (i = 0; i < s.len; i++) - cur = hex_byte_pack(cur, s.data[i]); -} - -int objlayout_autologin(struct pnfs_osd_deviceaddr *deviceaddr) -{ - int rc; - struct __auto_login login; - - if (!deviceaddr->oda_targetaddr.ota_netaddr.r_addr.len) - return -ENODEV; - - memset(&login, 0, sizeof(login)); - __copy_nfsS_and_zero_terminate( - deviceaddr->oda_targetaddr.ota_netaddr.r_addr, - login.uri, sizeof(login.uri), "URI"); - - __copy_nfsS_and_zero_terminate( - deviceaddr->oda_osdname, - login.osdname, sizeof(login.osdname), "OSDNAME"); - - _sysid_2_hex(deviceaddr->oda_systemid, login.systemid_hex); - - rc = __objlayout_upcall(&login); - if (rc > 0) /* script returns positive values */ - rc = -ENODEV; - - return rc; -} diff --git a/ANDROID_3.4.5/fs/nfs/objlayout/objlayout.h b/ANDROID_3.4.5/fs/nfs/objlayout/objlayout.h deleted file mode 100644 index 880ba086..00000000 --- a/ANDROID_3.4.5/fs/nfs/objlayout/objlayout.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Data types and function declerations for interfacing with the - * pNFS standard object layout driver. - * - * Copyright (C) 2007 Panasas Inc. [year of first publication] - * All rights reserved. - * - * Benny Halevy <bhalevy@panasas.com> - * Boaz Harrosh <bharrosh@panasas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * See the file COPYING included with this distribution for more details. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Panasas company nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _OBJLAYOUT_H -#define _OBJLAYOUT_H - -#include <linux/nfs_fs.h> -#include <linux/pnfs_osd_xdr.h> -#include "../pnfs.h" - -/* - * per-inode layout - */ -struct objlayout { - struct pnfs_layout_hdr pnfs_layout; - - /* for layout_commit */ - enum osd_delta_space_valid_enum { - OBJ_DSU_INIT = 0, - OBJ_DSU_VALID, - OBJ_DSU_INVALID, - } delta_space_valid; - s64 delta_space_used; /* consumed by write ops */ - - /* for layout_return */ - spinlock_t lock; - struct list_head err_list; -}; - -static inline struct objlayout * -OBJLAYOUT(struct pnfs_layout_hdr *lo) -{ - return container_of(lo, struct objlayout, pnfs_layout); -} - -/* - * per-I/O operation state - * embedded in objects provider io_state data structure - */ -struct objlayout_io_res { - struct objlayout *objlay; - - void *rpcdata; - int status; /* res */ - int committed; /* res */ - - /* Error reporting (layout_return) */ - struct list_head err_list; - unsigned num_comps; - /* Pointer to array of error descriptors of size num_comps. - * It should contain as many entries as devices in the osd_layout - * that participate in the I/O. It is up to the io_engine to allocate - * needed space and set num_comps. - */ - struct pnfs_osd_ioerr *ioerrs; -}; - -static inline -void objlayout_init_ioerrs(struct objlayout_io_res *oir, unsigned num_comps, - struct pnfs_osd_ioerr *ioerrs, void *rpcdata, - struct pnfs_layout_hdr *pnfs_layout_type) -{ - oir->objlay = OBJLAYOUT(pnfs_layout_type); - oir->rpcdata = rpcdata; - INIT_LIST_HEAD(&oir->err_list); - oir->num_comps = num_comps; - oir->ioerrs = ioerrs; -} - -/* - * Raid engine I/O API - */ -extern int objio_alloc_lseg(struct pnfs_layout_segment **outp, - struct pnfs_layout_hdr *pnfslay, - struct pnfs_layout_range *range, - struct xdr_stream *xdr, - gfp_t gfp_flags); -extern void objio_free_lseg(struct pnfs_layout_segment *lseg); - -/* objio_free_result will free these @oir structs recieved from - * objlayout_{read,write}_done - */ -extern void objio_free_result(struct objlayout_io_res *oir); - -extern int objio_read_pagelist(struct nfs_read_data *rdata); -extern int objio_write_pagelist(struct nfs_write_data *wdata, int how); - -/* - * callback API - */ -extern void objlayout_io_set_result(struct objlayout_io_res *oir, - unsigned index, struct pnfs_osd_objid *pooid, - int osd_error, u64 offset, u64 length, bool is_write); - -static inline void -objlayout_add_delta_space_used(struct objlayout *objlay, s64 space_used) -{ - /* If one of the I/Os errored out and the delta_space_used was - * invalid we render the complete report as invalid. Protocol mandate - * the DSU be accurate or not reported. - */ - spin_lock(&objlay->lock); - if (objlay->delta_space_valid != OBJ_DSU_INVALID) { - objlay->delta_space_valid = OBJ_DSU_VALID; - objlay->delta_space_used += space_used; - } - spin_unlock(&objlay->lock); -} - -extern void objlayout_read_done(struct objlayout_io_res *oir, - ssize_t status, bool sync); -extern void objlayout_write_done(struct objlayout_io_res *oir, - ssize_t status, bool sync); - -extern int objlayout_get_deviceinfo(struct pnfs_layout_hdr *pnfslay, - struct nfs4_deviceid *d_id, struct pnfs_osd_deviceaddr **deviceaddr, - gfp_t gfp_flags); -extern void objlayout_put_deviceinfo(struct pnfs_osd_deviceaddr *deviceaddr); - -/* - * exported generic objects function vectors - */ - -extern struct pnfs_layout_hdr *objlayout_alloc_layout_hdr(struct inode *, gfp_t gfp_flags); -extern void objlayout_free_layout_hdr(struct pnfs_layout_hdr *); - -extern struct pnfs_layout_segment *objlayout_alloc_lseg( - struct pnfs_layout_hdr *, - struct nfs4_layoutget_res *, - gfp_t gfp_flags); -extern void objlayout_free_lseg(struct pnfs_layout_segment *); - -extern enum pnfs_try_status objlayout_read_pagelist( - struct nfs_read_data *); - -extern enum pnfs_try_status objlayout_write_pagelist( - struct nfs_write_data *, - int how); - -extern void objlayout_encode_layoutcommit( - struct pnfs_layout_hdr *, - struct xdr_stream *, - const struct nfs4_layoutcommit_args *); - -extern void objlayout_encode_layoutreturn( - struct pnfs_layout_hdr *, - struct xdr_stream *, - const struct nfs4_layoutreturn_args *); - -extern int objlayout_autologin(struct pnfs_osd_deviceaddr *deviceaddr); - -#endif /* _OBJLAYOUT_H */ diff --git a/ANDROID_3.4.5/fs/nfs/objlayout/pnfs_osd_xdr_cli.c b/ANDROID_3.4.5/fs/nfs/objlayout/pnfs_osd_xdr_cli.c deleted file mode 100644 index b3918f7a..00000000 --- a/ANDROID_3.4.5/fs/nfs/objlayout/pnfs_osd_xdr_cli.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Object-Based pNFS Layout XDR layer - * - * Copyright (C) 2007 Panasas Inc. [year of first publication] - * All rights reserved. - * - * Benny Halevy <bhalevy@panasas.com> - * Boaz Harrosh <bharrosh@panasas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * See the file COPYING included with this distribution for more details. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Panasas company nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <linux/pnfs_osd_xdr.h> - -#define NFSDBG_FACILITY NFSDBG_PNFS_LD - -/* - * The following implementation is based on RFC5664 - */ - -/* - * struct pnfs_osd_objid { - * struct nfs4_deviceid oid_device_id; - * u64 oid_partition_id; - * u64 oid_object_id; - * }; // xdr size 32 bytes - */ -static __be32 * -_osd_xdr_decode_objid(__be32 *p, struct pnfs_osd_objid *objid) -{ - p = xdr_decode_opaque_fixed(p, objid->oid_device_id.data, - sizeof(objid->oid_device_id.data)); - - p = xdr_decode_hyper(p, &objid->oid_partition_id); - p = xdr_decode_hyper(p, &objid->oid_object_id); - return p; -} -/* - * struct pnfs_osd_opaque_cred { - * u32 cred_len; - * void *cred; - * }; // xdr size [variable] - * The return pointers are from the xdr buffer - */ -static int -_osd_xdr_decode_opaque_cred(struct pnfs_osd_opaque_cred *opaque_cred, - struct xdr_stream *xdr) -{ - __be32 *p = xdr_inline_decode(xdr, 1); - - if (!p) - return -EINVAL; - - opaque_cred->cred_len = be32_to_cpu(*p++); - - p = xdr_inline_decode(xdr, opaque_cred->cred_len); - if (!p) - return -EINVAL; - - opaque_cred->cred = p; - return 0; -} - -/* - * struct pnfs_osd_object_cred { - * struct pnfs_osd_objid oc_object_id; - * u32 oc_osd_version; - * u32 oc_cap_key_sec; - * struct pnfs_osd_opaque_cred oc_cap_key - * struct pnfs_osd_opaque_cred oc_cap; - * }; // xdr size 32 + 4 + 4 + [variable] + [variable] - */ -static int -_osd_xdr_decode_object_cred(struct pnfs_osd_object_cred *comp, - struct xdr_stream *xdr) -{ - __be32 *p = xdr_inline_decode(xdr, 32 + 4 + 4); - int ret; - - if (!p) - return -EIO; - - p = _osd_xdr_decode_objid(p, &comp->oc_object_id); - comp->oc_osd_version = be32_to_cpup(p++); - comp->oc_cap_key_sec = be32_to_cpup(p); - - ret = _osd_xdr_decode_opaque_cred(&comp->oc_cap_key, xdr); - if (unlikely(ret)) - return ret; - - ret = _osd_xdr_decode_opaque_cred(&comp->oc_cap, xdr); - return ret; -} - -/* - * struct pnfs_osd_data_map { - * u32 odm_num_comps; - * u64 odm_stripe_unit; - * u32 odm_group_width; - * u32 odm_group_depth; - * u32 odm_mirror_cnt; - * u32 odm_raid_algorithm; - * }; // xdr size 4 + 8 + 4 + 4 + 4 + 4 - */ -static inline int -_osd_data_map_xdr_sz(void) -{ - return 4 + 8 + 4 + 4 + 4 + 4; -} - -static __be32 * -_osd_xdr_decode_data_map(__be32 *p, struct pnfs_osd_data_map *data_map) -{ - data_map->odm_num_comps = be32_to_cpup(p++); - p = xdr_decode_hyper(p, &data_map->odm_stripe_unit); - data_map->odm_group_width = be32_to_cpup(p++); - data_map->odm_group_depth = be32_to_cpup(p++); - data_map->odm_mirror_cnt = be32_to_cpup(p++); - data_map->odm_raid_algorithm = be32_to_cpup(p++); - dprintk("%s: odm_num_comps=%u odm_stripe_unit=%llu odm_group_width=%u " - "odm_group_depth=%u odm_mirror_cnt=%u odm_raid_algorithm=%u\n", - __func__, - data_map->odm_num_comps, - (unsigned long long)data_map->odm_stripe_unit, - data_map->odm_group_width, - data_map->odm_group_depth, - data_map->odm_mirror_cnt, - data_map->odm_raid_algorithm); - return p; -} - -int pnfs_osd_xdr_decode_layout_map(struct pnfs_osd_layout *layout, - struct pnfs_osd_xdr_decode_layout_iter *iter, struct xdr_stream *xdr) -{ - __be32 *p; - - memset(iter, 0, sizeof(*iter)); - - p = xdr_inline_decode(xdr, _osd_data_map_xdr_sz() + 4 + 4); - if (unlikely(!p)) - return -EINVAL; - - p = _osd_xdr_decode_data_map(p, &layout->olo_map); - layout->olo_comps_index = be32_to_cpup(p++); - layout->olo_num_comps = be32_to_cpup(p++); - dprintk("%s: olo_comps_index=%d olo_num_comps=%d\n", __func__, - layout->olo_comps_index, layout->olo_num_comps); - - iter->total_comps = layout->olo_num_comps; - return 0; -} - -bool pnfs_osd_xdr_decode_layout_comp(struct pnfs_osd_object_cred *comp, - struct pnfs_osd_xdr_decode_layout_iter *iter, struct xdr_stream *xdr, - int *err) -{ - BUG_ON(iter->decoded_comps > iter->total_comps); - if (iter->decoded_comps == iter->total_comps) - return false; - - *err = _osd_xdr_decode_object_cred(comp, xdr); - if (unlikely(*err)) { - dprintk("%s: _osd_xdr_decode_object_cred=>%d decoded_comps=%d " - "total_comps=%d\n", __func__, *err, - iter->decoded_comps, iter->total_comps); - return false; /* stop the loop */ - } - dprintk("%s: dev(%llx:%llx) par=0x%llx obj=0x%llx " - "key_len=%u cap_len=%u\n", - __func__, - _DEVID_LO(&comp->oc_object_id.oid_device_id), - _DEVID_HI(&comp->oc_object_id.oid_device_id), - comp->oc_object_id.oid_partition_id, - comp->oc_object_id.oid_object_id, - comp->oc_cap_key.cred_len, comp->oc_cap.cred_len); - - iter->decoded_comps++; - return true; -} - -/* - * Get Device Information Decoding - * - * Note: since Device Information is currently done synchronously, all - * variable strings fields are left inside the rpc buffer and are only - * pointed to by the pnfs_osd_deviceaddr members. So the read buffer - * should not be freed while the returned information is in use. - */ -/* - *struct nfs4_string { - * unsigned int len; - * char *data; - *}; // size [variable] - * NOTE: Returned string points to inside the XDR buffer - */ -static __be32 * -__read_u8_opaque(__be32 *p, struct nfs4_string *str) -{ - str->len = be32_to_cpup(p++); - str->data = (char *)p; - - p += XDR_QUADLEN(str->len); - return p; -} - -/* - * struct pnfs_osd_targetid { - * u32 oti_type; - * struct nfs4_string oti_scsi_device_id; - * };// size 4 + [variable] - */ -static __be32 * -__read_targetid(__be32 *p, struct pnfs_osd_targetid* targetid) -{ - u32 oti_type; - - oti_type = be32_to_cpup(p++); - targetid->oti_type = oti_type; - - switch (oti_type) { - case OBJ_TARGET_SCSI_NAME: - case OBJ_TARGET_SCSI_DEVICE_ID: - p = __read_u8_opaque(p, &targetid->oti_scsi_device_id); - } - - return p; -} - -/* - * struct pnfs_osd_net_addr { - * struct nfs4_string r_netid; - * struct nfs4_string r_addr; - * }; - */ -static __be32 * -__read_net_addr(__be32 *p, struct pnfs_osd_net_addr* netaddr) -{ - p = __read_u8_opaque(p, &netaddr->r_netid); - p = __read_u8_opaque(p, &netaddr->r_addr); - - return p; -} - -/* - * struct pnfs_osd_targetaddr { - * u32 ota_available; - * struct pnfs_osd_net_addr ota_netaddr; - * }; - */ -static __be32 * -__read_targetaddr(__be32 *p, struct pnfs_osd_targetaddr *targetaddr) -{ - u32 ota_available; - - ota_available = be32_to_cpup(p++); - targetaddr->ota_available = ota_available; - - if (ota_available) - p = __read_net_addr(p, &targetaddr->ota_netaddr); - - - return p; -} - -/* - * struct pnfs_osd_deviceaddr { - * struct pnfs_osd_targetid oda_targetid; - * struct pnfs_osd_targetaddr oda_targetaddr; - * u8 oda_lun[8]; - * struct nfs4_string oda_systemid; - * struct pnfs_osd_object_cred oda_root_obj_cred; - * struct nfs4_string oda_osdname; - * }; - */ - -/* We need this version for the pnfs_osd_xdr_decode_deviceaddr which does - * not have an xdr_stream - */ -static __be32 * -__read_opaque_cred(__be32 *p, - struct pnfs_osd_opaque_cred *opaque_cred) -{ - opaque_cred->cred_len = be32_to_cpu(*p++); - opaque_cred->cred = p; - return p + XDR_QUADLEN(opaque_cred->cred_len); -} - -static __be32 * -__read_object_cred(__be32 *p, struct pnfs_osd_object_cred *comp) -{ - p = _osd_xdr_decode_objid(p, &comp->oc_object_id); - comp->oc_osd_version = be32_to_cpup(p++); - comp->oc_cap_key_sec = be32_to_cpup(p++); - - p = __read_opaque_cred(p, &comp->oc_cap_key); - p = __read_opaque_cred(p, &comp->oc_cap); - return p; -} - -void pnfs_osd_xdr_decode_deviceaddr( - struct pnfs_osd_deviceaddr *deviceaddr, __be32 *p) -{ - p = __read_targetid(p, &deviceaddr->oda_targetid); - - p = __read_targetaddr(p, &deviceaddr->oda_targetaddr); - - p = xdr_decode_opaque_fixed(p, deviceaddr->oda_lun, - sizeof(deviceaddr->oda_lun)); - - p = __read_u8_opaque(p, &deviceaddr->oda_systemid); - - p = __read_object_cred(p, &deviceaddr->oda_root_obj_cred); - - p = __read_u8_opaque(p, &deviceaddr->oda_osdname); - - /* libosd likes this terminated in dbg. It's last, so no problems */ - deviceaddr->oda_osdname.data[deviceaddr->oda_osdname.len] = 0; -} - -/* - * struct pnfs_osd_layoutupdate { - * u32 dsu_valid; - * s64 dsu_delta; - * u32 olu_ioerr_flag; - * }; xdr size 4 + 8 + 4 - */ -int -pnfs_osd_xdr_encode_layoutupdate(struct xdr_stream *xdr, - struct pnfs_osd_layoutupdate *lou) -{ - __be32 *p = xdr_reserve_space(xdr, 4 + 8 + 4); - - if (!p) - return -E2BIG; - - *p++ = cpu_to_be32(lou->dsu_valid); - if (lou->dsu_valid) - p = xdr_encode_hyper(p, lou->dsu_delta); - *p++ = cpu_to_be32(lou->olu_ioerr_flag); - return 0; -} - -/* - * struct pnfs_osd_objid { - * struct nfs4_deviceid oid_device_id; - * u64 oid_partition_id; - * u64 oid_object_id; - * }; // xdr size 32 bytes - */ -static inline __be32 * -pnfs_osd_xdr_encode_objid(__be32 *p, struct pnfs_osd_objid *object_id) -{ - p = xdr_encode_opaque_fixed(p, &object_id->oid_device_id.data, - sizeof(object_id->oid_device_id.data)); - p = xdr_encode_hyper(p, object_id->oid_partition_id); - p = xdr_encode_hyper(p, object_id->oid_object_id); - - return p; -} - -/* - * struct pnfs_osd_ioerr { - * struct pnfs_osd_objid oer_component; - * u64 oer_comp_offset; - * u64 oer_comp_length; - * u32 oer_iswrite; - * u32 oer_errno; - * }; // xdr size 32 + 24 bytes - */ -void pnfs_osd_xdr_encode_ioerr(__be32 *p, struct pnfs_osd_ioerr *ioerr) -{ - p = pnfs_osd_xdr_encode_objid(p, &ioerr->oer_component); - p = xdr_encode_hyper(p, ioerr->oer_comp_offset); - p = xdr_encode_hyper(p, ioerr->oer_comp_length); - *p++ = cpu_to_be32(ioerr->oer_iswrite); - *p = cpu_to_be32(ioerr->oer_errno); -} - -__be32 *pnfs_osd_xdr_ioerr_reserve_space(struct xdr_stream *xdr) -{ - __be32 *p; - - p = xdr_reserve_space(xdr, 32 + 24); - if (unlikely(!p)) - dprintk("%s: out of xdr space\n", __func__); - - return p; -} diff --git a/ANDROID_3.4.5/fs/nfs/pagelist.c b/ANDROID_3.4.5/fs/nfs/pagelist.c deleted file mode 100644 index d21fceaa..00000000 --- a/ANDROID_3.4.5/fs/nfs/pagelist.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * linux/fs/nfs/pagelist.c - * - * A set of helper functions for managing NFS read and write requests. - * The main purpose of these routines is to provide support for the - * coalescing of several requests into a single RPC call. - * - * Copyright 2000, 2001 (c) Trond Myklebust <trond.myklebust@fys.uio.no> - * - */ - -#include <linux/slab.h> -#include <linux/file.h> -#include <linux/sched.h> -#include <linux/sunrpc/clnt.h> -#include <linux/nfs.h> -#include <linux/nfs3.h> -#include <linux/nfs4.h> -#include <linux/nfs_page.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_mount.h> -#include <linux/export.h> - -#include "internal.h" -#include "pnfs.h" - -static struct kmem_cache *nfs_page_cachep; - -static inline struct nfs_page * -nfs_page_alloc(void) -{ - struct nfs_page *p = kmem_cache_zalloc(nfs_page_cachep, GFP_KERNEL); - if (p) - INIT_LIST_HEAD(&p->wb_list); - return p; -} - -static inline void -nfs_page_free(struct nfs_page *p) -{ - kmem_cache_free(nfs_page_cachep, p); -} - -/** - * nfs_create_request - Create an NFS read/write request. - * @ctx: open context to use - * @inode: inode to which the request is attached - * @page: page to write - * @offset: starting offset within the page for the write - * @count: number of bytes to read/write - * - * The page must be locked by the caller. This makes sure we never - * create two different requests for the same page. - * User should ensure it is safe to sleep in this function. - */ -struct nfs_page * -nfs_create_request(struct nfs_open_context *ctx, struct inode *inode, - struct page *page, - unsigned int offset, unsigned int count) -{ - struct nfs_page *req; - - /* try to allocate the request struct */ - req = nfs_page_alloc(); - if (req == NULL) - return ERR_PTR(-ENOMEM); - - /* get lock context early so we can deal with alloc failures */ - req->wb_lock_context = nfs_get_lock_context(ctx); - if (req->wb_lock_context == NULL) { - nfs_page_free(req); - return ERR_PTR(-ENOMEM); - } - - /* Initialize the request struct. Initially, we assume a - * long write-back delay. This will be adjusted in - * update_nfs_request below if the region is not locked. */ - req->wb_page = page; - atomic_set(&req->wb_complete, 0); - req->wb_index = page->index; - page_cache_get(page); - BUG_ON(PagePrivate(page)); - BUG_ON(!PageLocked(page)); - BUG_ON(page->mapping->host != inode); - req->wb_offset = offset; - req->wb_pgbase = offset; - req->wb_bytes = count; - req->wb_context = get_nfs_open_context(ctx); - kref_init(&req->wb_kref); - return req; -} - -/** - * nfs_unlock_request - Unlock request and wake up sleepers. - * @req: - */ -void nfs_unlock_request(struct nfs_page *req) -{ - if (!NFS_WBACK_BUSY(req)) { - printk(KERN_ERR "NFS: Invalid unlock attempted\n"); - BUG(); - } - smp_mb__before_clear_bit(); - clear_bit(PG_BUSY, &req->wb_flags); - smp_mb__after_clear_bit(); - wake_up_bit(&req->wb_flags, PG_BUSY); - nfs_release_request(req); -} - -/* - * nfs_clear_request - Free up all resources allocated to the request - * @req: - * - * Release page and open context resources associated with a read/write - * request after it has completed. - */ -static void nfs_clear_request(struct nfs_page *req) -{ - struct page *page = req->wb_page; - struct nfs_open_context *ctx = req->wb_context; - struct nfs_lock_context *l_ctx = req->wb_lock_context; - - if (page != NULL) { - page_cache_release(page); - req->wb_page = NULL; - } - if (l_ctx != NULL) { - nfs_put_lock_context(l_ctx); - req->wb_lock_context = NULL; - } - if (ctx != NULL) { - put_nfs_open_context(ctx); - req->wb_context = NULL; - } -} - - -/** - * nfs_release_request - Release the count on an NFS read/write request - * @req: request to release - * - * Note: Should never be called with the spinlock held! - */ -static void nfs_free_request(struct kref *kref) -{ - struct nfs_page *req = container_of(kref, struct nfs_page, wb_kref); - - /* Release struct file and open context */ - nfs_clear_request(req); - nfs_page_free(req); -} - -void nfs_release_request(struct nfs_page *req) -{ - kref_put(&req->wb_kref, nfs_free_request); -} - -static int nfs_wait_bit_uninterruptible(void *word) -{ - io_schedule(); - return 0; -} - -/** - * nfs_wait_on_request - Wait for a request to complete. - * @req: request to wait upon. - * - * Interruptible by fatal signals only. - * The user is responsible for holding a count on the request. - */ -int -nfs_wait_on_request(struct nfs_page *req) -{ - return wait_on_bit(&req->wb_flags, PG_BUSY, - nfs_wait_bit_uninterruptible, - TASK_UNINTERRUPTIBLE); -} - -bool nfs_generic_pg_test(struct nfs_pageio_descriptor *desc, struct nfs_page *prev, struct nfs_page *req) -{ - /* - * FIXME: ideally we should be able to coalesce all requests - * that are not block boundary aligned, but currently this - * is problematic for the case of bsize < PAGE_CACHE_SIZE, - * since nfs_flush_multi and nfs_pagein_multi assume you - * can have only one struct nfs_page. - */ - if (desc->pg_bsize < PAGE_SIZE) - return 0; - - return desc->pg_count + req->wb_bytes <= desc->pg_bsize; -} -EXPORT_SYMBOL_GPL(nfs_generic_pg_test); - -/** - * nfs_pageio_init - initialise a page io descriptor - * @desc: pointer to descriptor - * @inode: pointer to inode - * @doio: pointer to io function - * @bsize: io block size - * @io_flags: extra parameters for the io function - */ -void nfs_pageio_init(struct nfs_pageio_descriptor *desc, - struct inode *inode, - const struct nfs_pageio_ops *pg_ops, - size_t bsize, - int io_flags) -{ - INIT_LIST_HEAD(&desc->pg_list); - desc->pg_bytes_written = 0; - desc->pg_count = 0; - desc->pg_bsize = bsize; - desc->pg_base = 0; - desc->pg_moreio = 0; - desc->pg_recoalesce = 0; - desc->pg_inode = inode; - desc->pg_ops = pg_ops; - desc->pg_ioflags = io_flags; - desc->pg_error = 0; - desc->pg_lseg = NULL; -} - -/** - * nfs_can_coalesce_requests - test two requests for compatibility - * @prev: pointer to nfs_page - * @req: pointer to nfs_page - * - * The nfs_page structures 'prev' and 'req' are compared to ensure that the - * page data area they describe is contiguous, and that their RPC - * credentials, NFSv4 open state, and lockowners are the same. - * - * Return 'true' if this is the case, else return 'false'. - */ -static bool nfs_can_coalesce_requests(struct nfs_page *prev, - struct nfs_page *req, - struct nfs_pageio_descriptor *pgio) -{ - if (req->wb_context->cred != prev->wb_context->cred) - return false; - if (req->wb_lock_context->lockowner != prev->wb_lock_context->lockowner) - return false; - if (req->wb_context->state != prev->wb_context->state) - return false; - if (req->wb_index != (prev->wb_index + 1)) - return false; - if (req->wb_pgbase != 0) - return false; - if (prev->wb_pgbase + prev->wb_bytes != PAGE_CACHE_SIZE) - return false; - return pgio->pg_ops->pg_test(pgio, prev, req); -} - -/** - * nfs_pageio_do_add_request - Attempt to coalesce a request into a page list. - * @desc: destination io descriptor - * @req: request - * - * Returns true if the request 'req' was successfully coalesced into the - * existing list of pages 'desc'. - */ -static int nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc, - struct nfs_page *req) -{ - if (desc->pg_count != 0) { - struct nfs_page *prev; - - prev = nfs_list_entry(desc->pg_list.prev); - if (!nfs_can_coalesce_requests(prev, req, desc)) - return 0; - } else { - if (desc->pg_ops->pg_init) - desc->pg_ops->pg_init(desc, req); - desc->pg_base = req->wb_pgbase; - } - nfs_list_remove_request(req); - nfs_list_add_request(req, &desc->pg_list); - desc->pg_count += req->wb_bytes; - return 1; -} - -/* - * Helper for nfs_pageio_add_request and nfs_pageio_complete - */ -static void nfs_pageio_doio(struct nfs_pageio_descriptor *desc) -{ - if (!list_empty(&desc->pg_list)) { - int error = desc->pg_ops->pg_doio(desc); - if (error < 0) - desc->pg_error = error; - else - desc->pg_bytes_written += desc->pg_count; - } - if (list_empty(&desc->pg_list)) { - desc->pg_count = 0; - desc->pg_base = 0; - } -} - -/** - * nfs_pageio_add_request - Attempt to coalesce a request into a page list. - * @desc: destination io descriptor - * @req: request - * - * Returns true if the request 'req' was successfully coalesced into the - * existing list of pages 'desc'. - */ -static int __nfs_pageio_add_request(struct nfs_pageio_descriptor *desc, - struct nfs_page *req) -{ - while (!nfs_pageio_do_add_request(desc, req)) { - desc->pg_moreio = 1; - nfs_pageio_doio(desc); - if (desc->pg_error < 0) - return 0; - desc->pg_moreio = 0; - if (desc->pg_recoalesce) - return 0; - } - return 1; -} - -static int nfs_do_recoalesce(struct nfs_pageio_descriptor *desc) -{ - LIST_HEAD(head); - - do { - list_splice_init(&desc->pg_list, &head); - desc->pg_bytes_written -= desc->pg_count; - desc->pg_count = 0; - desc->pg_base = 0; - desc->pg_recoalesce = 0; - - while (!list_empty(&head)) { - struct nfs_page *req; - - req = list_first_entry(&head, struct nfs_page, wb_list); - nfs_list_remove_request(req); - if (__nfs_pageio_add_request(desc, req)) - continue; - if (desc->pg_error < 0) - return 0; - break; - } - } while (desc->pg_recoalesce); - return 1; -} - -int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc, - struct nfs_page *req) -{ - int ret; - - do { - ret = __nfs_pageio_add_request(desc, req); - if (ret) - break; - if (desc->pg_error < 0) - break; - ret = nfs_do_recoalesce(desc); - } while (ret); - return ret; -} - -/** - * nfs_pageio_complete - Complete I/O on an nfs_pageio_descriptor - * @desc: pointer to io descriptor - */ -void nfs_pageio_complete(struct nfs_pageio_descriptor *desc) -{ - for (;;) { - nfs_pageio_doio(desc); - if (!desc->pg_recoalesce) - break; - if (!nfs_do_recoalesce(desc)) - break; - } -} - -/** - * nfs_pageio_cond_complete - Conditional I/O completion - * @desc: pointer to io descriptor - * @index: page index - * - * It is important to ensure that processes don't try to take locks - * on non-contiguous ranges of pages as that might deadlock. This - * function should be called before attempting to wait on a locked - * nfs_page. It will complete the I/O if the page index 'index' - * is not contiguous with the existing list of pages in 'desc'. - */ -void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *desc, pgoff_t index) -{ - if (!list_empty(&desc->pg_list)) { - struct nfs_page *prev = nfs_list_entry(desc->pg_list.prev); - if (index != prev->wb_index + 1) - nfs_pageio_complete(desc); - } -} - -int __init nfs_init_nfspagecache(void) -{ - nfs_page_cachep = kmem_cache_create("nfs_page", - sizeof(struct nfs_page), - 0, SLAB_HWCACHE_ALIGN, - NULL); - if (nfs_page_cachep == NULL) - return -ENOMEM; - - return 0; -} - -void nfs_destroy_nfspagecache(void) -{ - kmem_cache_destroy(nfs_page_cachep); -} - diff --git a/ANDROID_3.4.5/fs/nfs/pnfs.c b/ANDROID_3.4.5/fs/nfs/pnfs.c deleted file mode 100644 index 38512bcd..00000000 --- a/ANDROID_3.4.5/fs/nfs/pnfs.c +++ /dev/null @@ -1,1552 +0,0 @@ -/* - * pNFS functions to call and manage layout drivers. - * - * Copyright (c) 2002 [year of first publication] - * The Regents of the University of Michigan - * All Rights Reserved - * - * Dean Hildebrand <dhildebz@umich.edu> - * - * Permission is granted to use, copy, create derivative works, and - * redistribute this software and such derivative works for any purpose, - * so long as the name of the University of Michigan is not used in - * any advertising or publicity pertaining to the use or distribution - * of this software without specific, written prior authorization. If - * the above copyright notice or any other identification of the - * University of Michigan is included in any copy of any portion of - * this software, then the disclaimer below must also be included. - * - * This software is provided as is, without representation or warranty - * of any kind either express or implied, including without limitation - * the implied warranties of merchantability, fitness for a particular - * purpose, or noninfringement. The Regents of the University of - * Michigan shall not be liable for any damages, including special, - * indirect, incidental, or consequential damages, with respect to any - * claim arising out of or in connection with the use of the software, - * even if it has been or is hereafter advised of the possibility of - * such damages. - */ - -#include <linux/nfs_fs.h> -#include <linux/nfs_page.h> -#include <linux/module.h> -#include "internal.h" -#include "pnfs.h" -#include "iostat.h" - -#define NFSDBG_FACILITY NFSDBG_PNFS - -/* Locking: - * - * pnfs_spinlock: - * protects pnfs_modules_tbl. - */ -static DEFINE_SPINLOCK(pnfs_spinlock); - -/* - * pnfs_modules_tbl holds all pnfs modules - */ -static LIST_HEAD(pnfs_modules_tbl); - -/* Return the registered pnfs layout driver module matching given id */ -static struct pnfs_layoutdriver_type * -find_pnfs_driver_locked(u32 id) -{ - struct pnfs_layoutdriver_type *local; - - list_for_each_entry(local, &pnfs_modules_tbl, pnfs_tblid) - if (local->id == id) - goto out; - local = NULL; -out: - dprintk("%s: Searching for id %u, found %p\n", __func__, id, local); - return local; -} - -static struct pnfs_layoutdriver_type * -find_pnfs_driver(u32 id) -{ - struct pnfs_layoutdriver_type *local; - - spin_lock(&pnfs_spinlock); - local = find_pnfs_driver_locked(id); - spin_unlock(&pnfs_spinlock); - return local; -} - -void -unset_pnfs_layoutdriver(struct nfs_server *nfss) -{ - if (nfss->pnfs_curr_ld) { - if (nfss->pnfs_curr_ld->clear_layoutdriver) - nfss->pnfs_curr_ld->clear_layoutdriver(nfss); - module_put(nfss->pnfs_curr_ld->owner); - } - nfss->pnfs_curr_ld = NULL; -} - -/* - * Try to set the server's pnfs module to the pnfs layout type specified by id. - * Currently only one pNFS layout driver per filesystem is supported. - * - * @id layout type. Zero (illegal layout type) indicates pNFS not in use. - */ -void -set_pnfs_layoutdriver(struct nfs_server *server, const struct nfs_fh *mntfh, - u32 id) -{ - struct pnfs_layoutdriver_type *ld_type = NULL; - - if (id == 0) - goto out_no_driver; - if (!(server->nfs_client->cl_exchange_flags & - (EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS))) { - printk(KERN_ERR "NFS: %s: id %u cl_exchange_flags 0x%x\n", - __func__, id, server->nfs_client->cl_exchange_flags); - goto out_no_driver; - } - ld_type = find_pnfs_driver(id); - if (!ld_type) { - request_module("%s-%u", LAYOUT_NFSV4_1_MODULE_PREFIX, id); - ld_type = find_pnfs_driver(id); - if (!ld_type) { - dprintk("%s: No pNFS module found for %u.\n", - __func__, id); - goto out_no_driver; - } - } - if (!try_module_get(ld_type->owner)) { - dprintk("%s: Could not grab reference on module\n", __func__); - goto out_no_driver; - } - server->pnfs_curr_ld = ld_type; - if (ld_type->set_layoutdriver - && ld_type->set_layoutdriver(server, mntfh)) { - printk(KERN_ERR "NFS: %s: Error initializing pNFS layout " - "driver %u.\n", __func__, id); - module_put(ld_type->owner); - goto out_no_driver; - } - - dprintk("%s: pNFS module for %u set\n", __func__, id); - return; - -out_no_driver: - dprintk("%s: Using NFSv4 I/O\n", __func__); - server->pnfs_curr_ld = NULL; -} - -int -pnfs_register_layoutdriver(struct pnfs_layoutdriver_type *ld_type) -{ - int status = -EINVAL; - struct pnfs_layoutdriver_type *tmp; - - if (ld_type->id == 0) { - printk(KERN_ERR "NFS: %s id 0 is reserved\n", __func__); - return status; - } - if (!ld_type->alloc_lseg || !ld_type->free_lseg) { - printk(KERN_ERR "NFS: %s Layout driver must provide " - "alloc_lseg and free_lseg.\n", __func__); - return status; - } - - spin_lock(&pnfs_spinlock); - tmp = find_pnfs_driver_locked(ld_type->id); - if (!tmp) { - list_add(&ld_type->pnfs_tblid, &pnfs_modules_tbl); - status = 0; - dprintk("%s Registering id:%u name:%s\n", __func__, ld_type->id, - ld_type->name); - } else { - printk(KERN_ERR "NFS: %s Module with id %d already loaded!\n", - __func__, ld_type->id); - } - spin_unlock(&pnfs_spinlock); - - return status; -} -EXPORT_SYMBOL_GPL(pnfs_register_layoutdriver); - -void -pnfs_unregister_layoutdriver(struct pnfs_layoutdriver_type *ld_type) -{ - dprintk("%s Deregistering id:%u\n", __func__, ld_type->id); - spin_lock(&pnfs_spinlock); - list_del(&ld_type->pnfs_tblid); - spin_unlock(&pnfs_spinlock); -} -EXPORT_SYMBOL_GPL(pnfs_unregister_layoutdriver); - -/* - * pNFS client layout cache - */ - -/* Need to hold i_lock if caller does not already hold reference */ -void -get_layout_hdr(struct pnfs_layout_hdr *lo) -{ - atomic_inc(&lo->plh_refcount); -} - -static struct pnfs_layout_hdr * -pnfs_alloc_layout_hdr(struct inode *ino, gfp_t gfp_flags) -{ - struct pnfs_layoutdriver_type *ld = NFS_SERVER(ino)->pnfs_curr_ld; - return ld->alloc_layout_hdr ? ld->alloc_layout_hdr(ino, gfp_flags) : - kzalloc(sizeof(struct pnfs_layout_hdr), gfp_flags); -} - -static void -pnfs_free_layout_hdr(struct pnfs_layout_hdr *lo) -{ - struct pnfs_layoutdriver_type *ld = NFS_SERVER(lo->plh_inode)->pnfs_curr_ld; - put_rpccred(lo->plh_lc_cred); - return ld->alloc_layout_hdr ? ld->free_layout_hdr(lo) : kfree(lo); -} - -static void -destroy_layout_hdr(struct pnfs_layout_hdr *lo) -{ - dprintk("%s: freeing layout cache %p\n", __func__, lo); - BUG_ON(!list_empty(&lo->plh_layouts)); - NFS_I(lo->plh_inode)->layout = NULL; - pnfs_free_layout_hdr(lo); -} - -static void -put_layout_hdr_locked(struct pnfs_layout_hdr *lo) -{ - if (atomic_dec_and_test(&lo->plh_refcount)) - destroy_layout_hdr(lo); -} - -void -put_layout_hdr(struct pnfs_layout_hdr *lo) -{ - struct inode *inode = lo->plh_inode; - - if (atomic_dec_and_lock(&lo->plh_refcount, &inode->i_lock)) { - destroy_layout_hdr(lo); - spin_unlock(&inode->i_lock); - } -} - -static void -init_lseg(struct pnfs_layout_hdr *lo, struct pnfs_layout_segment *lseg) -{ - INIT_LIST_HEAD(&lseg->pls_list); - INIT_LIST_HEAD(&lseg->pls_lc_list); - atomic_set(&lseg->pls_refcount, 1); - smp_mb(); - set_bit(NFS_LSEG_VALID, &lseg->pls_flags); - lseg->pls_layout = lo; -} - -static void free_lseg(struct pnfs_layout_segment *lseg) -{ - struct inode *ino = lseg->pls_layout->plh_inode; - - NFS_SERVER(ino)->pnfs_curr_ld->free_lseg(lseg); - /* Matched by get_layout_hdr in pnfs_insert_layout */ - put_layout_hdr(NFS_I(ino)->layout); -} - -static void -put_lseg_common(struct pnfs_layout_segment *lseg) -{ - struct inode *inode = lseg->pls_layout->plh_inode; - - WARN_ON(test_bit(NFS_LSEG_VALID, &lseg->pls_flags)); - list_del_init(&lseg->pls_list); - if (list_empty(&lseg->pls_layout->plh_segs)) { - set_bit(NFS_LAYOUT_DESTROYED, &lseg->pls_layout->plh_flags); - /* Matched by initial refcount set in alloc_init_layout_hdr */ - put_layout_hdr_locked(lseg->pls_layout); - } - rpc_wake_up(&NFS_SERVER(inode)->roc_rpcwaitq); -} - -void -put_lseg(struct pnfs_layout_segment *lseg) -{ - struct inode *inode; - - if (!lseg) - return; - - dprintk("%s: lseg %p ref %d valid %d\n", __func__, lseg, - atomic_read(&lseg->pls_refcount), - test_bit(NFS_LSEG_VALID, &lseg->pls_flags)); - inode = lseg->pls_layout->plh_inode; - if (atomic_dec_and_lock(&lseg->pls_refcount, &inode->i_lock)) { - LIST_HEAD(free_me); - - put_lseg_common(lseg); - list_add(&lseg->pls_list, &free_me); - spin_unlock(&inode->i_lock); - pnfs_free_lseg_list(&free_me); - } -} -EXPORT_SYMBOL_GPL(put_lseg); - -static inline u64 -end_offset(u64 start, u64 len) -{ - u64 end; - - end = start + len; - return end >= start ? end : NFS4_MAX_UINT64; -} - -/* last octet in a range */ -static inline u64 -last_byte_offset(u64 start, u64 len) -{ - u64 end; - - BUG_ON(!len); - end = start + len; - return end > start ? end - 1 : NFS4_MAX_UINT64; -} - -/* - * is l2 fully contained in l1? - * start1 end1 - * [----------------------------------) - * start2 end2 - * [----------------) - */ -static inline int -lo_seg_contained(struct pnfs_layout_range *l1, - struct pnfs_layout_range *l2) -{ - u64 start1 = l1->offset; - u64 end1 = end_offset(start1, l1->length); - u64 start2 = l2->offset; - u64 end2 = end_offset(start2, l2->length); - - return (start1 <= start2) && (end1 >= end2); -} - -/* - * is l1 and l2 intersecting? - * start1 end1 - * [----------------------------------) - * start2 end2 - * [----------------) - */ -static inline int -lo_seg_intersecting(struct pnfs_layout_range *l1, - struct pnfs_layout_range *l2) -{ - u64 start1 = l1->offset; - u64 end1 = end_offset(start1, l1->length); - u64 start2 = l2->offset; - u64 end2 = end_offset(start2, l2->length); - - return (end1 == NFS4_MAX_UINT64 || end1 > start2) && - (end2 == NFS4_MAX_UINT64 || end2 > start1); -} - -static bool -should_free_lseg(struct pnfs_layout_range *lseg_range, - struct pnfs_layout_range *recall_range) -{ - return (recall_range->iomode == IOMODE_ANY || - lseg_range->iomode == recall_range->iomode) && - lo_seg_intersecting(lseg_range, recall_range); -} - -/* Returns 1 if lseg is removed from list, 0 otherwise */ -static int mark_lseg_invalid(struct pnfs_layout_segment *lseg, - struct list_head *tmp_list) -{ - int rv = 0; - - if (test_and_clear_bit(NFS_LSEG_VALID, &lseg->pls_flags)) { - /* Remove the reference keeping the lseg in the - * list. It will now be removed when all - * outstanding io is finished. - */ - dprintk("%s: lseg %p ref %d\n", __func__, lseg, - atomic_read(&lseg->pls_refcount)); - if (atomic_dec_and_test(&lseg->pls_refcount)) { - put_lseg_common(lseg); - list_add(&lseg->pls_list, tmp_list); - rv = 1; - } - } - return rv; -} - -/* Returns count of number of matching invalid lsegs remaining in list - * after call. - */ -int -mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo, - struct list_head *tmp_list, - struct pnfs_layout_range *recall_range) -{ - struct pnfs_layout_segment *lseg, *next; - int invalid = 0, removed = 0; - - dprintk("%s:Begin lo %p\n", __func__, lo); - - if (list_empty(&lo->plh_segs)) { - if (!test_and_set_bit(NFS_LAYOUT_DESTROYED, &lo->plh_flags)) - put_layout_hdr_locked(lo); - return 0; - } - list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list) - if (!recall_range || - should_free_lseg(&lseg->pls_range, recall_range)) { - dprintk("%s: freeing lseg %p iomode %d " - "offset %llu length %llu\n", __func__, - lseg, lseg->pls_range.iomode, lseg->pls_range.offset, - lseg->pls_range.length); - invalid++; - removed += mark_lseg_invalid(lseg, tmp_list); - } - dprintk("%s:Return %i\n", __func__, invalid - removed); - return invalid - removed; -} - -/* note free_me must contain lsegs from a single layout_hdr */ -void -pnfs_free_lseg_list(struct list_head *free_me) -{ - struct pnfs_layout_segment *lseg, *tmp; - struct pnfs_layout_hdr *lo; - - if (list_empty(free_me)) - return; - - lo = list_first_entry(free_me, struct pnfs_layout_segment, - pls_list)->pls_layout; - - if (test_bit(NFS_LAYOUT_DESTROYED, &lo->plh_flags)) { - struct nfs_client *clp; - - clp = NFS_SERVER(lo->plh_inode)->nfs_client; - spin_lock(&clp->cl_lock); - list_del_init(&lo->plh_layouts); - spin_unlock(&clp->cl_lock); - } - list_for_each_entry_safe(lseg, tmp, free_me, pls_list) { - list_del(&lseg->pls_list); - free_lseg(lseg); - } -} - -void -pnfs_destroy_layout(struct nfs_inode *nfsi) -{ - struct pnfs_layout_hdr *lo; - LIST_HEAD(tmp_list); - - spin_lock(&nfsi->vfs_inode.i_lock); - lo = nfsi->layout; - if (lo) { - lo->plh_block_lgets++; /* permanently block new LAYOUTGETs */ - mark_matching_lsegs_invalid(lo, &tmp_list, NULL); - } - spin_unlock(&nfsi->vfs_inode.i_lock); - pnfs_free_lseg_list(&tmp_list); -} - -/* - * Called by the state manger to remove all layouts established under an - * expired lease. - */ -void -pnfs_destroy_all_layouts(struct nfs_client *clp) -{ - struct nfs_server *server; - struct pnfs_layout_hdr *lo; - LIST_HEAD(tmp_list); - - nfs4_deviceid_mark_client_invalid(clp); - nfs4_deviceid_purge_client(clp); - - spin_lock(&clp->cl_lock); - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { - if (!list_empty(&server->layouts)) - list_splice_init(&server->layouts, &tmp_list); - } - rcu_read_unlock(); - spin_unlock(&clp->cl_lock); - - while (!list_empty(&tmp_list)) { - lo = list_entry(tmp_list.next, struct pnfs_layout_hdr, - plh_layouts); - dprintk("%s freeing layout for inode %lu\n", __func__, - lo->plh_inode->i_ino); - list_del_init(&lo->plh_layouts); - pnfs_destroy_layout(NFS_I(lo->plh_inode)); - } -} - -/* update lo->plh_stateid with new if is more recent */ -void -pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo, const nfs4_stateid *new, - bool update_barrier) -{ - u32 oldseq, newseq; - - oldseq = be32_to_cpu(lo->plh_stateid.seqid); - newseq = be32_to_cpu(new->seqid); - if ((int)(newseq - oldseq) > 0) { - nfs4_stateid_copy(&lo->plh_stateid, new); - if (update_barrier) { - u32 new_barrier = be32_to_cpu(new->seqid); - - if ((int)(new_barrier - lo->plh_barrier)) - lo->plh_barrier = new_barrier; - } else { - /* Because of wraparound, we want to keep the barrier - * "close" to the current seqids. It needs to be - * within 2**31 to count as "behind", so if it - * gets too near that limit, give us a litle leeway - * and bring it to within 2**30. - * NOTE - and yes, this is all unsigned arithmetic. - */ - if (unlikely((newseq - lo->plh_barrier) > (3 << 29))) - lo->plh_barrier = newseq - (1 << 30); - } - } -} - -/* lget is set to 1 if called from inside send_layoutget call chain */ -static bool -pnfs_layoutgets_blocked(struct pnfs_layout_hdr *lo, nfs4_stateid *stateid, - int lget) -{ - if ((stateid) && - (int)(lo->plh_barrier - be32_to_cpu(stateid->seqid)) >= 0) - return true; - return lo->plh_block_lgets || - test_bit(NFS_LAYOUT_DESTROYED, &lo->plh_flags) || - test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags) || - (list_empty(&lo->plh_segs) && - (atomic_read(&lo->plh_outstanding) > lget)); -} - -int -pnfs_choose_layoutget_stateid(nfs4_stateid *dst, struct pnfs_layout_hdr *lo, - struct nfs4_state *open_state) -{ - int status = 0; - - dprintk("--> %s\n", __func__); - spin_lock(&lo->plh_inode->i_lock); - if (pnfs_layoutgets_blocked(lo, NULL, 1)) { - status = -EAGAIN; - } else if (list_empty(&lo->plh_segs)) { - int seq; - - do { - seq = read_seqbegin(&open_state->seqlock); - nfs4_stateid_copy(dst, &open_state->stateid); - } while (read_seqretry(&open_state->seqlock, seq)); - } else - nfs4_stateid_copy(dst, &lo->plh_stateid); - spin_unlock(&lo->plh_inode->i_lock); - dprintk("<-- %s\n", __func__); - return status; -} - -/* -* Get layout from server. -* for now, assume that whole file layouts are requested. -* arg->offset: 0 -* arg->length: all ones -*/ -static struct pnfs_layout_segment * -send_layoutget(struct pnfs_layout_hdr *lo, - struct nfs_open_context *ctx, - struct pnfs_layout_range *range, - gfp_t gfp_flags) -{ - struct inode *ino = lo->plh_inode; - struct nfs_server *server = NFS_SERVER(ino); - struct nfs4_layoutget *lgp; - struct pnfs_layout_segment *lseg = NULL; - struct page **pages = NULL; - int i; - u32 max_resp_sz, max_pages; - - dprintk("--> %s\n", __func__); - - BUG_ON(ctx == NULL); - lgp = kzalloc(sizeof(*lgp), gfp_flags); - if (lgp == NULL) - return NULL; - - /* allocate pages for xdr post processing */ - max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz; - max_pages = nfs_page_array_len(0, max_resp_sz); - - pages = kcalloc(max_pages, sizeof(struct page *), gfp_flags); - if (!pages) - goto out_err_free; - - for (i = 0; i < max_pages; i++) { - pages[i] = alloc_page(gfp_flags); - if (!pages[i]) - goto out_err_free; - } - - lgp->args.minlength = PAGE_CACHE_SIZE; - if (lgp->args.minlength > range->length) - lgp->args.minlength = range->length; - lgp->args.maxcount = PNFS_LAYOUT_MAXSIZE; - lgp->args.range = *range; - lgp->args.type = server->pnfs_curr_ld->id; - lgp->args.inode = ino; - lgp->args.ctx = get_nfs_open_context(ctx); - lgp->args.layout.pages = pages; - lgp->args.layout.pglen = max_pages * PAGE_SIZE; - lgp->lsegpp = &lseg; - lgp->gfp_flags = gfp_flags; - - /* Synchronously retrieve layout information from server and - * store in lseg. - */ - nfs4_proc_layoutget(lgp); - if (!lseg) { - /* remember that LAYOUTGET failed and suspend trying */ - set_bit(lo_fail_bit(range->iomode), &lo->plh_flags); - } - - /* free xdr pages */ - for (i = 0; i < max_pages; i++) - __free_page(pages[i]); - kfree(pages); - - return lseg; - -out_err_free: - /* free any allocated xdr pages, lgp as it's not used */ - if (pages) { - for (i = 0; i < max_pages; i++) { - if (!pages[i]) - break; - __free_page(pages[i]); - } - kfree(pages); - } - kfree(lgp); - return NULL; -} - -/* Initiates a LAYOUTRETURN(FILE) */ -int -_pnfs_return_layout(struct inode *ino) -{ - struct pnfs_layout_hdr *lo = NULL; - struct nfs_inode *nfsi = NFS_I(ino); - LIST_HEAD(tmp_list); - struct nfs4_layoutreturn *lrp; - nfs4_stateid stateid; - int status = 0; - - dprintk("--> %s\n", __func__); - - spin_lock(&ino->i_lock); - lo = nfsi->layout; - if (!lo) { - spin_unlock(&ino->i_lock); - dprintk("%s: no layout to return\n", __func__); - return status; - } - stateid = nfsi->layout->plh_stateid; - /* Reference matched in nfs4_layoutreturn_release */ - get_layout_hdr(lo); - mark_matching_lsegs_invalid(lo, &tmp_list, NULL); - lo->plh_block_lgets++; - spin_unlock(&ino->i_lock); - pnfs_free_lseg_list(&tmp_list); - - WARN_ON(test_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)); - - lrp = kzalloc(sizeof(*lrp), GFP_KERNEL); - if (unlikely(lrp == NULL)) { - status = -ENOMEM; - set_bit(NFS_LAYOUT_RW_FAILED, &lo->plh_flags); - set_bit(NFS_LAYOUT_RO_FAILED, &lo->plh_flags); - put_layout_hdr(lo); - goto out; - } - - lrp->args.stateid = stateid; - lrp->args.layout_type = NFS_SERVER(ino)->pnfs_curr_ld->id; - lrp->args.inode = ino; - lrp->args.layout = lo; - lrp->clp = NFS_SERVER(ino)->nfs_client; - - status = nfs4_proc_layoutreturn(lrp); -out: - dprintk("<-- %s status: %d\n", __func__, status); - return status; -} - -bool pnfs_roc(struct inode *ino) -{ - struct pnfs_layout_hdr *lo; - struct pnfs_layout_segment *lseg, *tmp; - LIST_HEAD(tmp_list); - bool found = false; - - spin_lock(&ino->i_lock); - lo = NFS_I(ino)->layout; - if (!lo || !test_and_clear_bit(NFS_LAYOUT_ROC, &lo->plh_flags) || - test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags)) - goto out_nolayout; - list_for_each_entry_safe(lseg, tmp, &lo->plh_segs, pls_list) - if (test_bit(NFS_LSEG_ROC, &lseg->pls_flags)) { - mark_lseg_invalid(lseg, &tmp_list); - found = true; - } - if (!found) - goto out_nolayout; - lo->plh_block_lgets++; - get_layout_hdr(lo); /* matched in pnfs_roc_release */ - spin_unlock(&ino->i_lock); - pnfs_free_lseg_list(&tmp_list); - return true; - -out_nolayout: - spin_unlock(&ino->i_lock); - return false; -} - -void pnfs_roc_release(struct inode *ino) -{ - struct pnfs_layout_hdr *lo; - - spin_lock(&ino->i_lock); - lo = NFS_I(ino)->layout; - lo->plh_block_lgets--; - put_layout_hdr_locked(lo); - spin_unlock(&ino->i_lock); -} - -void pnfs_roc_set_barrier(struct inode *ino, u32 barrier) -{ - struct pnfs_layout_hdr *lo; - - spin_lock(&ino->i_lock); - lo = NFS_I(ino)->layout; - if ((int)(barrier - lo->plh_barrier) > 0) - lo->plh_barrier = barrier; - spin_unlock(&ino->i_lock); -} - -bool pnfs_roc_drain(struct inode *ino, u32 *barrier) -{ - struct nfs_inode *nfsi = NFS_I(ino); - struct pnfs_layout_segment *lseg; - bool found = false; - - spin_lock(&ino->i_lock); - list_for_each_entry(lseg, &nfsi->layout->plh_segs, pls_list) - if (test_bit(NFS_LSEG_ROC, &lseg->pls_flags)) { - found = true; - break; - } - if (!found) { - struct pnfs_layout_hdr *lo = nfsi->layout; - u32 current_seqid = be32_to_cpu(lo->plh_stateid.seqid); - - /* Since close does not return a layout stateid for use as - * a barrier, we choose the worst-case barrier. - */ - *barrier = current_seqid + atomic_read(&lo->plh_outstanding); - } - spin_unlock(&ino->i_lock); - return found; -} - -/* - * Compare two layout segments for sorting into layout cache. - * We want to preferentially return RW over RO layouts, so ensure those - * are seen first. - */ -static s64 -cmp_layout(struct pnfs_layout_range *l1, - struct pnfs_layout_range *l2) -{ - s64 d; - - /* high offset > low offset */ - d = l1->offset - l2->offset; - if (d) - return d; - - /* short length > long length */ - d = l2->length - l1->length; - if (d) - return d; - - /* read > read/write */ - return (int)(l1->iomode == IOMODE_READ) - (int)(l2->iomode == IOMODE_READ); -} - -static void -pnfs_insert_layout(struct pnfs_layout_hdr *lo, - struct pnfs_layout_segment *lseg) -{ - struct pnfs_layout_segment *lp; - - dprintk("%s:Begin\n", __func__); - - assert_spin_locked(&lo->plh_inode->i_lock); - list_for_each_entry(lp, &lo->plh_segs, pls_list) { - if (cmp_layout(&lseg->pls_range, &lp->pls_range) > 0) - continue; - list_add_tail(&lseg->pls_list, &lp->pls_list); - dprintk("%s: inserted lseg %p " - "iomode %d offset %llu length %llu before " - "lp %p iomode %d offset %llu length %llu\n", - __func__, lseg, lseg->pls_range.iomode, - lseg->pls_range.offset, lseg->pls_range.length, - lp, lp->pls_range.iomode, lp->pls_range.offset, - lp->pls_range.length); - goto out; - } - list_add_tail(&lseg->pls_list, &lo->plh_segs); - dprintk("%s: inserted lseg %p " - "iomode %d offset %llu length %llu at tail\n", - __func__, lseg, lseg->pls_range.iomode, - lseg->pls_range.offset, lseg->pls_range.length); -out: - get_layout_hdr(lo); - - dprintk("%s:Return\n", __func__); -} - -static struct pnfs_layout_hdr * -alloc_init_layout_hdr(struct inode *ino, - struct nfs_open_context *ctx, - gfp_t gfp_flags) -{ - struct pnfs_layout_hdr *lo; - - lo = pnfs_alloc_layout_hdr(ino, gfp_flags); - if (!lo) - return NULL; - atomic_set(&lo->plh_refcount, 1); - INIT_LIST_HEAD(&lo->plh_layouts); - INIT_LIST_HEAD(&lo->plh_segs); - INIT_LIST_HEAD(&lo->plh_bulk_recall); - lo->plh_inode = ino; - lo->plh_lc_cred = get_rpccred(ctx->state->owner->so_cred); - return lo; -} - -static struct pnfs_layout_hdr * -pnfs_find_alloc_layout(struct inode *ino, - struct nfs_open_context *ctx, - gfp_t gfp_flags) -{ - struct nfs_inode *nfsi = NFS_I(ino); - struct pnfs_layout_hdr *new = NULL; - - dprintk("%s Begin ino=%p layout=%p\n", __func__, ino, nfsi->layout); - - assert_spin_locked(&ino->i_lock); - if (nfsi->layout) { - if (test_bit(NFS_LAYOUT_DESTROYED, &nfsi->layout->plh_flags)) - return NULL; - else - return nfsi->layout; - } - spin_unlock(&ino->i_lock); - new = alloc_init_layout_hdr(ino, ctx, gfp_flags); - spin_lock(&ino->i_lock); - - if (likely(nfsi->layout == NULL)) /* Won the race? */ - nfsi->layout = new; - else - pnfs_free_layout_hdr(new); - return nfsi->layout; -} - -/* - * iomode matching rules: - * iomode lseg match - * ----- ----- ----- - * ANY READ true - * ANY RW true - * RW READ false - * RW RW true - * READ READ true - * READ RW true - */ -static int -is_matching_lseg(struct pnfs_layout_range *ls_range, - struct pnfs_layout_range *range) -{ - struct pnfs_layout_range range1; - - if ((range->iomode == IOMODE_RW && - ls_range->iomode != IOMODE_RW) || - !lo_seg_intersecting(ls_range, range)) - return 0; - - /* range1 covers only the first byte in the range */ - range1 = *range; - range1.length = 1; - return lo_seg_contained(ls_range, &range1); -} - -/* - * lookup range in layout - */ -static struct pnfs_layout_segment * -pnfs_find_lseg(struct pnfs_layout_hdr *lo, - struct pnfs_layout_range *range) -{ - struct pnfs_layout_segment *lseg, *ret = NULL; - - dprintk("%s:Begin\n", __func__); - - assert_spin_locked(&lo->plh_inode->i_lock); - list_for_each_entry(lseg, &lo->plh_segs, pls_list) { - if (test_bit(NFS_LSEG_VALID, &lseg->pls_flags) && - is_matching_lseg(&lseg->pls_range, range)) { - ret = get_lseg(lseg); - break; - } - if (lseg->pls_range.offset > range->offset) - break; - } - - dprintk("%s:Return lseg %p ref %d\n", - __func__, ret, ret ? atomic_read(&ret->pls_refcount) : 0); - return ret; -} - -/* - * Layout segment is retreived from the server if not cached. - * The appropriate layout segment is referenced and returned to the caller. - */ -struct pnfs_layout_segment * -pnfs_update_layout(struct inode *ino, - struct nfs_open_context *ctx, - loff_t pos, - u64 count, - enum pnfs_iomode iomode, - gfp_t gfp_flags) -{ - struct pnfs_layout_range arg = { - .iomode = iomode, - .offset = pos, - .length = count, - }; - unsigned pg_offset; - struct nfs_inode *nfsi = NFS_I(ino); - struct nfs_server *server = NFS_SERVER(ino); - struct nfs_client *clp = server->nfs_client; - struct pnfs_layout_hdr *lo; - struct pnfs_layout_segment *lseg = NULL; - bool first = false; - - if (!pnfs_enabled_sb(NFS_SERVER(ino))) - return NULL; - spin_lock(&ino->i_lock); - lo = pnfs_find_alloc_layout(ino, ctx, gfp_flags); - if (lo == NULL) { - dprintk("%s ERROR: can't get pnfs_layout_hdr\n", __func__); - goto out_unlock; - } - - /* Do we even need to bother with this? */ - if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags)) { - dprintk("%s matches recall, use MDS\n", __func__); - goto out_unlock; - } - - /* if LAYOUTGET already failed once we don't try again */ - if (test_bit(lo_fail_bit(iomode), &nfsi->layout->plh_flags)) - goto out_unlock; - - /* Check to see if the layout for the given range already exists */ - lseg = pnfs_find_lseg(lo, &arg); - if (lseg) - goto out_unlock; - - if (pnfs_layoutgets_blocked(lo, NULL, 0)) - goto out_unlock; - atomic_inc(&lo->plh_outstanding); - - get_layout_hdr(lo); - if (list_empty(&lo->plh_segs)) - first = true; - spin_unlock(&ino->i_lock); - if (first) { - /* The lo must be on the clp list if there is any - * chance of a CB_LAYOUTRECALL(FILE) coming in. - */ - spin_lock(&clp->cl_lock); - BUG_ON(!list_empty(&lo->plh_layouts)); - list_add_tail(&lo->plh_layouts, &server->layouts); - spin_unlock(&clp->cl_lock); - } - - pg_offset = arg.offset & ~PAGE_CACHE_MASK; - if (pg_offset) { - arg.offset -= pg_offset; - arg.length += pg_offset; - } - if (arg.length != NFS4_MAX_UINT64) - arg.length = PAGE_CACHE_ALIGN(arg.length); - - lseg = send_layoutget(lo, ctx, &arg, gfp_flags); - if (!lseg && first) { - spin_lock(&clp->cl_lock); - list_del_init(&lo->plh_layouts); - spin_unlock(&clp->cl_lock); - } - atomic_dec(&lo->plh_outstanding); - put_layout_hdr(lo); -out: - dprintk("%s end, state 0x%lx lseg %p\n", __func__, - nfsi->layout ? nfsi->layout->plh_flags : -1, lseg); - return lseg; -out_unlock: - spin_unlock(&ino->i_lock); - goto out; -} -EXPORT_SYMBOL_GPL(pnfs_update_layout); - -int -pnfs_layout_process(struct nfs4_layoutget *lgp) -{ - struct pnfs_layout_hdr *lo = NFS_I(lgp->args.inode)->layout; - struct nfs4_layoutget_res *res = &lgp->res; - struct pnfs_layout_segment *lseg; - struct inode *ino = lo->plh_inode; - int status = 0; - - /* Inject layout blob into I/O device driver */ - lseg = NFS_SERVER(ino)->pnfs_curr_ld->alloc_lseg(lo, res, lgp->gfp_flags); - if (!lseg || IS_ERR(lseg)) { - if (!lseg) - status = -ENOMEM; - else - status = PTR_ERR(lseg); - dprintk("%s: Could not allocate layout: error %d\n", - __func__, status); - goto out; - } - - spin_lock(&ino->i_lock); - if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags)) { - dprintk("%s forget reply due to recall\n", __func__); - goto out_forget_reply; - } - - if (pnfs_layoutgets_blocked(lo, &res->stateid, 1)) { - dprintk("%s forget reply due to state\n", __func__); - goto out_forget_reply; - } - init_lseg(lo, lseg); - lseg->pls_range = res->range; - *lgp->lsegpp = get_lseg(lseg); - pnfs_insert_layout(lo, lseg); - - if (res->return_on_close) { - set_bit(NFS_LSEG_ROC, &lseg->pls_flags); - set_bit(NFS_LAYOUT_ROC, &lo->plh_flags); - } - - /* Done processing layoutget. Set the layout stateid */ - pnfs_set_layout_stateid(lo, &res->stateid, false); - spin_unlock(&ino->i_lock); -out: - return status; - -out_forget_reply: - spin_unlock(&ino->i_lock); - lseg->pls_layout = lo; - NFS_SERVER(ino)->pnfs_curr_ld->free_lseg(lseg); - goto out; -} - -void -pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req) -{ - BUG_ON(pgio->pg_lseg != NULL); - - pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, - req->wb_context, - req_offset(req), - req->wb_bytes, - IOMODE_READ, - GFP_KERNEL); - /* If no lseg, fall back to read through mds */ - if (pgio->pg_lseg == NULL) - nfs_pageio_reset_read_mds(pgio); - -} -EXPORT_SYMBOL_GPL(pnfs_generic_pg_init_read); - -void -pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req) -{ - BUG_ON(pgio->pg_lseg != NULL); - - pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, - req->wb_context, - req_offset(req), - req->wb_bytes, - IOMODE_RW, - GFP_NOFS); - /* If no lseg, fall back to write through mds */ - if (pgio->pg_lseg == NULL) - nfs_pageio_reset_write_mds(pgio); -} -EXPORT_SYMBOL_GPL(pnfs_generic_pg_init_write); - -bool -pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld; - - if (ld == NULL) - return false; - nfs_pageio_init(pgio, inode, ld->pg_read_ops, server->rsize, 0); - return true; -} - -bool -pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld; - - if (ld == NULL) - return false; - nfs_pageio_init(pgio, inode, ld->pg_write_ops, server->wsize, ioflags); - return true; -} - -bool -pnfs_generic_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev, - struct nfs_page *req) -{ - if (pgio->pg_lseg == NULL) - return nfs_generic_pg_test(pgio, prev, req); - - /* - * Test if a nfs_page is fully contained in the pnfs_layout_range. - * Note that this test makes several assumptions: - * - that the previous nfs_page in the struct nfs_pageio_descriptor - * is known to lie within the range. - * - that the nfs_page being tested is known to be contiguous with the - * previous nfs_page. - * - Layout ranges are page aligned, so we only have to test the - * start offset of the request. - * - * Please also note that 'end_offset' is actually the offset of the - * first byte that lies outside the pnfs_layout_range. FIXME? - * - */ - return req_offset(req) < end_offset(pgio->pg_lseg->pls_range.offset, - pgio->pg_lseg->pls_range.length); -} -EXPORT_SYMBOL_GPL(pnfs_generic_pg_test); - -static int pnfs_write_done_resend_to_mds(struct inode *inode, struct list_head *head) -{ - struct nfs_pageio_descriptor pgio; - LIST_HEAD(failed); - - /* Resend all requests through the MDS */ - nfs_pageio_init_write_mds(&pgio, inode, FLUSH_STABLE); - while (!list_empty(head)) { - struct nfs_page *req = nfs_list_entry(head->next); - - nfs_list_remove_request(req); - if (!nfs_pageio_add_request(&pgio, req)) - nfs_list_add_request(req, &failed); - } - nfs_pageio_complete(&pgio); - - if (!list_empty(&failed)) { - /* For some reason our attempt to resend pages. Mark the - * overall send request as having failed, and let - * nfs_writeback_release_full deal with the error. - */ - list_move(&failed, head); - return -EIO; - } - return 0; -} - -/* - * Called by non rpc-based layout drivers - */ -void pnfs_ld_write_done(struct nfs_write_data *data) -{ - if (likely(!data->pnfs_error)) { - pnfs_set_layoutcommit(data); - data->mds_ops->rpc_call_done(&data->task, data); - } else { - dprintk("pnfs write error = %d\n", data->pnfs_error); - if (NFS_SERVER(data->inode)->pnfs_curr_ld->flags & - PNFS_LAYOUTRET_ON_ERROR) { - /* Don't lo_commit on error, Server will needs to - * preform a file recovery. - */ - clear_bit(NFS_INO_LAYOUTCOMMIT, - &NFS_I(data->inode)->flags); - pnfs_return_layout(data->inode); - } - data->task.tk_status = pnfs_write_done_resend_to_mds(data->inode, &data->pages); - } - put_lseg(data->lseg); - data->mds_ops->rpc_release(data); -} -EXPORT_SYMBOL_GPL(pnfs_ld_write_done); - -static void -pnfs_write_through_mds(struct nfs_pageio_descriptor *desc, - struct nfs_write_data *data) -{ - list_splice_tail_init(&data->pages, &desc->pg_list); - if (data->req && list_empty(&data->req->wb_list)) - nfs_list_add_request(data->req, &desc->pg_list); - nfs_pageio_reset_write_mds(desc); - desc->pg_recoalesce = 1; - put_lseg(data->lseg); - nfs_writedata_release(data); -} - -static enum pnfs_try_status -pnfs_try_to_write_data(struct nfs_write_data *wdata, - const struct rpc_call_ops *call_ops, - struct pnfs_layout_segment *lseg, - int how) -{ - struct inode *inode = wdata->inode; - enum pnfs_try_status trypnfs; - struct nfs_server *nfss = NFS_SERVER(inode); - - wdata->mds_ops = call_ops; - wdata->lseg = get_lseg(lseg); - - dprintk("%s: Writing ino:%lu %u@%llu (how %d)\n", __func__, - inode->i_ino, wdata->args.count, wdata->args.offset, how); - - trypnfs = nfss->pnfs_curr_ld->write_pagelist(wdata, how); - if (trypnfs == PNFS_NOT_ATTEMPTED) { - put_lseg(wdata->lseg); - wdata->lseg = NULL; - } else - nfs_inc_stats(inode, NFSIOS_PNFS_WRITE); - - dprintk("%s End (trypnfs:%d)\n", __func__, trypnfs); - return trypnfs; -} - -static void -pnfs_do_multiple_writes(struct nfs_pageio_descriptor *desc, struct list_head *head, int how) -{ - struct nfs_write_data *data; - const struct rpc_call_ops *call_ops = desc->pg_rpc_callops; - struct pnfs_layout_segment *lseg = desc->pg_lseg; - - desc->pg_lseg = NULL; - while (!list_empty(head)) { - enum pnfs_try_status trypnfs; - - data = list_entry(head->next, struct nfs_write_data, list); - list_del_init(&data->list); - - trypnfs = pnfs_try_to_write_data(data, call_ops, lseg, how); - if (trypnfs == PNFS_NOT_ATTEMPTED) - pnfs_write_through_mds(desc, data); - } - put_lseg(lseg); -} - -int -pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc) -{ - LIST_HEAD(head); - int ret; - - ret = nfs_generic_flush(desc, &head); - if (ret != 0) { - put_lseg(desc->pg_lseg); - desc->pg_lseg = NULL; - return ret; - } - pnfs_do_multiple_writes(desc, &head, desc->pg_ioflags); - return 0; -} -EXPORT_SYMBOL_GPL(pnfs_generic_pg_writepages); - -static void pnfs_ld_handle_read_error(struct nfs_read_data *data) -{ - struct nfs_pageio_descriptor pgio; - - put_lseg(data->lseg); - data->lseg = NULL; - dprintk("pnfs write error = %d\n", data->pnfs_error); - if (NFS_SERVER(data->inode)->pnfs_curr_ld->flags & - PNFS_LAYOUTRET_ON_ERROR) - pnfs_return_layout(data->inode); - - nfs_pageio_init_read_mds(&pgio, data->inode); - - while (!list_empty(&data->pages)) { - struct nfs_page *req = nfs_list_entry(data->pages.next); - - nfs_list_remove_request(req); - nfs_pageio_add_request(&pgio, req); - } - nfs_pageio_complete(&pgio); -} - -/* - * Called by non rpc-based layout drivers - */ -void pnfs_ld_read_done(struct nfs_read_data *data) -{ - if (likely(!data->pnfs_error)) { - __nfs4_read_done_cb(data); - data->mds_ops->rpc_call_done(&data->task, data); - } else - pnfs_ld_handle_read_error(data); - put_lseg(data->lseg); - data->mds_ops->rpc_release(data); -} -EXPORT_SYMBOL_GPL(pnfs_ld_read_done); - -static void -pnfs_read_through_mds(struct nfs_pageio_descriptor *desc, - struct nfs_read_data *data) -{ - list_splice_tail_init(&data->pages, &desc->pg_list); - if (data->req && list_empty(&data->req->wb_list)) - nfs_list_add_request(data->req, &desc->pg_list); - nfs_pageio_reset_read_mds(desc); - desc->pg_recoalesce = 1; - nfs_readdata_release(data); -} - -/* - * Call the appropriate parallel I/O subsystem read function. - */ -static enum pnfs_try_status -pnfs_try_to_read_data(struct nfs_read_data *rdata, - const struct rpc_call_ops *call_ops, - struct pnfs_layout_segment *lseg) -{ - struct inode *inode = rdata->inode; - struct nfs_server *nfss = NFS_SERVER(inode); - enum pnfs_try_status trypnfs; - - rdata->mds_ops = call_ops; - rdata->lseg = get_lseg(lseg); - - dprintk("%s: Reading ino:%lu %u@%llu\n", - __func__, inode->i_ino, rdata->args.count, rdata->args.offset); - - trypnfs = nfss->pnfs_curr_ld->read_pagelist(rdata); - if (trypnfs == PNFS_NOT_ATTEMPTED) { - put_lseg(rdata->lseg); - rdata->lseg = NULL; - } else { - nfs_inc_stats(inode, NFSIOS_PNFS_READ); - } - dprintk("%s End (trypnfs:%d)\n", __func__, trypnfs); - return trypnfs; -} - -static void -pnfs_do_multiple_reads(struct nfs_pageio_descriptor *desc, struct list_head *head) -{ - struct nfs_read_data *data; - const struct rpc_call_ops *call_ops = desc->pg_rpc_callops; - struct pnfs_layout_segment *lseg = desc->pg_lseg; - - desc->pg_lseg = NULL; - while (!list_empty(head)) { - enum pnfs_try_status trypnfs; - - data = list_entry(head->next, struct nfs_read_data, list); - list_del_init(&data->list); - - trypnfs = pnfs_try_to_read_data(data, call_ops, lseg); - if (trypnfs == PNFS_NOT_ATTEMPTED) - pnfs_read_through_mds(desc, data); - } - put_lseg(lseg); -} - -int -pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc) -{ - LIST_HEAD(head); - int ret; - - ret = nfs_generic_pagein(desc, &head); - if (ret != 0) { - put_lseg(desc->pg_lseg); - desc->pg_lseg = NULL; - return ret; - } - pnfs_do_multiple_reads(desc, &head); - return 0; -} -EXPORT_SYMBOL_GPL(pnfs_generic_pg_readpages); - -/* - * There can be multiple RW segments. - */ -static void pnfs_list_write_lseg(struct inode *inode, struct list_head *listp) -{ - struct pnfs_layout_segment *lseg; - - list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) { - if (lseg->pls_range.iomode == IOMODE_RW && - test_bit(NFS_LSEG_LAYOUTCOMMIT, &lseg->pls_flags)) - list_add(&lseg->pls_lc_list, listp); - } -} - -void pnfs_set_lo_fail(struct pnfs_layout_segment *lseg) -{ - if (lseg->pls_range.iomode == IOMODE_RW) { - dprintk("%s Setting layout IOMODE_RW fail bit\n", __func__); - set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags); - } else { - dprintk("%s Setting layout IOMODE_READ fail bit\n", __func__); - set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags); - } -} -EXPORT_SYMBOL_GPL(pnfs_set_lo_fail); - -void -pnfs_set_layoutcommit(struct nfs_write_data *wdata) -{ - struct nfs_inode *nfsi = NFS_I(wdata->inode); - loff_t end_pos = wdata->mds_offset + wdata->res.count; - bool mark_as_dirty = false; - - spin_lock(&nfsi->vfs_inode.i_lock); - if (!test_and_set_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) { - mark_as_dirty = true; - dprintk("%s: Set layoutcommit for inode %lu ", - __func__, wdata->inode->i_ino); - } - if (!test_and_set_bit(NFS_LSEG_LAYOUTCOMMIT, &wdata->lseg->pls_flags)) { - /* references matched in nfs4_layoutcommit_release */ - get_lseg(wdata->lseg); - } - if (end_pos > nfsi->layout->plh_lwb) - nfsi->layout->plh_lwb = end_pos; - spin_unlock(&nfsi->vfs_inode.i_lock); - dprintk("%s: lseg %p end_pos %llu\n", - __func__, wdata->lseg, nfsi->layout->plh_lwb); - - /* if pnfs_layoutcommit_inode() runs between inode locks, the next one - * will be a noop because NFS_INO_LAYOUTCOMMIT will not be set */ - if (mark_as_dirty) - mark_inode_dirty_sync(wdata->inode); -} -EXPORT_SYMBOL_GPL(pnfs_set_layoutcommit); - -void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data) -{ - struct nfs_server *nfss = NFS_SERVER(data->args.inode); - - if (nfss->pnfs_curr_ld->cleanup_layoutcommit) - nfss->pnfs_curr_ld->cleanup_layoutcommit(data); -} - -/* - * For the LAYOUT4_NFSV4_1_FILES layout type, NFS_DATA_SYNC WRITEs and - * NFS_UNSTABLE WRITEs with a COMMIT to data servers must store enough - * data to disk to allow the server to recover the data if it crashes. - * LAYOUTCOMMIT is only needed when the NFL4_UFLG_COMMIT_THRU_MDS flag - * is off, and a COMMIT is sent to a data server, or - * if WRITEs to a data server return NFS_DATA_SYNC. - */ -int -pnfs_layoutcommit_inode(struct inode *inode, bool sync) -{ - struct nfs4_layoutcommit_data *data; - struct nfs_inode *nfsi = NFS_I(inode); - loff_t end_pos; - int status = 0; - - dprintk("--> %s inode %lu\n", __func__, inode->i_ino); - - if (!test_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) - return 0; - - /* Note kzalloc ensures data->res.seq_res.sr_slot == NULL */ - data = kzalloc(sizeof(*data), GFP_NOFS); - if (!data) { - status = -ENOMEM; - goto out; - } - - if (!test_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) - goto out_free; - - if (test_and_set_bit(NFS_INO_LAYOUTCOMMITTING, &nfsi->flags)) { - if (!sync) { - status = -EAGAIN; - goto out_free; - } - status = wait_on_bit_lock(&nfsi->flags, NFS_INO_LAYOUTCOMMITTING, - nfs_wait_bit_killable, TASK_KILLABLE); - if (status) - goto out_free; - } - - INIT_LIST_HEAD(&data->lseg_list); - spin_lock(&inode->i_lock); - if (!test_and_clear_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) { - clear_bit(NFS_INO_LAYOUTCOMMITTING, &nfsi->flags); - spin_unlock(&inode->i_lock); - wake_up_bit(&nfsi->flags, NFS_INO_LAYOUTCOMMITTING); - goto out_free; - } - - pnfs_list_write_lseg(inode, &data->lseg_list); - - end_pos = nfsi->layout->plh_lwb; - nfsi->layout->plh_lwb = 0; - - nfs4_stateid_copy(&data->args.stateid, &nfsi->layout->plh_stateid); - spin_unlock(&inode->i_lock); - - data->args.inode = inode; - data->cred = get_rpccred(nfsi->layout->plh_lc_cred); - nfs_fattr_init(&data->fattr); - data->args.bitmask = NFS_SERVER(inode)->cache_consistency_bitmask; - data->res.fattr = &data->fattr; - data->args.lastbytewritten = end_pos - 1; - data->res.server = NFS_SERVER(inode); - - status = nfs4_proc_layoutcommit(data, sync); -out: - if (status) - mark_inode_dirty_sync(inode); - dprintk("<-- %s status %d\n", __func__, status); - return status; -out_free: - kfree(data); - goto out; -} diff --git a/ANDROID_3.4.5/fs/nfs/pnfs.h b/ANDROID_3.4.5/fs/nfs/pnfs.h deleted file mode 100644 index 442ebf68..00000000 --- a/ANDROID_3.4.5/fs/nfs/pnfs.h +++ /dev/null @@ -1,440 +0,0 @@ -/* - * pNFS client data structures. - * - * Copyright (c) 2002 - * The Regents of the University of Michigan - * All Rights Reserved - * - * Dean Hildebrand <dhildebz@umich.edu> - * - * Permission is granted to use, copy, create derivative works, and - * redistribute this software and such derivative works for any purpose, - * so long as the name of the University of Michigan is not used in - * any advertising or publicity pertaining to the use or distribution - * of this software without specific, written prior authorization. If - * the above copyright notice or any other identification of the - * University of Michigan is included in any copy of any portion of - * this software, then the disclaimer below must also be included. - * - * This software is provided as is, without representation or warranty - * of any kind either express or implied, including without limitation - * the implied warranties of merchantability, fitness for a particular - * purpose, or noninfringement. The Regents of the University of - * Michigan shall not be liable for any damages, including special, - * indirect, incidental, or consequential damages, with respect to any - * claim arising out of or in connection with the use of the software, - * even if it has been or is hereafter advised of the possibility of - * such damages. - */ - -#ifndef FS_NFS_PNFS_H -#define FS_NFS_PNFS_H - -#include <linux/nfs_fs.h> -#include <linux/nfs_page.h> - -enum { - NFS_LSEG_VALID = 0, /* cleared when lseg is recalled/returned */ - NFS_LSEG_ROC, /* roc bit received from server */ - NFS_LSEG_LAYOUTCOMMIT, /* layoutcommit bit set for layoutcommit */ -}; - -struct pnfs_layout_segment { - struct list_head pls_list; - struct list_head pls_lc_list; - struct pnfs_layout_range pls_range; - atomic_t pls_refcount; - unsigned long pls_flags; - struct pnfs_layout_hdr *pls_layout; -}; - -enum pnfs_try_status { - PNFS_ATTEMPTED = 0, - PNFS_NOT_ATTEMPTED = 1, -}; - -#ifdef CONFIG_NFS_V4_1 - -#define LAYOUT_NFSV4_1_MODULE_PREFIX "nfs-layouttype4" - -enum { - NFS_LAYOUT_RO_FAILED = 0, /* get ro layout failed stop trying */ - NFS_LAYOUT_RW_FAILED, /* get rw layout failed stop trying */ - NFS_LAYOUT_BULK_RECALL, /* bulk recall affecting layout */ - NFS_LAYOUT_ROC, /* some lseg had roc bit set */ - NFS_LAYOUT_DESTROYED, /* no new use of layout allowed */ -}; - -enum layoutdriver_policy_flags { - /* Should the pNFS client commit and return the layout upon a setattr */ - PNFS_LAYOUTRET_ON_SETATTR = 1 << 0, - PNFS_LAYOUTRET_ON_ERROR = 1 << 1, -}; - -struct nfs4_deviceid_node; - -/* Per-layout driver specific registration structure */ -struct pnfs_layoutdriver_type { - struct list_head pnfs_tblid; - const u32 id; - const char *name; - struct module *owner; - unsigned flags; - - int (*set_layoutdriver) (struct nfs_server *, const struct nfs_fh *); - int (*clear_layoutdriver) (struct nfs_server *); - - struct pnfs_layout_hdr * (*alloc_layout_hdr) (struct inode *inode, gfp_t gfp_flags); - void (*free_layout_hdr) (struct pnfs_layout_hdr *); - - struct pnfs_layout_segment * (*alloc_lseg) (struct pnfs_layout_hdr *layoutid, struct nfs4_layoutget_res *lgr, gfp_t gfp_flags); - void (*free_lseg) (struct pnfs_layout_segment *lseg); - - /* test for nfs page cache coalescing */ - const struct nfs_pageio_ops *pg_read_ops; - const struct nfs_pageio_ops *pg_write_ops; - - void (*mark_request_commit) (struct nfs_page *req, - struct pnfs_layout_segment *lseg); - void (*clear_request_commit) (struct nfs_page *req); - int (*scan_commit_lists) (struct inode *inode, int max, spinlock_t *lock); - int (*commit_pagelist)(struct inode *inode, struct list_head *mds_pages, int how); - - /* - * Return PNFS_ATTEMPTED to indicate the layout code has attempted - * I/O, else return PNFS_NOT_ATTEMPTED to fall back to normal NFS - */ - enum pnfs_try_status (*read_pagelist) (struct nfs_read_data *nfs_data); - enum pnfs_try_status (*write_pagelist) (struct nfs_write_data *nfs_data, int how); - - void (*free_deviceid_node) (struct nfs4_deviceid_node *); - - void (*encode_layoutreturn) (struct pnfs_layout_hdr *layoutid, - struct xdr_stream *xdr, - const struct nfs4_layoutreturn_args *args); - - void (*cleanup_layoutcommit) (struct nfs4_layoutcommit_data *data); - - void (*encode_layoutcommit) (struct pnfs_layout_hdr *layoutid, - struct xdr_stream *xdr, - const struct nfs4_layoutcommit_args *args); -}; - -struct pnfs_layout_hdr { - atomic_t plh_refcount; - struct list_head plh_layouts; /* other client layouts */ - struct list_head plh_bulk_recall; /* clnt list of bulk recalls */ - struct list_head plh_segs; /* layout segments list */ - nfs4_stateid plh_stateid; - atomic_t plh_outstanding; /* number of RPCs out */ - unsigned long plh_block_lgets; /* block LAYOUTGET if >0 */ - u32 plh_barrier; /* ignore lower seqids */ - unsigned long plh_flags; - loff_t plh_lwb; /* last write byte for layoutcommit */ - struct rpc_cred *plh_lc_cred; /* layoutcommit cred */ - struct inode *plh_inode; -}; - -struct pnfs_device { - struct nfs4_deviceid dev_id; - unsigned int layout_type; - unsigned int mincount; - struct page **pages; - unsigned int pgbase; - unsigned int pglen; -}; - -#define NFS4_PNFS_GETDEVLIST_MAXNUM 16 - -struct pnfs_devicelist { - unsigned int eof; - unsigned int num_devs; - struct nfs4_deviceid dev_id[NFS4_PNFS_GETDEVLIST_MAXNUM]; -}; - -extern int pnfs_register_layoutdriver(struct pnfs_layoutdriver_type *); -extern void pnfs_unregister_layoutdriver(struct pnfs_layoutdriver_type *); - -/* nfs4proc.c */ -extern int nfs4_proc_getdevicelist(struct nfs_server *server, - const struct nfs_fh *fh, - struct pnfs_devicelist *devlist); -extern int nfs4_proc_getdeviceinfo(struct nfs_server *server, - struct pnfs_device *dev); -extern int nfs4_proc_layoutget(struct nfs4_layoutget *lgp); -extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp); - -/* pnfs.c */ -void get_layout_hdr(struct pnfs_layout_hdr *lo); -void put_lseg(struct pnfs_layout_segment *lseg); - -bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *); -bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *, int); - -void set_pnfs_layoutdriver(struct nfs_server *, const struct nfs_fh *, u32); -void unset_pnfs_layoutdriver(struct nfs_server *); -void pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *, struct nfs_page *); -int pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc); -void pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *, struct nfs_page *); -int pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc); -bool pnfs_generic_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev, struct nfs_page *req); -void pnfs_set_lo_fail(struct pnfs_layout_segment *lseg); -int pnfs_layout_process(struct nfs4_layoutget *lgp); -void pnfs_free_lseg_list(struct list_head *tmp_list); -void pnfs_destroy_layout(struct nfs_inode *); -void pnfs_destroy_all_layouts(struct nfs_client *); -void put_layout_hdr(struct pnfs_layout_hdr *lo); -void pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo, - const nfs4_stateid *new, - bool update_barrier); -int pnfs_choose_layoutget_stateid(nfs4_stateid *dst, - struct pnfs_layout_hdr *lo, - struct nfs4_state *open_state); -int mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo, - struct list_head *tmp_list, - struct pnfs_layout_range *recall_range); -bool pnfs_roc(struct inode *ino); -void pnfs_roc_release(struct inode *ino); -void pnfs_roc_set_barrier(struct inode *ino, u32 barrier); -bool pnfs_roc_drain(struct inode *ino, u32 *barrier); -void pnfs_set_layoutcommit(struct nfs_write_data *wdata); -void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data); -int pnfs_layoutcommit_inode(struct inode *inode, bool sync); -int _pnfs_return_layout(struct inode *); -void pnfs_ld_write_done(struct nfs_write_data *); -void pnfs_ld_read_done(struct nfs_read_data *); -struct pnfs_layout_segment *pnfs_update_layout(struct inode *ino, - struct nfs_open_context *ctx, - loff_t pos, - u64 count, - enum pnfs_iomode iomode, - gfp_t gfp_flags); - -void nfs4_deviceid_mark_client_invalid(struct nfs_client *clp); - -/* nfs4_deviceid_flags */ -enum { - NFS_DEVICEID_INVALID = 0, /* set when MDS clientid recalled */ -}; - -/* pnfs_dev.c */ -struct nfs4_deviceid_node { - struct hlist_node node; - struct hlist_node tmpnode; - const struct pnfs_layoutdriver_type *ld; - const struct nfs_client *nfs_client; - unsigned long flags; - struct nfs4_deviceid deviceid; - atomic_t ref; -}; - -struct nfs4_deviceid_node *nfs4_find_get_deviceid(const struct pnfs_layoutdriver_type *, const struct nfs_client *, const struct nfs4_deviceid *); -void nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *, const struct nfs_client *, const struct nfs4_deviceid *); -void nfs4_init_deviceid_node(struct nfs4_deviceid_node *, - const struct pnfs_layoutdriver_type *, - const struct nfs_client *, - const struct nfs4_deviceid *); -struct nfs4_deviceid_node *nfs4_insert_deviceid_node(struct nfs4_deviceid_node *); -bool nfs4_put_deviceid_node(struct nfs4_deviceid_node *); -void nfs4_deviceid_purge_client(const struct nfs_client *); - -static inline int lo_fail_bit(u32 iomode) -{ - return iomode == IOMODE_RW ? - NFS_LAYOUT_RW_FAILED : NFS_LAYOUT_RO_FAILED; -} - -static inline struct pnfs_layout_segment * -get_lseg(struct pnfs_layout_segment *lseg) -{ - if (lseg) { - atomic_inc(&lseg->pls_refcount); - smp_mb__after_atomic_inc(); - } - return lseg; -} - -/* Return true if a layout driver is being used for this mountpoint */ -static inline int pnfs_enabled_sb(struct nfs_server *nfss) -{ - return nfss->pnfs_curr_ld != NULL; -} - -static inline int -pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how) -{ - if (!test_and_clear_bit(NFS_INO_PNFS_COMMIT, &NFS_I(inode)->flags)) - return PNFS_NOT_ATTEMPTED; - return NFS_SERVER(inode)->pnfs_curr_ld->commit_pagelist(inode, mds_pages, how); -} - -static inline bool -pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) -{ - struct inode *inode = req->wb_context->dentry->d_inode; - struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld; - - if (lseg == NULL || ld->mark_request_commit == NULL) - return false; - ld->mark_request_commit(req, lseg); - return true; -} - -static inline bool -pnfs_clear_request_commit(struct nfs_page *req) -{ - struct inode *inode = req->wb_context->dentry->d_inode; - struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld; - - if (ld == NULL || ld->clear_request_commit == NULL) - return false; - ld->clear_request_commit(req); - return true; -} - -static inline int -pnfs_scan_commit_lists(struct inode *inode, int max, spinlock_t *lock) -{ - struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld; - int ret; - - if (ld == NULL || ld->scan_commit_lists == NULL) - return 0; - ret = ld->scan_commit_lists(inode, max, lock); - if (ret != 0) - set_bit(NFS_INO_PNFS_COMMIT, &NFS_I(inode)->flags); - return ret; -} - -/* Should the pNFS client commit and return the layout upon a setattr */ -static inline bool -pnfs_ld_layoutret_on_setattr(struct inode *inode) -{ - if (!pnfs_enabled_sb(NFS_SERVER(inode))) - return false; - return NFS_SERVER(inode)->pnfs_curr_ld->flags & - PNFS_LAYOUTRET_ON_SETATTR; -} - -static inline int pnfs_return_layout(struct inode *ino) -{ - struct nfs_inode *nfsi = NFS_I(ino); - struct nfs_server *nfss = NFS_SERVER(ino); - - if (pnfs_enabled_sb(nfss) && nfsi->layout) - return _pnfs_return_layout(ino); - - return 0; -} - -#ifdef NFS_DEBUG -void nfs4_print_deviceid(const struct nfs4_deviceid *dev_id); -#else -static inline void nfs4_print_deviceid(const struct nfs4_deviceid *dev_id) -{ -} -#endif /* NFS_DEBUG */ -#else /* CONFIG_NFS_V4_1 */ - -static inline void pnfs_destroy_all_layouts(struct nfs_client *clp) -{ -} - -static inline void pnfs_destroy_layout(struct nfs_inode *nfsi) -{ -} - -static inline struct pnfs_layout_segment * -get_lseg(struct pnfs_layout_segment *lseg) -{ - return NULL; -} - -static inline void put_lseg(struct pnfs_layout_segment *lseg) -{ -} - -static inline int pnfs_return_layout(struct inode *ino) -{ - return 0; -} - -static inline bool -pnfs_ld_layoutret_on_setattr(struct inode *inode) -{ - return false; -} - -static inline bool -pnfs_roc(struct inode *ino) -{ - return false; -} - -static inline void -pnfs_roc_release(struct inode *ino) -{ -} - -static inline void -pnfs_roc_set_barrier(struct inode *ino, u32 barrier) -{ -} - -static inline bool -pnfs_roc_drain(struct inode *ino, u32 *barrier) -{ - return false; -} - -static inline void set_pnfs_layoutdriver(struct nfs_server *s, - const struct nfs_fh *mntfh, u32 id) -{ -} - -static inline void unset_pnfs_layoutdriver(struct nfs_server *s) -{ -} - -static inline bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode) -{ - return false; -} - -static inline bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags) -{ - return false; -} - -static inline int -pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how) -{ - return PNFS_NOT_ATTEMPTED; -} - -static inline bool -pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) -{ - return false; -} - -static inline bool -pnfs_clear_request_commit(struct nfs_page *req) -{ - return false; -} - -static inline int -pnfs_scan_commit_lists(struct inode *inode, int max, spinlock_t *lock) -{ - return 0; -} - -static inline int pnfs_layoutcommit_inode(struct inode *inode, bool sync) -{ - return 0; -} - -#endif /* CONFIG_NFS_V4_1 */ - -#endif /* FS_NFS_PNFS_H */ diff --git a/ANDROID_3.4.5/fs/nfs/pnfs_dev.c b/ANDROID_3.4.5/fs/nfs/pnfs_dev.c deleted file mode 100644 index 73f701f1..00000000 --- a/ANDROID_3.4.5/fs/nfs/pnfs_dev.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Device operations for the pnfs client. - * - * Copyright (c) 2002 - * The Regents of the University of Michigan - * All Rights Reserved - * - * Dean Hildebrand <dhildebz@umich.edu> - * Garth Goodson <Garth.Goodson@netapp.com> - * - * Permission is granted to use, copy, create derivative works, and - * redistribute this software and such derivative works for any purpose, - * so long as the name of the University of Michigan is not used in - * any advertising or publicity pertaining to the use or distribution - * of this software without specific, written prior authorization. If - * the above copyright notice or any other identification of the - * University of Michigan is included in any copy of any portion of - * this software, then the disclaimer below must also be included. - * - * This software is provided as is, without representation or warranty - * of any kind either express or implied, including without limitation - * the implied warranties of merchantability, fitness for a particular - * purpose, or noninfringement. The Regents of the University of - * Michigan shall not be liable for any damages, including special, - * indirect, incidental, or consequential damages, with respect to any - * claim arising out of or in connection with the use of the software, - * even if it has been or is hereafter advised of the possibility of - * such damages. - */ - -#include <linux/export.h> -#include "pnfs.h" - -#define NFSDBG_FACILITY NFSDBG_PNFS - -/* - * Device ID RCU cache. A device ID is unique per server and layout type. - */ -#define NFS4_DEVICE_ID_HASH_BITS 5 -#define NFS4_DEVICE_ID_HASH_SIZE (1 << NFS4_DEVICE_ID_HASH_BITS) -#define NFS4_DEVICE_ID_HASH_MASK (NFS4_DEVICE_ID_HASH_SIZE - 1) - -static struct hlist_head nfs4_deviceid_cache[NFS4_DEVICE_ID_HASH_SIZE]; -static DEFINE_SPINLOCK(nfs4_deviceid_lock); - -#ifdef NFS_DEBUG -void -nfs4_print_deviceid(const struct nfs4_deviceid *id) -{ - u32 *p = (u32 *)id; - - dprintk("%s: device id= [%x%x%x%x]\n", __func__, - p[0], p[1], p[2], p[3]); -} -EXPORT_SYMBOL_GPL(nfs4_print_deviceid); -#endif - -static inline u32 -nfs4_deviceid_hash(const struct nfs4_deviceid *id) -{ - unsigned char *cptr = (unsigned char *)id->data; - unsigned int nbytes = NFS4_DEVICEID4_SIZE; - u32 x = 0; - - while (nbytes--) { - x *= 37; - x += *cptr++; - } - return x & NFS4_DEVICE_ID_HASH_MASK; -} - -static struct nfs4_deviceid_node * -_lookup_deviceid(const struct pnfs_layoutdriver_type *ld, - const struct nfs_client *clp, const struct nfs4_deviceid *id, - long hash) -{ - struct nfs4_deviceid_node *d; - struct hlist_node *n; - - hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[hash], node) - if (d->ld == ld && d->nfs_client == clp && - !memcmp(&d->deviceid, id, sizeof(*id))) { - if (atomic_read(&d->ref)) - return d; - else - continue; - } - return NULL; -} - -/* - * Lookup a deviceid in cache and get a reference count on it if found - * - * @clp nfs_client associated with deviceid - * @id deviceid to look up - */ -static struct nfs4_deviceid_node * -_find_get_deviceid(const struct pnfs_layoutdriver_type *ld, - const struct nfs_client *clp, const struct nfs4_deviceid *id, - long hash) -{ - struct nfs4_deviceid_node *d; - - rcu_read_lock(); - d = _lookup_deviceid(ld, clp, id, hash); - if (d != NULL) - atomic_inc(&d->ref); - rcu_read_unlock(); - return d; -} - -struct nfs4_deviceid_node * -nfs4_find_get_deviceid(const struct pnfs_layoutdriver_type *ld, - const struct nfs_client *clp, const struct nfs4_deviceid *id) -{ - return _find_get_deviceid(ld, clp, id, nfs4_deviceid_hash(id)); -} -EXPORT_SYMBOL_GPL(nfs4_find_get_deviceid); - -/* - * Remove a deviceid from cache - * - * @clp nfs_client associated with deviceid - * @id the deviceid to unhash - * - * @ret the unhashed node, if found and dereferenced to zero, NULL otherwise. - */ -void -nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *ld, - const struct nfs_client *clp, const struct nfs4_deviceid *id) -{ - struct nfs4_deviceid_node *d; - - spin_lock(&nfs4_deviceid_lock); - rcu_read_lock(); - d = _lookup_deviceid(ld, clp, id, nfs4_deviceid_hash(id)); - rcu_read_unlock(); - if (!d) { - spin_unlock(&nfs4_deviceid_lock); - return; - } - hlist_del_init_rcu(&d->node); - spin_unlock(&nfs4_deviceid_lock); - synchronize_rcu(); - - /* balance the initial ref set in pnfs_insert_deviceid */ - if (atomic_dec_and_test(&d->ref)) - d->ld->free_deviceid_node(d); -} -EXPORT_SYMBOL_GPL(nfs4_delete_deviceid); - -void -nfs4_init_deviceid_node(struct nfs4_deviceid_node *d, - const struct pnfs_layoutdriver_type *ld, - const struct nfs_client *nfs_client, - const struct nfs4_deviceid *id) -{ - INIT_HLIST_NODE(&d->node); - INIT_HLIST_NODE(&d->tmpnode); - d->ld = ld; - d->nfs_client = nfs_client; - d->flags = 0; - d->deviceid = *id; - atomic_set(&d->ref, 1); -} -EXPORT_SYMBOL_GPL(nfs4_init_deviceid_node); - -/* - * Uniquely initialize and insert a deviceid node into cache - * - * @new new deviceid node - * Note that the caller must set up the following members: - * new->ld - * new->nfs_client - * new->deviceid - * - * @ret the inserted node, if none found, otherwise, the found entry. - */ -struct nfs4_deviceid_node * -nfs4_insert_deviceid_node(struct nfs4_deviceid_node *new) -{ - struct nfs4_deviceid_node *d; - long hash; - - spin_lock(&nfs4_deviceid_lock); - hash = nfs4_deviceid_hash(&new->deviceid); - d = _find_get_deviceid(new->ld, new->nfs_client, &new->deviceid, hash); - if (d) { - spin_unlock(&nfs4_deviceid_lock); - return d; - } - - hlist_add_head_rcu(&new->node, &nfs4_deviceid_cache[hash]); - spin_unlock(&nfs4_deviceid_lock); - atomic_inc(&new->ref); - - return new; -} -EXPORT_SYMBOL_GPL(nfs4_insert_deviceid_node); - -/* - * Dereference a deviceid node and delete it when its reference count drops - * to zero. - * - * @d deviceid node to put - * - * return true iff the node was deleted - * Note that since the test for d->ref == 0 is sufficient to establish - * that the node is no longer hashed in the global device id cache. - */ -bool -nfs4_put_deviceid_node(struct nfs4_deviceid_node *d) -{ - if (!atomic_dec_and_test(&d->ref)) - return false; - d->ld->free_deviceid_node(d); - return true; -} -EXPORT_SYMBOL_GPL(nfs4_put_deviceid_node); - -static void -_deviceid_purge_client(const struct nfs_client *clp, long hash) -{ - struct nfs4_deviceid_node *d; - struct hlist_node *n; - HLIST_HEAD(tmp); - - spin_lock(&nfs4_deviceid_lock); - rcu_read_lock(); - hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[hash], node) - if (d->nfs_client == clp && atomic_read(&d->ref)) { - hlist_del_init_rcu(&d->node); - hlist_add_head(&d->tmpnode, &tmp); - } - rcu_read_unlock(); - spin_unlock(&nfs4_deviceid_lock); - - if (hlist_empty(&tmp)) - return; - - synchronize_rcu(); - while (!hlist_empty(&tmp)) { - d = hlist_entry(tmp.first, struct nfs4_deviceid_node, tmpnode); - hlist_del(&d->tmpnode); - if (atomic_dec_and_test(&d->ref)) - d->ld->free_deviceid_node(d); - } -} - -void -nfs4_deviceid_purge_client(const struct nfs_client *clp) -{ - long h; - - if (!(clp->cl_exchange_flags & EXCHGID4_FLAG_USE_PNFS_MDS)) - return; - for (h = 0; h < NFS4_DEVICE_ID_HASH_SIZE; h++) - _deviceid_purge_client(clp, h); -} - -/* - * Stop use of all deviceids associated with an nfs_client - */ -void -nfs4_deviceid_mark_client_invalid(struct nfs_client *clp) -{ - struct nfs4_deviceid_node *d; - struct hlist_node *n; - int i; - - rcu_read_lock(); - for (i = 0; i < NFS4_DEVICE_ID_HASH_SIZE; i ++){ - hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[i], node) - if (d->nfs_client == clp) - set_bit(NFS_DEVICEID_INVALID, &d->flags); - } - rcu_read_unlock(); -} diff --git a/ANDROID_3.4.5/fs/nfs/proc.c b/ANDROID_3.4.5/fs/nfs/proc.c deleted file mode 100644 index b63b6f4d..00000000 --- a/ANDROID_3.4.5/fs/nfs/proc.c +++ /dev/null @@ -1,771 +0,0 @@ -/* - * linux/fs/nfs/proc.c - * - * Copyright (C) 1992, 1993, 1994 Rick Sladkey - * - * OS-independent nfs remote procedure call functions - * - * Tuned by Alan Cox <A.Cox@swansea.ac.uk> for >3K buffers - * so at last we can have decent(ish) throughput off a - * Sun server. - * - * Coding optimized and cleaned up by Florian La Roche. - * 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. - * - * Also, the code currently doesn't check the size of the packet, when - * it decodes the packet. - * - * Feel free to fix it and mail me the diffs if it worries you. - * - * 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). - */ - -#include <linux/types.h> -#include <linux/param.h> -#include <linux/time.h> -#include <linux/mm.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 <linux/nfs_page.h> -#include <linux/lockd/bind.h> -#include <linux/freezer.h> -#include "internal.h" - -#define NFSDBG_FACILITY NFSDBG_PROC - -/* - * wrapper to handle the -EKEYEXPIRED error message. This should generally - * only happen if using krb5 auth and a user's TGT expires. NFSv2 doesn't - * support the NFSERR_JUKEBOX error code, but we handle this situation in the - * same way that we handle that error with NFSv3. - */ -static int -nfs_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) -{ - int res; - do { - res = rpc_call_sync(clnt, msg, flags); - if (res != -EKEYEXPIRED) - break; - freezable_schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME); - res = -ERESTARTSYS; - } while (!fatal_signal_pending(current)); - return res; -} - -#define rpc_call_sync(clnt, msg, flags) nfs_rpc_wrapper(clnt, msg, flags) - -static int -nfs_async_handle_expired_key(struct rpc_task *task) -{ - if (task->tk_status != -EKEYEXPIRED) - return 0; - task->tk_status = 0; - rpc_restart_call(task); - rpc_delay(task, NFS_JUKEBOX_RETRY_TIME); - return 1; -} - -/* - * 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_fsinfo *info) -{ - struct nfs_fattr *fattr = info->fattr; - struct nfs2_fsstat fsinfo; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_GETATTR], - .rpc_argp = fhandle, - .rpc_resp = fattr, - }; - int status; - - dprintk("%s: call getattr\n", __func__); - nfs_fattr_init(fattr); - status = rpc_call_sync(server->client, &msg, 0); - /* Retry with default authentication if different */ - if (status && server->nfs_client->cl_rpcclient != server->client) - status = rpc_call_sync(server->nfs_client->cl_rpcclient, &msg, 0); - dprintk("%s: reply getattr: %d\n", __func__, status); - if (status) - return status; - dprintk("%s: call statfs\n", __func__); - msg.rpc_proc = &nfs_procedures[NFSPROC_STATFS]; - msg.rpc_resp = &fsinfo; - status = rpc_call_sync(server->client, &msg, 0); - /* Retry with default authentication if different */ - if (status && server->nfs_client->cl_rpcclient != server->client) - status = rpc_call_sync(server->nfs_client->cl_rpcclient, &msg, 0); - dprintk("%s: reply statfs: %d\n", __func__, status); - if (status) - return status; - info->rtmax = NFS_MAXDATA; - info->rtpref = fsinfo.tsize; - info->rtmult = fsinfo.bsize; - info->wtmax = NFS_MAXDATA; - info->wtpref = fsinfo.tsize; - info->wtmult = fsinfo.bsize; - info->dtpref = fsinfo.tsize; - info->maxfilesize = 0x7FFFFFFF; - info->lease_time = 0; - return 0; -} - -/* - * One function for each procedure in the NFS protocol. - */ -static int -nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) -{ - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_GETATTR], - .rpc_argp = fhandle, - .rpc_resp = fattr, - }; - int status; - - dprintk("NFS call getattr\n"); - nfs_fattr_init(fattr); - status = rpc_call_sync(server->client, &msg, 0); - dprintk("NFS reply getattr: %d\n", status); - return status; -} - -static int -nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, - struct iattr *sattr) -{ - struct inode *inode = dentry->d_inode; - struct nfs_sattrargs arg = { - .fh = NFS_FH(inode), - .sattr = sattr - }; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_SETATTR], - .rpc_argp = &arg, - .rpc_resp = fattr, - }; - int status; - - /* Mask out the non-modebit related stuff from attr->ia_mode */ - sattr->ia_mode &= S_IALLUGO; - - dprintk("NFS call setattr\n"); - if (sattr->ia_valid & ATTR_FILE) - msg.rpc_cred = nfs_file_cred(sattr->ia_file); - nfs_fattr_init(fattr); - status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); - if (status == 0) - nfs_setattr_update_inode(inode, sattr); - dprintk("NFS reply setattr: %d\n", status); - return status; -} - -static int -nfs_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) -{ - struct nfs_diropargs arg = { - .fh = NFS_FH(dir), - .name = name->name, - .len = name->len - }; - struct nfs_diropok res = { - .fh = fhandle, - .fattr = fattr - }; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_LOOKUP], - .rpc_argp = &arg, - .rpc_resp = &res, - }; - int status; - - dprintk("NFS call lookup %s\n", name->name); - nfs_fattr_init(fattr); - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - dprintk("NFS reply lookup: %d\n", status); - return status; -} - -static int nfs_proc_readlink(struct inode *inode, struct page *page, - unsigned int pgbase, unsigned int pglen) -{ - struct nfs_readlinkargs args = { - .fh = NFS_FH(inode), - .pgbase = pgbase, - .pglen = pglen, - .pages = &page - }; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_READLINK], - .rpc_argp = &args, - }; - int status; - - dprintk("NFS call readlink\n"); - status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); - dprintk("NFS reply readlink: %d\n", status); - return status; -} - -struct nfs_createdata { - struct nfs_createargs arg; - struct nfs_diropok res; - struct nfs_fh fhandle; - struct nfs_fattr fattr; -}; - -static struct nfs_createdata *nfs_alloc_createdata(struct inode *dir, - struct dentry *dentry, struct iattr *sattr) -{ - struct nfs_createdata *data; - - data = kmalloc(sizeof(*data), GFP_KERNEL); - - if (data != NULL) { - data->arg.fh = NFS_FH(dir); - data->arg.name = dentry->d_name.name; - data->arg.len = dentry->d_name.len; - data->arg.sattr = sattr; - nfs_fattr_init(&data->fattr); - data->fhandle.size = 0; - data->res.fh = &data->fhandle; - data->res.fattr = &data->fattr; - } - return data; -}; - -static void nfs_free_createdata(const struct nfs_createdata *data) -{ - kfree(data); -} - -static int -nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, - int flags, struct nfs_open_context *ctx) -{ - struct nfs_createdata *data; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_CREATE], - }; - int status = -ENOMEM; - - dprintk("NFS call create %s\n", dentry->d_name.name); - data = nfs_alloc_createdata(dir, dentry, sattr); - if (data == NULL) - goto out; - msg.rpc_argp = &data->arg; - msg.rpc_resp = &data->res; - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - nfs_mark_for_revalidate(dir); - if (status == 0) - status = nfs_instantiate(dentry, data->res.fh, data->res.fattr); - nfs_free_createdata(data); -out: - dprintk("NFS reply create: %d\n", status); - return status; -} - -/* - * In NFSv2, mknod is grafted onto the create call. - */ -static int -nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, - dev_t rdev) -{ - struct nfs_createdata *data; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_CREATE], - }; - umode_t mode; - int status = -ENOMEM; - - dprintk("NFS call mknod %s\n", dentry->d_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(mode) || S_ISBLK(mode)) { - sattr->ia_valid |= ATTR_SIZE; - sattr->ia_size = new_encode_dev(rdev);/* get out your barf bag */ - } - - data = nfs_alloc_createdata(dir, dentry, sattr); - if (data == NULL) - goto out; - msg.rpc_argp = &data->arg; - msg.rpc_resp = &data->res; - - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - nfs_mark_for_revalidate(dir); - - if (status == -EINVAL && S_ISFIFO(mode)) { - sattr->ia_mode = mode; - nfs_fattr_init(data->res.fattr); - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - } - if (status == 0) - status = nfs_instantiate(dentry, data->res.fh, data->res.fattr); - nfs_free_createdata(data); -out: - dprintk("NFS reply mknod: %d\n", status); - return status; -} - -static int -nfs_proc_remove(struct inode *dir, struct qstr *name) -{ - struct nfs_removeargs arg = { - .fh = NFS_FH(dir), - .name.len = name->len, - .name.name = name->name, - }; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_REMOVE], - .rpc_argp = &arg, - }; - int status; - - dprintk("NFS call remove %s\n", name->name); - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - nfs_mark_for_revalidate(dir); - - dprintk("NFS reply remove: %d\n", status); - return status; -} - -static void -nfs_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) -{ - msg->rpc_proc = &nfs_procedures[NFSPROC_REMOVE]; -} - -static void nfs_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data) -{ - rpc_call_start(task); -} - -static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir) -{ - if (nfs_async_handle_expired_key(task)) - return 0; - nfs_mark_for_revalidate(dir); - return 1; -} - -static void -nfs_proc_rename_setup(struct rpc_message *msg, struct inode *dir) -{ - msg->rpc_proc = &nfs_procedures[NFSPROC_RENAME]; -} - -static void nfs_proc_rename_rpc_prepare(struct rpc_task *task, struct nfs_renamedata *data) -{ - rpc_call_start(task); -} - -static int -nfs_proc_rename_done(struct rpc_task *task, struct inode *old_dir, - struct inode *new_dir) -{ - if (nfs_async_handle_expired_key(task)) - return 0; - nfs_mark_for_revalidate(old_dir); - nfs_mark_for_revalidate(new_dir); - return 1; -} - -static int -nfs_proc_rename(struct inode *old_dir, struct qstr *old_name, - struct inode *new_dir, struct qstr *new_name) -{ - struct nfs_renameargs arg = { - .old_dir = NFS_FH(old_dir), - .old_name = old_name, - .new_dir = NFS_FH(new_dir), - .new_name = new_name, - }; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_RENAME], - .rpc_argp = &arg, - }; - int status; - - dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name); - status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0); - nfs_mark_for_revalidate(old_dir); - nfs_mark_for_revalidate(new_dir); - dprintk("NFS reply rename: %d\n", status); - return status; -} - -static int -nfs_proc_link(struct inode *inode, struct inode *dir, struct qstr *name) -{ - struct nfs_linkargs arg = { - .fromfh = NFS_FH(inode), - .tofh = NFS_FH(dir), - .toname = name->name, - .tolen = name->len - }; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_LINK], - .rpc_argp = &arg, - }; - int status; - - dprintk("NFS call link %s\n", name->name); - status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); - nfs_mark_for_revalidate(inode); - nfs_mark_for_revalidate(dir); - dprintk("NFS reply link: %d\n", status); - return status; -} - -static int -nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page, - unsigned int len, struct iattr *sattr) -{ - struct nfs_fh *fh; - struct nfs_fattr *fattr; - struct nfs_symlinkargs arg = { - .fromfh = NFS_FH(dir), - .fromname = dentry->d_name.name, - .fromlen = dentry->d_name.len, - .pages = &page, - .pathlen = len, - .sattr = sattr - }; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_SYMLINK], - .rpc_argp = &arg, - }; - int status = -ENAMETOOLONG; - - dprintk("NFS call symlink %s\n", dentry->d_name.name); - - if (len > NFS2_MAXPATHLEN) - goto out; - - fh = nfs_alloc_fhandle(); - fattr = nfs_alloc_fattr(); - status = -ENOMEM; - if (fh == NULL || fattr == NULL) - goto out_free; - - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - nfs_mark_for_revalidate(dir); - - /* - * V2 SYMLINK requests don't return any attributes. Setting the - * filehandle size to zero indicates to nfs_instantiate that it - * should fill in the data with a LOOKUP call on the wire. - */ - if (status == 0) - status = nfs_instantiate(dentry, fh, fattr); - -out_free: - nfs_free_fattr(fattr); - nfs_free_fhandle(fh); -out: - dprintk("NFS reply symlink: %d\n", status); - return status; -} - -static int -nfs_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr) -{ - struct nfs_createdata *data; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_MKDIR], - }; - int status = -ENOMEM; - - dprintk("NFS call mkdir %s\n", dentry->d_name.name); - data = nfs_alloc_createdata(dir, dentry, sattr); - if (data == NULL) - goto out; - msg.rpc_argp = &data->arg; - msg.rpc_resp = &data->res; - - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - nfs_mark_for_revalidate(dir); - if (status == 0) - status = nfs_instantiate(dentry, data->res.fh, data->res.fattr); - nfs_free_createdata(data); -out: - dprintk("NFS reply mkdir: %d\n", status); - return status; -} - -static int -nfs_proc_rmdir(struct inode *dir, struct qstr *name) -{ - struct nfs_diropargs arg = { - .fh = NFS_FH(dir), - .name = name->name, - .len = name->len - }; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_RMDIR], - .rpc_argp = &arg, - }; - int status; - - dprintk("NFS call rmdir %s\n", name->name); - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - nfs_mark_for_revalidate(dir); - dprintk("NFS reply rmdir: %d\n", status); - return status; -} - -/* - * 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 *dentry, struct rpc_cred *cred, - u64 cookie, struct page **pages, unsigned int count, int plus) -{ - struct inode *dir = dentry->d_inode; - struct nfs_readdirargs arg = { - .fh = NFS_FH(dir), - .cookie = cookie, - .count = count, - .pages = pages, - }; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_READDIR], - .rpc_argp = &arg, - .rpc_cred = cred, - }; - int status; - - dprintk("NFS call readdir %d\n", (unsigned int)cookie); - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - - nfs_invalidate_atime(dir); - - dprintk("NFS reply readdir: %d\n", status); - return status; -} - -static int -nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsstat *stat) -{ - struct nfs2_fsstat fsinfo; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_STATFS], - .rpc_argp = fhandle, - .rpc_resp = &fsinfo, - }; - int status; - - dprintk("NFS call statfs\n"); - nfs_fattr_init(stat->fattr); - status = rpc_call_sync(server->client, &msg, 0); - dprintk("NFS reply statfs: %d\n", status); - if (status) - goto out; - stat->tbytes = (u64)fsinfo.blocks * fsinfo.bsize; - stat->fbytes = (u64)fsinfo.bfree * fsinfo.bsize; - stat->abytes = (u64)fsinfo.bavail * fsinfo.bsize; - stat->tfiles = 0; - stat->ffiles = 0; - stat->afiles = 0; -out: - return status; -} - -static int -nfs_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info) -{ - struct nfs2_fsstat fsinfo; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_STATFS], - .rpc_argp = fhandle, - .rpc_resp = &fsinfo, - }; - int status; - - dprintk("NFS call fsinfo\n"); - nfs_fattr_init(info->fattr); - status = rpc_call_sync(server->client, &msg, 0); - dprintk("NFS reply fsinfo: %d\n", status); - if (status) - goto out; - info->rtmax = NFS_MAXDATA; - info->rtpref = fsinfo.tsize; - info->rtmult = fsinfo.bsize; - info->wtmax = NFS_MAXDATA; - info->wtpref = fsinfo.tsize; - info->wtmult = fsinfo.bsize; - info->dtpref = fsinfo.tsize; - info->maxfilesize = 0x7FFFFFFF; - info->lease_time = 0; -out: - return status; -} - -static int -nfs_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_pathconf *info) -{ - info->max_link = 0; - info->max_namelen = NFS2_MAXNAMLEN; - return 0; -} - -static int nfs_read_done(struct rpc_task *task, struct nfs_read_data *data) -{ - if (nfs_async_handle_expired_key(task)) - return -EAGAIN; - - nfs_invalidate_atime(data->inode); - if (task->tk_status >= 0) { - nfs_refresh_inode(data->inode, data->res.fattr); - /* Emulate the eof flag, which isn't normally needed in NFSv2 - * as it is guaranteed to always return the file attributes - */ - if (data->args.offset + data->args.count >= data->res.fattr->size) - data->res.eof = 1; - } - return 0; -} - -static void nfs_proc_read_setup(struct nfs_read_data *data, struct rpc_message *msg) -{ - msg->rpc_proc = &nfs_procedures[NFSPROC_READ]; -} - -static void nfs_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) -{ - rpc_call_start(task); -} - -static int nfs_write_done(struct rpc_task *task, struct nfs_write_data *data) -{ - if (nfs_async_handle_expired_key(task)) - return -EAGAIN; - - if (task->tk_status >= 0) - nfs_post_op_update_inode_force_wcc(data->inode, data->res.fattr); - return 0; -} - -static void nfs_proc_write_setup(struct nfs_write_data *data, struct rpc_message *msg) -{ - /* Note: NFSv2 ignores @stable and always uses NFS_FILE_SYNC */ - data->args.stable = NFS_FILE_SYNC; - msg->rpc_proc = &nfs_procedures[NFSPROC_WRITE]; -} - -static void nfs_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) -{ - rpc_call_start(task); -} - -static void -nfs_proc_commit_setup(struct nfs_write_data *data, struct rpc_message *msg) -{ - BUG(); -} - -static int -nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl) -{ - struct inode *inode = filp->f_path.dentry->d_inode; - - return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl); -} - -/* Helper functions for NFS lock bounds checking */ -#define NFS_LOCK32_OFFSET_MAX ((__s32)0x7fffffffUL) -static int nfs_lock_check_bounds(const struct file_lock *fl) -{ - __s32 start, end; - - start = (__s32)fl->fl_start; - if ((loff_t)start != fl->fl_start) - goto out_einval; - - if (fl->fl_end != OFFSET_MAX) { - end = (__s32)fl->fl_end; - if ((loff_t)end != fl->fl_end) - goto out_einval; - } else - end = NFS_LOCK32_OFFSET_MAX; - - if (start < 0 || start > end) - goto out_einval; - return 0; -out_einval: - return -EINVAL; -} - -const struct nfs_rpc_ops nfs_v2_clientops = { - .version = 2, /* protocol version */ - .dentry_ops = &nfs_dentry_operations, - .dir_inode_ops = &nfs_dir_inode_operations, - .file_inode_ops = &nfs_file_inode_operations, - .file_ops = &nfs_file_operations, - .getroot = nfs_proc_get_root, - .getattr = nfs_proc_getattr, - .setattr = nfs_proc_setattr, - .lookup = nfs_proc_lookup, - .access = NULL, /* access */ - .readlink = nfs_proc_readlink, - .create = nfs_proc_create, - .remove = nfs_proc_remove, - .unlink_setup = nfs_proc_unlink_setup, - .unlink_rpc_prepare = nfs_proc_unlink_rpc_prepare, - .unlink_done = nfs_proc_unlink_done, - .rename = nfs_proc_rename, - .rename_setup = nfs_proc_rename_setup, - .rename_rpc_prepare = nfs_proc_rename_rpc_prepare, - .rename_done = nfs_proc_rename_done, - .link = nfs_proc_link, - .symlink = nfs_proc_symlink, - .mkdir = nfs_proc_mkdir, - .rmdir = nfs_proc_rmdir, - .readdir = nfs_proc_readdir, - .mknod = nfs_proc_mknod, - .statfs = nfs_proc_statfs, - .fsinfo = nfs_proc_fsinfo, - .pathconf = nfs_proc_pathconf, - .decode_dirent = nfs2_decode_dirent, - .read_setup = nfs_proc_read_setup, - .read_rpc_prepare = nfs_proc_read_rpc_prepare, - .read_done = nfs_read_done, - .write_setup = nfs_proc_write_setup, - .write_rpc_prepare = nfs_proc_write_rpc_prepare, - .write_done = nfs_write_done, - .commit_setup = nfs_proc_commit_setup, - .lock = nfs_proc_lock, - .lock_check_bounds = nfs_lock_check_bounds, - .close_context = nfs_close_context, - .init_client = nfs_init_client, -}; diff --git a/ANDROID_3.4.5/fs/nfs/read.c b/ANDROID_3.4.5/fs/nfs/read.c deleted file mode 100644 index 0a4be28c..00000000 --- a/ANDROID_3.4.5/fs/nfs/read.c +++ /dev/null @@ -1,699 +0,0 @@ -/* - * linux/fs/nfs/read.c - * - * Block I/O for NFS - * - * Partial copy of Linus' read cache modifications to fs/nfs/file.c - * modified for async RPC by okir@monad.swb.de - */ - -#include <linux/time.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/fcntl.h> -#include <linux/stat.h> -#include <linux/mm.h> -#include <linux/slab.h> -#include <linux/pagemap.h> -#include <linux/sunrpc/clnt.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_page.h> -#include <linux/module.h> - -#include "pnfs.h" - -#include "nfs4_fs.h" -#include "internal.h" -#include "iostat.h" -#include "fscache.h" - -#define NFSDBG_FACILITY NFSDBG_PAGECACHE - -static const struct nfs_pageio_ops nfs_pageio_read_ops; -static const struct rpc_call_ops nfs_read_partial_ops; -static const struct rpc_call_ops nfs_read_full_ops; - -static struct kmem_cache *nfs_rdata_cachep; - -struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) -{ - struct nfs_read_data *p; - - p = kmem_cache_zalloc(nfs_rdata_cachep, GFP_KERNEL); - if (p) { - INIT_LIST_HEAD(&p->pages); - p->npages = pagecount; - if (pagecount <= ARRAY_SIZE(p->page_array)) - p->pagevec = p->page_array; - else { - p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_KERNEL); - if (!p->pagevec) { - kmem_cache_free(nfs_rdata_cachep, p); - p = NULL; - } - } - } - return p; -} - -void nfs_readdata_free(struct nfs_read_data *p) -{ - if (p && (p->pagevec != &p->page_array[0])) - kfree(p->pagevec); - kmem_cache_free(nfs_rdata_cachep, p); -} - -void nfs_readdata_release(struct nfs_read_data *rdata) -{ - put_nfs_open_context(rdata->args.context); - nfs_readdata_free(rdata); -} - -static -int nfs_return_empty_page(struct page *page) -{ - zero_user(page, 0, PAGE_CACHE_SIZE); - SetPageUptodate(page); - unlock_page(page); - return 0; -} - -static void nfs_readpage_truncate_uninitialised_page(struct nfs_read_data *data) -{ - unsigned int remainder = data->args.count - data->res.count; - unsigned int base = data->args.pgbase + data->res.count; - unsigned int pglen; - struct page **pages; - - if (data->res.eof == 0 || remainder == 0) - return; - /* - * Note: "remainder" can never be negative, since we check for - * this in the XDR code. - */ - pages = &data->args.pages[base >> PAGE_CACHE_SHIFT]; - base &= ~PAGE_CACHE_MASK; - pglen = PAGE_CACHE_SIZE - base; - for (;;) { - if (remainder <= pglen) { - zero_user(*pages, base, remainder); - break; - } - zero_user(*pages, base, pglen); - pages++; - remainder -= pglen; - pglen = PAGE_CACHE_SIZE; - base = 0; - } -} - -void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio, - struct inode *inode) -{ - nfs_pageio_init(pgio, inode, &nfs_pageio_read_ops, - NFS_SERVER(inode)->rsize, 0); -} - -void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio) -{ - pgio->pg_ops = &nfs_pageio_read_ops; - pgio->pg_bsize = NFS_SERVER(pgio->pg_inode)->rsize; -} -EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds); - -static void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, - struct inode *inode) -{ - if (!pnfs_pageio_init_read(pgio, inode)) - nfs_pageio_init_read_mds(pgio, inode); -} - -int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, - struct page *page) -{ - struct nfs_page *new; - unsigned int len; - struct nfs_pageio_descriptor pgio; - - len = nfs_page_length(page); - if (len == 0) - return nfs_return_empty_page(page); - new = nfs_create_request(ctx, inode, page, 0, len); - if (IS_ERR(new)) { - unlock_page(page); - return PTR_ERR(new); - } - if (len < PAGE_CACHE_SIZE) - zero_user_segment(page, len, PAGE_CACHE_SIZE); - - nfs_pageio_init_read(&pgio, inode); - nfs_pageio_add_request(&pgio, new); - nfs_pageio_complete(&pgio); - return 0; -} - -static void nfs_readpage_release(struct nfs_page *req) -{ - struct inode *d_inode = req->wb_context->dentry->d_inode; - - if (PageUptodate(req->wb_page)) - nfs_readpage_to_fscache(d_inode, req->wb_page, 0); - - unlock_page(req->wb_page); - - dprintk("NFS: read done (%s/%Ld %d@%Ld)\n", - req->wb_context->dentry->d_inode->i_sb->s_id, - (long long)NFS_FILEID(req->wb_context->dentry->d_inode), - req->wb_bytes, - (long long)req_offset(req)); - nfs_release_request(req); -} - -int nfs_initiate_read(struct nfs_read_data *data, struct rpc_clnt *clnt, - const struct rpc_call_ops *call_ops) -{ - struct inode *inode = data->inode; - int swap_flags = IS_SWAPFILE(inode) ? NFS_RPC_SWAPFLAGS : 0; - struct rpc_task *task; - struct rpc_message msg = { - .rpc_argp = &data->args, - .rpc_resp = &data->res, - .rpc_cred = data->cred, - }; - struct rpc_task_setup task_setup_data = { - .task = &data->task, - .rpc_client = clnt, - .rpc_message = &msg, - .callback_ops = call_ops, - .callback_data = data, - .workqueue = nfsiod_workqueue, - .flags = RPC_TASK_ASYNC | swap_flags, - }; - - /* Set up the initial task struct. */ - NFS_PROTO(inode)->read_setup(data, &msg); - - dprintk("NFS: %5u initiated read call (req %s/%lld, %u bytes @ " - "offset %llu)\n", - data->task.tk_pid, - inode->i_sb->s_id, - (long long)NFS_FILEID(inode), - data->args.count, - (unsigned long long)data->args.offset); - - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) - return PTR_ERR(task); - rpc_put_task(task); - return 0; -} -EXPORT_SYMBOL_GPL(nfs_initiate_read); - -/* - * Set up the NFS read request struct - */ -static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, - unsigned int count, unsigned int offset) -{ - struct inode *inode = req->wb_context->dentry->d_inode; - - data->req = req; - data->inode = inode; - data->cred = req->wb_context->cred; - - data->args.fh = NFS_FH(inode); - data->args.offset = req_offset(req) + offset; - data->args.pgbase = req->wb_pgbase + offset; - data->args.pages = data->pagevec; - data->args.count = count; - data->args.context = get_nfs_open_context(req->wb_context); - data->args.lock_context = req->wb_lock_context; - - data->res.fattr = &data->fattr; - data->res.count = count; - data->res.eof = 0; - nfs_fattr_init(&data->fattr); -} - -static int nfs_do_read(struct nfs_read_data *data, - const struct rpc_call_ops *call_ops) -{ - struct inode *inode = data->args.context->dentry->d_inode; - - return nfs_initiate_read(data, NFS_CLIENT(inode), call_ops); -} - -static int -nfs_do_multiple_reads(struct list_head *head, - const struct rpc_call_ops *call_ops) -{ - struct nfs_read_data *data; - int ret = 0; - - while (!list_empty(head)) { - int ret2; - - data = list_entry(head->next, struct nfs_read_data, list); - list_del_init(&data->list); - - ret2 = nfs_do_read(data, call_ops); - if (ret == 0) - ret = ret2; - } - return ret; -} - -static void -nfs_async_read_error(struct list_head *head) -{ - struct nfs_page *req; - - while (!list_empty(head)) { - req = nfs_list_entry(head->next); - nfs_list_remove_request(req); - nfs_readpage_release(req); - } -} - -/* - * Generate multiple requests to fill a single page. - * - * We optimize to reduce the number of read operations on the wire. If we - * detect that we're reading a page, or an area of a page, that is past the - * end of file, we do not generate NFS read operations but just clear the - * parts of the page that would have come back zero from the server anyway. - * - * We rely on the cached value of i_size to make this determination; another - * client can fill pages on the server past our cached end-of-file, but we - * won't see the new data until our attribute cache is updated. This is more - * or less conventional NFS client behavior. - */ -static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc, struct list_head *res) -{ - struct nfs_page *req = nfs_list_entry(desc->pg_list.next); - struct page *page = req->wb_page; - struct nfs_read_data *data; - size_t rsize = desc->pg_bsize, nbytes; - unsigned int offset; - int requests = 0; - int ret = 0; - - nfs_list_remove_request(req); - - offset = 0; - nbytes = desc->pg_count; - do { - size_t len = min(nbytes,rsize); - - data = nfs_readdata_alloc(1); - if (!data) - goto out_bad; - data->pagevec[0] = page; - nfs_read_rpcsetup(req, data, len, offset); - list_add(&data->list, res); - requests++; - nbytes -= len; - offset += len; - } while(nbytes != 0); - atomic_set(&req->wb_complete, requests); - desc->pg_rpc_callops = &nfs_read_partial_ops; - return ret; -out_bad: - while (!list_empty(res)) { - data = list_entry(res->next, struct nfs_read_data, list); - list_del(&data->list); - nfs_readdata_release(data); - } - nfs_readpage_release(req); - return -ENOMEM; -} - -static int nfs_pagein_one(struct nfs_pageio_descriptor *desc, struct list_head *res) -{ - struct nfs_page *req; - struct page **pages; - struct nfs_read_data *data; - struct list_head *head = &desc->pg_list; - int ret = 0; - - data = nfs_readdata_alloc(nfs_page_array_len(desc->pg_base, - desc->pg_count)); - if (!data) { - nfs_async_read_error(head); - ret = -ENOMEM; - goto out; - } - - pages = data->pagevec; - while (!list_empty(head)) { - req = nfs_list_entry(head->next); - nfs_list_remove_request(req); - nfs_list_add_request(req, &data->pages); - *pages++ = req->wb_page; - } - req = nfs_list_entry(data->pages.next); - - nfs_read_rpcsetup(req, data, desc->pg_count, 0); - list_add(&data->list, res); - desc->pg_rpc_callops = &nfs_read_full_ops; -out: - return ret; -} - -int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, struct list_head *head) -{ - if (desc->pg_bsize < PAGE_CACHE_SIZE) - return nfs_pagein_multi(desc, head); - return nfs_pagein_one(desc, head); -} - -static int nfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc) -{ - LIST_HEAD(head); - int ret; - - ret = nfs_generic_pagein(desc, &head); - if (ret == 0) - ret = nfs_do_multiple_reads(&head, desc->pg_rpc_callops); - return ret; -} - -static const struct nfs_pageio_ops nfs_pageio_read_ops = { - .pg_test = nfs_generic_pg_test, - .pg_doio = nfs_generic_pg_readpages, -}; - -/* - * This is the callback from RPC telling us whether a reply was - * received or some error occurred (timeout or socket shutdown). - */ -int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data) -{ - int status; - - dprintk("NFS: %s: %5u, (status %d)\n", __func__, task->tk_pid, - task->tk_status); - - status = NFS_PROTO(data->inode)->read_done(task, data); - if (status != 0) - return status; - - nfs_add_stats(data->inode, NFSIOS_SERVERREADBYTES, data->res.count); - - if (task->tk_status == -ESTALE) { - set_bit(NFS_INO_STALE, &NFS_I(data->inode)->flags); - nfs_mark_for_revalidate(data->inode); - } - return 0; -} - -static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data) -{ - struct nfs_readargs *argp = &data->args; - struct nfs_readres *resp = &data->res; - - if (resp->eof || resp->count == argp->count) - return; - - /* This is a short read! */ - nfs_inc_stats(data->inode, NFSIOS_SHORTREAD); - /* Has the server at least made some progress? */ - if (resp->count == 0) - return; - - /* Yes, so retry the read at the end of the data */ - data->mds_offset += resp->count; - argp->offset += resp->count; - argp->pgbase += resp->count; - argp->count -= resp->count; - rpc_restart_call_prepare(task); -} - -/* - * Handle a read reply that fills part of a page. - */ -static void nfs_readpage_result_partial(struct rpc_task *task, void *calldata) -{ - struct nfs_read_data *data = calldata; - - if (nfs_readpage_result(task, data) != 0) - return; - if (task->tk_status < 0) - return; - - nfs_readpage_truncate_uninitialised_page(data); - nfs_readpage_retry(task, data); -} - -static void nfs_readpage_release_partial(void *calldata) -{ - struct nfs_read_data *data = calldata; - struct nfs_page *req = data->req; - struct page *page = req->wb_page; - int status = data->task.tk_status; - - if (status < 0) - set_bit(PG_PARTIAL_READ_FAILED, &req->wb_flags); - - if (atomic_dec_and_test(&req->wb_complete)) { - if (!test_bit(PG_PARTIAL_READ_FAILED, &req->wb_flags)) - SetPageUptodate(page); - nfs_readpage_release(req); - } - nfs_readdata_release(calldata); -} - -void nfs_read_prepare(struct rpc_task *task, void *calldata) -{ - struct nfs_read_data *data = calldata; - NFS_PROTO(data->inode)->read_rpc_prepare(task, data); -} - -static const struct rpc_call_ops nfs_read_partial_ops = { - .rpc_call_prepare = nfs_read_prepare, - .rpc_call_done = nfs_readpage_result_partial, - .rpc_release = nfs_readpage_release_partial, -}; - -static void nfs_readpage_set_pages_uptodate(struct nfs_read_data *data) -{ - unsigned int count = data->res.count; - unsigned int base = data->args.pgbase; - struct page **pages; - - if (data->res.eof) - count = data->args.count; - if (unlikely(count == 0)) - return; - pages = &data->args.pages[base >> PAGE_CACHE_SHIFT]; - base &= ~PAGE_CACHE_MASK; - count += base; - for (;count >= PAGE_CACHE_SIZE; count -= PAGE_CACHE_SIZE, pages++) - SetPageUptodate(*pages); - if (count == 0) - return; - /* Was this a short read? */ - if (data->res.eof || data->res.count == data->args.count) - SetPageUptodate(*pages); -} - -/* - * This is the callback from RPC telling us whether a reply was - * received or some error occurred (timeout or socket shutdown). - */ -static void nfs_readpage_result_full(struct rpc_task *task, void *calldata) -{ - struct nfs_read_data *data = calldata; - - if (nfs_readpage_result(task, data) != 0) - return; - if (task->tk_status < 0) - return; - /* - * Note: nfs_readpage_retry may change the values of - * data->args. In the multi-page case, we therefore need - * to ensure that we call nfs_readpage_set_pages_uptodate() - * first. - */ - nfs_readpage_truncate_uninitialised_page(data); - nfs_readpage_set_pages_uptodate(data); - nfs_readpage_retry(task, data); -} - -static void nfs_readpage_release_full(void *calldata) -{ - struct nfs_read_data *data = calldata; - - while (!list_empty(&data->pages)) { - struct nfs_page *req = nfs_list_entry(data->pages.next); - - nfs_list_remove_request(req); - nfs_readpage_release(req); - } - nfs_readdata_release(calldata); -} - -static const struct rpc_call_ops nfs_read_full_ops = { - .rpc_call_prepare = nfs_read_prepare, - .rpc_call_done = nfs_readpage_result_full, - .rpc_release = nfs_readpage_release_full, -}; - -/* - * Read a page over NFS. - * We read the page synchronously in the following case: - * - The error flag is set for this page. This happens only when a - * previous async read operation failed. - */ -int nfs_readpage(struct file *file, struct page *page) -{ - struct nfs_open_context *ctx; - struct inode *inode = page->mapping->host; - int error; - - dprintk("NFS: nfs_readpage (%p %ld@%lu)\n", - page, PAGE_CACHE_SIZE, page->index); - nfs_inc_stats(inode, NFSIOS_VFSREADPAGE); - nfs_add_stats(inode, NFSIOS_READPAGES, 1); - - /* - * Try to flush any pending writes to the file.. - * - * NOTE! Because we own the page lock, there cannot - * be any new pending writes generated at this point - * for this page (other pages can be written to). - */ - error = nfs_wb_page(inode, page); - if (error) - goto out_unlock; - if (PageUptodate(page)) - goto out_unlock; - - error = -ESTALE; - if (NFS_STALE(inode)) - goto out_unlock; - - if (file == NULL) { - error = -EBADF; - ctx = nfs_find_open_context(inode, NULL, FMODE_READ); - if (ctx == NULL) - goto out_unlock; - } else - ctx = get_nfs_open_context(nfs_file_open_context(file)); - - if (!IS_SYNC(inode)) { - error = nfs_readpage_from_fscache(ctx, inode, page); - if (error == 0) - goto out; - } - - error = nfs_readpage_async(ctx, inode, page); - -out: - put_nfs_open_context(ctx); - return error; -out_unlock: - unlock_page(page); - return error; -} - -struct nfs_readdesc { - struct nfs_pageio_descriptor *pgio; - struct nfs_open_context *ctx; -}; - -static int -readpage_async_filler(void *data, struct page *page) -{ - struct nfs_readdesc *desc = (struct nfs_readdesc *)data; - struct inode *inode = page->mapping->host; - struct nfs_page *new; - unsigned int len; - int error; - - len = nfs_page_length(page); - if (len == 0) - return nfs_return_empty_page(page); - - new = nfs_create_request(desc->ctx, inode, page, 0, len); - if (IS_ERR(new)) - goto out_error; - - if (len < PAGE_CACHE_SIZE) - zero_user_segment(page, len, PAGE_CACHE_SIZE); - if (!nfs_pageio_add_request(desc->pgio, new)) { - error = desc->pgio->pg_error; - goto out_unlock; - } - return 0; -out_error: - error = PTR_ERR(new); -out_unlock: - unlock_page(page); - return error; -} - -int nfs_readpages(struct file *filp, struct address_space *mapping, - struct list_head *pages, unsigned nr_pages) -{ - struct nfs_pageio_descriptor pgio; - struct nfs_readdesc desc = { - .pgio = &pgio, - }; - struct inode *inode = mapping->host; - unsigned long npages; - int ret = -ESTALE; - - dprintk("NFS: nfs_readpages (%s/%Ld %d)\n", - inode->i_sb->s_id, - (long long)NFS_FILEID(inode), - nr_pages); - nfs_inc_stats(inode, NFSIOS_VFSREADPAGES); - - if (NFS_STALE(inode)) - goto out; - - if (filp == NULL) { - desc.ctx = nfs_find_open_context(inode, NULL, FMODE_READ); - if (desc.ctx == NULL) - return -EBADF; - } else - desc.ctx = get_nfs_open_context(nfs_file_open_context(filp)); - - /* attempt to read as many of the pages as possible from the cache - * - this returns -ENOBUFS immediately if the cookie is negative - */ - ret = nfs_readpages_from_fscache(desc.ctx, inode, mapping, - pages, &nr_pages); - if (ret == 0) - goto read_complete; /* all pages were read */ - - nfs_pageio_init_read(&pgio, inode); - - ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc); - - nfs_pageio_complete(&pgio); - npages = (pgio.pg_bytes_written + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - nfs_add_stats(inode, NFSIOS_READPAGES, npages); -read_complete: - put_nfs_open_context(desc.ctx); -out: - return ret; -} - -int __init nfs_init_readpagecache(void) -{ - nfs_rdata_cachep = kmem_cache_create("nfs_read_data", - sizeof(struct nfs_read_data), - 0, SLAB_HWCACHE_ALIGN, - NULL); - if (nfs_rdata_cachep == NULL) - return -ENOMEM; - - return 0; -} - -void nfs_destroy_readpagecache(void) -{ - kmem_cache_destroy(nfs_rdata_cachep); -} diff --git a/ANDROID_3.4.5/fs/nfs/super.c b/ANDROID_3.4.5/fs/nfs/super.c deleted file mode 100644 index 4ac7fca7..00000000 --- a/ANDROID_3.4.5/fs/nfs/super.c +++ /dev/null @@ -1,3149 +0,0 @@ -/* - * linux/fs/nfs/super.c - * - * Copyright (C) 1992 Rick Sladkey - * - * nfs superblock handling functions - * - * Modularised by Alan Cox <alan@lxorguk.ukuu.org.uk>, while hacking some - * experimental NFS changes. Modularisation taken straight from SYS5 fs. - * - * Change to nfs_read_super() to permit NFS mounts to multi-homed hosts. - * J.S.Peatfield@damtp.cam.ac.uk - * - * Split from inode.c by David Howells <dhowells@redhat.com> - * - * - superblocks are indexed on server only - all inodes, dentries, etc. associated with a - * particular server are held in the same superblock - * - NFS superblocks can have several effective roots to the dentry tree - * - directory type roots are spliced into the tree when a path from one root reaches the root - * of another (see nfs_lookup()) - */ - -#include <linux/module.h> -#include <linux/init.h> - -#include <linux/time.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/string.h> -#include <linux/stat.h> -#include <linux/errno.h> -#include <linux/unistd.h> -#include <linux/sunrpc/clnt.h> -#include <linux/sunrpc/stats.h> -#include <linux/sunrpc/metrics.h> -#include <linux/sunrpc/xprtsock.h> -#include <linux/sunrpc/xprtrdma.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_mount.h> -#include <linux/nfs4_mount.h> -#include <linux/lockd/bind.h> -#include <linux/seq_file.h> -#include <linux/mount.h> -#include <linux/namei.h> -#include <linux/nfs_idmap.h> -#include <linux/vfs.h> -#include <linux/inet.h> -#include <linux/in6.h> -#include <linux/slab.h> -#include <net/ipv6.h> -#include <linux/netdevice.h> -#include <linux/nfs_xdr.h> -#include <linux/magic.h> -#include <linux/parser.h> -#include <linux/nsproxy.h> -#include <linux/rcupdate.h> - -#include <asm/uaccess.h> - -#include "nfs4_fs.h" -#include "callback.h" -#include "delegation.h" -#include "iostat.h" -#include "internal.h" -#include "fscache.h" -#include "pnfs.h" - -#define NFSDBG_FACILITY NFSDBG_VFS - -#ifdef CONFIG_NFS_V3 -#define NFS_DEFAULT_VERSION 3 -#else -#define NFS_DEFAULT_VERSION 2 -#endif - -enum { - /* Mount options that take no arguments */ - Opt_soft, Opt_hard, - Opt_posix, Opt_noposix, - Opt_cto, Opt_nocto, - Opt_ac, Opt_noac, - Opt_lock, Opt_nolock, - Opt_udp, Opt_tcp, Opt_rdma, - Opt_acl, Opt_noacl, - Opt_rdirplus, Opt_nordirplus, - Opt_sharecache, Opt_nosharecache, - Opt_resvport, Opt_noresvport, - Opt_fscache, Opt_nofscache, - - /* Mount options that take integer arguments */ - Opt_port, - Opt_rsize, Opt_wsize, Opt_bsize, - Opt_timeo, Opt_retrans, - Opt_acregmin, Opt_acregmax, - Opt_acdirmin, Opt_acdirmax, - Opt_actimeo, - Opt_namelen, - Opt_mountport, - Opt_mountvers, - Opt_minorversion, - - /* Mount options that take string arguments */ - Opt_nfsvers, - Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost, - Opt_addr, Opt_mountaddr, Opt_clientaddr, - Opt_lookupcache, - Opt_fscache_uniq, - Opt_local_lock, - - /* Special mount options */ - Opt_userspace, Opt_deprecated, Opt_sloppy, - - Opt_err -}; - -static const match_table_t nfs_mount_option_tokens = { - { Opt_userspace, "bg" }, - { Opt_userspace, "fg" }, - { Opt_userspace, "retry=%s" }, - - { Opt_sloppy, "sloppy" }, - - { Opt_soft, "soft" }, - { Opt_hard, "hard" }, - { Opt_deprecated, "intr" }, - { Opt_deprecated, "nointr" }, - { Opt_posix, "posix" }, - { Opt_noposix, "noposix" }, - { Opt_cto, "cto" }, - { Opt_nocto, "nocto" }, - { Opt_ac, "ac" }, - { Opt_noac, "noac" }, - { Opt_lock, "lock" }, - { Opt_nolock, "nolock" }, - { Opt_udp, "udp" }, - { Opt_tcp, "tcp" }, - { Opt_rdma, "rdma" }, - { Opt_acl, "acl" }, - { Opt_noacl, "noacl" }, - { Opt_rdirplus, "rdirplus" }, - { Opt_nordirplus, "nordirplus" }, - { Opt_sharecache, "sharecache" }, - { Opt_nosharecache, "nosharecache" }, - { Opt_resvport, "resvport" }, - { Opt_noresvport, "noresvport" }, - { Opt_fscache, "fsc" }, - { Opt_nofscache, "nofsc" }, - - { Opt_port, "port=%s" }, - { Opt_rsize, "rsize=%s" }, - { Opt_wsize, "wsize=%s" }, - { Opt_bsize, "bsize=%s" }, - { Opt_timeo, "timeo=%s" }, - { Opt_retrans, "retrans=%s" }, - { Opt_acregmin, "acregmin=%s" }, - { Opt_acregmax, "acregmax=%s" }, - { Opt_acdirmin, "acdirmin=%s" }, - { Opt_acdirmax, "acdirmax=%s" }, - { Opt_actimeo, "actimeo=%s" }, - { Opt_namelen, "namlen=%s" }, - { Opt_mountport, "mountport=%s" }, - { Opt_mountvers, "mountvers=%s" }, - { Opt_minorversion, "minorversion=%s" }, - - { Opt_nfsvers, "nfsvers=%s" }, - { Opt_nfsvers, "vers=%s" }, - - { Opt_sec, "sec=%s" }, - { Opt_proto, "proto=%s" }, - { Opt_mountproto, "mountproto=%s" }, - { Opt_addr, "addr=%s" }, - { Opt_clientaddr, "clientaddr=%s" }, - { Opt_mounthost, "mounthost=%s" }, - { Opt_mountaddr, "mountaddr=%s" }, - - { Opt_lookupcache, "lookupcache=%s" }, - { Opt_fscache_uniq, "fsc=%s" }, - { Opt_local_lock, "local_lock=%s" }, - - /* The following needs to be listed after all other options */ - { Opt_nfsvers, "v%s" }, - - { Opt_err, NULL } -}; - -enum { - Opt_xprt_udp, Opt_xprt_udp6, Opt_xprt_tcp, Opt_xprt_tcp6, Opt_xprt_rdma, - - Opt_xprt_err -}; - -static const match_table_t nfs_xprt_protocol_tokens = { - { Opt_xprt_udp, "udp" }, - { Opt_xprt_udp6, "udp6" }, - { Opt_xprt_tcp, "tcp" }, - { Opt_xprt_tcp6, "tcp6" }, - { Opt_xprt_rdma, "rdma" }, - - { Opt_xprt_err, NULL } -}; - -enum { - Opt_sec_none, Opt_sec_sys, - Opt_sec_krb5, Opt_sec_krb5i, Opt_sec_krb5p, - Opt_sec_lkey, Opt_sec_lkeyi, Opt_sec_lkeyp, - Opt_sec_spkm, Opt_sec_spkmi, Opt_sec_spkmp, - - Opt_sec_err -}; - -static const match_table_t nfs_secflavor_tokens = { - { Opt_sec_none, "none" }, - { Opt_sec_none, "null" }, - { Opt_sec_sys, "sys" }, - - { Opt_sec_krb5, "krb5" }, - { Opt_sec_krb5i, "krb5i" }, - { Opt_sec_krb5p, "krb5p" }, - - { Opt_sec_lkey, "lkey" }, - { Opt_sec_lkeyi, "lkeyi" }, - { Opt_sec_lkeyp, "lkeyp" }, - - { Opt_sec_spkm, "spkm3" }, - { Opt_sec_spkmi, "spkm3i" }, - { Opt_sec_spkmp, "spkm3p" }, - - { Opt_sec_err, NULL } -}; - -enum { - Opt_lookupcache_all, Opt_lookupcache_positive, - Opt_lookupcache_none, - - Opt_lookupcache_err -}; - -static match_table_t nfs_lookupcache_tokens = { - { Opt_lookupcache_all, "all" }, - { Opt_lookupcache_positive, "pos" }, - { Opt_lookupcache_positive, "positive" }, - { Opt_lookupcache_none, "none" }, - - { Opt_lookupcache_err, NULL } -}; - -enum { - Opt_local_lock_all, Opt_local_lock_flock, Opt_local_lock_posix, - Opt_local_lock_none, - - Opt_local_lock_err -}; - -static match_table_t nfs_local_lock_tokens = { - { Opt_local_lock_all, "all" }, - { Opt_local_lock_flock, "flock" }, - { Opt_local_lock_posix, "posix" }, - { Opt_local_lock_none, "none" }, - - { Opt_local_lock_err, NULL } -}; - -enum { - Opt_vers_2, Opt_vers_3, Opt_vers_4, Opt_vers_4_0, - Opt_vers_4_1, - - Opt_vers_err -}; - -static match_table_t nfs_vers_tokens = { - { Opt_vers_2, "2" }, - { Opt_vers_3, "3" }, - { Opt_vers_4, "4" }, - { Opt_vers_4_0, "4.0" }, - { Opt_vers_4_1, "4.1" }, - - { Opt_vers_err, NULL } -}; - -static void nfs_umount_begin(struct super_block *); -static int nfs_statfs(struct dentry *, struct kstatfs *); -static int nfs_show_options(struct seq_file *, struct dentry *); -static int nfs_show_devname(struct seq_file *, struct dentry *); -static int nfs_show_path(struct seq_file *, struct dentry *); -static int nfs_show_stats(struct seq_file *, struct dentry *); -static struct dentry *nfs_fs_mount(struct file_system_type *, - int, const char *, void *); -static struct dentry *nfs_xdev_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *raw_data); -static void nfs_put_super(struct super_block *); -static void nfs_kill_super(struct super_block *); -static int nfs_remount(struct super_block *sb, int *flags, char *raw_data); - -static struct file_system_type nfs_fs_type = { - .owner = THIS_MODULE, - .name = "nfs", - .mount = nfs_fs_mount, - .kill_sb = nfs_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, -}; - -struct file_system_type nfs_xdev_fs_type = { - .owner = THIS_MODULE, - .name = "nfs", - .mount = nfs_xdev_mount, - .kill_sb = nfs_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, -}; - -static const struct super_operations nfs_sops = { - .alloc_inode = nfs_alloc_inode, - .destroy_inode = nfs_destroy_inode, - .write_inode = nfs_write_inode, - .put_super = nfs_put_super, - .statfs = nfs_statfs, - .evict_inode = nfs_evict_inode, - .umount_begin = nfs_umount_begin, - .show_options = nfs_show_options, - .show_devname = nfs_show_devname, - .show_path = nfs_show_path, - .show_stats = nfs_show_stats, - .remount_fs = nfs_remount, -}; - -#ifdef CONFIG_NFS_V4 -static int nfs4_validate_text_mount_data(void *options, - struct nfs_parsed_mount_data *args, const char *dev_name); -static struct dentry *nfs4_try_mount(int flags, const char *dev_name, - struct nfs_parsed_mount_data *data); -static struct dentry *nfs4_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *raw_data); -static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *raw_data); -static struct dentry *nfs4_xdev_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *raw_data); -static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *raw_data); -static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *raw_data); -static void nfs4_kill_super(struct super_block *sb); - -static struct file_system_type nfs4_fs_type = { - .owner = THIS_MODULE, - .name = "nfs4", - .mount = nfs4_mount, - .kill_sb = nfs4_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, -}; - -static struct file_system_type nfs4_remote_fs_type = { - .owner = THIS_MODULE, - .name = "nfs4", - .mount = nfs4_remote_mount, - .kill_sb = nfs4_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, -}; - -struct file_system_type nfs4_xdev_fs_type = { - .owner = THIS_MODULE, - .name = "nfs4", - .mount = nfs4_xdev_mount, - .kill_sb = nfs4_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, -}; - -static struct file_system_type nfs4_remote_referral_fs_type = { - .owner = THIS_MODULE, - .name = "nfs4", - .mount = nfs4_remote_referral_mount, - .kill_sb = nfs4_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, -}; - -struct file_system_type nfs4_referral_fs_type = { - .owner = THIS_MODULE, - .name = "nfs4", - .mount = nfs4_referral_mount, - .kill_sb = nfs4_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, -}; - -static const struct super_operations nfs4_sops = { - .alloc_inode = nfs_alloc_inode, - .destroy_inode = nfs_destroy_inode, - .write_inode = nfs_write_inode, - .put_super = nfs_put_super, - .statfs = nfs_statfs, - .evict_inode = nfs4_evict_inode, - .umount_begin = nfs_umount_begin, - .show_options = nfs_show_options, - .show_devname = nfs_show_devname, - .show_path = nfs_show_path, - .show_stats = nfs_show_stats, - .remount_fs = nfs_remount, -}; -#endif - -static struct shrinker acl_shrinker = { - .shrink = nfs_access_cache_shrinker, - .seeks = DEFAULT_SEEKS, -}; - -/* - * Register the NFS filesystems - */ -int __init register_nfs_fs(void) -{ - int ret; - - ret = register_filesystem(&nfs_fs_type); - if (ret < 0) - goto error_0; - - ret = nfs_register_sysctl(); - if (ret < 0) - goto error_1; -#ifdef CONFIG_NFS_V4 - ret = register_filesystem(&nfs4_fs_type); - if (ret < 0) - goto error_2; -#endif - register_shrinker(&acl_shrinker); - return 0; - -#ifdef CONFIG_NFS_V4 -error_2: - nfs_unregister_sysctl(); -#endif -error_1: - unregister_filesystem(&nfs_fs_type); -error_0: - return ret; -} - -/* - * Unregister the NFS filesystems - */ -void __exit unregister_nfs_fs(void) -{ - unregister_shrinker(&acl_shrinker); -#ifdef CONFIG_NFS_V4 - unregister_filesystem(&nfs4_fs_type); -#endif - nfs_unregister_sysctl(); - unregister_filesystem(&nfs_fs_type); -} - -void nfs_sb_active(struct super_block *sb) -{ - struct nfs_server *server = NFS_SB(sb); - - if (atomic_inc_return(&server->active) == 1) - atomic_inc(&sb->s_active); -} - -void nfs_sb_deactive(struct super_block *sb) -{ - struct nfs_server *server = NFS_SB(sb); - - if (atomic_dec_and_test(&server->active)) - deactivate_super(sb); -} - -/* - * Deliver file system statistics to userspace - */ -static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - struct nfs_server *server = NFS_SB(dentry->d_sb); - unsigned char blockbits; - unsigned long blockres; - struct nfs_fh *fh = NFS_FH(dentry->d_inode); - struct nfs_fsstat res; - int error = -ENOMEM; - - res.fattr = nfs_alloc_fattr(); - if (res.fattr == NULL) - goto out_err; - - error = server->nfs_client->rpc_ops->statfs(server, fh, &res); - if (unlikely(error == -ESTALE)) { - struct dentry *pd_dentry; - - pd_dentry = dget_parent(dentry); - if (pd_dentry != NULL) { - nfs_zap_caches(pd_dentry->d_inode); - dput(pd_dentry); - } - } - nfs_free_fattr(res.fattr); - if (error < 0) - goto out_err; - - buf->f_type = NFS_SUPER_MAGIC; - - /* - * Current versions of glibc do not correctly handle the - * case where f_frsize != f_bsize. Eventually we want to - * report the value of wtmult in this field. - */ - buf->f_frsize = dentry->d_sb->s_blocksize; - - /* - * On most *nix systems, f_blocks, f_bfree, and f_bavail - * are reported in units of f_frsize. Linux hasn't had - * an f_frsize field in its statfs struct until recently, - * thus historically Linux's sys_statfs reports these - * fields in units of f_bsize. - */ - buf->f_bsize = dentry->d_sb->s_blocksize; - blockbits = dentry->d_sb->s_blocksize_bits; - 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; - - buf->f_namelen = server->namelen; - - return 0; - - out_err: - dprintk("%s: statfs error = %d\n", __func__, -error); - return error; -} - -/* - * Map the security flavour number to a name - */ -static const char *nfs_pseudoflavour_to_name(rpc_authflavor_t flavour) -{ - static const struct { - rpc_authflavor_t flavour; - const char *str; - } sec_flavours[] = { - { RPC_AUTH_NULL, "null" }, - { RPC_AUTH_UNIX, "sys" }, - { RPC_AUTH_GSS_KRB5, "krb5" }, - { RPC_AUTH_GSS_KRB5I, "krb5i" }, - { RPC_AUTH_GSS_KRB5P, "krb5p" }, - { RPC_AUTH_GSS_LKEY, "lkey" }, - { RPC_AUTH_GSS_LKEYI, "lkeyi" }, - { RPC_AUTH_GSS_LKEYP, "lkeyp" }, - { RPC_AUTH_GSS_SPKM, "spkm" }, - { RPC_AUTH_GSS_SPKMI, "spkmi" }, - { RPC_AUTH_GSS_SPKMP, "spkmp" }, - { UINT_MAX, "unknown" } - }; - int i; - - for (i = 0; sec_flavours[i].flavour != UINT_MAX; i++) { - if (sec_flavours[i].flavour == flavour) - break; - } - return sec_flavours[i].str; -} - -static void nfs_show_mountd_netid(struct seq_file *m, struct nfs_server *nfss, - int showdefaults) -{ - struct sockaddr *sap = (struct sockaddr *) &nfss->mountd_address; - - seq_printf(m, ",mountproto="); - switch (sap->sa_family) { - case AF_INET: - switch (nfss->mountd_protocol) { - case IPPROTO_UDP: - seq_printf(m, RPCBIND_NETID_UDP); - break; - case IPPROTO_TCP: - seq_printf(m, RPCBIND_NETID_TCP); - break; - default: - if (showdefaults) - seq_printf(m, "auto"); - } - break; - case AF_INET6: - switch (nfss->mountd_protocol) { - case IPPROTO_UDP: - seq_printf(m, RPCBIND_NETID_UDP6); - break; - case IPPROTO_TCP: - seq_printf(m, RPCBIND_NETID_TCP6); - break; - default: - if (showdefaults) - seq_printf(m, "auto"); - } - break; - default: - if (showdefaults) - seq_printf(m, "auto"); - } -} - -static void nfs_show_mountd_options(struct seq_file *m, struct nfs_server *nfss, - int showdefaults) -{ - struct sockaddr *sap = (struct sockaddr *)&nfss->mountd_address; - - if (nfss->flags & NFS_MOUNT_LEGACY_INTERFACE) - return; - - switch (sap->sa_family) { - case AF_INET: { - struct sockaddr_in *sin = (struct sockaddr_in *)sap; - seq_printf(m, ",mountaddr=%pI4", &sin->sin_addr.s_addr); - break; - } - case AF_INET6: { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; - seq_printf(m, ",mountaddr=%pI6c", &sin6->sin6_addr); - break; - } - default: - if (showdefaults) - seq_printf(m, ",mountaddr=unspecified"); - } - - if (nfss->mountd_version || showdefaults) - seq_printf(m, ",mountvers=%u", nfss->mountd_version); - if ((nfss->mountd_port && - nfss->mountd_port != (unsigned short)NFS_UNSPEC_PORT) || - showdefaults) - seq_printf(m, ",mountport=%u", nfss->mountd_port); - - nfs_show_mountd_netid(m, nfss, showdefaults); -} - -#ifdef CONFIG_NFS_V4 -static void nfs_show_nfsv4_options(struct seq_file *m, struct nfs_server *nfss, - int showdefaults) -{ - struct nfs_client *clp = nfss->nfs_client; - - seq_printf(m, ",clientaddr=%s", clp->cl_ipaddr); -} -#else -static void nfs_show_nfsv4_options(struct seq_file *m, struct nfs_server *nfss, - int showdefaults) -{ -} -#endif - -static void nfs_show_nfs_version(struct seq_file *m, - unsigned int version, - unsigned int minorversion) -{ - seq_printf(m, ",vers=%u", version); - if (version == 4) - seq_printf(m, ".%u", minorversion); -} - -/* - * Describe the mount options in force on this server representation - */ -static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, - int showdefaults) -{ - static const struct proc_nfs_info { - int flag; - const char *str; - const char *nostr; - } nfs_info[] = { - { NFS_MOUNT_SOFT, ",soft", ",hard" }, - { NFS_MOUNT_POSIX, ",posix", "" }, - { NFS_MOUNT_NOCTO, ",nocto", "" }, - { NFS_MOUNT_NOAC, ",noac", "" }, - { NFS_MOUNT_NONLM, ",nolock", "" }, - { NFS_MOUNT_NOACL, ",noacl", "" }, - { NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" }, - { NFS_MOUNT_UNSHARED, ",nosharecache", "" }, - { NFS_MOUNT_NORESVPORT, ",noresvport", "" }, - { 0, NULL, NULL } - }; - const struct proc_nfs_info *nfs_infop; - struct nfs_client *clp = nfss->nfs_client; - u32 version = clp->rpc_ops->version; - int local_flock, local_fcntl; - - nfs_show_nfs_version(m, version, clp->cl_minorversion); - seq_printf(m, ",rsize=%u", nfss->rsize); - seq_printf(m, ",wsize=%u", nfss->wsize); - if (nfss->bsize != 0) - seq_printf(m, ",bsize=%u", nfss->bsize); - seq_printf(m, ",namlen=%u", nfss->namelen); - if (nfss->acregmin != NFS_DEF_ACREGMIN*HZ || showdefaults) - seq_printf(m, ",acregmin=%u", nfss->acregmin/HZ); - if (nfss->acregmax != NFS_DEF_ACREGMAX*HZ || showdefaults) - seq_printf(m, ",acregmax=%u", nfss->acregmax/HZ); - if (nfss->acdirmin != NFS_DEF_ACDIRMIN*HZ || showdefaults) - seq_printf(m, ",acdirmin=%u", nfss->acdirmin/HZ); - if (nfss->acdirmax != NFS_DEF_ACDIRMAX*HZ || showdefaults) - seq_printf(m, ",acdirmax=%u", nfss->acdirmax/HZ); - for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) { - if (nfss->flags & nfs_infop->flag) - seq_puts(m, nfs_infop->str); - else - seq_puts(m, nfs_infop->nostr); - } - rcu_read_lock(); - seq_printf(m, ",proto=%s", - rpc_peeraddr2str(nfss->client, RPC_DISPLAY_NETID)); - rcu_read_unlock(); - if (version == 4) { - if (nfss->port != NFS_PORT) - seq_printf(m, ",port=%u", nfss->port); - } else - if (nfss->port) - seq_printf(m, ",port=%u", nfss->port); - - seq_printf(m, ",timeo=%lu", 10U * nfss->client->cl_timeout->to_initval / HZ); - seq_printf(m, ",retrans=%u", nfss->client->cl_timeout->to_retries); - seq_printf(m, ",sec=%s", nfs_pseudoflavour_to_name(nfss->client->cl_auth->au_flavor)); - - if (version != 4) - nfs_show_mountd_options(m, nfss, showdefaults); - else - nfs_show_nfsv4_options(m, nfss, showdefaults); - - if (nfss->options & NFS_OPTION_FSCACHE) - seq_printf(m, ",fsc"); - - if (nfss->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG) { - if (nfss->flags & NFS_MOUNT_LOOKUP_CACHE_NONE) - seq_printf(m, ",lookupcache=none"); - else - seq_printf(m, ",lookupcache=pos"); - } - - local_flock = nfss->flags & NFS_MOUNT_LOCAL_FLOCK; - local_fcntl = nfss->flags & NFS_MOUNT_LOCAL_FCNTL; - - if (!local_flock && !local_fcntl) - seq_printf(m, ",local_lock=none"); - else if (local_flock && local_fcntl) - seq_printf(m, ",local_lock=all"); - else if (local_flock) - seq_printf(m, ",local_lock=flock"); - else - seq_printf(m, ",local_lock=posix"); -} - -/* - * Describe the mount options on this VFS mountpoint - */ -static int nfs_show_options(struct seq_file *m, struct dentry *root) -{ - struct nfs_server *nfss = NFS_SB(root->d_sb); - - nfs_show_mount_options(m, nfss, 0); - - rcu_read_lock(); - seq_printf(m, ",addr=%s", - rpc_peeraddr2str(nfss->nfs_client->cl_rpcclient, - RPC_DISPLAY_ADDR)); - rcu_read_unlock(); - - return 0; -} - -#ifdef CONFIG_NFS_V4 -#ifdef CONFIG_NFS_V4_1 -static void show_sessions(struct seq_file *m, struct nfs_server *server) -{ - if (nfs4_has_session(server->nfs_client)) - seq_printf(m, ",sessions"); -} -#else -static void show_sessions(struct seq_file *m, struct nfs_server *server) {} -#endif -#endif - -#ifdef CONFIG_NFS_V4_1 -static void show_pnfs(struct seq_file *m, struct nfs_server *server) -{ - seq_printf(m, ",pnfs="); - if (server->pnfs_curr_ld) - seq_printf(m, "%s", server->pnfs_curr_ld->name); - else - seq_printf(m, "not configured"); -} - -static void show_implementation_id(struct seq_file *m, struct nfs_server *nfss) -{ - if (nfss->nfs_client && nfss->nfs_client->impl_id) { - struct nfs41_impl_id *impl_id = nfss->nfs_client->impl_id; - seq_printf(m, "\n\timpl_id:\tname='%s',domain='%s'," - "date='%llu,%u'", - impl_id->name, impl_id->domain, - impl_id->date.seconds, impl_id->date.nseconds); - } -} -#else -#ifdef CONFIG_NFS_V4 -static void show_pnfs(struct seq_file *m, struct nfs_server *server) -{ -} -#endif -static void show_implementation_id(struct seq_file *m, struct nfs_server *nfss) -{ -} -#endif - -static int nfs_show_devname(struct seq_file *m, struct dentry *root) -{ - char *page = (char *) __get_free_page(GFP_KERNEL); - char *devname, *dummy; - int err = 0; - if (!page) - return -ENOMEM; - devname = nfs_path(&dummy, root, page, PAGE_SIZE); - if (IS_ERR(devname)) - err = PTR_ERR(devname); - else - seq_escape(m, devname, " \t\n\\"); - free_page((unsigned long)page); - return err; -} - -static int nfs_show_path(struct seq_file *m, struct dentry *dentry) -{ - seq_puts(m, "/"); - return 0; -} - -/* - * Present statistical information for this VFS mountpoint - */ -static int nfs_show_stats(struct seq_file *m, struct dentry *root) -{ - int i, cpu; - struct nfs_server *nfss = NFS_SB(root->d_sb); - struct rpc_auth *auth = nfss->client->cl_auth; - struct nfs_iostats totals = { }; - - seq_printf(m, "statvers=%s", NFS_IOSTAT_VERS); - - /* - * Display all mount option settings - */ - seq_printf(m, "\n\topts:\t"); - seq_puts(m, root->d_sb->s_flags & MS_RDONLY ? "ro" : "rw"); - seq_puts(m, root->d_sb->s_flags & MS_SYNCHRONOUS ? ",sync" : ""); - seq_puts(m, root->d_sb->s_flags & MS_NOATIME ? ",noatime" : ""); - seq_puts(m, root->d_sb->s_flags & MS_NODIRATIME ? ",nodiratime" : ""); - nfs_show_mount_options(m, nfss, 1); - - seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ); - - show_implementation_id(m, nfss); - - seq_printf(m, "\n\tcaps:\t"); - seq_printf(m, "caps=0x%x", nfss->caps); - seq_printf(m, ",wtmult=%u", nfss->wtmult); - seq_printf(m, ",dtsize=%u", nfss->dtsize); - seq_printf(m, ",bsize=%u", nfss->bsize); - seq_printf(m, ",namlen=%u", nfss->namelen); - -#ifdef CONFIG_NFS_V4 - if (nfss->nfs_client->rpc_ops->version == 4) { - seq_printf(m, "\n\tnfsv4:\t"); - seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]); - seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]); - seq_printf(m, ",acl=0x%x", nfss->acl_bitmask); - show_sessions(m, nfss); - show_pnfs(m, nfss); - } -#endif - - /* - * Display security flavor in effect for this mount - */ - seq_printf(m, "\n\tsec:\tflavor=%u", auth->au_ops->au_flavor); - if (auth->au_flavor) - seq_printf(m, ",pseudoflavor=%u", auth->au_flavor); - - /* - * Display superblock I/O counters - */ - for_each_possible_cpu(cpu) { - struct nfs_iostats *stats; - - preempt_disable(); - stats = per_cpu_ptr(nfss->io_stats, cpu); - - for (i = 0; i < __NFSIOS_COUNTSMAX; i++) - totals.events[i] += stats->events[i]; - for (i = 0; i < __NFSIOS_BYTESMAX; i++) - totals.bytes[i] += stats->bytes[i]; -#ifdef CONFIG_NFS_FSCACHE - for (i = 0; i < __NFSIOS_FSCACHEMAX; i++) - totals.fscache[i] += stats->fscache[i]; -#endif - - preempt_enable(); - } - - seq_printf(m, "\n\tevents:\t"); - for (i = 0; i < __NFSIOS_COUNTSMAX; i++) - seq_printf(m, "%lu ", totals.events[i]); - seq_printf(m, "\n\tbytes:\t"); - for (i = 0; i < __NFSIOS_BYTESMAX; i++) - seq_printf(m, "%Lu ", totals.bytes[i]); -#ifdef CONFIG_NFS_FSCACHE - if (nfss->options & NFS_OPTION_FSCACHE) { - seq_printf(m, "\n\tfsc:\t"); - for (i = 0; i < __NFSIOS_FSCACHEMAX; i++) - seq_printf(m, "%Lu ", totals.bytes[i]); - } -#endif - seq_printf(m, "\n"); - - rpc_print_iostats(m, nfss->client); - - return 0; -} - -/* - * Begin unmount by attempting to remove all automounted mountpoints we added - * in response to xdev traversals and referrals - */ -static void nfs_umount_begin(struct super_block *sb) -{ - struct nfs_server *server; - struct rpc_clnt *rpc; - - server = NFS_SB(sb); - /* -EIO all pending I/O */ - rpc = server->client_acl; - if (!IS_ERR(rpc)) - rpc_killall_tasks(rpc); - rpc = server->client; - if (!IS_ERR(rpc)) - rpc_killall_tasks(rpc); -} - -static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(unsigned int version) -{ - struct nfs_parsed_mount_data *data; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (data) { - data->acregmin = NFS_DEF_ACREGMIN; - data->acregmax = NFS_DEF_ACREGMAX; - data->acdirmin = NFS_DEF_ACDIRMIN; - data->acdirmax = NFS_DEF_ACDIRMAX; - data->mount_server.port = NFS_UNSPEC_PORT; - data->nfs_server.port = NFS_UNSPEC_PORT; - data->nfs_server.protocol = XPRT_TRANSPORT_TCP; - data->auth_flavors[0] = RPC_AUTH_UNIX; - data->auth_flavor_len = 1; - data->version = version; - data->minorversion = 0; - data->net = current->nsproxy->net_ns; - security_init_mnt_opts(&data->lsm_opts); - } - return data; -} - -static void nfs_free_parsed_mount_data(struct nfs_parsed_mount_data *data) -{ - if (data) { - kfree(data->client_address); - kfree(data->mount_server.hostname); - kfree(data->nfs_server.export_path); - kfree(data->nfs_server.hostname); - kfree(data->fscache_uniq); - security_free_mnt_opts(&data->lsm_opts); - kfree(data); - } -} - -/* - * Sanity-check a server address provided by the mount command. - * - * Address family must be initialized, and address must not be - * the ANY address for that family. - */ -static int nfs_verify_server_address(struct sockaddr *addr) -{ - switch (addr->sa_family) { - case AF_INET: { - struct sockaddr_in *sa = (struct sockaddr_in *)addr; - return sa->sin_addr.s_addr != htonl(INADDR_ANY); - } - case AF_INET6: { - struct in6_addr *sa = &((struct sockaddr_in6 *)addr)->sin6_addr; - return !ipv6_addr_any(sa); - } - } - - dfprintk(MOUNT, "NFS: Invalid IP address specified\n"); - return 0; -} - -/* - * Select between a default port value and a user-specified port value. - * If a zero value is set, then autobind will be used. - */ -static void nfs_set_port(struct sockaddr *sap, int *port, - const unsigned short default_port) -{ - if (*port == NFS_UNSPEC_PORT) - *port = default_port; - - rpc_set_port(sap, *port); -} - -/* - * Sanity check the NFS transport protocol. - * - */ -static void nfs_validate_transport_protocol(struct nfs_parsed_mount_data *mnt) -{ - switch (mnt->nfs_server.protocol) { - case XPRT_TRANSPORT_UDP: - case XPRT_TRANSPORT_TCP: - case XPRT_TRANSPORT_RDMA: - break; - default: - mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP; - } -} - -/* - * For text based NFSv2/v3 mounts, the mount protocol transport default - * settings should depend upon the specified NFS transport. - */ -static void nfs_set_mount_transport_protocol(struct nfs_parsed_mount_data *mnt) -{ - nfs_validate_transport_protocol(mnt); - - if (mnt->mount_server.protocol == XPRT_TRANSPORT_UDP || - mnt->mount_server.protocol == XPRT_TRANSPORT_TCP) - return; - switch (mnt->nfs_server.protocol) { - case XPRT_TRANSPORT_UDP: - mnt->mount_server.protocol = XPRT_TRANSPORT_UDP; - break; - case XPRT_TRANSPORT_TCP: - case XPRT_TRANSPORT_RDMA: - mnt->mount_server.protocol = XPRT_TRANSPORT_TCP; - } -} - -/* - * Parse the value of the 'sec=' option. - */ -static int nfs_parse_security_flavors(char *value, - struct nfs_parsed_mount_data *mnt) -{ - substring_t args[MAX_OPT_ARGS]; - - dfprintk(MOUNT, "NFS: parsing sec=%s option\n", value); - - switch (match_token(value, nfs_secflavor_tokens, args)) { - case Opt_sec_none: - mnt->auth_flavors[0] = RPC_AUTH_NULL; - break; - case Opt_sec_sys: - mnt->auth_flavors[0] = RPC_AUTH_UNIX; - break; - case Opt_sec_krb5: - mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5; - break; - case Opt_sec_krb5i: - mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5I; - break; - case Opt_sec_krb5p: - mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5P; - break; - case Opt_sec_lkey: - mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEY; - break; - case Opt_sec_lkeyi: - mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYI; - break; - case Opt_sec_lkeyp: - mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYP; - break; - case Opt_sec_spkm: - mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKM; - break; - case Opt_sec_spkmi: - mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMI; - break; - case Opt_sec_spkmp: - mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMP; - break; - default: - return 0; - } - - mnt->flags |= NFS_MOUNT_SECFLAVOUR; - mnt->auth_flavor_len = 1; - return 1; -} - -static int nfs_parse_version_string(char *string, - struct nfs_parsed_mount_data *mnt, - substring_t *args) -{ - mnt->flags &= ~NFS_MOUNT_VER3; - switch (match_token(string, nfs_vers_tokens, args)) { - case Opt_vers_2: - mnt->version = 2; - break; - case Opt_vers_3: - mnt->flags |= NFS_MOUNT_VER3; - mnt->version = 3; - break; - case Opt_vers_4: - /* Backward compatibility option. In future, - * the mount program should always supply - * a NFSv4 minor version number. - */ - mnt->version = 4; - break; - case Opt_vers_4_0: - mnt->version = 4; - mnt->minorversion = 0; - break; - case Opt_vers_4_1: - mnt->version = 4; - mnt->minorversion = 1; - break; - default: - return 0; - } - return 1; -} - -static int nfs_get_option_str(substring_t args[], char **option) -{ - kfree(*option); - *option = match_strdup(args); - return !option; -} - -static int nfs_get_option_ul(substring_t args[], unsigned long *option) -{ - int rc; - char *string; - - string = match_strdup(args); - if (string == NULL) - return -ENOMEM; - rc = strict_strtoul(string, 10, option); - kfree(string); - - return rc; -} - -/* - * Error-check and convert a string of mount options from user space into - * a data structure. The whole mount string is processed; bad options are - * skipped as they are encountered. If there were no errors, return 1; - * otherwise return 0 (zero). - */ -static int nfs_parse_mount_options(char *raw, - struct nfs_parsed_mount_data *mnt) -{ - char *p, *string, *secdata; - int rc, sloppy = 0, invalid_option = 0; - unsigned short protofamily = AF_UNSPEC; - unsigned short mountfamily = AF_UNSPEC; - - if (!raw) { - dfprintk(MOUNT, "NFS: mount options string was NULL.\n"); - return 1; - } - dfprintk(MOUNT, "NFS: nfs mount opts='%s'\n", raw); - - secdata = alloc_secdata(); - if (!secdata) - goto out_nomem; - - rc = security_sb_copy_data(raw, secdata); - if (rc) - goto out_security_failure; - - rc = security_sb_parse_opts_str(secdata, &mnt->lsm_opts); - if (rc) - goto out_security_failure; - - free_secdata(secdata); - - while ((p = strsep(&raw, ",")) != NULL) { - substring_t args[MAX_OPT_ARGS]; - unsigned long option; - int token; - - if (!*p) - continue; - - dfprintk(MOUNT, "NFS: parsing nfs mount option '%s'\n", p); - - token = match_token(p, nfs_mount_option_tokens, args); - switch (token) { - - /* - * boolean options: foo/nofoo - */ - case Opt_soft: - mnt->flags |= NFS_MOUNT_SOFT; - break; - case Opt_hard: - mnt->flags &= ~NFS_MOUNT_SOFT; - break; - case Opt_posix: - mnt->flags |= NFS_MOUNT_POSIX; - break; - case Opt_noposix: - mnt->flags &= ~NFS_MOUNT_POSIX; - break; - case Opt_cto: - mnt->flags &= ~NFS_MOUNT_NOCTO; - break; - case Opt_nocto: - mnt->flags |= NFS_MOUNT_NOCTO; - break; - case Opt_ac: - mnt->flags &= ~NFS_MOUNT_NOAC; - break; - case Opt_noac: - mnt->flags |= NFS_MOUNT_NOAC; - break; - case Opt_lock: - mnt->flags &= ~NFS_MOUNT_NONLM; - mnt->flags &= ~(NFS_MOUNT_LOCAL_FLOCK | - NFS_MOUNT_LOCAL_FCNTL); - break; - case Opt_nolock: - mnt->flags |= NFS_MOUNT_NONLM; - mnt->flags |= (NFS_MOUNT_LOCAL_FLOCK | - NFS_MOUNT_LOCAL_FCNTL); - break; - case Opt_udp: - mnt->flags &= ~NFS_MOUNT_TCP; - mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP; - break; - case Opt_tcp: - mnt->flags |= NFS_MOUNT_TCP; - mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP; - break; - case Opt_rdma: - mnt->flags |= NFS_MOUNT_TCP; /* for side protocols */ - mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA; - xprt_load_transport(p); - break; - case Opt_acl: - mnt->flags &= ~NFS_MOUNT_NOACL; - break; - case Opt_noacl: - mnt->flags |= NFS_MOUNT_NOACL; - break; - case Opt_rdirplus: - mnt->flags &= ~NFS_MOUNT_NORDIRPLUS; - break; - case Opt_nordirplus: - mnt->flags |= NFS_MOUNT_NORDIRPLUS; - break; - case Opt_sharecache: - mnt->flags &= ~NFS_MOUNT_UNSHARED; - break; - case Opt_nosharecache: - mnt->flags |= NFS_MOUNT_UNSHARED; - break; - case Opt_resvport: - mnt->flags &= ~NFS_MOUNT_NORESVPORT; - break; - case Opt_noresvport: - mnt->flags |= NFS_MOUNT_NORESVPORT; - break; - case Opt_fscache: - mnt->options |= NFS_OPTION_FSCACHE; - kfree(mnt->fscache_uniq); - mnt->fscache_uniq = NULL; - break; - case Opt_nofscache: - mnt->options &= ~NFS_OPTION_FSCACHE; - kfree(mnt->fscache_uniq); - mnt->fscache_uniq = NULL; - break; - - /* - * options that take numeric values - */ - case Opt_port: - if (nfs_get_option_ul(args, &option) || - option > USHRT_MAX) - goto out_invalid_value; - mnt->nfs_server.port = option; - break; - case Opt_rsize: - if (nfs_get_option_ul(args, &option)) - goto out_invalid_value; - mnt->rsize = option; - break; - case Opt_wsize: - if (nfs_get_option_ul(args, &option)) - goto out_invalid_value; - mnt->wsize = option; - break; - case Opt_bsize: - if (nfs_get_option_ul(args, &option)) - goto out_invalid_value; - mnt->bsize = option; - break; - case Opt_timeo: - if (nfs_get_option_ul(args, &option) || option == 0) - goto out_invalid_value; - mnt->timeo = option; - break; - case Opt_retrans: - if (nfs_get_option_ul(args, &option) || option == 0) - goto out_invalid_value; - mnt->retrans = option; - break; - case Opt_acregmin: - if (nfs_get_option_ul(args, &option)) - goto out_invalid_value; - mnt->acregmin = option; - break; - case Opt_acregmax: - if (nfs_get_option_ul(args, &option)) - goto out_invalid_value; - mnt->acregmax = option; - break; - case Opt_acdirmin: - if (nfs_get_option_ul(args, &option)) - goto out_invalid_value; - mnt->acdirmin = option; - break; - case Opt_acdirmax: - if (nfs_get_option_ul(args, &option)) - goto out_invalid_value; - mnt->acdirmax = option; - break; - case Opt_actimeo: - if (nfs_get_option_ul(args, &option)) - goto out_invalid_value; - mnt->acregmin = mnt->acregmax = - mnt->acdirmin = mnt->acdirmax = option; - break; - case Opt_namelen: - if (nfs_get_option_ul(args, &option)) - goto out_invalid_value; - mnt->namlen = option; - break; - case Opt_mountport: - if (nfs_get_option_ul(args, &option) || - option > USHRT_MAX) - goto out_invalid_value; - mnt->mount_server.port = option; - break; - case Opt_mountvers: - if (nfs_get_option_ul(args, &option) || - option < NFS_MNT_VERSION || - option > NFS_MNT3_VERSION) - goto out_invalid_value; - mnt->mount_server.version = option; - break; - case Opt_minorversion: - if (nfs_get_option_ul(args, &option)) - goto out_invalid_value; - if (option > NFS4_MAX_MINOR_VERSION) - goto out_invalid_value; - mnt->minorversion = option; - break; - - /* - * options that take text values - */ - case Opt_nfsvers: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - rc = nfs_parse_version_string(string, mnt, args); - kfree(string); - if (!rc) - goto out_invalid_value; - break; - case Opt_sec: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - rc = nfs_parse_security_flavors(string, mnt); - kfree(string); - if (!rc) { - dfprintk(MOUNT, "NFS: unrecognized " - "security flavor\n"); - return 0; - } - break; - case Opt_proto: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - token = match_token(string, - nfs_xprt_protocol_tokens, args); - - protofamily = AF_INET; - switch (token) { - case Opt_xprt_udp6: - protofamily = AF_INET6; - case Opt_xprt_udp: - mnt->flags &= ~NFS_MOUNT_TCP; - mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP; - break; - case Opt_xprt_tcp6: - protofamily = AF_INET6; - case Opt_xprt_tcp: - mnt->flags |= NFS_MOUNT_TCP; - mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP; - break; - case Opt_xprt_rdma: - /* vector side protocols to TCP */ - mnt->flags |= NFS_MOUNT_TCP; - mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA; - xprt_load_transport(string); - break; - default: - dfprintk(MOUNT, "NFS: unrecognized " - "transport protocol\n"); - kfree(string); - return 0; - } - kfree(string); - break; - case Opt_mountproto: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - token = match_token(string, - nfs_xprt_protocol_tokens, args); - kfree(string); - - mountfamily = AF_INET; - switch (token) { - case Opt_xprt_udp6: - mountfamily = AF_INET6; - case Opt_xprt_udp: - mnt->mount_server.protocol = XPRT_TRANSPORT_UDP; - break; - case Opt_xprt_tcp6: - mountfamily = AF_INET6; - case Opt_xprt_tcp: - mnt->mount_server.protocol = XPRT_TRANSPORT_TCP; - break; - case Opt_xprt_rdma: /* not used for side protocols */ - default: - dfprintk(MOUNT, "NFS: unrecognized " - "transport protocol\n"); - return 0; - } - break; - case Opt_addr: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - mnt->nfs_server.addrlen = - rpc_pton(mnt->net, string, strlen(string), - (struct sockaddr *) - &mnt->nfs_server.address, - sizeof(mnt->nfs_server.address)); - kfree(string); - if (mnt->nfs_server.addrlen == 0) - goto out_invalid_address; - break; - case Opt_clientaddr: - if (nfs_get_option_str(args, &mnt->client_address)) - goto out_nomem; - break; - case Opt_mounthost: - if (nfs_get_option_str(args, - &mnt->mount_server.hostname)) - goto out_nomem; - break; - case Opt_mountaddr: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - mnt->mount_server.addrlen = - rpc_pton(mnt->net, string, strlen(string), - (struct sockaddr *) - &mnt->mount_server.address, - sizeof(mnt->mount_server.address)); - kfree(string); - if (mnt->mount_server.addrlen == 0) - goto out_invalid_address; - break; - case Opt_lookupcache: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - token = match_token(string, - nfs_lookupcache_tokens, args); - kfree(string); - switch (token) { - case Opt_lookupcache_all: - mnt->flags &= ~(NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE); - break; - case Opt_lookupcache_positive: - mnt->flags &= ~NFS_MOUNT_LOOKUP_CACHE_NONE; - mnt->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG; - break; - case Opt_lookupcache_none: - mnt->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE; - break; - default: - dfprintk(MOUNT, "NFS: invalid " - "lookupcache argument\n"); - return 0; - }; - break; - case Opt_fscache_uniq: - if (nfs_get_option_str(args, &mnt->fscache_uniq)) - goto out_nomem; - mnt->options |= NFS_OPTION_FSCACHE; - break; - case Opt_local_lock: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - token = match_token(string, nfs_local_lock_tokens, - args); - kfree(string); - switch (token) { - case Opt_local_lock_all: - mnt->flags |= (NFS_MOUNT_LOCAL_FLOCK | - NFS_MOUNT_LOCAL_FCNTL); - break; - case Opt_local_lock_flock: - mnt->flags |= NFS_MOUNT_LOCAL_FLOCK; - break; - case Opt_local_lock_posix: - mnt->flags |= NFS_MOUNT_LOCAL_FCNTL; - break; - case Opt_local_lock_none: - mnt->flags &= ~(NFS_MOUNT_LOCAL_FLOCK | - NFS_MOUNT_LOCAL_FCNTL); - break; - default: - dfprintk(MOUNT, "NFS: invalid " - "local_lock argument\n"); - return 0; - }; - break; - - /* - * Special options - */ - case Opt_sloppy: - sloppy = 1; - dfprintk(MOUNT, "NFS: relaxing parsing rules\n"); - break; - case Opt_userspace: - case Opt_deprecated: - dfprintk(MOUNT, "NFS: ignoring mount option " - "'%s'\n", p); - break; - - default: - invalid_option = 1; - dfprintk(MOUNT, "NFS: unrecognized mount option " - "'%s'\n", p); - } - } - - if (!sloppy && invalid_option) - return 0; - - if (mnt->minorversion && mnt->version != 4) - goto out_minorversion_mismatch; - - /* - * verify that any proto=/mountproto= options match the address - * familiies in the addr=/mountaddr= options. - */ - if (protofamily != AF_UNSPEC && - protofamily != mnt->nfs_server.address.ss_family) - goto out_proto_mismatch; - - if (mountfamily != AF_UNSPEC) { - if (mnt->mount_server.addrlen) { - if (mountfamily != mnt->mount_server.address.ss_family) - goto out_mountproto_mismatch; - } else { - if (mountfamily != mnt->nfs_server.address.ss_family) - goto out_mountproto_mismatch; - } - } - - return 1; - -out_mountproto_mismatch: - printk(KERN_INFO "NFS: mount server address does not match mountproto= " - "option\n"); - return 0; -out_proto_mismatch: - printk(KERN_INFO "NFS: server address does not match proto= option\n"); - return 0; -out_invalid_address: - printk(KERN_INFO "NFS: bad IP address specified: %s\n", p); - return 0; -out_invalid_value: - printk(KERN_INFO "NFS: bad mount option value specified: %s\n", p); - return 0; -out_minorversion_mismatch: - printk(KERN_INFO "NFS: mount option vers=%u does not support " - "minorversion=%u\n", mnt->version, mnt->minorversion); - return 0; -out_nomem: - printk(KERN_INFO "NFS: not enough memory to parse option\n"); - return 0; -out_security_failure: - free_secdata(secdata); - printk(KERN_INFO "NFS: security options invalid: %d\n", rc); - return 0; -} - -/* - * Match the requested auth flavors with the list returned by - * the server. Returns zero and sets the mount's authentication - * flavor on success; returns -EACCES if server does not support - * the requested flavor. - */ -static int nfs_walk_authlist(struct nfs_parsed_mount_data *args, - struct nfs_mount_request *request) -{ - unsigned int i, j, server_authlist_len = *(request->auth_flav_len); - - /* - * Certain releases of Linux's mountd return an empty - * flavor list. To prevent behavioral regression with - * these servers (ie. rejecting mounts that used to - * succeed), revert to pre-2.6.32 behavior (no checking) - * if the returned flavor list is empty. - */ - if (server_authlist_len == 0) - return 0; - - /* - * We avoid sophisticated negotiating here, as there are - * plenty of cases where we can get it wrong, providing - * either too little or too much security. - * - * RFC 2623, section 2.7 suggests we SHOULD prefer the - * flavor listed first. However, some servers list - * AUTH_NULL first. Our caller plants AUTH_SYS, the - * preferred default, in args->auth_flavors[0] if user - * didn't specify sec= mount option. - */ - for (i = 0; i < args->auth_flavor_len; i++) - for (j = 0; j < server_authlist_len; j++) - if (args->auth_flavors[i] == request->auth_flavs[j]) { - dfprintk(MOUNT, "NFS: using auth flavor %d\n", - request->auth_flavs[j]); - args->auth_flavors[0] = request->auth_flavs[j]; - return 0; - } - - dfprintk(MOUNT, "NFS: server does not support requested auth flavor\n"); - nfs_umount(request); - return -EACCES; -} - -/* - * Use the remote server's MOUNT service to request the NFS file handle - * corresponding to the provided path. - */ -static int nfs_try_mount(struct nfs_parsed_mount_data *args, - struct nfs_fh *root_fh) -{ - rpc_authflavor_t server_authlist[NFS_MAX_SECFLAVORS]; - unsigned int server_authlist_len = ARRAY_SIZE(server_authlist); - struct nfs_mount_request request = { - .sap = (struct sockaddr *) - &args->mount_server.address, - .dirpath = args->nfs_server.export_path, - .protocol = args->mount_server.protocol, - .fh = root_fh, - .noresvport = args->flags & NFS_MOUNT_NORESVPORT, - .auth_flav_len = &server_authlist_len, - .auth_flavs = server_authlist, - .net = args->net, - }; - int status; - - if (args->mount_server.version == 0) { - switch (args->version) { - default: - args->mount_server.version = NFS_MNT3_VERSION; - break; - case 2: - args->mount_server.version = NFS_MNT_VERSION; - } - } - request.version = args->mount_server.version; - - if (args->mount_server.hostname) - request.hostname = args->mount_server.hostname; - else - request.hostname = args->nfs_server.hostname; - - /* - * Construct the mount server's address. - */ - if (args->mount_server.address.ss_family == AF_UNSPEC) { - memcpy(request.sap, &args->nfs_server.address, - args->nfs_server.addrlen); - args->mount_server.addrlen = args->nfs_server.addrlen; - } - request.salen = args->mount_server.addrlen; - nfs_set_port(request.sap, &args->mount_server.port, 0); - - /* - * Now ask the mount server to map our export path - * to a file handle. - */ - status = nfs_mount(&request); - if (status != 0) { - dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n", - request.hostname, status); - return status; - } - - /* - * MNTv1 (NFSv2) does not support auth flavor negotiation. - */ - if (args->mount_server.version != NFS_MNT3_VERSION) - return 0; - return nfs_walk_authlist(args, &request); -} - -/* - * Split "dev_name" into "hostname:export_path". - * - * The leftmost colon demarks the split between the server's hostname - * and the export path. If the hostname starts with a left square - * bracket, then it may contain colons. - * - * Note: caller frees hostname and export path, even on error. - */ -static int nfs_parse_devname(const char *dev_name, - char **hostname, size_t maxnamlen, - char **export_path, size_t maxpathlen) -{ - size_t len; - char *end; - - /* Is the host name protected with square brakcets? */ - if (*dev_name == '[') { - end = strchr(++dev_name, ']'); - if (end == NULL || end[1] != ':') - goto out_bad_devname; - - len = end - dev_name; - end++; - } else { - char *comma; - - end = strchr(dev_name, ':'); - if (end == NULL) - goto out_bad_devname; - len = end - dev_name; - - /* kill possible hostname list: not supported */ - comma = strchr(dev_name, ','); - if (comma != NULL && comma < end) - *comma = 0; - } - - if (len > maxnamlen) - goto out_hostname; - - /* N.B. caller will free nfs_server.hostname in all cases */ - *hostname = kstrndup(dev_name, len, GFP_KERNEL); - if (*hostname == NULL) - goto out_nomem; - len = strlen(++end); - if (len > maxpathlen) - goto out_path; - *export_path = kstrndup(end, len, GFP_KERNEL); - if (!*export_path) - goto out_nomem; - - dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", *export_path); - return 0; - -out_bad_devname: - dfprintk(MOUNT, "NFS: device name not in host:path format\n"); - return -EINVAL; - -out_nomem: - dfprintk(MOUNT, "NFS: not enough memory to parse device name\n"); - return -ENOMEM; - -out_hostname: - dfprintk(MOUNT, "NFS: server hostname too long\n"); - return -ENAMETOOLONG; - -out_path: - dfprintk(MOUNT, "NFS: export pathname too long\n"); - return -ENAMETOOLONG; -} - -/* - * Validate the NFS2/NFS3 mount data - * - fills in the mount root filehandle - * - * For option strings, user space handles the following behaviors: - * - * + DNS: mapping server host name to IP address ("addr=" option) - * - * + failure mode: how to behave if a mount request can't be handled - * immediately ("fg/bg" option) - * - * + retry: how often to retry a mount request ("retry=" option) - * - * + breaking back: trying proto=udp after proto=tcp, v2 after v3, - * mountproto=tcp after mountproto=udp, and so on - */ -static int nfs_validate_mount_data(void *options, - struct nfs_parsed_mount_data *args, - struct nfs_fh *mntfh, - const char *dev_name) -{ - struct nfs_mount_data *data = (struct nfs_mount_data *)options; - struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address; - - if (data == NULL) - goto out_no_data; - - switch (data->version) { - case 1: - data->namlen = 0; - case 2: - data->bsize = 0; - case 3: - if (data->flags & NFS_MOUNT_VER3) - goto out_no_v3; - data->root.size = NFS2_FHSIZE; - memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE); - case 4: - if (data->flags & NFS_MOUNT_SECFLAVOUR) - goto out_no_sec; - case 5: - memset(data->context, 0, sizeof(data->context)); - case 6: - if (data->flags & NFS_MOUNT_VER3) { - if (data->root.size > NFS3_FHSIZE || data->root.size == 0) - goto out_invalid_fh; - mntfh->size = data->root.size; - args->version = 3; - } else { - mntfh->size = NFS2_FHSIZE; - args->version = 2; - } - - - memcpy(mntfh->data, data->root.data, mntfh->size); - if (mntfh->size < sizeof(mntfh->data)) - memset(mntfh->data + mntfh->size, 0, - sizeof(mntfh->data) - mntfh->size); - - /* - * Translate to nfs_parsed_mount_data, which nfs_fill_super - * can deal with. - */ - args->flags = data->flags & NFS_MOUNT_FLAGMASK; - args->flags |= NFS_MOUNT_LEGACY_INTERFACE; - args->rsize = data->rsize; - args->wsize = data->wsize; - args->timeo = data->timeo; - args->retrans = data->retrans; - args->acregmin = data->acregmin; - args->acregmax = data->acregmax; - args->acdirmin = data->acdirmin; - args->acdirmax = data->acdirmax; - - memcpy(sap, &data->addr, sizeof(data->addr)); - args->nfs_server.addrlen = sizeof(data->addr); - if (!nfs_verify_server_address(sap)) - goto out_no_address; - - if (!(data->flags & NFS_MOUNT_TCP)) - args->nfs_server.protocol = XPRT_TRANSPORT_UDP; - /* N.B. caller will free nfs_server.hostname in all cases */ - args->nfs_server.hostname = kstrdup(data->hostname, GFP_KERNEL); - args->namlen = data->namlen; - args->bsize = data->bsize; - - if (data->flags & NFS_MOUNT_SECFLAVOUR) - args->auth_flavors[0] = data->pseudoflavor; - if (!args->nfs_server.hostname) - goto out_nomem; - - if (!(data->flags & NFS_MOUNT_NONLM)) - args->flags &= ~(NFS_MOUNT_LOCAL_FLOCK| - NFS_MOUNT_LOCAL_FCNTL); - else - args->flags |= (NFS_MOUNT_LOCAL_FLOCK| - NFS_MOUNT_LOCAL_FCNTL); - /* - * The legacy version 6 binary mount data from userspace has a - * field used only to transport selinux information into the - * the kernel. To continue to support that functionality we - * have a touch of selinux knowledge here in the NFS code. The - * userspace code converted context=blah to just blah so we are - * converting back to the full string selinux understands. - */ - if (data->context[0]){ -#ifdef CONFIG_SECURITY_SELINUX - int rc; - char *opts_str = kmalloc(sizeof(data->context) + 8, GFP_KERNEL); - if (!opts_str) - return -ENOMEM; - strcpy(opts_str, "context="); - data->context[NFS_MAX_CONTEXT_LEN] = '\0'; - strcat(opts_str, &data->context[0]); - rc = security_sb_parse_opts_str(opts_str, &args->lsm_opts); - kfree(opts_str); - if (rc) - return rc; -#else - return -EINVAL; -#endif - } - - break; - default: { - int status; - - if (nfs_parse_mount_options((char *)options, args) == 0) - return -EINVAL; - - if (!nfs_verify_server_address(sap)) - goto out_no_address; - - if (args->version == 4) -#ifdef CONFIG_NFS_V4 - return nfs4_validate_text_mount_data(options, - args, dev_name); -#else - goto out_v4_not_compiled; -#endif - - nfs_set_port(sap, &args->nfs_server.port, 0); - - nfs_set_mount_transport_protocol(args); - - status = nfs_parse_devname(dev_name, - &args->nfs_server.hostname, - PAGE_SIZE, - &args->nfs_server.export_path, - NFS_MAXPATHLEN); - if (!status) - status = nfs_try_mount(args, mntfh); - - kfree(args->nfs_server.export_path); - args->nfs_server.export_path = NULL; - - if (status) - return status; - - break; - } - } - -#ifndef CONFIG_NFS_V3 - if (args->version == 3) - goto out_v3_not_compiled; -#endif /* !CONFIG_NFS_V3 */ - - return 0; - -out_no_data: - dfprintk(MOUNT, "NFS: mount program didn't pass any mount data\n"); - return -EINVAL; - -out_no_v3: - dfprintk(MOUNT, "NFS: nfs_mount_data version %d does not support v3\n", - data->version); - return -EINVAL; - -out_no_sec: - dfprintk(MOUNT, "NFS: nfs_mount_data version supports only AUTH_SYS\n"); - return -EINVAL; - -#ifndef CONFIG_NFS_V3 -out_v3_not_compiled: - dfprintk(MOUNT, "NFS: NFSv3 is not compiled into kernel\n"); - return -EPROTONOSUPPORT; -#endif /* !CONFIG_NFS_V3 */ - -#ifndef CONFIG_NFS_V4 -out_v4_not_compiled: - dfprintk(MOUNT, "NFS: NFSv4 is not compiled into kernel\n"); - return -EPROTONOSUPPORT; -#endif /* !CONFIG_NFS_V4 */ - -out_nomem: - dfprintk(MOUNT, "NFS: not enough memory to handle mount options\n"); - return -ENOMEM; - -out_no_address: - dfprintk(MOUNT, "NFS: mount program didn't pass remote address\n"); - return -EINVAL; - -out_invalid_fh: - dfprintk(MOUNT, "NFS: invalid root filehandle\n"); - return -EINVAL; -} - -static int -nfs_compare_remount_data(struct nfs_server *nfss, - struct nfs_parsed_mount_data *data) -{ - if (data->flags != nfss->flags || - data->rsize != nfss->rsize || - data->wsize != nfss->wsize || - data->retrans != nfss->client->cl_timeout->to_retries || - data->auth_flavors[0] != nfss->client->cl_auth->au_flavor || - data->acregmin != nfss->acregmin / HZ || - data->acregmax != nfss->acregmax / HZ || - data->acdirmin != nfss->acdirmin / HZ || - data->acdirmax != nfss->acdirmax / HZ || - data->timeo != (10U * nfss->client->cl_timeout->to_initval / HZ) || - data->nfs_server.port != nfss->port || - data->nfs_server.addrlen != nfss->nfs_client->cl_addrlen || - !rpc_cmp_addr((struct sockaddr *)&data->nfs_server.address, - (struct sockaddr *)&nfss->nfs_client->cl_addr)) - return -EINVAL; - - return 0; -} - -static int -nfs_remount(struct super_block *sb, int *flags, char *raw_data) -{ - int error; - struct nfs_server *nfss = sb->s_fs_info; - struct nfs_parsed_mount_data *data; - struct nfs_mount_data *options = (struct nfs_mount_data *)raw_data; - struct nfs4_mount_data *options4 = (struct nfs4_mount_data *)raw_data; - u32 nfsvers = nfss->nfs_client->rpc_ops->version; - - /* - * Userspace mount programs that send binary options generally send - * them populated with default values. We have no way to know which - * ones were explicitly specified. Fall back to legacy behavior and - * just return success. - */ - if ((nfsvers == 4 && (!options4 || options4->version == 1)) || - (nfsvers <= 3 && (!options || (options->version >= 1 && - options->version <= 6)))) - return 0; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - /* fill out struct with values from existing mount */ - data->flags = nfss->flags; - data->rsize = nfss->rsize; - data->wsize = nfss->wsize; - data->retrans = nfss->client->cl_timeout->to_retries; - data->auth_flavors[0] = nfss->client->cl_auth->au_flavor; - data->acregmin = nfss->acregmin / HZ; - data->acregmax = nfss->acregmax / HZ; - data->acdirmin = nfss->acdirmin / HZ; - data->acdirmax = nfss->acdirmax / HZ; - data->timeo = 10U * nfss->client->cl_timeout->to_initval / HZ; - data->nfs_server.port = nfss->port; - data->nfs_server.addrlen = nfss->nfs_client->cl_addrlen; - memcpy(&data->nfs_server.address, &nfss->nfs_client->cl_addr, - data->nfs_server.addrlen); - - /* overwrite those values with any that were specified */ - error = nfs_parse_mount_options((char *)options, data); - if (error < 0) - goto out; - - /* - * noac is a special case. It implies -o sync, but that's not - * necessarily reflected in the mtab options. do_remount_sb - * will clear MS_SYNCHRONOUS if -o sync wasn't specified in the - * remount options, so we have to explicitly reset it. - */ - if (data->flags & NFS_MOUNT_NOAC) - *flags |= MS_SYNCHRONOUS; - - /* compare new mount options with old ones */ - error = nfs_compare_remount_data(nfss, data); -out: - kfree(data); - return error; -} - -/* - * Initialise the common bits of the superblock - */ -static inline void nfs_initialise_sb(struct super_block *sb) -{ - struct nfs_server *server = NFS_SB(sb); - - sb->s_magic = NFS_SUPER_MAGIC; - - /* We probably want something more informative here */ - snprintf(sb->s_id, sizeof(sb->s_id), - "%u:%u", MAJOR(sb->s_dev), MINOR(sb->s_dev)); - - if (sb->s_blocksize == 0) - sb->s_blocksize = nfs_block_bits(server->wsize, - &sb->s_blocksize_bits); - - sb->s_bdi = &server->backing_dev_info; - - nfs_super_set_maxbytes(sb, server->maxfilesize); -} - -/* - * Finish setting up an NFS2/3 superblock - */ -static void nfs_fill_super(struct super_block *sb, - struct nfs_parsed_mount_data *data) -{ - struct nfs_server *server = NFS_SB(sb); - - sb->s_blocksize_bits = 0; - sb->s_blocksize = 0; - if (data->bsize) - sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits); - - if (server->nfs_client->rpc_ops->version == 3) { - /* The VFS shouldn't apply the umask to mode bits. We will do - * so ourselves when necessary. - */ - sb->s_flags |= MS_POSIXACL; - sb->s_time_gran = 1; - } - - sb->s_op = &nfs_sops; - nfs_initialise_sb(sb); -} - -/* - * Finish setting up a cloned NFS2/3 superblock - */ -static void nfs_clone_super(struct super_block *sb, - const struct super_block *old_sb) -{ - struct nfs_server *server = NFS_SB(sb); - - sb->s_blocksize_bits = old_sb->s_blocksize_bits; - sb->s_blocksize = old_sb->s_blocksize; - sb->s_maxbytes = old_sb->s_maxbytes; - - if (server->nfs_client->rpc_ops->version == 3) { - /* The VFS shouldn't apply the umask to mode bits. We will do - * so ourselves when necessary. - */ - sb->s_flags |= MS_POSIXACL; - sb->s_time_gran = 1; - } - - sb->s_op = old_sb->s_op; - nfs_initialise_sb(sb); -} - -static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags) -{ - const struct nfs_server *a = s->s_fs_info; - const struct rpc_clnt *clnt_a = a->client; - const struct rpc_clnt *clnt_b = b->client; - - if ((s->s_flags & NFS_MS_MASK) != (flags & NFS_MS_MASK)) - goto Ebusy; - if (a->nfs_client != b->nfs_client) - goto Ebusy; - if (a->flags != b->flags) - goto Ebusy; - if (a->wsize != b->wsize) - goto Ebusy; - if (a->rsize != b->rsize) - goto Ebusy; - if (a->acregmin != b->acregmin) - goto Ebusy; - if (a->acregmax != b->acregmax) - goto Ebusy; - if (a->acdirmin != b->acdirmin) - goto Ebusy; - if (a->acdirmax != b->acdirmax) - goto Ebusy; - if (clnt_a->cl_auth->au_flavor != clnt_b->cl_auth->au_flavor) - goto Ebusy; - return 1; -Ebusy: - return 0; -} - -struct nfs_sb_mountdata { - struct nfs_server *server; - int mntflags; -}; - -static int nfs_set_super(struct super_block *s, void *data) -{ - struct nfs_sb_mountdata *sb_mntdata = data; - struct nfs_server *server = sb_mntdata->server; - int ret; - - s->s_flags = sb_mntdata->mntflags; - s->s_fs_info = server; - s->s_d_op = server->nfs_client->rpc_ops->dentry_ops; - ret = set_anon_super(s, server); - if (ret == 0) - server->s_dev = s->s_dev; - return ret; -} - -static int nfs_compare_super_address(struct nfs_server *server1, - struct nfs_server *server2) -{ - struct sockaddr *sap1, *sap2; - - sap1 = (struct sockaddr *)&server1->nfs_client->cl_addr; - sap2 = (struct sockaddr *)&server2->nfs_client->cl_addr; - - if (sap1->sa_family != sap2->sa_family) - return 0; - - switch (sap1->sa_family) { - case AF_INET: { - struct sockaddr_in *sin1 = (struct sockaddr_in *)sap1; - struct sockaddr_in *sin2 = (struct sockaddr_in *)sap2; - if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) - return 0; - if (sin1->sin_port != sin2->sin_port) - return 0; - break; - } - case AF_INET6: { - struct sockaddr_in6 *sin1 = (struct sockaddr_in6 *)sap1; - struct sockaddr_in6 *sin2 = (struct sockaddr_in6 *)sap2; - if (!ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr)) - return 0; - if (sin1->sin6_port != sin2->sin6_port) - return 0; - break; - } - default: - return 0; - } - - return 1; -} - -static int nfs_compare_super(struct super_block *sb, void *data) -{ - struct nfs_sb_mountdata *sb_mntdata = data; - struct nfs_server *server = sb_mntdata->server, *old = NFS_SB(sb); - int mntflags = sb_mntdata->mntflags; - - if (!nfs_compare_super_address(old, server)) - return 0; - /* Note: NFS_MOUNT_UNSHARED == NFS4_MOUNT_UNSHARED */ - if (old->flags & NFS_MOUNT_UNSHARED) - return 0; - if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0) - return 0; - return nfs_compare_mount_options(sb, server, mntflags); -} - -static int nfs_bdi_register(struct nfs_server *server) -{ - return bdi_register_dev(&server->backing_dev_info, server->s_dev); -} - -static struct dentry *nfs_fs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *raw_data) -{ - struct nfs_server *server = NULL; - struct super_block *s; - struct nfs_parsed_mount_data *data; - struct nfs_fh *mntfh; - struct dentry *mntroot = ERR_PTR(-ENOMEM); - int (*compare_super)(struct super_block *, void *) = nfs_compare_super; - struct nfs_sb_mountdata sb_mntdata = { - .mntflags = flags, - }; - int error; - - data = nfs_alloc_parsed_mount_data(NFS_DEFAULT_VERSION); - mntfh = nfs_alloc_fhandle(); - if (data == NULL || mntfh == NULL) - goto out; - - /* Validate the mount data */ - error = nfs_validate_mount_data(raw_data, data, mntfh, dev_name); - if (error < 0) { - mntroot = ERR_PTR(error); - goto out; - } - -#ifdef CONFIG_NFS_V4 - if (data->version == 4) { - mntroot = nfs4_try_mount(flags, dev_name, data); - goto out; - } -#endif /* CONFIG_NFS_V4 */ - - /* Get a volume representation */ - server = nfs_create_server(data, mntfh); - if (IS_ERR(server)) { - mntroot = ERR_CAST(server); - goto out; - } - sb_mntdata.server = server; - - if (server->flags & NFS_MOUNT_UNSHARED) - compare_super = NULL; - - /* -o noac implies -o sync */ - if (server->flags & NFS_MOUNT_NOAC) - sb_mntdata.mntflags |= MS_SYNCHRONOUS; - - /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(fs_type, compare_super, nfs_set_super, &sb_mntdata); - if (IS_ERR(s)) { - mntroot = ERR_CAST(s); - goto out_err_nosb; - } - - if (s->s_fs_info != server) { - nfs_free_server(server); - server = NULL; - } else { - error = nfs_bdi_register(server); - if (error) { - mntroot = ERR_PTR(error); - goto error_splat_bdi; - } - } - - if (!s->s_root) { - /* initial superblock/root creation */ - nfs_fill_super(s, data); - nfs_fscache_get_super_cookie(s, data->fscache_uniq, NULL); - } - - mntroot = nfs_get_root(s, mntfh, dev_name); - if (IS_ERR(mntroot)) - goto error_splat_super; - - error = security_sb_set_mnt_opts(s, &data->lsm_opts); - if (error) - goto error_splat_root; - - s->s_flags |= MS_ACTIVE; - -out: - nfs_free_parsed_mount_data(data); - nfs_free_fhandle(mntfh); - return mntroot; - -out_err_nosb: - nfs_free_server(server); - goto out; - -error_splat_root: - dput(mntroot); - mntroot = ERR_PTR(error); -error_splat_super: - if (server && !s->s_root) - bdi_unregister(&server->backing_dev_info); -error_splat_bdi: - deactivate_locked_super(s); - goto out; -} - -/* - * Ensure that we unregister the bdi before kill_anon_super - * releases the device name - */ -static void nfs_put_super(struct super_block *s) -{ - struct nfs_server *server = NFS_SB(s); - - bdi_unregister(&server->backing_dev_info); -} - -/* - * Destroy an NFS2/3 superblock - */ -static void nfs_kill_super(struct super_block *s) -{ - struct nfs_server *server = NFS_SB(s); - - kill_anon_super(s); - nfs_fscache_release_super_cookie(s); - nfs_free_server(server); -} - -/* - * Clone an NFS2/3 server record on xdev traversal (FSID-change) - */ -static struct dentry * -nfs_xdev_mount(struct file_system_type *fs_type, int flags, - const char *dev_name, void *raw_data) -{ - struct nfs_clone_mount *data = raw_data; - struct super_block *s; - struct nfs_server *server; - struct dentry *mntroot; - int (*compare_super)(struct super_block *, void *) = nfs_compare_super; - struct nfs_sb_mountdata sb_mntdata = { - .mntflags = flags, - }; - int error; - - dprintk("--> nfs_xdev_mount()\n"); - - /* create a new volume representation */ - server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr, data->authflavor); - if (IS_ERR(server)) { - error = PTR_ERR(server); - goto out_err_noserver; - } - sb_mntdata.server = server; - - if (server->flags & NFS_MOUNT_UNSHARED) - compare_super = NULL; - - /* -o noac implies -o sync */ - if (server->flags & NFS_MOUNT_NOAC) - sb_mntdata.mntflags |= MS_SYNCHRONOUS; - - /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(&nfs_fs_type, compare_super, nfs_set_super, &sb_mntdata); - if (IS_ERR(s)) { - error = PTR_ERR(s); - goto out_err_nosb; - } - - if (s->s_fs_info != server) { - nfs_free_server(server); - server = NULL; - } else { - error = nfs_bdi_register(server); - if (error) - goto error_splat_bdi; - } - - if (!s->s_root) { - /* initial superblock/root creation */ - nfs_clone_super(s, data->sb); - nfs_fscache_get_super_cookie(s, NULL, data); - } - - mntroot = nfs_get_root(s, data->fh, dev_name); - if (IS_ERR(mntroot)) { - error = PTR_ERR(mntroot); - goto error_splat_super; - } - if (mntroot->d_inode->i_op != NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops) { - dput(mntroot); - error = -ESTALE; - goto error_splat_super; - } - - s->s_flags |= MS_ACTIVE; - - /* clone any lsm security options from the parent to the new sb */ - security_sb_clone_mnt_opts(data->sb, s); - - dprintk("<-- nfs_xdev_mount() = 0\n"); - return mntroot; - -out_err_nosb: - nfs_free_server(server); -out_err_noserver: - dprintk("<-- nfs_xdev_mount() = %d [error]\n", error); - return ERR_PTR(error); - -error_splat_super: - if (server && !s->s_root) - bdi_unregister(&server->backing_dev_info); -error_splat_bdi: - deactivate_locked_super(s); - dprintk("<-- nfs_xdev_mount() = %d [splat]\n", error); - return ERR_PTR(error); -} - -#ifdef CONFIG_NFS_V4 - -/* - * Finish setting up a cloned NFS4 superblock - */ -static void nfs4_clone_super(struct super_block *sb, - const struct super_block *old_sb) -{ - sb->s_blocksize_bits = old_sb->s_blocksize_bits; - sb->s_blocksize = old_sb->s_blocksize; - sb->s_maxbytes = old_sb->s_maxbytes; - sb->s_time_gran = 1; - sb->s_op = old_sb->s_op; - /* - * The VFS shouldn't apply the umask to mode bits. We will do - * so ourselves when necessary. - */ - sb->s_flags |= MS_POSIXACL; - sb->s_xattr = old_sb->s_xattr; - nfs_initialise_sb(sb); -} - -/* - * Set up an NFS4 superblock - */ -static void nfs4_fill_super(struct super_block *sb) -{ - sb->s_time_gran = 1; - sb->s_op = &nfs4_sops; - /* - * The VFS shouldn't apply the umask to mode bits. We will do - * so ourselves when necessary. - */ - sb->s_flags |= MS_POSIXACL; - sb->s_xattr = nfs4_xattr_handlers; - nfs_initialise_sb(sb); -} - -static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args) -{ - args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3| - NFS_MOUNT_LOCAL_FLOCK|NFS_MOUNT_LOCAL_FCNTL); -} - -static int nfs4_validate_text_mount_data(void *options, - struct nfs_parsed_mount_data *args, - const char *dev_name) -{ - struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address; - - nfs_set_port(sap, &args->nfs_server.port, NFS_PORT); - - nfs_validate_transport_protocol(args); - - nfs4_validate_mount_flags(args); - - if (args->version != 4) { - dfprintk(MOUNT, - "NFS4: Illegal mount version\n"); - return -EINVAL; - } - - if (args->auth_flavor_len > 1) { - dfprintk(MOUNT, - "NFS4: Too many RPC auth flavours specified\n"); - return -EINVAL; - } - - return nfs_parse_devname(dev_name, - &args->nfs_server.hostname, - NFS4_MAXNAMLEN, - &args->nfs_server.export_path, - NFS4_MAXPATHLEN); -} - -/* - * Validate NFSv4 mount options - */ -static int nfs4_validate_mount_data(void *options, - struct nfs_parsed_mount_data *args, - const char *dev_name) -{ - struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address; - struct nfs4_mount_data *data = (struct nfs4_mount_data *)options; - char *c; - - if (data == NULL) - goto out_no_data; - - switch (data->version) { - case 1: - if (data->host_addrlen > sizeof(args->nfs_server.address)) - goto out_no_address; - if (data->host_addrlen == 0) - goto out_no_address; - args->nfs_server.addrlen = data->host_addrlen; - if (copy_from_user(sap, data->host_addr, data->host_addrlen)) - return -EFAULT; - if (!nfs_verify_server_address(sap)) - goto out_no_address; - - if (data->auth_flavourlen) { - if (data->auth_flavourlen > 1) - goto out_inval_auth; - if (copy_from_user(&args->auth_flavors[0], - data->auth_flavours, - sizeof(args->auth_flavors[0]))) - return -EFAULT; - } - - c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN); - if (IS_ERR(c)) - return PTR_ERR(c); - args->nfs_server.hostname = c; - - c = strndup_user(data->mnt_path.data, NFS4_MAXPATHLEN); - if (IS_ERR(c)) - return PTR_ERR(c); - args->nfs_server.export_path = c; - dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", c); - - c = strndup_user(data->client_addr.data, 16); - if (IS_ERR(c)) - return PTR_ERR(c); - args->client_address = c; - - /* - * Translate to nfs_parsed_mount_data, which nfs4_fill_super - * can deal with. - */ - - args->flags = data->flags & NFS4_MOUNT_FLAGMASK; - args->rsize = data->rsize; - args->wsize = data->wsize; - args->timeo = data->timeo; - args->retrans = data->retrans; - args->acregmin = data->acregmin; - args->acregmax = data->acregmax; - args->acdirmin = data->acdirmin; - args->acdirmax = data->acdirmax; - args->nfs_server.protocol = data->proto; - nfs_validate_transport_protocol(args); - - break; - default: - if (nfs_parse_mount_options((char *)options, args) == 0) - return -EINVAL; - - if (!nfs_verify_server_address(sap)) - return -EINVAL; - - return nfs4_validate_text_mount_data(options, args, dev_name); - } - - return 0; - -out_no_data: - dfprintk(MOUNT, "NFS4: mount program didn't pass any mount data\n"); - return -EINVAL; - -out_inval_auth: - dfprintk(MOUNT, "NFS4: Invalid number of RPC auth flavours %d\n", - data->auth_flavourlen); - return -EINVAL; - -out_no_address: - dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n"); - return -EINVAL; -} - -/* - * Get the superblock for the NFS4 root partition - */ -static struct dentry * -nfs4_remote_mount(struct file_system_type *fs_type, int flags, - const char *dev_name, void *raw_data) -{ - struct nfs_parsed_mount_data *data = raw_data; - struct super_block *s; - struct nfs_server *server; - struct nfs_fh *mntfh; - struct dentry *mntroot; - int (*compare_super)(struct super_block *, void *) = nfs_compare_super; - struct nfs_sb_mountdata sb_mntdata = { - .mntflags = flags, - }; - int error = -ENOMEM; - - mntfh = nfs_alloc_fhandle(); - if (data == NULL || mntfh == NULL) - goto out; - - /* Get a volume representation */ - server = nfs4_create_server(data, mntfh); - if (IS_ERR(server)) { - error = PTR_ERR(server); - goto out; - } - sb_mntdata.server = server; - - if (server->flags & NFS4_MOUNT_UNSHARED) - compare_super = NULL; - - /* -o noac implies -o sync */ - if (server->flags & NFS_MOUNT_NOAC) - sb_mntdata.mntflags |= MS_SYNCHRONOUS; - - /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(&nfs4_fs_type, compare_super, nfs_set_super, &sb_mntdata); - if (IS_ERR(s)) { - error = PTR_ERR(s); - goto out_free; - } - - if (s->s_fs_info != server) { - nfs_free_server(server); - server = NULL; - } else { - error = nfs_bdi_register(server); - if (error) - goto error_splat_bdi; - } - - if (!s->s_root) { - /* initial superblock/root creation */ - nfs4_fill_super(s); - nfs_fscache_get_super_cookie(s, data->fscache_uniq, NULL); - } - - mntroot = nfs4_get_root(s, mntfh, dev_name); - if (IS_ERR(mntroot)) { - error = PTR_ERR(mntroot); - goto error_splat_super; - } - - error = security_sb_set_mnt_opts(s, &data->lsm_opts); - if (error) - goto error_splat_root; - - s->s_flags |= MS_ACTIVE; - - nfs_free_fhandle(mntfh); - return mntroot; - -out: - nfs_free_fhandle(mntfh); - return ERR_PTR(error); - -out_free: - nfs_free_server(server); - goto out; - -error_splat_root: - dput(mntroot); -error_splat_super: - if (server && !s->s_root) - bdi_unregister(&server->backing_dev_info); -error_splat_bdi: - deactivate_locked_super(s); - goto out; -} - -static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type, - int flags, void *data, const char *hostname) -{ - struct vfsmount *root_mnt; - char *root_devname; - size_t len; - - len = strlen(hostname) + 5; - root_devname = kmalloc(len, GFP_KERNEL); - if (root_devname == NULL) - return ERR_PTR(-ENOMEM); - /* Does hostname needs to be enclosed in brackets? */ - if (strchr(hostname, ':')) - snprintf(root_devname, len, "[%s]:/", hostname); - else - snprintf(root_devname, len, "%s:/", hostname); - root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data); - kfree(root_devname); - return root_mnt; -} - -struct nfs_referral_count { - struct list_head list; - const struct task_struct *task; - unsigned int referral_count; -}; - -static LIST_HEAD(nfs_referral_count_list); -static DEFINE_SPINLOCK(nfs_referral_count_list_lock); - -static struct nfs_referral_count *nfs_find_referral_count(void) -{ - struct nfs_referral_count *p; - - list_for_each_entry(p, &nfs_referral_count_list, list) { - if (p->task == current) - return p; - } - return NULL; -} - -#define NFS_MAX_NESTED_REFERRALS 2 - -static int nfs_referral_loop_protect(void) -{ - struct nfs_referral_count *p, *new; - int ret = -ENOMEM; - - new = kmalloc(sizeof(*new), GFP_KERNEL); - if (!new) - goto out; - new->task = current; - new->referral_count = 1; - - ret = 0; - spin_lock(&nfs_referral_count_list_lock); - p = nfs_find_referral_count(); - if (p != NULL) { - if (p->referral_count >= NFS_MAX_NESTED_REFERRALS) - ret = -ELOOP; - else - p->referral_count++; - } else { - list_add(&new->list, &nfs_referral_count_list); - new = NULL; - } - spin_unlock(&nfs_referral_count_list_lock); - kfree(new); -out: - return ret; -} - -static void nfs_referral_loop_unprotect(void) -{ - struct nfs_referral_count *p; - - spin_lock(&nfs_referral_count_list_lock); - p = nfs_find_referral_count(); - p->referral_count--; - if (p->referral_count == 0) - list_del(&p->list); - else - p = NULL; - spin_unlock(&nfs_referral_count_list_lock); - kfree(p); -} - -static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt, - const char *export_path) -{ - struct dentry *dentry; - int err; - - if (IS_ERR(root_mnt)) - return ERR_CAST(root_mnt); - - err = nfs_referral_loop_protect(); - if (err) { - mntput(root_mnt); - return ERR_PTR(err); - } - - dentry = mount_subtree(root_mnt, export_path); - nfs_referral_loop_unprotect(); - - return dentry; -} - -static struct dentry *nfs4_try_mount(int flags, const char *dev_name, - struct nfs_parsed_mount_data *data) -{ - char *export_path; - struct vfsmount *root_mnt; - struct dentry *res; - - dfprintk(MOUNT, "--> nfs4_try_mount()\n"); - - export_path = data->nfs_server.export_path; - data->nfs_server.export_path = "/"; - root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, data, - data->nfs_server.hostname); - data->nfs_server.export_path = export_path; - - res = nfs_follow_remote_path(root_mnt, export_path); - - dfprintk(MOUNT, "<-- nfs4_try_mount() = %ld%s\n", - IS_ERR(res) ? PTR_ERR(res) : 0, - IS_ERR(res) ? " [error]" : ""); - return res; -} - -/* - * Get the superblock for an NFS4 mountpoint - */ -static struct dentry *nfs4_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *raw_data) -{ - struct nfs_parsed_mount_data *data; - int error = -ENOMEM; - struct dentry *res = ERR_PTR(-ENOMEM); - - data = nfs_alloc_parsed_mount_data(4); - if (data == NULL) - goto out; - - /* Validate the mount data */ - error = nfs4_validate_mount_data(raw_data, data, dev_name); - if (error < 0) { - res = ERR_PTR(error); - goto out; - } - - res = nfs4_try_mount(flags, dev_name, data); - if (IS_ERR(res)) - error = PTR_ERR(res); - -out: - nfs_free_parsed_mount_data(data); - dprintk("<-- nfs4_mount() = %d%s\n", error, - error != 0 ? " [error]" : ""); - return res; -} - -static void nfs4_kill_super(struct super_block *sb) -{ - struct nfs_server *server = NFS_SB(sb); - - dprintk("--> %s\n", __func__); - nfs_super_return_all_delegations(sb); - kill_anon_super(sb); - nfs_fscache_release_super_cookie(sb); - nfs_free_server(server); - dprintk("<-- %s\n", __func__); -} - -/* - * Clone an NFS4 server record on xdev traversal (FSID-change) - */ -static struct dentry * -nfs4_xdev_mount(struct file_system_type *fs_type, int flags, - const char *dev_name, void *raw_data) -{ - struct nfs_clone_mount *data = raw_data; - struct super_block *s; - struct nfs_server *server; - struct dentry *mntroot; - int (*compare_super)(struct super_block *, void *) = nfs_compare_super; - struct nfs_sb_mountdata sb_mntdata = { - .mntflags = flags, - }; - int error; - - dprintk("--> nfs4_xdev_mount()\n"); - - /* create a new volume representation */ - server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr, data->authflavor); - if (IS_ERR(server)) { - error = PTR_ERR(server); - goto out_err_noserver; - } - sb_mntdata.server = server; - - if (server->flags & NFS4_MOUNT_UNSHARED) - compare_super = NULL; - - /* -o noac implies -o sync */ - if (server->flags & NFS_MOUNT_NOAC) - sb_mntdata.mntflags |= MS_SYNCHRONOUS; - - /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(&nfs4_fs_type, compare_super, nfs_set_super, &sb_mntdata); - if (IS_ERR(s)) { - error = PTR_ERR(s); - goto out_err_nosb; - } - - if (s->s_fs_info != server) { - nfs_free_server(server); - server = NULL; - } else { - error = nfs_bdi_register(server); - if (error) - goto error_splat_bdi; - } - - if (!s->s_root) { - /* initial superblock/root creation */ - nfs4_clone_super(s, data->sb); - nfs_fscache_get_super_cookie(s, NULL, data); - } - - mntroot = nfs4_get_root(s, data->fh, dev_name); - if (IS_ERR(mntroot)) { - error = PTR_ERR(mntroot); - goto error_splat_super; - } - if (mntroot->d_inode->i_op != NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops) { - dput(mntroot); - error = -ESTALE; - goto error_splat_super; - } - - s->s_flags |= MS_ACTIVE; - - security_sb_clone_mnt_opts(data->sb, s); - - dprintk("<-- nfs4_xdev_mount() = 0\n"); - return mntroot; - -out_err_nosb: - nfs_free_server(server); -out_err_noserver: - dprintk("<-- nfs4_xdev_mount() = %d [error]\n", error); - return ERR_PTR(error); - -error_splat_super: - if (server && !s->s_root) - bdi_unregister(&server->backing_dev_info); -error_splat_bdi: - deactivate_locked_super(s); - dprintk("<-- nfs4_xdev_mount() = %d [splat]\n", error); - return ERR_PTR(error); -} - -static struct dentry * -nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags, - const char *dev_name, void *raw_data) -{ - struct nfs_clone_mount *data = raw_data; - struct super_block *s; - struct nfs_server *server; - struct dentry *mntroot; - struct nfs_fh *mntfh; - int (*compare_super)(struct super_block *, void *) = nfs_compare_super; - struct nfs_sb_mountdata sb_mntdata = { - .mntflags = flags, - }; - int error = -ENOMEM; - - dprintk("--> nfs4_referral_get_sb()\n"); - - mntfh = nfs_alloc_fhandle(); - if (mntfh == NULL) - goto out_err_nofh; - - /* create a new volume representation */ - server = nfs4_create_referral_server(data, mntfh); - if (IS_ERR(server)) { - error = PTR_ERR(server); - goto out_err_noserver; - } - sb_mntdata.server = server; - - if (server->flags & NFS4_MOUNT_UNSHARED) - compare_super = NULL; - - /* -o noac implies -o sync */ - if (server->flags & NFS_MOUNT_NOAC) - sb_mntdata.mntflags |= MS_SYNCHRONOUS; - - /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(&nfs4_fs_type, compare_super, nfs_set_super, &sb_mntdata); - if (IS_ERR(s)) { - error = PTR_ERR(s); - goto out_err_nosb; - } - - if (s->s_fs_info != server) { - nfs_free_server(server); - server = NULL; - } else { - error = nfs_bdi_register(server); - if (error) - goto error_splat_bdi; - } - - if (!s->s_root) { - /* initial superblock/root creation */ - nfs4_fill_super(s); - nfs_fscache_get_super_cookie(s, NULL, data); - } - - mntroot = nfs4_get_root(s, mntfh, dev_name); - if (IS_ERR(mntroot)) { - error = PTR_ERR(mntroot); - goto error_splat_super; - } - if (mntroot->d_inode->i_op != NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops) { - dput(mntroot); - error = -ESTALE; - goto error_splat_super; - } - - s->s_flags |= MS_ACTIVE; - - security_sb_clone_mnt_opts(data->sb, s); - - nfs_free_fhandle(mntfh); - dprintk("<-- nfs4_referral_get_sb() = 0\n"); - return mntroot; - -out_err_nosb: - nfs_free_server(server); -out_err_noserver: - nfs_free_fhandle(mntfh); -out_err_nofh: - dprintk("<-- nfs4_referral_get_sb() = %d [error]\n", error); - return ERR_PTR(error); - -error_splat_super: - if (server && !s->s_root) - bdi_unregister(&server->backing_dev_info); -error_splat_bdi: - deactivate_locked_super(s); - nfs_free_fhandle(mntfh); - dprintk("<-- nfs4_referral_get_sb() = %d [splat]\n", error); - return ERR_PTR(error); -} - -/* - * Create an NFS4 server record on referral traversal - */ -static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *raw_data) -{ - struct nfs_clone_mount *data = raw_data; - char *export_path; - struct vfsmount *root_mnt; - struct dentry *res; - - dprintk("--> nfs4_referral_mount()\n"); - - export_path = data->mnt_path; - data->mnt_path = "/"; - - root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type, - flags, data, data->hostname); - data->mnt_path = export_path; - - res = nfs_follow_remote_path(root_mnt, export_path); - dprintk("<-- nfs4_referral_mount() = %ld%s\n", - IS_ERR(res) ? PTR_ERR(res) : 0, - IS_ERR(res) ? " [error]" : ""); - return res; -} - -#endif /* CONFIG_NFS_V4 */ diff --git a/ANDROID_3.4.5/fs/nfs/symlink.c b/ANDROID_3.4.5/fs/nfs/symlink.c deleted file mode 100644 index 05c9e02f..00000000 --- a/ANDROID_3.4.5/fs/nfs/symlink.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * linux/fs/nfs/symlink.c - * - * Copyright (C) 1992 Rick Sladkey - * - * Optimization changes Copyright (C) 1994 Florian La Roche - * - * Jun 7 1999, cache symlink lookups in the page cache. -DaveM - * - * nfs symlink handling code - */ - -#include <linux/time.h> -#include <linux/errno.h> -#include <linux/sunrpc/clnt.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> -#include <linux/string.h> -#include <linux/namei.h> - -/* Symlink caching in the page cache is even more simplistic - * and straight-forward than readdir caching. - */ - -static int nfs_symlink_filler(struct inode *inode, struct page *page) -{ - int error; - - error = NFS_PROTO(inode)->readlink(inode, page, 0, PAGE_SIZE); - if (error < 0) - goto error; - SetPageUptodate(page); - unlock_page(page); - return 0; - -error: - SetPageError(page); - unlock_page(page); - return -EIO; -} - -static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct inode *inode = dentry->d_inode; - struct page *page; - void *err; - - err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping)); - if (err) - goto read_failed; - page = read_cache_page(&inode->i_data, 0, - (filler_t *)nfs_symlink_filler, inode); - if (IS_ERR(page)) { - err = page; - goto read_failed; - } - nd_set_link(nd, kmap(page)); - return page; - -read_failed: - nd_set_link(nd, err); - return NULL; -} - -/* - * symlinks can't do much... - */ -const struct inode_operations nfs_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = nfs_follow_link, - .put_link = page_put_link, - .getattr = nfs_getattr, - .setattr = nfs_setattr, -}; diff --git a/ANDROID_3.4.5/fs/nfs/sysctl.c b/ANDROID_3.4.5/fs/nfs/sysctl.c deleted file mode 100644 index ad4d2e78..00000000 --- a/ANDROID_3.4.5/fs/nfs/sysctl.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * linux/fs/nfs/sysctl.c - * - * Sysctl interface to NFS parameters - */ -#include <linux/types.h> -#include <linux/linkage.h> -#include <linux/ctype.h> -#include <linux/fs.h> -#include <linux/sysctl.h> -#include <linux/module.h> -#include <linux/nfs4.h> -#include <linux/nfs_idmap.h> -#include <linux/nfs_fs.h> - -#include "callback.h" - -#ifdef CONFIG_NFS_V4 -static const int nfs_set_port_min = 0; -static const int nfs_set_port_max = 65535; -#endif -static struct ctl_table_header *nfs_callback_sysctl_table; - -static ctl_table nfs_cb_sysctls[] = { -#ifdef CONFIG_NFS_V4 - { - .procname = "nfs_callback_tcpport", - .data = &nfs_callback_set_tcpport, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = (int *)&nfs_set_port_min, - .extra2 = (int *)&nfs_set_port_max, - }, - { - .procname = "idmap_cache_timeout", - .data = &nfs_idmap_cache_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, -#endif - { - .procname = "nfs_mountpoint_timeout", - .data = &nfs_mountpoint_expiry_timeout, - .maxlen = sizeof(nfs_mountpoint_expiry_timeout), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { - .procname = "nfs_congestion_kb", - .data = &nfs_congestion_kb, - .maxlen = sizeof(nfs_congestion_kb), - .mode = 0644, - .proc_handler = proc_dointvec, - }, - { } -}; - -static ctl_table nfs_cb_sysctl_dir[] = { - { - .procname = "nfs", - .mode = 0555, - .child = nfs_cb_sysctls, - }, - { } -}; - -static ctl_table nfs_cb_sysctl_root[] = { - { - .procname = "fs", - .mode = 0555, - .child = nfs_cb_sysctl_dir, - }, - { } -}; - -int nfs_register_sysctl(void) -{ - nfs_callback_sysctl_table = register_sysctl_table(nfs_cb_sysctl_root); - if (nfs_callback_sysctl_table == NULL) - return -ENOMEM; - return 0; -} - -void nfs_unregister_sysctl(void) -{ - unregister_sysctl_table(nfs_callback_sysctl_table); - nfs_callback_sysctl_table = NULL; -} diff --git a/ANDROID_3.4.5/fs/nfs/unlink.c b/ANDROID_3.4.5/fs/nfs/unlink.c deleted file mode 100644 index 3210a033..00000000 --- a/ANDROID_3.4.5/fs/nfs/unlink.c +++ /dev/null @@ -1,558 +0,0 @@ -/* - * linux/fs/nfs/unlink.c - * - * nfs sillydelete handling - * - */ - -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/dcache.h> -#include <linux/sunrpc/sched.h> -#include <linux/sunrpc/clnt.h> -#include <linux/nfs_fs.h> -#include <linux/sched.h> -#include <linux/wait.h> -#include <linux/namei.h> - -#include "internal.h" -#include "nfs4_fs.h" -#include "iostat.h" -#include "delegation.h" - -/** - * nfs_free_unlinkdata - release data from a sillydelete operation. - * @data: pointer to unlink structure. - */ -static void -nfs_free_unlinkdata(struct nfs_unlinkdata *data) -{ - iput(data->dir); - put_rpccred(data->cred); - kfree(data->args.name.name); - kfree(data); -} - -#define NAME_ALLOC_LEN(len) ((len+16) & ~15) -/** - * nfs_copy_dname - copy dentry name to data structure - * @dentry: pointer to dentry - * @data: nfs_unlinkdata - */ -static int nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data) -{ - char *str; - int len = dentry->d_name.len; - - str = kmemdup(dentry->d_name.name, NAME_ALLOC_LEN(len), GFP_KERNEL); - if (!str) - return -ENOMEM; - data->args.name.len = len; - data->args.name.name = str; - return 0; -} - -static void nfs_free_dname(struct nfs_unlinkdata *data) -{ - kfree(data->args.name.name); - data->args.name.name = NULL; - data->args.name.len = 0; -} - -static void nfs_dec_sillycount(struct inode *dir) -{ - struct nfs_inode *nfsi = NFS_I(dir); - if (atomic_dec_return(&nfsi->silly_count) == 1) - wake_up(&nfsi->waitqueue); -} - -/** - * nfs_async_unlink_done - Sillydelete post-processing - * @task: rpc_task of the sillydelete - * - * Do the directory attribute update. - */ -static void nfs_async_unlink_done(struct rpc_task *task, void *calldata) -{ - struct nfs_unlinkdata *data = calldata; - struct inode *dir = data->dir; - - if (!NFS_PROTO(dir)->unlink_done(task, dir)) - rpc_restart_call_prepare(task); -} - -/** - * nfs_async_unlink_release - Release the sillydelete data. - * @task: rpc_task of the sillydelete - * - * We need to call nfs_put_unlinkdata as a 'tk_release' task since the - * rpc_task would be freed too. - */ -static void nfs_async_unlink_release(void *calldata) -{ - struct nfs_unlinkdata *data = calldata; - struct super_block *sb = data->dir->i_sb; - - nfs_dec_sillycount(data->dir); - nfs_free_unlinkdata(data); - nfs_sb_deactive(sb); -} - -static void nfs_unlink_prepare(struct rpc_task *task, void *calldata) -{ - struct nfs_unlinkdata *data = calldata; - NFS_PROTO(data->dir)->unlink_rpc_prepare(task, data); -} - -static const struct rpc_call_ops nfs_unlink_ops = { - .rpc_call_done = nfs_async_unlink_done, - .rpc_release = nfs_async_unlink_release, - .rpc_call_prepare = nfs_unlink_prepare, -}; - -static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data) -{ - struct rpc_message msg = { - .rpc_argp = &data->args, - .rpc_resp = &data->res, - .rpc_cred = data->cred, - }; - struct rpc_task_setup task_setup_data = { - .rpc_message = &msg, - .callback_ops = &nfs_unlink_ops, - .callback_data = data, - .workqueue = nfsiod_workqueue, - .flags = RPC_TASK_ASYNC, - }; - struct rpc_task *task; - struct dentry *alias; - - alias = d_lookup(parent, &data->args.name); - if (alias != NULL) { - int ret; - void *devname_garbage = NULL; - - /* - * Hey, we raced with lookup... See if we need to transfer - * the sillyrename information to the aliased dentry. - */ - nfs_free_dname(data); - ret = nfs_copy_dname(alias, data); - spin_lock(&alias->d_lock); - if (ret == 0 && alias->d_inode != NULL && - !(alias->d_flags & DCACHE_NFSFS_RENAMED)) { - devname_garbage = alias->d_fsdata; - alias->d_fsdata = data; - alias->d_flags |= DCACHE_NFSFS_RENAMED; - ret = 1; - } else - ret = 0; - spin_unlock(&alias->d_lock); - nfs_dec_sillycount(dir); - dput(alias); - /* - * If we'd displaced old cached devname, free it. At that - * point dentry is definitely not a root, so we won't need - * that anymore. - */ - kfree(devname_garbage); - return ret; - } - data->dir = igrab(dir); - if (!data->dir) { - nfs_dec_sillycount(dir); - return 0; - } - nfs_sb_active(dir->i_sb); - data->args.fh = NFS_FH(dir); - nfs_fattr_init(data->res.dir_attr); - - NFS_PROTO(dir)->unlink_setup(&msg, dir); - - task_setup_data.rpc_client = NFS_CLIENT(dir); - task = rpc_run_task(&task_setup_data); - if (!IS_ERR(task)) - rpc_put_task_async(task); - return 1; -} - -static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data) -{ - struct dentry *parent; - struct inode *dir; - int ret = 0; - - - parent = dget_parent(dentry); - if (parent == NULL) - goto out_free; - dir = parent->d_inode; - /* Non-exclusive lock protects against concurrent lookup() calls */ - spin_lock(&dir->i_lock); - if (atomic_inc_not_zero(&NFS_I(dir)->silly_count) == 0) { - /* Deferred delete */ - hlist_add_head(&data->list, &NFS_I(dir)->silly_list); - spin_unlock(&dir->i_lock); - ret = 1; - goto out_dput; - } - spin_unlock(&dir->i_lock); - ret = nfs_do_call_unlink(parent, dir, data); -out_dput: - dput(parent); -out_free: - return ret; -} - -void nfs_block_sillyrename(struct dentry *dentry) -{ - struct nfs_inode *nfsi = NFS_I(dentry->d_inode); - - wait_event(nfsi->waitqueue, atomic_cmpxchg(&nfsi->silly_count, 1, 0) == 1); -} - -void nfs_unblock_sillyrename(struct dentry *dentry) -{ - struct inode *dir = dentry->d_inode; - struct nfs_inode *nfsi = NFS_I(dir); - struct nfs_unlinkdata *data; - - atomic_inc(&nfsi->silly_count); - spin_lock(&dir->i_lock); - while (!hlist_empty(&nfsi->silly_list)) { - if (!atomic_inc_not_zero(&nfsi->silly_count)) - break; - data = hlist_entry(nfsi->silly_list.first, struct nfs_unlinkdata, list); - hlist_del(&data->list); - spin_unlock(&dir->i_lock); - if (nfs_do_call_unlink(dentry, dir, data) == 0) - nfs_free_unlinkdata(data); - spin_lock(&dir->i_lock); - } - spin_unlock(&dir->i_lock); -} - -/** - * nfs_async_unlink - asynchronous unlinking of a file - * @dir: parent directory of dentry - * @dentry: dentry to unlink - */ -static int -nfs_async_unlink(struct inode *dir, struct dentry *dentry) -{ - struct nfs_unlinkdata *data; - int status = -ENOMEM; - void *devname_garbage = NULL; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (data == NULL) - goto out; - - data->cred = rpc_lookup_cred(); - if (IS_ERR(data->cred)) { - status = PTR_ERR(data->cred); - goto out_free; - } - data->res.dir_attr = &data->dir_attr; - - status = -EBUSY; - spin_lock(&dentry->d_lock); - if (dentry->d_flags & DCACHE_NFSFS_RENAMED) - goto out_unlock; - dentry->d_flags |= DCACHE_NFSFS_RENAMED; - devname_garbage = dentry->d_fsdata; - dentry->d_fsdata = data; - spin_unlock(&dentry->d_lock); - /* - * If we'd displaced old cached devname, free it. At that - * point dentry is definitely not a root, so we won't need - * that anymore. - */ - if (devname_garbage) - kfree(devname_garbage); - return 0; -out_unlock: - spin_unlock(&dentry->d_lock); - put_rpccred(data->cred); -out_free: - kfree(data); -out: - return status; -} - -/** - * nfs_complete_unlink - Initialize completion of the sillydelete - * @dentry: dentry to delete - * @inode: inode - * - * Since we're most likely to be called by dentry_iput(), we - * only use the dentry to find the sillydelete. We then copy the name - * into the qstr. - */ -void -nfs_complete_unlink(struct dentry *dentry, struct inode *inode) -{ - struct nfs_unlinkdata *data = NULL; - - spin_lock(&dentry->d_lock); - if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { - dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; - data = dentry->d_fsdata; - dentry->d_fsdata = NULL; - } - spin_unlock(&dentry->d_lock); - - if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data))) - nfs_free_unlinkdata(data); -} - -/* Cancel a queued async unlink. Called when a sillyrename run fails. */ -static void -nfs_cancel_async_unlink(struct dentry *dentry) -{ - spin_lock(&dentry->d_lock); - if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { - struct nfs_unlinkdata *data = dentry->d_fsdata; - - dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; - dentry->d_fsdata = NULL; - spin_unlock(&dentry->d_lock); - nfs_free_unlinkdata(data); - return; - } - spin_unlock(&dentry->d_lock); -} - -/** - * nfs_async_rename_done - Sillyrename post-processing - * @task: rpc_task of the sillyrename - * @calldata: nfs_renamedata for the sillyrename - * - * Do the directory attribute updates and the d_move - */ -static void nfs_async_rename_done(struct rpc_task *task, void *calldata) -{ - struct nfs_renamedata *data = calldata; - struct inode *old_dir = data->old_dir; - struct inode *new_dir = data->new_dir; - struct dentry *old_dentry = data->old_dentry; - struct dentry *new_dentry = data->new_dentry; - - if (!NFS_PROTO(old_dir)->rename_done(task, old_dir, new_dir)) { - rpc_restart_call_prepare(task); - return; - } - - if (task->tk_status != 0) { - nfs_cancel_async_unlink(old_dentry); - return; - } - - d_drop(old_dentry); - d_drop(new_dentry); -} - -/** - * nfs_async_rename_release - Release the sillyrename data. - * @calldata: the struct nfs_renamedata to be released - */ -static void nfs_async_rename_release(void *calldata) -{ - struct nfs_renamedata *data = calldata; - struct super_block *sb = data->old_dir->i_sb; - - if (data->old_dentry->d_inode) - nfs_mark_for_revalidate(data->old_dentry->d_inode); - - dput(data->old_dentry); - dput(data->new_dentry); - iput(data->old_dir); - iput(data->new_dir); - nfs_sb_deactive(sb); - put_rpccred(data->cred); - kfree(data); -} - -static void nfs_rename_prepare(struct rpc_task *task, void *calldata) -{ - struct nfs_renamedata *data = calldata; - NFS_PROTO(data->old_dir)->rename_rpc_prepare(task, data); -} - -static const struct rpc_call_ops nfs_rename_ops = { - .rpc_call_done = nfs_async_rename_done, - .rpc_release = nfs_async_rename_release, - .rpc_call_prepare = nfs_rename_prepare, -}; - -/** - * nfs_async_rename - perform an asynchronous rename operation - * @old_dir: directory that currently holds the dentry to be renamed - * @new_dir: target directory for the rename - * @old_dentry: original dentry to be renamed - * @new_dentry: dentry to which the old_dentry should be renamed - * - * It's expected that valid references to the dentries and inodes are held - */ -static struct rpc_task * -nfs_async_rename(struct inode *old_dir, struct inode *new_dir, - struct dentry *old_dentry, struct dentry *new_dentry) -{ - struct nfs_renamedata *data; - struct rpc_message msg = { }; - struct rpc_task_setup task_setup_data = { - .rpc_message = &msg, - .callback_ops = &nfs_rename_ops, - .workqueue = nfsiod_workqueue, - .rpc_client = NFS_CLIENT(old_dir), - .flags = RPC_TASK_ASYNC, - }; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (data == NULL) - return ERR_PTR(-ENOMEM); - task_setup_data.callback_data = data; - - data->cred = rpc_lookup_cred(); - if (IS_ERR(data->cred)) { - struct rpc_task *task = ERR_CAST(data->cred); - kfree(data); - return task; - } - - msg.rpc_argp = &data->args; - msg.rpc_resp = &data->res; - msg.rpc_cred = data->cred; - - /* set up nfs_renamedata */ - data->old_dir = old_dir; - ihold(old_dir); - data->new_dir = new_dir; - ihold(new_dir); - data->old_dentry = dget(old_dentry); - data->new_dentry = dget(new_dentry); - nfs_fattr_init(&data->old_fattr); - nfs_fattr_init(&data->new_fattr); - - /* set up nfs_renameargs */ - data->args.old_dir = NFS_FH(old_dir); - data->args.old_name = &old_dentry->d_name; - data->args.new_dir = NFS_FH(new_dir); - data->args.new_name = &new_dentry->d_name; - - /* set up nfs_renameres */ - data->res.old_fattr = &data->old_fattr; - data->res.new_fattr = &data->new_fattr; - - nfs_sb_active(old_dir->i_sb); - - NFS_PROTO(data->old_dir)->rename_setup(&msg, old_dir); - - return rpc_run_task(&task_setup_data); -} - -/** - * nfs_sillyrename - Perform a silly-rename of a dentry - * @dir: inode of directory that contains dentry - * @dentry: dentry to be sillyrenamed - * - * NFSv2/3 is stateless and the server doesn't know when the client is - * holding a file open. To prevent application problems when a file is - * unlinked while it's still open, the client performs a "silly-rename". - * That is, it renames the file to a hidden file in the same directory, - * and only performs the unlink once the last reference to it is put. - * - * The final cleanup is done during dentry_iput. - * - * (Note: NFSv4 is stateful, and has opens, so in theory an NFSv4 server - * could take responsibility for keeping open files referenced. The server - * would also need to ensure that opened-but-deleted files were kept over - * reboots. However, we may not assume a server does so. (RFC 5661 - * does provide an OPEN4_RESULT_PRESERVE_UNLINKED flag that a server can - * use to advertise that it does this; some day we may take advantage of - * it.)) - */ -int -nfs_sillyrename(struct inode *dir, struct dentry *dentry) -{ - static unsigned int sillycounter; - const int fileidsize = sizeof(NFS_FILEID(dentry->d_inode))*2; - const int countersize = sizeof(sillycounter)*2; - const int slen = sizeof(".nfs")+fileidsize+countersize-1; - char silly[slen+1]; - struct dentry *sdentry; - struct rpc_task *task; - int error = -EIO; - - dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - dentry->d_count); - nfs_inc_stats(dir, NFSIOS_SILLYRENAME); - - /* - * We don't allow a dentry to be silly-renamed twice. - */ - error = -EBUSY; - if (dentry->d_flags & DCACHE_NFSFS_RENAMED) - goto out; - - sprintf(silly, ".nfs%*.*Lx", - fileidsize, fileidsize, - (unsigned long long)NFS_FILEID(dentry->d_inode)); - - /* Return delegation in anticipation of the rename */ - nfs_inode_return_delegation(dentry->d_inode); - - sdentry = NULL; - do { - char *suffix = silly + slen - countersize; - - dput(sdentry); - sillycounter++; - sprintf(suffix, "%*.*x", countersize, countersize, sillycounter); - - dfprintk(VFS, "NFS: trying to rename %s to %s\n", - dentry->d_name.name, silly); - - sdentry = lookup_one_len(silly, dentry->d_parent, slen); - /* - * N.B. Better to return EBUSY here ... it could be - * dangerous to delete the file while it's in use. - */ - if (IS_ERR(sdentry)) - goto out; - } while (sdentry->d_inode != NULL); /* need negative lookup */ - - /* queue unlink first. Can't do this from rpc_release as it - * has to allocate memory - */ - error = nfs_async_unlink(dir, dentry); - if (error) - goto out_dput; - - /* populate unlinkdata with the right dname */ - error = nfs_copy_dname(sdentry, - (struct nfs_unlinkdata *)dentry->d_fsdata); - if (error) { - nfs_cancel_async_unlink(dentry); - goto out_dput; - } - - /* run the rename task, undo unlink if it fails */ - task = nfs_async_rename(dir, dir, dentry, sdentry); - if (IS_ERR(task)) { - error = -EBUSY; - nfs_cancel_async_unlink(dentry); - goto out_dput; - } - - /* wait for the RPC task to complete, unless a SIGKILL intervenes */ - error = rpc_wait_for_completion_task(task); - if (error == 0) - error = task->tk_status; - rpc_put_task(task); -out_dput: - dput(sdentry); -out: - return error; -} diff --git a/ANDROID_3.4.5/fs/nfs/write.c b/ANDROID_3.4.5/fs/nfs/write.c deleted file mode 100644 index c0746232..00000000 --- a/ANDROID_3.4.5/fs/nfs/write.c +++ /dev/null @@ -1,1790 +0,0 @@ -/* - * linux/fs/nfs/write.c - * - * Write file data over NFS. - * - * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de> - */ - -#include <linux/types.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/pagemap.h> -#include <linux/file.h> -#include <linux/writeback.h> -#include <linux/swap.h> -#include <linux/migrate.h> - -#include <linux/sunrpc/clnt.h> -#include <linux/nfs_fs.h> -#include <linux/nfs_mount.h> -#include <linux/nfs_page.h> -#include <linux/backing-dev.h> -#include <linux/export.h> - -#include <asm/uaccess.h> - -#include "delegation.h" -#include "internal.h" -#include "iostat.h" -#include "nfs4_fs.h" -#include "fscache.h" -#include "pnfs.h" - -#define NFSDBG_FACILITY NFSDBG_PAGECACHE - -#define MIN_POOL_WRITE (32) -#define MIN_POOL_COMMIT (4) - -/* - * Local function declarations - */ -static void nfs_pageio_init_write(struct nfs_pageio_descriptor *desc, - struct inode *inode, int ioflags); -static void nfs_redirty_request(struct nfs_page *req); -static const struct rpc_call_ops nfs_write_partial_ops; -static const struct rpc_call_ops nfs_write_full_ops; -static const struct rpc_call_ops nfs_commit_ops; - -static struct kmem_cache *nfs_wdata_cachep; -static mempool_t *nfs_wdata_mempool; -static mempool_t *nfs_commit_mempool; - -struct nfs_write_data *nfs_commitdata_alloc(void) -{ - struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS); - - if (p) { - memset(p, 0, sizeof(*p)); - INIT_LIST_HEAD(&p->pages); - } - return p; -} -EXPORT_SYMBOL_GPL(nfs_commitdata_alloc); - -void nfs_commit_free(struct nfs_write_data *p) -{ - if (p && (p->pagevec != &p->page_array[0])) - kfree(p->pagevec); - mempool_free(p, nfs_commit_mempool); -} -EXPORT_SYMBOL_GPL(nfs_commit_free); - -struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) -{ - struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS); - - if (p) { - memset(p, 0, sizeof(*p)); - INIT_LIST_HEAD(&p->pages); - p->npages = pagecount; - if (pagecount <= ARRAY_SIZE(p->page_array)) - p->pagevec = p->page_array; - else { - p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS); - if (!p->pagevec) { - mempool_free(p, nfs_wdata_mempool); - p = NULL; - } - } - } - return p; -} - -void nfs_writedata_free(struct nfs_write_data *p) -{ - if (p && (p->pagevec != &p->page_array[0])) - kfree(p->pagevec); - mempool_free(p, nfs_wdata_mempool); -} - -void nfs_writedata_release(struct nfs_write_data *wdata) -{ - put_nfs_open_context(wdata->args.context); - nfs_writedata_free(wdata); -} - -static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error) -{ - ctx->error = error; - smp_wmb(); - set_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); -} - -static struct nfs_page *nfs_page_find_request_locked(struct page *page) -{ - struct nfs_page *req = NULL; - - if (PagePrivate(page)) { - req = (struct nfs_page *)page_private(page); - if (req != NULL) - kref_get(&req->wb_kref); - } - return req; -} - -static struct nfs_page *nfs_page_find_request(struct page *page) -{ - struct inode *inode = page->mapping->host; - struct nfs_page *req = NULL; - - spin_lock(&inode->i_lock); - req = nfs_page_find_request_locked(page); - spin_unlock(&inode->i_lock); - return req; -} - -/* Adjust the file length if we're writing beyond the end */ -static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count) -{ - struct inode *inode = page->mapping->host; - loff_t end, i_size; - pgoff_t end_index; - - spin_lock(&inode->i_lock); - i_size = i_size_read(inode); - end_index = (i_size - 1) >> PAGE_CACHE_SHIFT; - if (i_size > 0 && page->index < end_index) - goto out; - end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + ((loff_t)offset+count); - if (i_size >= end) - goto out; - i_size_write(inode, end); - nfs_inc_stats(inode, NFSIOS_EXTENDWRITE); -out: - spin_unlock(&inode->i_lock); -} - -/* A writeback failed: mark the page as bad, and invalidate the page cache */ -static void nfs_set_pageerror(struct page *page) -{ - SetPageError(page); - nfs_zap_mapping(page->mapping->host, page->mapping); -} - -/* We can set the PG_uptodate flag if we see that a write request - * covers the full page. - */ -static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int count) -{ - if (PageUptodate(page)) - return; - if (base != 0) - return; - if (count != nfs_page_length(page)) - return; - SetPageUptodate(page); -} - -static int wb_priority(struct writeback_control *wbc) -{ - if (wbc->for_reclaim) - return FLUSH_HIGHPRI | FLUSH_STABLE; - if (wbc->for_kupdate || wbc->for_background) - return FLUSH_LOWPRI | FLUSH_COND_STABLE; - return FLUSH_COND_STABLE; -} - -/* - * NFS congestion control - */ - -int nfs_congestion_kb; - -#define NFS_CONGESTION_ON_THRESH (nfs_congestion_kb >> (PAGE_SHIFT-10)) -#define NFS_CONGESTION_OFF_THRESH \ - (NFS_CONGESTION_ON_THRESH - (NFS_CONGESTION_ON_THRESH >> 2)) - -static int nfs_set_page_writeback(struct page *page) -{ - int ret = test_set_page_writeback(page); - - if (!ret) { - struct inode *inode = page->mapping->host; - struct nfs_server *nfss = NFS_SERVER(inode); - - page_cache_get(page); - if (atomic_long_inc_return(&nfss->writeback) > - NFS_CONGESTION_ON_THRESH) { - set_bdi_congested(&nfss->backing_dev_info, - BLK_RW_ASYNC); - } - } - return ret; -} - -static void nfs_end_page_writeback(struct page *page) -{ - struct inode *inode = page->mapping->host; - struct nfs_server *nfss = NFS_SERVER(inode); - - end_page_writeback(page); - page_cache_release(page); - if (atomic_long_dec_return(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH) - clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC); -} - -static struct nfs_page *nfs_find_and_lock_request(struct page *page, bool nonblock) -{ - struct inode *inode = page->mapping->host; - struct nfs_page *req; - int ret; - - spin_lock(&inode->i_lock); - for (;;) { - req = nfs_page_find_request_locked(page); - if (req == NULL) - break; - if (nfs_lock_request_dontget(req)) - break; - /* Note: If we hold the page lock, as is the case in nfs_writepage, - * then the call to nfs_lock_request_dontget() will always - * succeed provided that someone hasn't already marked the - * request as dirty (in which case we don't care). - */ - spin_unlock(&inode->i_lock); - if (!nonblock) - ret = nfs_wait_on_request(req); - else - ret = -EAGAIN; - nfs_release_request(req); - if (ret != 0) - return ERR_PTR(ret); - spin_lock(&inode->i_lock); - } - spin_unlock(&inode->i_lock); - return req; -} - -/* - * Find an associated nfs write request, and prepare to flush it out - * May return an error if the user signalled nfs_wait_on_request(). - */ -static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, - struct page *page, bool nonblock) -{ - struct nfs_page *req; - int ret = 0; - - req = nfs_find_and_lock_request(page, nonblock); - if (!req) - goto out; - ret = PTR_ERR(req); - if (IS_ERR(req)) - goto out; - - ret = nfs_set_page_writeback(page); - BUG_ON(ret != 0); - BUG_ON(test_bit(PG_CLEAN, &req->wb_flags)); - - if (!nfs_pageio_add_request(pgio, req)) { - nfs_redirty_request(req); - ret = pgio->pg_error; - } -out: - return ret; -} - -static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, struct nfs_pageio_descriptor *pgio) -{ - struct inode *inode = page->mapping->host; - int ret; - - nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE); - nfs_add_stats(inode, NFSIOS_WRITEPAGES, 1); - - nfs_pageio_cond_complete(pgio, page->index); - ret = nfs_page_async_flush(pgio, page, wbc->sync_mode == WB_SYNC_NONE); - if (ret == -EAGAIN) { - redirty_page_for_writepage(wbc, page); - ret = 0; - } - return ret; -} - -/* - * Write an mmapped page to the server. - */ -static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc) -{ - struct nfs_pageio_descriptor pgio; - int err; - - nfs_pageio_init_write(&pgio, page->mapping->host, wb_priority(wbc)); - err = nfs_do_writepage(page, wbc, &pgio); - nfs_pageio_complete(&pgio); - if (err < 0) - return err; - if (pgio.pg_error < 0) - return pgio.pg_error; - return 0; -} - -int nfs_writepage(struct page *page, struct writeback_control *wbc) -{ - int ret; - - ret = nfs_writepage_locked(page, wbc); - unlock_page(page); - return ret; -} - -static int nfs_writepages_callback(struct page *page, struct writeback_control *wbc, void *data) -{ - int ret; - - ret = nfs_do_writepage(page, wbc, data); - unlock_page(page); - return ret; -} - -int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) -{ - struct inode *inode = mapping->host; - unsigned long *bitlock = &NFS_I(inode)->flags; - struct nfs_pageio_descriptor pgio; - int err; - - /* Stop dirtying of new pages while we sync */ - err = wait_on_bit_lock(bitlock, NFS_INO_FLUSHING, - nfs_wait_bit_killable, TASK_KILLABLE); - if (err) - goto out_err; - - nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES); - - nfs_pageio_init_write(&pgio, inode, wb_priority(wbc)); - err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio); - nfs_pageio_complete(&pgio); - - clear_bit_unlock(NFS_INO_FLUSHING, bitlock); - smp_mb__after_clear_bit(); - wake_up_bit(bitlock, NFS_INO_FLUSHING); - - if (err < 0) - goto out_err; - err = pgio.pg_error; - if (err < 0) - goto out_err; - return 0; -out_err: - return err; -} - -/* - * Insert a write request into an inode - */ -static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req) -{ - struct nfs_inode *nfsi = NFS_I(inode); - - /* Lock the request! */ - nfs_lock_request_dontget(req); - - spin_lock(&inode->i_lock); - if (!nfsi->npages && nfs_have_delegation(inode, FMODE_WRITE)) - inode->i_version++; - set_bit(PG_MAPPED, &req->wb_flags); - SetPagePrivate(req->wb_page); - set_page_private(req->wb_page, (unsigned long)req); - nfsi->npages++; - kref_get(&req->wb_kref); - spin_unlock(&inode->i_lock); -} - -/* - * Remove a write request from an inode - */ -static void nfs_inode_remove_request(struct nfs_page *req) -{ - struct inode *inode = req->wb_context->dentry->d_inode; - struct nfs_inode *nfsi = NFS_I(inode); - - BUG_ON (!NFS_WBACK_BUSY(req)); - - spin_lock(&inode->i_lock); - set_page_private(req->wb_page, 0); - ClearPagePrivate(req->wb_page); - clear_bit(PG_MAPPED, &req->wb_flags); - nfsi->npages--; - spin_unlock(&inode->i_lock); - nfs_release_request(req); -} - -static void -nfs_mark_request_dirty(struct nfs_page *req) -{ - __set_page_dirty_nobuffers(req->wb_page); -} - -#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) -/** - * nfs_request_add_commit_list - add request to a commit list - * @req: pointer to a struct nfs_page - * @head: commit list head - * - * This sets the PG_CLEAN bit, updates the inode global count of - * number of outstanding requests requiring a commit as well as - * the MM page stats. - * - * The caller must _not_ hold the inode->i_lock, but must be - * holding the nfs_page lock. - */ -void -nfs_request_add_commit_list(struct nfs_page *req, struct list_head *head) -{ - struct inode *inode = req->wb_context->dentry->d_inode; - - set_bit(PG_CLEAN, &(req)->wb_flags); - spin_lock(&inode->i_lock); - nfs_list_add_request(req, head); - NFS_I(inode)->ncommit++; - spin_unlock(&inode->i_lock); - inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); - inc_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE); - __mark_inode_dirty(inode, I_DIRTY_DATASYNC); -} -EXPORT_SYMBOL_GPL(nfs_request_add_commit_list); - -/** - * nfs_request_remove_commit_list - Remove request from a commit list - * @req: pointer to a nfs_page - * - * This clears the PG_CLEAN bit, and updates the inode global count of - * number of outstanding requests requiring a commit - * It does not update the MM page stats. - * - * The caller _must_ hold the inode->i_lock and the nfs_page lock. - */ -void -nfs_request_remove_commit_list(struct nfs_page *req) -{ - struct inode *inode = req->wb_context->dentry->d_inode; - - if (!test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) - return; - nfs_list_remove_request(req); - NFS_I(inode)->ncommit--; -} -EXPORT_SYMBOL_GPL(nfs_request_remove_commit_list); - - -/* - * Add a request to the inode's commit list. - */ -static void -nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) -{ - struct inode *inode = req->wb_context->dentry->d_inode; - - if (pnfs_mark_request_commit(req, lseg)) - return; - nfs_request_add_commit_list(req, &NFS_I(inode)->commit_list); -} - -static void -nfs_clear_page_commit(struct page *page) -{ - dec_zone_page_state(page, NR_UNSTABLE_NFS); - dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE); -} - -static void -nfs_clear_request_commit(struct nfs_page *req) -{ - if (test_bit(PG_CLEAN, &req->wb_flags)) { - struct inode *inode = req->wb_context->dentry->d_inode; - - if (!pnfs_clear_request_commit(req)) { - spin_lock(&inode->i_lock); - nfs_request_remove_commit_list(req); - spin_unlock(&inode->i_lock); - } - nfs_clear_page_commit(req->wb_page); - } -} - -static inline -int nfs_write_need_commit(struct nfs_write_data *data) -{ - if (data->verf.committed == NFS_DATA_SYNC) - return data->lseg == NULL; - else - return data->verf.committed != NFS_FILE_SYNC; -} - -static inline -int nfs_reschedule_unstable_write(struct nfs_page *req, - struct nfs_write_data *data) -{ - if (test_and_clear_bit(PG_NEED_COMMIT, &req->wb_flags)) { - nfs_mark_request_commit(req, data->lseg); - return 1; - } - if (test_and_clear_bit(PG_NEED_RESCHED, &req->wb_flags)) { - nfs_mark_request_dirty(req); - return 1; - } - return 0; -} -#else -static void -nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) -{ -} - -static void -nfs_clear_request_commit(struct nfs_page *req) -{ -} - -static inline -int nfs_write_need_commit(struct nfs_write_data *data) -{ - return 0; -} - -static inline -int nfs_reschedule_unstable_write(struct nfs_page *req, - struct nfs_write_data *data) -{ - return 0; -} -#endif - -#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) -static int -nfs_need_commit(struct nfs_inode *nfsi) -{ - return nfsi->ncommit > 0; -} - -/* i_lock held by caller */ -static int -nfs_scan_commit_list(struct list_head *src, struct list_head *dst, int max, - spinlock_t *lock) -{ - struct nfs_page *req, *tmp; - int ret = 0; - - list_for_each_entry_safe(req, tmp, src, wb_list) { - if (!nfs_lock_request(req)) - continue; - if (cond_resched_lock(lock)) - list_safe_reset_next(req, tmp, wb_list); - nfs_request_remove_commit_list(req); - nfs_list_add_request(req, dst); - ret++; - if (ret == max) - break; - } - return ret; -} - -/* - * nfs_scan_commit - Scan an inode for commit requests - * @inode: NFS inode to scan - * @dst: destination list - * - * Moves requests from the inode's 'commit' request list. - * The requests are *not* checked to ensure that they form a contiguous set. - */ -static int -nfs_scan_commit(struct inode *inode, struct list_head *dst) -{ - struct nfs_inode *nfsi = NFS_I(inode); - int ret = 0; - - spin_lock(&inode->i_lock); - if (nfsi->ncommit > 0) { - const int max = INT_MAX; - - ret = nfs_scan_commit_list(&nfsi->commit_list, dst, max, - &inode->i_lock); - ret += pnfs_scan_commit_lists(inode, max - ret, - &inode->i_lock); - } - spin_unlock(&inode->i_lock); - return ret; -} - -#else -static inline int nfs_need_commit(struct nfs_inode *nfsi) -{ - return 0; -} - -static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst) -{ - return 0; -} -#endif - -/* - * Search for an existing write request, and attempt to update - * it to reflect a new dirty region on a given page. - * - * If the attempt fails, then the existing request is flushed out - * to disk. - */ -static struct nfs_page *nfs_try_to_update_request(struct inode *inode, - struct page *page, - unsigned int offset, - unsigned int bytes) -{ - struct nfs_page *req; - unsigned int rqend; - unsigned int end; - int error; - - if (!PagePrivate(page)) - return NULL; - - end = offset + bytes; - spin_lock(&inode->i_lock); - - for (;;) { - req = nfs_page_find_request_locked(page); - if (req == NULL) - goto out_unlock; - - rqend = req->wb_offset + req->wb_bytes; - /* - * Tell the caller to flush out the request if - * the offsets are non-contiguous. - * Note: nfs_flush_incompatible() will already - * have flushed out requests having wrong owners. - */ - if (offset > rqend - || end < req->wb_offset) - goto out_flushme; - - if (nfs_lock_request_dontget(req)) - break; - - /* The request is locked, so wait and then retry */ - spin_unlock(&inode->i_lock); - error = nfs_wait_on_request(req); - nfs_release_request(req); - if (error != 0) - goto out_err; - spin_lock(&inode->i_lock); - } - - /* Okay, the request matches. Update the region */ - if (offset < req->wb_offset) { - req->wb_offset = offset; - req->wb_pgbase = offset; - } - if (end > rqend) - req->wb_bytes = end - req->wb_offset; - else - req->wb_bytes = rqend - req->wb_offset; -out_unlock: - spin_unlock(&inode->i_lock); - if (req) - nfs_clear_request_commit(req); - return req; -out_flushme: - spin_unlock(&inode->i_lock); - nfs_release_request(req); - error = nfs_wb_page(inode, page); -out_err: - return ERR_PTR(error); -} - -/* - * Try to update an existing write request, or create one if there is none. - * - * Note: Should always be called with the Page Lock held to prevent races - * if we have to add a new request. Also assumes that the caller has - * already called nfs_flush_incompatible() if necessary. - */ -static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx, - struct page *page, unsigned int offset, unsigned int bytes) -{ - struct inode *inode = page->mapping->host; - struct nfs_page *req; - - req = nfs_try_to_update_request(inode, page, offset, bytes); - if (req != NULL) - goto out; - req = nfs_create_request(ctx, inode, page, offset, bytes); - if (IS_ERR(req)) - goto out; - nfs_inode_add_request(inode, req); -out: - return req; -} - -static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page, - unsigned int offset, unsigned int count) -{ - struct nfs_page *req; - - req = nfs_setup_write_request(ctx, page, offset, count); - if (IS_ERR(req)) - return PTR_ERR(req); - /* Update file length */ - nfs_grow_file(page, offset, count); - nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes); - nfs_mark_request_dirty(req); - nfs_unlock_request(req); - return 0; -} - -int nfs_flush_incompatible(struct file *file, struct page *page) -{ - struct nfs_open_context *ctx = nfs_file_open_context(file); - struct nfs_page *req; - int do_flush, status; - /* - * Look for a request corresponding to this page. If there - * is one, and it belongs to another file, we flush it out - * before we try to copy anything into the page. Do this - * due to the lack of an ACCESS-type call in NFSv2. - * Also do the same if we find a request from an existing - * dropped page. - */ - do { - req = nfs_page_find_request(page); - if (req == NULL) - return 0; - do_flush = req->wb_page != page || req->wb_context != ctx || - req->wb_lock_context->lockowner != current->files || - req->wb_lock_context->pid != current->tgid; - nfs_release_request(req); - if (!do_flush) - return 0; - status = nfs_wb_page(page->mapping->host, page); - } while (status == 0); - return status; -} - -/* - * If the page cache is marked as unsafe or invalid, then we can't rely on - * the PageUptodate() flag. In this case, we will need to turn off - * write optimisations that depend on the page contents being correct. - */ -static int nfs_write_pageuptodate(struct page *page, struct inode *inode) -{ - return PageUptodate(page) && - !(NFS_I(inode)->cache_validity & (NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA)); -} - -/* - * Update and possibly write a cached page of an NFS file. - * - * XXX: Keep an eye on generic_file_read to make sure it doesn't do bad - * things with a page scheduled for an RPC call (e.g. invalidate it). - */ -int nfs_updatepage(struct file *file, struct page *page, - unsigned int offset, unsigned int count) -{ - struct nfs_open_context *ctx = nfs_file_open_context(file); - struct inode *inode = page->mapping->host; - int status = 0; - - nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE); - - dprintk("NFS: nfs_updatepage(%s/%s %d@%lld)\n", - file->f_path.dentry->d_parent->d_name.name, - file->f_path.dentry->d_name.name, count, - (long long)(page_offset(page) + offset)); - - /* If we're not using byte range locks, and we know the page - * is up to date, it may be more efficient to extend the write - * to cover the entire page in order to avoid fragmentation - * inefficiencies. - */ - if (nfs_write_pageuptodate(page, inode) && - inode->i_flock == NULL && - !(file->f_flags & O_DSYNC)) { - count = max(count + offset, nfs_page_length(page)); - offset = 0; - } - - status = nfs_writepage_setup(ctx, page, offset, count); - if (status < 0) - nfs_set_pageerror(page); - else - __set_page_dirty_nobuffers(page); - - dprintk("NFS: nfs_updatepage returns %d (isize %lld)\n", - status, (long long)i_size_read(inode)); - return status; -} - -static void nfs_writepage_release(struct nfs_page *req, - struct nfs_write_data *data) -{ - struct page *page = req->wb_page; - - if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req, data)) - nfs_inode_remove_request(req); - nfs_unlock_request(req); - nfs_end_page_writeback(page); -} - -static int flush_task_priority(int how) -{ - switch (how & (FLUSH_HIGHPRI|FLUSH_LOWPRI)) { - case FLUSH_HIGHPRI: - return RPC_PRIORITY_HIGH; - case FLUSH_LOWPRI: - return RPC_PRIORITY_LOW; - } - return RPC_PRIORITY_NORMAL; -} - -int nfs_initiate_write(struct nfs_write_data *data, - struct rpc_clnt *clnt, - const struct rpc_call_ops *call_ops, - int how) -{ - struct inode *inode = data->inode; - int priority = flush_task_priority(how); - struct rpc_task *task; - struct rpc_message msg = { - .rpc_argp = &data->args, - .rpc_resp = &data->res, - .rpc_cred = data->cred, - }; - struct rpc_task_setup task_setup_data = { - .rpc_client = clnt, - .task = &data->task, - .rpc_message = &msg, - .callback_ops = call_ops, - .callback_data = data, - .workqueue = nfsiod_workqueue, - .flags = RPC_TASK_ASYNC, - .priority = priority, - }; - int ret = 0; - - /* Set up the initial task struct. */ - NFS_PROTO(inode)->write_setup(data, &msg); - - dprintk("NFS: %5u initiated write call " - "(req %s/%lld, %u bytes @ offset %llu)\n", - data->task.tk_pid, - inode->i_sb->s_id, - (long long)NFS_FILEID(inode), - data->args.count, - (unsigned long long)data->args.offset); - - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) { - ret = PTR_ERR(task); - goto out; - } - if (how & FLUSH_SYNC) { - ret = rpc_wait_for_completion_task(task); - if (ret == 0) - ret = task->tk_status; - } - rpc_put_task(task); -out: - return ret; -} -EXPORT_SYMBOL_GPL(nfs_initiate_write); - -/* - * Set up the argument/result storage required for the RPC call. - */ -static void nfs_write_rpcsetup(struct nfs_page *req, - struct nfs_write_data *data, - unsigned int count, unsigned int offset, - int how) -{ - struct inode *inode = req->wb_context->dentry->d_inode; - - /* Set up the RPC argument and reply structs - * NB: take care not to mess about with data->commit et al. */ - - data->req = req; - data->inode = inode = req->wb_context->dentry->d_inode; - data->cred = req->wb_context->cred; - - data->args.fh = NFS_FH(inode); - data->args.offset = req_offset(req) + offset; - /* pnfs_set_layoutcommit needs this */ - data->mds_offset = data->args.offset; - data->args.pgbase = req->wb_pgbase + offset; - data->args.pages = data->pagevec; - data->args.count = count; - data->args.context = get_nfs_open_context(req->wb_context); - data->args.lock_context = req->wb_lock_context; - data->args.stable = NFS_UNSTABLE; - switch (how & (FLUSH_STABLE | FLUSH_COND_STABLE)) { - case 0: - break; - case FLUSH_COND_STABLE: - if (nfs_need_commit(NFS_I(inode))) - break; - default: - data->args.stable = NFS_FILE_SYNC; - } - - data->res.fattr = &data->fattr; - data->res.count = count; - data->res.verf = &data->verf; - nfs_fattr_init(&data->fattr); -} - -static int nfs_do_write(struct nfs_write_data *data, - const struct rpc_call_ops *call_ops, - int how) -{ - struct inode *inode = data->args.context->dentry->d_inode; - - return nfs_initiate_write(data, NFS_CLIENT(inode), call_ops, how); -} - -static int nfs_do_multiple_writes(struct list_head *head, - const struct rpc_call_ops *call_ops, - int how) -{ - struct nfs_write_data *data; - int ret = 0; - - while (!list_empty(head)) { - int ret2; - - data = list_entry(head->next, struct nfs_write_data, list); - list_del_init(&data->list); - - ret2 = nfs_do_write(data, call_ops, how); - if (ret == 0) - ret = ret2; - } - return ret; -} - -/* If a nfs_flush_* function fails, it should remove reqs from @head and - * call this on each, which will prepare them to be retried on next - * writeback using standard nfs. - */ -static void nfs_redirty_request(struct nfs_page *req) -{ - struct page *page = req->wb_page; - - nfs_mark_request_dirty(req); - nfs_unlock_request(req); - nfs_end_page_writeback(page); -} - -/* - * Generate multiple small requests to write out a single - * contiguous dirty area on one page. - */ -static int nfs_flush_multi(struct nfs_pageio_descriptor *desc, struct list_head *res) -{ - struct nfs_page *req = nfs_list_entry(desc->pg_list.next); - struct page *page = req->wb_page; - struct nfs_write_data *data; - size_t wsize = desc->pg_bsize, nbytes; - unsigned int offset; - int requests = 0; - int ret = 0; - - nfs_list_remove_request(req); - - if ((desc->pg_ioflags & FLUSH_COND_STABLE) && - (desc->pg_moreio || NFS_I(desc->pg_inode)->ncommit || - desc->pg_count > wsize)) - desc->pg_ioflags &= ~FLUSH_COND_STABLE; - - - offset = 0; - nbytes = desc->pg_count; - do { - size_t len = min(nbytes, wsize); - - data = nfs_writedata_alloc(1); - if (!data) - goto out_bad; - data->pagevec[0] = page; - nfs_write_rpcsetup(req, data, len, offset, desc->pg_ioflags); - list_add(&data->list, res); - requests++; - nbytes -= len; - offset += len; - } while (nbytes != 0); - atomic_set(&req->wb_complete, requests); - desc->pg_rpc_callops = &nfs_write_partial_ops; - return ret; - -out_bad: - while (!list_empty(res)) { - data = list_entry(res->next, struct nfs_write_data, list); - list_del(&data->list); - nfs_writedata_release(data); - } - nfs_redirty_request(req); - return -ENOMEM; -} - -/* - * Create an RPC task for the given write request and kick it. - * The page must have been locked by the caller. - * - * It may happen that the page we're passed is not marked dirty. - * This is the case if nfs_updatepage detects a conflicting request - * that has been written but not committed. - */ -static int nfs_flush_one(struct nfs_pageio_descriptor *desc, struct list_head *res) -{ - struct nfs_page *req; - struct page **pages; - struct nfs_write_data *data; - struct list_head *head = &desc->pg_list; - int ret = 0; - - data = nfs_writedata_alloc(nfs_page_array_len(desc->pg_base, - desc->pg_count)); - if (!data) { - while (!list_empty(head)) { - req = nfs_list_entry(head->next); - nfs_list_remove_request(req); - nfs_redirty_request(req); - } - ret = -ENOMEM; - goto out; - } - pages = data->pagevec; - while (!list_empty(head)) { - req = nfs_list_entry(head->next); - nfs_list_remove_request(req); - nfs_list_add_request(req, &data->pages); - *pages++ = req->wb_page; - } - req = nfs_list_entry(data->pages.next); - - if ((desc->pg_ioflags & FLUSH_COND_STABLE) && - (desc->pg_moreio || NFS_I(desc->pg_inode)->ncommit)) - desc->pg_ioflags &= ~FLUSH_COND_STABLE; - - /* Set up the argument struct */ - nfs_write_rpcsetup(req, data, desc->pg_count, 0, desc->pg_ioflags); - list_add(&data->list, res); - desc->pg_rpc_callops = &nfs_write_full_ops; -out: - return ret; -} - -int nfs_generic_flush(struct nfs_pageio_descriptor *desc, struct list_head *head) -{ - if (desc->pg_bsize < PAGE_CACHE_SIZE) - return nfs_flush_multi(desc, head); - return nfs_flush_one(desc, head); -} - -static int nfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc) -{ - LIST_HEAD(head); - int ret; - - ret = nfs_generic_flush(desc, &head); - if (ret == 0) - ret = nfs_do_multiple_writes(&head, desc->pg_rpc_callops, - desc->pg_ioflags); - return ret; -} - -static const struct nfs_pageio_ops nfs_pageio_write_ops = { - .pg_test = nfs_generic_pg_test, - .pg_doio = nfs_generic_pg_writepages, -}; - -void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio, - struct inode *inode, int ioflags) -{ - nfs_pageio_init(pgio, inode, &nfs_pageio_write_ops, - NFS_SERVER(inode)->wsize, ioflags); -} - -void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio) -{ - pgio->pg_ops = &nfs_pageio_write_ops; - pgio->pg_bsize = NFS_SERVER(pgio->pg_inode)->wsize; -} -EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds); - -static void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, - struct inode *inode, int ioflags) -{ - if (!pnfs_pageio_init_write(pgio, inode, ioflags)) - nfs_pageio_init_write_mds(pgio, inode, ioflags); -} - -/* - * Handle a write reply that flushed part of a page. - */ -static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata) -{ - struct nfs_write_data *data = calldata; - - dprintk("NFS: %5u write(%s/%lld %d@%lld)", - task->tk_pid, - data->req->wb_context->dentry->d_inode->i_sb->s_id, - (long long) - NFS_FILEID(data->req->wb_context->dentry->d_inode), - data->req->wb_bytes, (long long)req_offset(data->req)); - - nfs_writeback_done(task, data); -} - -static void nfs_writeback_release_partial(void *calldata) -{ - struct nfs_write_data *data = calldata; - struct nfs_page *req = data->req; - struct page *page = req->wb_page; - int status = data->task.tk_status; - - if (status < 0) { - nfs_set_pageerror(page); - nfs_context_set_write_error(req->wb_context, status); - dprintk(", error = %d\n", status); - goto out; - } - - if (nfs_write_need_commit(data)) { - struct inode *inode = page->mapping->host; - - spin_lock(&inode->i_lock); - if (test_bit(PG_NEED_RESCHED, &req->wb_flags)) { - /* Do nothing we need to resend the writes */ - } else if (!test_and_set_bit(PG_NEED_COMMIT, &req->wb_flags)) { - memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); - dprintk(" defer commit\n"); - } else if (memcmp(&req->wb_verf, &data->verf, sizeof(req->wb_verf))) { - set_bit(PG_NEED_RESCHED, &req->wb_flags); - clear_bit(PG_NEED_COMMIT, &req->wb_flags); - dprintk(" server reboot detected\n"); - } - spin_unlock(&inode->i_lock); - } else - dprintk(" OK\n"); - -out: - if (atomic_dec_and_test(&req->wb_complete)) - nfs_writepage_release(req, data); - nfs_writedata_release(calldata); -} - -void nfs_write_prepare(struct rpc_task *task, void *calldata) -{ - struct nfs_write_data *data = calldata; - NFS_PROTO(data->inode)->write_rpc_prepare(task, data); -} - -static const struct rpc_call_ops nfs_write_partial_ops = { - .rpc_call_prepare = nfs_write_prepare, - .rpc_call_done = nfs_writeback_done_partial, - .rpc_release = nfs_writeback_release_partial, -}; - -/* - * Handle a write reply that flushes a whole page. - * - * FIXME: There is an inherent race with invalidate_inode_pages and - * writebacks since the page->count is kept > 1 for as long - * as the page has a write request pending. - */ -static void nfs_writeback_done_full(struct rpc_task *task, void *calldata) -{ - struct nfs_write_data *data = calldata; - - nfs_writeback_done(task, data); -} - -static void nfs_writeback_release_full(void *calldata) -{ - struct nfs_write_data *data = calldata; - int status = data->task.tk_status; - - /* Update attributes as result of writeback. */ - while (!list_empty(&data->pages)) { - struct nfs_page *req = nfs_list_entry(data->pages.next); - struct page *page = req->wb_page; - - nfs_list_remove_request(req); - - dprintk("NFS: %5u write (%s/%lld %d@%lld)", - data->task.tk_pid, - req->wb_context->dentry->d_inode->i_sb->s_id, - (long long)NFS_FILEID(req->wb_context->dentry->d_inode), - req->wb_bytes, - (long long)req_offset(req)); - - if (status < 0) { - nfs_set_pageerror(page); - nfs_context_set_write_error(req->wb_context, status); - dprintk(", error = %d\n", status); - goto remove_request; - } - - if (nfs_write_need_commit(data)) { - memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); - nfs_mark_request_commit(req, data->lseg); - dprintk(" marked for commit\n"); - goto next; - } - dprintk(" OK\n"); -remove_request: - nfs_inode_remove_request(req); - next: - nfs_unlock_request(req); - nfs_end_page_writeback(page); - } - nfs_writedata_release(calldata); -} - -static const struct rpc_call_ops nfs_write_full_ops = { - .rpc_call_prepare = nfs_write_prepare, - .rpc_call_done = nfs_writeback_done_full, - .rpc_release = nfs_writeback_release_full, -}; - - -/* - * This function is called when the WRITE call is complete. - */ -void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) -{ - struct nfs_writeargs *argp = &data->args; - struct nfs_writeres *resp = &data->res; - int status; - - dprintk("NFS: %5u nfs_writeback_done (status %d)\n", - task->tk_pid, task->tk_status); - - /* - * ->write_done will attempt to use post-op attributes to detect - * conflicting writes by other clients. A strict interpretation - * of close-to-open would allow us to continue caching even if - * another writer had changed the file, but some applications - * depend on tighter cache coherency when writing. - */ - status = NFS_PROTO(data->inode)->write_done(task, data); - if (status != 0) - return; - nfs_add_stats(data->inode, NFSIOS_SERVERWRITTENBYTES, resp->count); - -#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) - if (resp->verf->committed < argp->stable && task->tk_status >= 0) { - /* 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; - - /* Note this will print the MDS for a DS write */ - if (time_before(complain, jiffies)) { - dprintk("NFS: faulty NFS server %s:" - " (committed = %d) != (stable = %d)\n", - NFS_SERVER(data->inode)->nfs_client->cl_hostname, - resp->verf->committed, argp->stable); - complain = jiffies + 300 * HZ; - } - } -#endif - /* Is this a short write? */ - if (task->tk_status >= 0 && resp->count < argp->count) { - static unsigned long complain; - - nfs_inc_stats(data->inode, NFSIOS_SHORTWRITE); - - /* Has the server at least made some progress? */ - if (resp->count != 0) { - /* Was this an NFSv2 write or an NFSv3 stable write? */ - if (resp->verf->committed != NFS_UNSTABLE) { - /* Resend from where the server left off */ - data->mds_offset += resp->count; - argp->offset += resp->count; - argp->pgbase += resp->count; - argp->count -= resp->count; - } else { - /* Resend as a stable write in order to avoid - * headaches in the case of a server crash. - */ - argp->stable = NFS_FILE_SYNC; - } - rpc_restart_call_prepare(task); - return; - } - if (time_before(complain, jiffies)) { - printk(KERN_WARNING - "NFS: Server wrote zero bytes, expected %u.\n", - argp->count); - complain = jiffies + 300 * HZ; - } - /* Can't do anything about it except throw an error. */ - task->tk_status = -EIO; - } - return; -} - - -#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) -static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait) -{ - int ret; - - if (!test_and_set_bit(NFS_INO_COMMIT, &nfsi->flags)) - return 1; - if (!may_wait) - return 0; - ret = out_of_line_wait_on_bit_lock(&nfsi->flags, - NFS_INO_COMMIT, - nfs_wait_bit_killable, - TASK_KILLABLE); - return (ret < 0) ? ret : 1; -} - -void nfs_commit_clear_lock(struct nfs_inode *nfsi) -{ - clear_bit(NFS_INO_COMMIT, &nfsi->flags); - smp_mb__after_clear_bit(); - wake_up_bit(&nfsi->flags, NFS_INO_COMMIT); -} -EXPORT_SYMBOL_GPL(nfs_commit_clear_lock); - -void nfs_commitdata_release(void *data) -{ - struct nfs_write_data *wdata = data; - - put_nfs_open_context(wdata->args.context); - nfs_commit_free(wdata); -} -EXPORT_SYMBOL_GPL(nfs_commitdata_release); - -int nfs_initiate_commit(struct nfs_write_data *data, struct rpc_clnt *clnt, - const struct rpc_call_ops *call_ops, - int how) -{ - struct rpc_task *task; - int priority = flush_task_priority(how); - struct rpc_message msg = { - .rpc_argp = &data->args, - .rpc_resp = &data->res, - .rpc_cred = data->cred, - }; - struct rpc_task_setup task_setup_data = { - .task = &data->task, - .rpc_client = clnt, - .rpc_message = &msg, - .callback_ops = call_ops, - .callback_data = data, - .workqueue = nfsiod_workqueue, - .flags = RPC_TASK_ASYNC, - .priority = priority, - }; - /* Set up the initial task struct. */ - NFS_PROTO(data->inode)->commit_setup(data, &msg); - - dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid); - - task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) - return PTR_ERR(task); - if (how & FLUSH_SYNC) - rpc_wait_for_completion_task(task); - rpc_put_task(task); - return 0; -} -EXPORT_SYMBOL_GPL(nfs_initiate_commit); - -/* - * Set up the argument/result storage required for the RPC call. - */ -void nfs_init_commit(struct nfs_write_data *data, - struct list_head *head, - struct pnfs_layout_segment *lseg) -{ - struct nfs_page *first = nfs_list_entry(head->next); - struct inode *inode = first->wb_context->dentry->d_inode; - - /* Set up the RPC argument and reply structs - * NB: take care not to mess about with data->commit et al. */ - - list_splice_init(head, &data->pages); - - data->inode = inode; - data->cred = first->wb_context->cred; - data->lseg = lseg; /* reference transferred */ - data->mds_ops = &nfs_commit_ops; - - data->args.fh = NFS_FH(data->inode); - /* Note: we always request a commit of the entire inode */ - data->args.offset = 0; - data->args.count = 0; - data->args.context = get_nfs_open_context(first->wb_context); - data->res.count = 0; - data->res.fattr = &data->fattr; - data->res.verf = &data->verf; - nfs_fattr_init(&data->fattr); -} -EXPORT_SYMBOL_GPL(nfs_init_commit); - -void nfs_retry_commit(struct list_head *page_list, - struct pnfs_layout_segment *lseg) -{ - struct nfs_page *req; - - while (!list_empty(page_list)) { - req = nfs_list_entry(page_list->next); - nfs_list_remove_request(req); - nfs_mark_request_commit(req, lseg); - dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); - dec_bdi_stat(req->wb_page->mapping->backing_dev_info, - BDI_RECLAIMABLE); - nfs_unlock_request(req); - } -} -EXPORT_SYMBOL_GPL(nfs_retry_commit); - -/* - * Commit dirty pages - */ -static int -nfs_commit_list(struct inode *inode, struct list_head *head, int how) -{ - struct nfs_write_data *data; - - data = nfs_commitdata_alloc(); - - if (!data) - goto out_bad; - - /* Set up the argument struct */ - nfs_init_commit(data, head, NULL); - return nfs_initiate_commit(data, NFS_CLIENT(inode), data->mds_ops, how); - out_bad: - nfs_retry_commit(head, NULL); - nfs_commit_clear_lock(NFS_I(inode)); - return -ENOMEM; -} - -/* - * COMMIT call returned - */ -static void nfs_commit_done(struct rpc_task *task, void *calldata) -{ - struct nfs_write_data *data = calldata; - - dprintk("NFS: %5u nfs_commit_done (status %d)\n", - task->tk_pid, task->tk_status); - - /* Call the NFS version-specific code */ - NFS_PROTO(data->inode)->commit_done(task, data); -} - -void nfs_commit_release_pages(struct nfs_write_data *data) -{ - struct nfs_page *req; - int status = data->task.tk_status; - - while (!list_empty(&data->pages)) { - req = nfs_list_entry(data->pages.next); - nfs_list_remove_request(req); - nfs_clear_page_commit(req->wb_page); - - dprintk("NFS: commit (%s/%lld %d@%lld)", - req->wb_context->dentry->d_sb->s_id, - (long long)NFS_FILEID(req->wb_context->dentry->d_inode), - req->wb_bytes, - (long long)req_offset(req)); - if (status < 0) { - nfs_context_set_write_error(req->wb_context, status); - nfs_inode_remove_request(req); - dprintk(", error = %d\n", status); - goto next; - } - - /* Okay, COMMIT succeeded, apparently. Check the verifier - * returned by the server against all stored verfs. */ - if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) { - /* We have a match */ - nfs_inode_remove_request(req); - dprintk(" OK\n"); - goto next; - } - /* We have a mismatch. Write the page again */ - dprintk(" mismatch\n"); - nfs_mark_request_dirty(req); - next: - nfs_unlock_request(req); - } -} -EXPORT_SYMBOL_GPL(nfs_commit_release_pages); - -static void nfs_commit_release(void *calldata) -{ - struct nfs_write_data *data = calldata; - - nfs_commit_release_pages(data); - nfs_commit_clear_lock(NFS_I(data->inode)); - nfs_commitdata_release(calldata); -} - -static const struct rpc_call_ops nfs_commit_ops = { - .rpc_call_prepare = nfs_write_prepare, - .rpc_call_done = nfs_commit_done, - .rpc_release = nfs_commit_release, -}; - -int nfs_commit_inode(struct inode *inode, int how) -{ - LIST_HEAD(head); - int may_wait = how & FLUSH_SYNC; - int res; - - res = nfs_commit_set_lock(NFS_I(inode), may_wait); - if (res <= 0) - goto out_mark_dirty; - res = nfs_scan_commit(inode, &head); - if (res) { - int error; - - error = pnfs_commit_list(inode, &head, how); - if (error == PNFS_NOT_ATTEMPTED) - error = nfs_commit_list(inode, &head, how); - if (error < 0) - return error; - if (!may_wait) - goto out_mark_dirty; - error = wait_on_bit(&NFS_I(inode)->flags, - NFS_INO_COMMIT, - nfs_wait_bit_killable, - TASK_KILLABLE); - if (error < 0) - return error; - } else - nfs_commit_clear_lock(NFS_I(inode)); - return res; - /* Note: If we exit without ensuring that the commit is complete, - * we must mark the inode as dirty. Otherwise, future calls to - * sync_inode() with the WB_SYNC_ALL flag set will fail to ensure - * that the data is on the disk. - */ -out_mark_dirty: - __mark_inode_dirty(inode, I_DIRTY_DATASYNC); - return res; -} - -static int nfs_commit_unstable_pages(struct inode *inode, struct writeback_control *wbc) -{ - struct nfs_inode *nfsi = NFS_I(inode); - int flags = FLUSH_SYNC; - int ret = 0; - - /* no commits means nothing needs to be done */ - if (!nfsi->ncommit) - return ret; - - if (wbc->sync_mode == WB_SYNC_NONE) { - /* Don't commit yet if this is a non-blocking flush and there - * are a lot of outstanding writes for this mapping. - */ - if (nfsi->ncommit <= (nfsi->npages >> 1)) - goto out_mark_dirty; - - /* don't wait for the COMMIT response */ - flags = 0; - } - - ret = nfs_commit_inode(inode, flags); - if (ret >= 0) { - if (wbc->sync_mode == WB_SYNC_NONE) { - if (ret < wbc->nr_to_write) - wbc->nr_to_write -= ret; - else - wbc->nr_to_write = 0; - } - return 0; - } -out_mark_dirty: - __mark_inode_dirty(inode, I_DIRTY_DATASYNC); - return ret; -} -#else -static int nfs_commit_unstable_pages(struct inode *inode, struct writeback_control *wbc) -{ - return 0; -} -#endif - -int nfs_write_inode(struct inode *inode, struct writeback_control *wbc) -{ - int ret; - - ret = nfs_commit_unstable_pages(inode, wbc); - if (ret >= 0 && test_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags)) { - int status; - bool sync = true; - - if (wbc->sync_mode == WB_SYNC_NONE) - sync = false; - - status = pnfs_layoutcommit_inode(inode, sync); - if (status < 0) - return status; - } - return ret; -} - -/* - * flush the inode to disk. - */ -int nfs_wb_all(struct inode *inode) -{ - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = LONG_MAX, - .range_start = 0, - .range_end = LLONG_MAX, - }; - - return sync_inode(inode, &wbc); -} - -int nfs_wb_page_cancel(struct inode *inode, struct page *page) -{ - struct nfs_page *req; - int ret = 0; - - BUG_ON(!PageLocked(page)); - for (;;) { - wait_on_page_writeback(page); - req = nfs_page_find_request(page); - if (req == NULL) - break; - if (nfs_lock_request_dontget(req)) { - nfs_clear_request_commit(req); - nfs_inode_remove_request(req); - /* - * In case nfs_inode_remove_request has marked the - * page as being dirty - */ - cancel_dirty_page(page, PAGE_CACHE_SIZE); - nfs_unlock_request(req); - break; - } - ret = nfs_wait_on_request(req); - nfs_release_request(req); - if (ret < 0) - break; - } - return ret; -} - -/* - * Write back all requests on one page - we do this before reading it. - */ -int nfs_wb_page(struct inode *inode, struct page *page) -{ - loff_t range_start = page_offset(page); - loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1); - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = 0, - .range_start = range_start, - .range_end = range_end, - }; - int ret; - - for (;;) { - wait_on_page_writeback(page); - if (clear_page_dirty_for_io(page)) { - ret = nfs_writepage_locked(page, &wbc); - if (ret < 0) - goto out_error; - continue; - } - if (!PagePrivate(page)) - break; - ret = nfs_commit_inode(inode, FLUSH_SYNC); - if (ret < 0) - goto out_error; - } - return 0; -out_error: - return ret; -} - -#ifdef CONFIG_MIGRATION -int nfs_migrate_page(struct address_space *mapping, struct page *newpage, - struct page *page, enum migrate_mode mode) -{ - /* - * If PagePrivate is set, then the page is currently associated with - * an in-progress read or write request. Don't try to migrate it. - * - * FIXME: we could do this in principle, but we'll need a way to ensure - * that we can safely release the inode reference while holding - * the page lock. - */ - if (PagePrivate(page)) - return -EBUSY; - - nfs_fscache_release_page(page, GFP_KERNEL); - - return migrate_page(mapping, newpage, page, mode); -} -#endif - -int __init nfs_init_writepagecache(void) -{ - nfs_wdata_cachep = kmem_cache_create("nfs_write_data", - sizeof(struct nfs_write_data), - 0, SLAB_HWCACHE_ALIGN, - NULL); - if (nfs_wdata_cachep == NULL) - return -ENOMEM; - - nfs_wdata_mempool = mempool_create_slab_pool(MIN_POOL_WRITE, - nfs_wdata_cachep); - if (nfs_wdata_mempool == NULL) - return -ENOMEM; - - nfs_commit_mempool = mempool_create_slab_pool(MIN_POOL_COMMIT, - nfs_wdata_cachep); - if (nfs_commit_mempool == NULL) - return -ENOMEM; - - /* - * NFS congestion size, scale with available memory. - * - * 64MB: 8192k - * 128MB: 11585k - * 256MB: 16384k - * 512MB: 23170k - * 1GB: 32768k - * 2GB: 46340k - * 4GB: 65536k - * 8GB: 92681k - * 16GB: 131072k - * - * This allows larger machines to have larger/more transfers. - * Limit the default to 256M - */ - nfs_congestion_kb = (16*int_sqrt(totalram_pages)) << (PAGE_SHIFT-10); - if (nfs_congestion_kb > 256*1024) - nfs_congestion_kb = 256*1024; - - return 0; -} - -void nfs_destroy_writepagecache(void) -{ - mempool_destroy(nfs_commit_mempool); - mempool_destroy(nfs_wdata_mempool); - kmem_cache_destroy(nfs_wdata_cachep); -} - |