提交 74eb94b2 编写于 作者: L Linus Torvalds

Merge branch 'nfs-for-2.6.37' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6

* 'nfs-for-2.6.37' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6: (67 commits)
  SUNRPC: Cleanup duplicate assignment in rpcauth_refreshcred
  nfs: fix unchecked value
  Ask for time_delta during fsinfo probe
  Revalidate caches on lock
  SUNRPC: After calling xprt_release(), we must restart from call_reserve
  NFSv4: Fix up the 'dircount' hint in encode_readdir
  NFSv4: Clean up nfs4_decode_dirent
  NFSv4: nfs4_decode_dirent must clear entry->fattr->valid
  NFSv4: Fix a regression in decode_getfattr
  NFSv4: Fix up decode_attr_filehandle() to handle the case of empty fh pointer
  NFS: Ensure we check all allocation return values in new readdir code
  NFS: Readdir plus in v4
  NFS: introduce generic decode_getattr function
  NFS: check xdr_decode for errors
  NFS: nfs_readdir_filler catch all errors
  NFS: readdir with vmapped pages
  NFS: remove page size checking code
  NFS: decode_dirent should use an xdr_stream
  SUNRPC: Add a helper function xdr_inline_peek
  NFS: remove readdir plus limit
  ...
...@@ -14,3 +14,5 @@ nfsroot.txt ...@@ -14,3 +14,5 @@ nfsroot.txt
- short guide on setting up a diskless box with NFS root filesystem. - short guide on setting up a diskless box with NFS root filesystem.
rpc-cache.txt rpc-cache.txt
- introduction to the caching mechanisms in the sunrpc layer. - introduction to the caching mechanisms in the sunrpc layer.
idmapper.txt
- information for configuring request-keys to be used by idmapper
=========
ID Mapper
=========
Id mapper is used by NFS to translate user and group ids into names, and to
translate user and group names into ids. Part of this translation involves
performing an upcall to userspace to request the information. Id mapper will
user request-key to perform this upcall and cache the result. The program
/usr/sbin/nfs.upcall should be called by request-key, and will perform the
translation and initialize a key with the resulting information.
NFS_USE_NEW_IDMAPPER must be selected when configuring the kernel to use this
feature.
===========
Configuring
===========
The file /etc/request-key.conf will need to be modified so /sbin/request-key can
direct the upcall. The following line should be added:
#OP TYPE DESCRIPTION CALLOUT INFO PROGRAM ARG1 ARG2 ARG3 ...
#====== ======= =============== =============== ===============================
create id_resolver * * /usr/sbin/nfs.upcall %k %d 600
This will direct all id_resolver requests to the program /usr/sbin/nfs.upcall.
The last parameter, 600, defines how many seconds into the future the key will
expire. This parameter is optional for /usr/sbin/nfs.upcall. When the timeout
is not specified, nfs.upcall will default to 600 seconds.
id mapper uses for key descriptions:
uid: Find the UID for the given user
gid: Find the GID for the given group
user: Find the user name for the given UID
group: Find the group name for the given GID
You can handle any of these individually, rather than using the generic upcall
program. If you would like to use your own program for a uid lookup then you
would edit your request-key.conf so it look similar to this:
#OP TYPE DESCRIPTION CALLOUT INFO PROGRAM ARG1 ARG2 ARG3 ...
#====== ======= =============== =============== ===============================
create id_resolver uid:* * /some/other/program %k %d 600
create id_resolver * * /usr/sbin/nfs.upcall %k %d 600
Notice that the new line was added above the line for the generic program.
request-key will find the first matching line and corresponding program. In
this case, /some/other/program will handle all uid lookups and
/usr/sbin/nfs.upcall will handle gid, user, and group lookups.
See <file:Documentation/keys-request-keys.txt> for more information about the
request-key function.
==========
nfs.upcall
==========
nfs.upcall is designed to be called by request-key, and should not be run "by
hand". This program takes two arguments, a serialized key and a key
description. The serialized key is first converted into a key_serial_t, and
then passed as an argument to keyctl_instantiate (both are part of keyutils.h).
The actual lookups are performed by functions found in nfsidmap.h. nfs.upcall
determines the correct function to call by looking at the first part of the
description string. For example, a uid lookup description will appear as
"uid:user@domain".
nfs.upcall will return 0 if the key was instantiated, and non-zero otherwise.
...@@ -159,6 +159,28 @@ ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf> ...@@ -159,6 +159,28 @@ ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>
Default: any Default: any
nfsrootdebug
This parameter enables debugging messages to appear in the kernel
log at boot time so that administrators can verify that the correct
NFS mount options, server address, and root path are passed to the
NFS client.
rdinit=<executable file>
To specify which file contains the program that starts system
initialization, administrators can use this command line parameter.
The default value of this parameter is "/init". If the specified
file exists and the kernel can execute it, root filesystem related
kernel command line parameters, including `nfsroot=', are ignored.
A description of the process of mounting the root file system can be
found in:
Documentation/early-userspace/README
3.) Boot Loader 3.) Boot Loader
......
...@@ -1541,12 +1541,15 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -1541,12 +1541,15 @@ and is between 256 and 4096 characters. It is defined in the file
1 to enable accounting 1 to enable accounting
Default value is 0. Default value is 0.
nfsaddrs= [NFS] nfsaddrs= [NFS] Deprecated. Use ip= instead.
See Documentation/filesystems/nfs/nfsroot.txt. See Documentation/filesystems/nfs/nfsroot.txt.
nfsroot= [NFS] nfs root filesystem for disk-less boxes. nfsroot= [NFS] nfs root filesystem for disk-less boxes.
See Documentation/filesystems/nfs/nfsroot.txt. See Documentation/filesystems/nfs/nfsroot.txt.
nfsrootdebug [NFS] enable nfsroot debugging messages.
See Documentation/filesystems/nfs/nfsroot.txt.
nfs.callback_tcpport= nfs.callback_tcpport=
[NFS] set the TCP port on which the NFSv4 callback [NFS] set the TCP port on which the NFSv4 callback
channel should listen. channel should listen.
......
...@@ -42,6 +42,7 @@ struct nlm_wait { ...@@ -42,6 +42,7 @@ struct nlm_wait {
}; };
static LIST_HEAD(nlm_blocked); static LIST_HEAD(nlm_blocked);
static DEFINE_SPINLOCK(nlm_blocked_lock);
/** /**
* nlmclnt_init - Set up per-NFS mount point lockd data structures * nlmclnt_init - Set up per-NFS mount point lockd data structures
...@@ -97,7 +98,10 @@ struct nlm_wait *nlmclnt_prepare_block(struct nlm_host *host, struct file_lock * ...@@ -97,7 +98,10 @@ struct nlm_wait *nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *
block->b_lock = fl; block->b_lock = fl;
init_waitqueue_head(&block->b_wait); init_waitqueue_head(&block->b_wait);
block->b_status = nlm_lck_blocked; block->b_status = nlm_lck_blocked;
spin_lock(&nlm_blocked_lock);
list_add(&block->b_list, &nlm_blocked); list_add(&block->b_list, &nlm_blocked);
spin_unlock(&nlm_blocked_lock);
} }
return block; return block;
} }
...@@ -106,7 +110,9 @@ void nlmclnt_finish_block(struct nlm_wait *block) ...@@ -106,7 +110,9 @@ void nlmclnt_finish_block(struct nlm_wait *block)
{ {
if (block == NULL) if (block == NULL)
return; return;
spin_lock(&nlm_blocked_lock);
list_del(&block->b_list); list_del(&block->b_list);
spin_unlock(&nlm_blocked_lock);
kfree(block); kfree(block);
} }
...@@ -154,6 +160,7 @@ __be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock) ...@@ -154,6 +160,7 @@ __be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
* Look up blocked request based on arguments. * Look up blocked request based on arguments.
* Warning: must not use cookie to match it! * Warning: must not use cookie to match it!
*/ */
spin_lock(&nlm_blocked_lock);
list_for_each_entry(block, &nlm_blocked, b_list) { list_for_each_entry(block, &nlm_blocked, b_list) {
struct file_lock *fl_blocked = block->b_lock; struct file_lock *fl_blocked = block->b_lock;
...@@ -178,6 +185,7 @@ __be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock) ...@@ -178,6 +185,7 @@ __be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
wake_up(&block->b_wait); wake_up(&block->b_wait);
res = nlm_granted; res = nlm_granted;
} }
spin_unlock(&nlm_blocked_lock);
return res; return res;
} }
...@@ -216,10 +224,6 @@ reclaimer(void *ptr) ...@@ -216,10 +224,6 @@ reclaimer(void *ptr)
allow_signal(SIGKILL); allow_signal(SIGKILL);
down_write(&host->h_rwsem); down_write(&host->h_rwsem);
/* This one ensures that our parent doesn't terminate while the
* reclaim is in progress */
lock_kernel();
lockd_up(); /* note: this cannot fail as lockd is already running */ lockd_up(); /* note: this cannot fail as lockd is already running */
dprintk("lockd: reclaiming locks for host %s\n", host->h_name); dprintk("lockd: reclaiming locks for host %s\n", host->h_name);
...@@ -260,16 +264,17 @@ reclaimer(void *ptr) ...@@ -260,16 +264,17 @@ reclaimer(void *ptr)
dprintk("NLM: done reclaiming locks for host %s\n", host->h_name); dprintk("NLM: done reclaiming locks for host %s\n", host->h_name);
/* Now, wake up all processes that sleep on a blocked lock */ /* Now, wake up all processes that sleep on a blocked lock */
spin_lock(&nlm_blocked_lock);
list_for_each_entry(block, &nlm_blocked, b_list) { list_for_each_entry(block, &nlm_blocked, b_list) {
if (block->b_host == host) { if (block->b_host == host) {
block->b_status = nlm_lck_denied_grace_period; block->b_status = nlm_lck_denied_grace_period;
wake_up(&block->b_wait); wake_up(&block->b_wait);
} }
} }
spin_unlock(&nlm_blocked_lock);
/* Release host handle after use */ /* Release host handle after use */
nlm_release_host(host); nlm_release_host(host);
lockd_down(); lockd_down();
unlock_kernel();
return 0; return 0;
} }
...@@ -166,7 +166,6 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl) ...@@ -166,7 +166,6 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
/* Set up the argument struct */ /* Set up the argument struct */
nlmclnt_setlockargs(call, fl); nlmclnt_setlockargs(call, fl);
lock_kernel();
if (IS_SETLK(cmd) || IS_SETLKW(cmd)) { if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
if (fl->fl_type != F_UNLCK) { if (fl->fl_type != F_UNLCK) {
call->a_args.block = IS_SETLKW(cmd) ? 1 : 0; call->a_args.block = IS_SETLKW(cmd) ? 1 : 0;
...@@ -177,10 +176,8 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl) ...@@ -177,10 +176,8 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
status = nlmclnt_test(call, fl); status = nlmclnt_test(call, fl);
else else
status = -EINVAL; status = -EINVAL;
fl->fl_ops->fl_release_private(fl); fl->fl_ops->fl_release_private(fl);
fl->fl_ops = NULL; fl->fl_ops = NULL;
unlock_kernel();
dprintk("lockd: clnt proc returns %d\n", status); dprintk("lockd: clnt proc returns %d\n", status);
return status; return status;
...@@ -226,9 +223,7 @@ void nlm_release_call(struct nlm_rqst *call) ...@@ -226,9 +223,7 @@ void nlm_release_call(struct nlm_rqst *call)
static void nlmclnt_rpc_release(void *data) static void nlmclnt_rpc_release(void *data)
{ {
lock_kernel();
nlm_release_call(data); nlm_release_call(data);
unlock_kernel();
} }
static int nlm_wait_on_grace(wait_queue_head_t *queue) static int nlm_wait_on_grace(wait_queue_head_t *queue)
...@@ -448,14 +443,18 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) ...@@ -448,14 +443,18 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
static void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl) static void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl)
{ {
spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock);
new->fl_u.nfs_fl.state = fl->fl_u.nfs_fl.state; new->fl_u.nfs_fl.state = fl->fl_u.nfs_fl.state;
new->fl_u.nfs_fl.owner = nlm_get_lockowner(fl->fl_u.nfs_fl.owner); new->fl_u.nfs_fl.owner = nlm_get_lockowner(fl->fl_u.nfs_fl.owner);
list_add_tail(&new->fl_u.nfs_fl.list, &fl->fl_u.nfs_fl.owner->host->h_granted); list_add_tail(&new->fl_u.nfs_fl.list, &fl->fl_u.nfs_fl.owner->host->h_granted);
spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock);
} }
static void nlmclnt_locks_release_private(struct file_lock *fl) static void nlmclnt_locks_release_private(struct file_lock *fl)
{ {
spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock);
list_del(&fl->fl_u.nfs_fl.list); list_del(&fl->fl_u.nfs_fl.list);
spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock);
nlm_put_lockowner(fl->fl_u.nfs_fl.owner); nlm_put_lockowner(fl->fl_u.nfs_fl.owner);
} }
...@@ -721,9 +720,7 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data) ...@@ -721,9 +720,7 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
die: die:
return; return;
retry_rebind: retry_rebind:
lock_kernel();
nlm_rebind_host(req->a_host); nlm_rebind_host(req->a_host);
unlock_kernel();
retry_unlock: retry_unlock:
rpc_restart_call(task); rpc_restart_call(task);
} }
...@@ -801,9 +798,7 @@ static void nlmclnt_cancel_callback(struct rpc_task *task, void *data) ...@@ -801,9 +798,7 @@ static void nlmclnt_cancel_callback(struct rpc_task *task, void *data)
/* Don't ever retry more than 3 times */ /* Don't ever retry more than 3 times */
if (req->a_retries++ >= NLMCLNT_MAX_RETRIES) if (req->a_retries++ >= NLMCLNT_MAX_RETRIES)
goto die; goto die;
lock_kernel();
nlm_rebind_host(req->a_host); nlm_rebind_host(req->a_host);
unlock_kernel();
rpc_restart_call(task); rpc_restart_call(task);
rpc_delay(task, 30 * HZ); rpc_delay(task, 30 * HZ);
} }
......
...@@ -118,3 +118,14 @@ config NFS_USE_KERNEL_DNS ...@@ -118,3 +118,14 @@ config NFS_USE_KERNEL_DNS
select DNS_RESOLVER select DNS_RESOLVER
select KEYS select KEYS
default y default y
config NFS_USE_NEW_IDMAPPER
bool "Use the new idmapper upcall routine"
depends on NFS_V4 && KEYS
help
Say Y here if you want NFS to use the new idmapper upcall functions.
You will need /sbin/request-key (usually provided by the keyutils
package). For details, read
<file:Documentation/filesystems/nfs/idmapper.txt>.
If you are unsure, say N.
...@@ -635,7 +635,8 @@ static int nfs_create_rpc_client(struct nfs_client *clp, ...@@ -635,7 +635,8 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
*/ */
static void nfs_destroy_server(struct nfs_server *server) static void nfs_destroy_server(struct nfs_server *server)
{ {
if (!(server->flags & NFS_MOUNT_NONLM)) if (!(server->flags & NFS_MOUNT_LOCAL_FLOCK) ||
!(server->flags & NFS_MOUNT_LOCAL_FCNTL))
nlmclnt_done(server->nlm_host); nlmclnt_done(server->nlm_host);
} }
...@@ -657,7 +658,8 @@ static int nfs_start_lockd(struct nfs_server *server) ...@@ -657,7 +658,8 @@ static int nfs_start_lockd(struct nfs_server *server)
if (nlm_init.nfs_version > 3) if (nlm_init.nfs_version > 3)
return 0; return 0;
if (server->flags & NFS_MOUNT_NONLM) if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) &&
(server->flags & NFS_MOUNT_LOCAL_FCNTL))
return 0; return 0;
switch (clp->cl_proto) { switch (clp->cl_proto) {
...@@ -901,8 +903,8 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo * ...@@ -901,8 +903,8 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *
server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL);
server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); server->dtsize = nfs_block_size(fsinfo->dtpref, NULL);
if (server->dtsize > PAGE_CACHE_SIZE) if (server->dtsize > PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES)
server->dtsize = PAGE_CACHE_SIZE; server->dtsize = PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES;
if (server->dtsize > server->rsize) if (server->dtsize > server->rsize)
server->dtsize = server->rsize; server->dtsize = server->rsize;
...@@ -913,6 +915,8 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo * ...@@ -913,6 +915,8 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *
server->maxfilesize = fsinfo->maxfilesize; server->maxfilesize = fsinfo->maxfilesize;
server->time_delta = fsinfo->time_delta;
/* We're airborne Set socket buffersize */ /* We're airborne Set socket buffersize */
rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100);
} }
...@@ -1356,8 +1360,9 @@ static int nfs4_init_server(struct nfs_server *server, ...@@ -1356,8 +1360,9 @@ static int nfs4_init_server(struct nfs_server *server,
/* Initialise the client representation from the mount data */ /* Initialise the client representation from the mount data */
server->flags = data->flags; server->flags = data->flags;
server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR| server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK;
NFS_CAP_POSIX_LOCK; if (!(data->flags & NFS_MOUNT_NORDIRPLUS))
server->caps |= NFS_CAP_READDIRPLUS;
server->options = data->options; server->options = data->options;
/* Get a client record */ /* Get a client record */
......
此差异已折叠。
...@@ -551,7 +551,7 @@ static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) ...@@ -551,7 +551,7 @@ static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
struct file *filp = vma->vm_file; struct file *filp = vma->vm_file;
struct dentry *dentry = filp->f_path.dentry; struct dentry *dentry = filp->f_path.dentry;
unsigned pagelen; unsigned pagelen;
int ret = -EINVAL; int ret = VM_FAULT_NOPAGE;
struct address_space *mapping; struct address_space *mapping;
dfprintk(PAGECACHE, "NFS: vm_page_mkwrite(%s/%s(%ld), offset %lld)\n", dfprintk(PAGECACHE, "NFS: vm_page_mkwrite(%s/%s(%ld), offset %lld)\n",
...@@ -567,21 +567,20 @@ static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) ...@@ -567,21 +567,20 @@ static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
if (mapping != dentry->d_inode->i_mapping) if (mapping != dentry->d_inode->i_mapping)
goto out_unlock; goto out_unlock;
ret = 0;
pagelen = nfs_page_length(page); pagelen = nfs_page_length(page);
if (pagelen == 0) if (pagelen == 0)
goto out_unlock; goto out_unlock;
ret = nfs_flush_incompatible(filp, page); ret = VM_FAULT_LOCKED;
if (ret != 0) if (nfs_flush_incompatible(filp, page) == 0 &&
goto out_unlock; nfs_updatepage(filp, page, 0, pagelen) == 0)
goto out;
ret = nfs_updatepage(filp, page, 0, pagelen); ret = VM_FAULT_SIGBUS;
out_unlock: out_unlock:
if (!ret)
return VM_FAULT_LOCKED;
unlock_page(page); unlock_page(page);
return VM_FAULT_SIGBUS; out:
return ret;
} }
static const struct vm_operations_struct nfs_file_vm_ops = { static const struct vm_operations_struct nfs_file_vm_ops = {
...@@ -684,7 +683,8 @@ static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe, ...@@ -684,7 +683,8 @@ static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
return ret; return ret;
} }
static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) static int
do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
{ {
struct inode *inode = filp->f_mapping->host; struct inode *inode = filp->f_mapping->host;
int status = 0; int status = 0;
...@@ -699,7 +699,7 @@ static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) ...@@ -699,7 +699,7 @@ static int do_getlk(struct file *filp, int cmd, struct file_lock *fl)
if (nfs_have_delegation(inode, FMODE_READ)) if (nfs_have_delegation(inode, FMODE_READ))
goto out_noconflict; goto out_noconflict;
if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) if (is_local)
goto out_noconflict; goto out_noconflict;
status = NFS_PROTO(inode)->lock(filp, cmd, fl); status = NFS_PROTO(inode)->lock(filp, cmd, fl);
...@@ -726,7 +726,8 @@ static int do_vfs_lock(struct file *file, struct file_lock *fl) ...@@ -726,7 +726,8 @@ static int do_vfs_lock(struct file *file, struct file_lock *fl)
return res; return res;
} }
static int do_unlk(struct file *filp, int cmd, struct file_lock *fl) static int
do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
{ {
struct inode *inode = filp->f_mapping->host; struct inode *inode = filp->f_mapping->host;
int status; int status;
...@@ -741,15 +742,24 @@ static int do_unlk(struct file *filp, int cmd, struct file_lock *fl) ...@@ -741,15 +742,24 @@ static int do_unlk(struct file *filp, int cmd, struct file_lock *fl)
* If we're signalled while cleaning up locks on process exit, we * If we're signalled while cleaning up locks on process exit, we
* still need to complete the unlock. * still need to complete the unlock.
*/ */
/* Use local locking if mounted with "-onolock" */ /*
if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) * Use local locking if mounted with "-onolock" or with appropriate
* "-olocal_lock="
*/
if (!is_local)
status = NFS_PROTO(inode)->lock(filp, cmd, fl); status = NFS_PROTO(inode)->lock(filp, cmd, fl);
else else
status = do_vfs_lock(filp, fl); status = do_vfs_lock(filp, fl);
return status; return status;
} }
static int do_setlk(struct file *filp, int cmd, struct file_lock *fl) 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; struct inode *inode = filp->f_mapping->host;
int status; int status;
...@@ -762,20 +772,31 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl) ...@@ -762,20 +772,31 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
if (status != 0) if (status != 0)
goto out; goto out;
/* Use local locking if mounted with "-onolock" */ /*
if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) * Use local locking if mounted with "-onolock" or with appropriate
* "-olocal_lock="
*/
if (!is_local)
status = NFS_PROTO(inode)->lock(filp, cmd, fl); status = NFS_PROTO(inode)->lock(filp, cmd, fl);
else else
status = do_vfs_lock(filp, fl); status = do_vfs_lock(filp, fl);
if (status < 0) if (status < 0)
goto out; goto out;
/* /*
* Make sure we clear the cache whenever we try to get the lock. * 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. * This makes locking act as a cache coherency point.
*/ */
nfs_sync_mapping(filp->f_mapping); nfs_sync_mapping(filp->f_mapping);
if (!nfs_have_delegation(inode, FMODE_READ)) if (!nfs_have_delegation(inode, FMODE_READ)) {
nfs_zap_caches(inode); if (is_time_granular(&NFS_SERVER(inode)->time_delta))
__nfs_revalidate_inode(NFS_SERVER(inode), inode);
else
nfs_zap_caches(inode);
}
out: out:
return status; return status;
} }
...@@ -787,6 +808,7 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) ...@@ -787,6 +808,7 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
{ {
struct inode *inode = filp->f_mapping->host; struct inode *inode = filp->f_mapping->host;
int ret = -ENOLCK; int ret = -ENOLCK;
int is_local = 0;
dprintk("NFS: lock(%s/%s, t=%x, fl=%x, r=%lld:%lld)\n", 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_parent->d_name.name,
...@@ -800,6 +822,9 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) ...@@ -800,6 +822,9 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
goto out_err; goto out_err;
if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FCNTL)
is_local = 1;
if (NFS_PROTO(inode)->lock_check_bounds != NULL) { if (NFS_PROTO(inode)->lock_check_bounds != NULL) {
ret = NFS_PROTO(inode)->lock_check_bounds(fl); ret = NFS_PROTO(inode)->lock_check_bounds(fl);
if (ret < 0) if (ret < 0)
...@@ -807,11 +832,11 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) ...@@ -807,11 +832,11 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
} }
if (IS_GETLK(cmd)) if (IS_GETLK(cmd))
ret = do_getlk(filp, cmd, fl); ret = do_getlk(filp, cmd, fl, is_local);
else if (fl->fl_type == F_UNLCK) else if (fl->fl_type == F_UNLCK)
ret = do_unlk(filp, cmd, fl); ret = do_unlk(filp, cmd, fl, is_local);
else else
ret = do_setlk(filp, cmd, fl); ret = do_setlk(filp, cmd, fl, is_local);
out_err: out_err:
return ret; return ret;
} }
...@@ -821,6 +846,9 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) ...@@ -821,6 +846,9 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
*/ */
static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) 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", dprintk("NFS: flock(%s/%s, t=%x, fl=%x)\n",
filp->f_path.dentry->d_parent->d_name.name, filp->f_path.dentry->d_parent->d_name.name,
filp->f_path.dentry->d_name.name, filp->f_path.dentry->d_name.name,
...@@ -829,14 +857,17 @@ static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) ...@@ -829,14 +857,17 @@ static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
if (!(fl->fl_flags & FL_FLOCK)) if (!(fl->fl_flags & FL_FLOCK))
return -ENOLCK; 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 */ /* We're simulating flock() locks using posix locks on the server */
fl->fl_owner = (fl_owner_t)filp; fl->fl_owner = (fl_owner_t)filp;
fl->fl_start = 0; fl->fl_start = 0;
fl->fl_end = OFFSET_MAX; fl->fl_end = OFFSET_MAX;
if (fl->fl_type == F_UNLCK) if (fl->fl_type == F_UNLCK)
return do_unlk(filp, cmd, fl); return do_unlk(filp, cmd, fl, is_local);
return do_setlk(filp, cmd, fl); return do_setlk(filp, cmd, fl, is_local);
} }
/* /*
......
...@@ -34,6 +34,212 @@ ...@@ -34,6 +34,212 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifdef CONFIG_NFS_USE_NEW_IDMAPPER
#include <linux/slab.h>
#include <linux/cred.h>
#include <linux/nfs_idmap.h>
#include <linux/keyctl.h>
#include <linux/key-type.h>
#include <linux/rcupdate.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <keys/user-type.h>
#define NFS_UINT_MAXLEN 11
const struct cred *id_resolver_cache;
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,
};
int nfs_idmap_init(void)
{
struct cred *cred;
struct key *keyring;
int ret = 0;
printk(KERN_NOTICE "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;
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;
}
void nfs_idmap_quit(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(const char *name, size_t namelen,
const char *type, void *data, size_t data_size)
{
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);
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;
}
/* ID -> Name */
static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf, size_t buflen)
{
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_request_key(id_str, id_len, type, buf, buflen);
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)
{
char id_str[NFS_UINT_MAXLEN];
long id_long;
ssize_t data_size;
int ret = 0;
data_size = nfs_idmap_request_key(name, namelen, type, id_str, NFS_UINT_MAXLEN);
if (data_size <= 0) {
ret = -EINVAL;
} else {
ret = strict_strtol(id_str, 10, &id_long);
*id = (__u32)id_long;
}
return ret;
}
int nfs_map_name_to_uid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *uid)
{
return nfs_idmap_lookup_id(name, namelen, "uid", uid);
}
int nfs_map_group_to_gid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *gid)
{
return nfs_idmap_lookup_id(name, namelen, "gid", gid);
}
int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen)
{
return nfs_idmap_lookup_name(uid, "user", buf, buflen);
}
int nfs_map_gid_to_group(struct nfs_client *clp, __u32 gid, char *buf, size_t buflen)
{
return nfs_idmap_lookup_name(gid, "group", buf, buflen);
}
#else /* CONFIG_NFS_USE_IDMAPPER not defined */
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -503,16 +709,17 @@ int nfs_map_group_to_gid(struct nfs_client *clp, const char *name, size_t namele ...@@ -503,16 +709,17 @@ int nfs_map_group_to_gid(struct nfs_client *clp, const char *name, size_t namele
return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid);
} }
int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf) int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen)
{ {
struct idmap *idmap = clp->cl_idmap; struct idmap *idmap = clp->cl_idmap;
return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf);
} }
int nfs_map_gid_to_group(struct nfs_client *clp, __u32 uid, char *buf) int nfs_map_gid_to_group(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen)
{ {
struct idmap *idmap = clp->cl_idmap; struct idmap *idmap = clp->cl_idmap;
return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf); return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf);
} }
#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */
...@@ -234,9 +234,6 @@ nfs_init_locked(struct inode *inode, void *opaque) ...@@ -234,9 +234,6 @@ nfs_init_locked(struct inode *inode, void *opaque)
return 0; return 0;
} }
/* Don't use READDIRPLUS on directories that we believe are too large */
#define NFS_LIMIT_READDIRPLUS (8*PAGE_SIZE)
/* /*
* This is our front-end to iget that looks up inodes by file handle * This is our front-end to iget that looks up inodes by file handle
* instead of inode number. * instead of inode number.
...@@ -291,8 +288,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) ...@@ -291,8 +288,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
} else if (S_ISDIR(inode->i_mode)) { } else if (S_ISDIR(inode->i_mode)) {
inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops; inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
inode->i_fop = &nfs_dir_operations; inode->i_fop = &nfs_dir_operations;
if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS) if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS))
&& fattr->size <= NFS_LIMIT_READDIRPLUS)
set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
/* Deal with crossing mountpoints */ /* Deal with crossing mountpoints */
if ((fattr->valid & NFS_ATTR_FATTR_FSID) if ((fattr->valid & NFS_ATTR_FATTR_FSID)
...@@ -623,7 +619,7 @@ void nfs_close_context(struct nfs_open_context *ctx, int is_sync) ...@@ -623,7 +619,7 @@ void nfs_close_context(struct nfs_open_context *ctx, int is_sync)
nfs_revalidate_inode(server, inode); nfs_revalidate_inode(server, inode);
} }
static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct rpc_cred *cred) struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct rpc_cred *cred, fmode_t f_mode)
{ {
struct nfs_open_context *ctx; struct nfs_open_context *ctx;
...@@ -633,11 +629,13 @@ static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct ...@@ -633,11 +629,13 @@ static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct
path_get(&ctx->path); path_get(&ctx->path);
ctx->cred = get_rpccred(cred); ctx->cred = get_rpccred(cred);
ctx->state = NULL; ctx->state = NULL;
ctx->mode = f_mode;
ctx->flags = 0; ctx->flags = 0;
ctx->error = 0; ctx->error = 0;
ctx->dir_cookie = 0; ctx->dir_cookie = 0;
nfs_init_lock_context(&ctx->lock_context); nfs_init_lock_context(&ctx->lock_context);
ctx->lock_context.open_context = ctx; ctx->lock_context.open_context = ctx;
INIT_LIST_HEAD(&ctx->list);
} }
return ctx; return ctx;
} }
...@@ -653,11 +651,15 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync) ...@@ -653,11 +651,15 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
{ {
struct inode *inode = ctx->path.dentry->d_inode; struct inode *inode = ctx->path.dentry->d_inode;
if (!atomic_dec_and_lock(&ctx->lock_context.count, &inode->i_lock)) 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; return;
list_del(&ctx->list); if (inode != NULL)
spin_unlock(&inode->i_lock); NFS_PROTO(inode)->close_context(ctx, is_sync);
NFS_PROTO(inode)->close_context(ctx, is_sync);
if (ctx->cred != NULL) if (ctx->cred != NULL)
put_rpccred(ctx->cred); put_rpccred(ctx->cred);
path_put(&ctx->path); path_put(&ctx->path);
...@@ -673,7 +675,7 @@ void put_nfs_open_context(struct nfs_open_context *ctx) ...@@ -673,7 +675,7 @@ void put_nfs_open_context(struct nfs_open_context *ctx)
* Ensure that mmap has a recent RPC credential for use when writing out * Ensure that mmap has a recent RPC credential for use when writing out
* shared pages * shared pages
*/ */
static void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx) void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
{ {
struct inode *inode = filp->f_path.dentry->d_inode; struct inode *inode = filp->f_path.dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
...@@ -730,11 +732,10 @@ int nfs_open(struct inode *inode, struct file *filp) ...@@ -730,11 +732,10 @@ int nfs_open(struct inode *inode, struct file *filp)
cred = rpc_lookup_cred(); cred = rpc_lookup_cred();
if (IS_ERR(cred)) if (IS_ERR(cred))
return PTR_ERR(cred); return PTR_ERR(cred);
ctx = alloc_nfs_open_context(&filp->f_path, cred); ctx = alloc_nfs_open_context(&filp->f_path, cred, filp->f_mode);
put_rpccred(cred); put_rpccred(cred);
if (ctx == NULL) if (ctx == NULL)
return -ENOMEM; return -ENOMEM;
ctx->mode = filp->f_mode;
nfs_file_set_open_context(filp, ctx); nfs_file_set_open_context(filp, ctx);
put_nfs_open_context(ctx); put_nfs_open_context(ctx);
nfs_fscache_set_inode_cookie(inode, filp); nfs_fscache_set_inode_cookie(inode, filp);
...@@ -1493,7 +1494,7 @@ static int nfsiod_start(void) ...@@ -1493,7 +1494,7 @@ static int nfsiod_start(void)
{ {
struct workqueue_struct *wq; struct workqueue_struct *wq;
dprintk("RPC: creating workqueue nfsiod\n"); dprintk("RPC: creating workqueue nfsiod\n");
wq = create_singlethread_workqueue("nfsiod"); wq = alloc_workqueue("nfsiod", WQ_RESCUER, 0);
if (wq == NULL) if (wq == NULL)
return -ENOMEM; return -ENOMEM;
nfsiod_workqueue = wq; nfsiod_workqueue = wq;
...@@ -1521,6 +1522,10 @@ static int __init init_nfs_fs(void) ...@@ -1521,6 +1522,10 @@ static int __init init_nfs_fs(void)
{ {
int err; int err;
err = nfs_idmap_init();
if (err < 0)
goto out9;
err = nfs_dns_resolver_init(); err = nfs_dns_resolver_init();
if (err < 0) if (err < 0)
goto out8; goto out8;
...@@ -1585,6 +1590,8 @@ static int __init init_nfs_fs(void) ...@@ -1585,6 +1590,8 @@ static int __init init_nfs_fs(void)
out7: out7:
nfs_dns_resolver_destroy(); nfs_dns_resolver_destroy();
out8: out8:
nfs_idmap_quit();
out9:
return err; return err;
} }
...@@ -1597,6 +1604,7 @@ static void __exit exit_nfs_fs(void) ...@@ -1597,6 +1604,7 @@ static void __exit exit_nfs_fs(void)
nfs_destroy_nfspagecache(); nfs_destroy_nfspagecache();
nfs_fscache_unregister(); nfs_fscache_unregister();
nfs_dns_resolver_destroy(); nfs_dns_resolver_destroy();
nfs_idmap_quit();
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
rpc_proc_unregister("nfs"); rpc_proc_unregister("nfs");
#endif #endif
......
...@@ -62,6 +62,12 @@ struct nfs_clone_mount { ...@@ -62,6 +62,12 @@ struct nfs_clone_mount {
*/ */
#define NFS_UNSPEC_PORT (-1) #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 * In-kernel mount arguments
*/ */
...@@ -181,15 +187,15 @@ extern void nfs_destroy_directcache(void); ...@@ -181,15 +187,15 @@ extern void nfs_destroy_directcache(void);
/* nfs2xdr.c */ /* nfs2xdr.c */
extern int nfs_stat_to_errno(int); extern int nfs_stat_to_errno(int);
extern struct rpc_procinfo nfs_procedures[]; extern struct rpc_procinfo nfs_procedures[];
extern __be32 * nfs_decode_dirent(__be32 *, struct nfs_entry *, int); extern __be32 *nfs_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
/* nfs3xdr.c */ /* nfs3xdr.c */
extern struct rpc_procinfo nfs3_procedures[]; extern struct rpc_procinfo nfs3_procedures[];
extern __be32 *nfs3_decode_dirent(__be32 *, struct nfs_entry *, int); extern __be32 *nfs3_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
/* nfs4xdr.c */ /* nfs4xdr.c */
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus); extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
#endif #endif
#ifdef CONFIG_NFS_V4_1 #ifdef CONFIG_NFS_V4_1
extern const u32 nfs41_maxread_overhead; extern const u32 nfs41_maxread_overhead;
......
...@@ -436,7 +436,7 @@ static int decode_auth_flavors(struct xdr_stream *xdr, struct mountres *res) ...@@ -436,7 +436,7 @@ static int decode_auth_flavors(struct xdr_stream *xdr, struct mountres *res)
for (i = 0; i < entries; i++) { for (i = 0; i < entries; i++) {
flavors[i] = ntohl(*p++); flavors[i] = ntohl(*p++);
dprintk("NFS:\tflavor %u: %d\n", i, flavors[i]); dprintk("NFS: auth flavor[%u]: %d\n", i, flavors[i]);
} }
*count = i; *count = i;
......
...@@ -337,10 +337,10 @@ nfs_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs_createargs *args) ...@@ -337,10 +337,10 @@ nfs_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs_createargs *args)
static int static int
nfs_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args) nfs_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args)
{ {
p = xdr_encode_fhandle(p, args->fromfh); p = xdr_encode_fhandle(p, args->old_dir);
p = xdr_encode_array(p, args->fromname, args->fromlen); p = xdr_encode_array(p, args->old_name->name, args->old_name->len);
p = xdr_encode_fhandle(p, args->tofh); p = xdr_encode_fhandle(p, args->new_dir);
p = xdr_encode_array(p, args->toname, args->tolen); p = xdr_encode_array(p, args->new_name->name, args->new_name->len);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0; return 0;
} }
...@@ -423,9 +423,7 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy) ...@@ -423,9 +423,7 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
struct page **page; struct page **page;
size_t hdrlen; size_t hdrlen;
unsigned int pglen, recvd; unsigned int pglen, recvd;
u32 len;
int status, nr = 0; int status, nr = 0;
__be32 *end, *entry, *kaddr;
if ((status = ntohl(*p++))) if ((status = ntohl(*p++)))
return nfs_stat_to_errno(status); return nfs_stat_to_errno(status);
...@@ -445,80 +443,59 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy) ...@@ -445,80 +443,59 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
if (pglen > recvd) if (pglen > recvd)
pglen = recvd; pglen = recvd;
page = rcvbuf->pages; page = rcvbuf->pages;
kaddr = p = kmap_atomic(*page, KM_USER0);
end = (__be32 *)((char *)p + pglen);
entry = p;
/* Make sure the packet actually has a value_follows and EOF entry */
if ((entry + 1) > end)
goto short_pkt;
for (; *p++; nr++) {
if (p + 2 > end)
goto short_pkt;
p++; /* fileid */
len = ntohl(*p++);
p += XDR_QUADLEN(len) + 1; /* name plus cookie */
if (len > NFS2_MAXNAMLEN) {
dprintk("NFS: giant filename in readdir (len 0x%x)!\n",
len);
goto err_unmap;
}
if (p + 2 > end)
goto short_pkt;
entry = p;
}
/*
* Apparently some server sends responses that are a valid size, but
* contain no entries, and have value_follows==0 and EOF==0. For
* those, just set the EOF marker.
*/
if (!nr && entry[1] == 0) {
dprintk("NFS: readdir reply truncated!\n");
entry[1] = 1;
}
out:
kunmap_atomic(kaddr, KM_USER0);
return nr; return nr;
short_pkt: }
/*
* When we get a short packet there are 2 possibilities. We can static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
* return an error, or fix up the response to look like a valid {
* response and return what we have so far. If there are no dprintk("nfs: %s: prematurely hit end of receive buffer. "
* entries and the packet was short, then return -EIO. If there "Remaining buffer length is %tu words.\n",
* are valid entries in the response, return them and pretend that func, xdr->end - xdr->p);
* the call was successful, but incomplete. The caller can retry the
* readdir starting at the last cookie.
*/
entry[0] = entry[1] = 0;
if (!nr)
nr = -errno_NFSERR_IO;
goto out;
err_unmap:
nr = -errno_NFSERR_IO;
goto out;
} }
__be32 * __be32 *
nfs_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus)
{ {
if (!*p++) { __be32 *p;
if (!*p) p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
if (!ntohl(*p++)) {
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
if (!ntohl(*p++))
return ERR_PTR(-EAGAIN); return ERR_PTR(-EAGAIN);
entry->eof = 1; entry->eof = 1;
return ERR_PTR(-EBADCOOKIE); return ERR_PTR(-EBADCOOKIE);
} }
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
entry->ino = ntohl(*p++); entry->ino = ntohl(*p++);
entry->len = ntohl(*p++); entry->len = ntohl(*p++);
p = xdr_inline_decode(xdr, entry->len + 4);
if (unlikely(!p))
goto out_overflow;
entry->name = (const char *) p; entry->name = (const char *) p;
p += XDR_QUADLEN(entry->len); p += XDR_QUADLEN(entry->len);
entry->prev_cookie = entry->cookie; entry->prev_cookie = entry->cookie;
entry->cookie = ntohl(*p++); entry->cookie = ntohl(*p++);
entry->eof = !p[0] && p[1];
p = xdr_inline_peek(xdr, 8);
if (p != NULL)
entry->eof = !p[0] && p[1];
else
entry->eof = 0;
return p; return p;
out_overflow:
print_overflow_msg(__func__, xdr);
return ERR_PTR(-EIO);
} }
/* /*
...@@ -596,7 +573,6 @@ nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy) ...@@ -596,7 +573,6 @@ nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy)
struct kvec *iov = rcvbuf->head; struct kvec *iov = rcvbuf->head;
size_t hdrlen; size_t hdrlen;
u32 len, recvd; u32 len, recvd;
char *kaddr;
int status; int status;
if ((status = ntohl(*p++))) if ((status = ntohl(*p++)))
...@@ -623,10 +599,7 @@ nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy) ...@@ -623,10 +599,7 @@ nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy)
return -EIO; return -EIO;
} }
/* NULL terminate the string we got */ xdr_terminate_string(rcvbuf, len);
kaddr = (char *)kmap_atomic(rcvbuf->pages[0], KM_USER0);
kaddr[len+rcvbuf->page_base] = '\0';
kunmap_atomic(kaddr, KM_USER0);
return 0; return 0;
} }
......
...@@ -313,7 +313,7 @@ static void nfs3_free_createdata(struct nfs3_createdata *data) ...@@ -313,7 +313,7 @@ static void nfs3_free_createdata(struct nfs3_createdata *data)
*/ */
static int static int
nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags, struct nameidata *nd) int flags, struct nfs_open_context *ctx)
{ {
struct nfs3_createdata *data; struct nfs3_createdata *data;
mode_t mode = sattr->ia_mode; mode_t mode = sattr->ia_mode;
...@@ -438,19 +438,38 @@ nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir) ...@@ -438,19 +438,38 @@ nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir)
return 1; return 1;
} }
static void
nfs3_proc_rename_setup(struct rpc_message *msg, struct inode *dir)
{
msg->rpc_proc = &nfs3_procedures[NFS3PROC_RENAME];
}
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 static int
nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name, nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name,
struct inode *new_dir, struct qstr *new_name) struct inode *new_dir, struct qstr *new_name)
{ {
struct nfs3_renameargs arg = { struct nfs_renameargs arg = {
.fromfh = NFS_FH(old_dir), .old_dir = NFS_FH(old_dir),
.fromname = old_name->name, .old_name = old_name,
.fromlen = old_name->len, .new_dir = NFS_FH(new_dir),
.tofh = NFS_FH(new_dir), .new_name = new_name,
.toname = new_name->name,
.tolen = new_name->len
}; };
struct nfs3_renameres res; struct nfs_renameres res;
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs3_procedures[NFS3PROC_RENAME], .rpc_proc = &nfs3_procedures[NFS3PROC_RENAME],
.rpc_argp = &arg, .rpc_argp = &arg,
...@@ -460,17 +479,17 @@ nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name, ...@@ -460,17 +479,17 @@ nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name,
dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name); dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name);
res.fromattr = nfs_alloc_fattr(); res.old_fattr = nfs_alloc_fattr();
res.toattr = nfs_alloc_fattr(); res.new_fattr = nfs_alloc_fattr();
if (res.fromattr == NULL || res.toattr == NULL) if (res.old_fattr == NULL || res.new_fattr == NULL)
goto out; goto out;
status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0); status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0);
nfs_post_op_update_inode(old_dir, res.fromattr); nfs_post_op_update_inode(old_dir, res.old_fattr);
nfs_post_op_update_inode(new_dir, res.toattr); nfs_post_op_update_inode(new_dir, res.new_fattr);
out: out:
nfs_free_fattr(res.toattr); nfs_free_fattr(res.old_fattr);
nfs_free_fattr(res.fromattr); nfs_free_fattr(res.new_fattr);
dprintk("NFS reply rename: %d\n", status); dprintk("NFS reply rename: %d\n", status);
return status; return status;
} }
...@@ -611,7 +630,7 @@ nfs3_proc_rmdir(struct inode *dir, struct qstr *name) ...@@ -611,7 +630,7 @@ nfs3_proc_rmdir(struct inode *dir, struct qstr *name)
*/ */
static int static int
nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
u64 cookie, struct page *page, unsigned int count, int plus) u64 cookie, struct page **pages, unsigned int count, int plus)
{ {
struct inode *dir = dentry->d_inode; struct inode *dir = dentry->d_inode;
__be32 *verf = NFS_COOKIEVERF(dir); __be32 *verf = NFS_COOKIEVERF(dir);
...@@ -621,7 +640,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, ...@@ -621,7 +640,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
.verf = {verf[0], verf[1]}, .verf = {verf[0], verf[1]},
.plus = plus, .plus = plus,
.count = count, .count = count,
.pages = &page .pages = pages
}; };
struct nfs3_readdirres res = { struct nfs3_readdirres res = {
.verf = verf, .verf = verf,
...@@ -652,7 +671,8 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, ...@@ -652,7 +671,8 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
nfs_free_fattr(res.dir_attr); nfs_free_fattr(res.dir_attr);
out: out:
dprintk("NFS reply readdir: %d\n", status); dprintk("NFS reply readdir%s: %d\n",
plus? "plus" : "", status);
return status; return status;
} }
...@@ -722,7 +742,7 @@ nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -722,7 +742,7 @@ nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
dprintk("NFS call fsstat\n"); dprintk("NFS call fsstat\n");
nfs_fattr_init(stat->fattr); nfs_fattr_init(stat->fattr);
status = rpc_call_sync(server->client, &msg, 0); status = rpc_call_sync(server->client, &msg, 0);
dprintk("NFS reply statfs: %d\n", status); dprintk("NFS reply fsstat: %d\n", status);
return status; return status;
} }
...@@ -844,6 +864,8 @@ const struct nfs_rpc_ops nfs_v3_clientops = { ...@@ -844,6 +864,8 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
.unlink_setup = nfs3_proc_unlink_setup, .unlink_setup = nfs3_proc_unlink_setup,
.unlink_done = nfs3_proc_unlink_done, .unlink_done = nfs3_proc_unlink_done,
.rename = nfs3_proc_rename, .rename = nfs3_proc_rename,
.rename_setup = nfs3_proc_rename_setup,
.rename_done = nfs3_proc_rename_done,
.link = nfs3_proc_link, .link = nfs3_proc_link,
.symlink = nfs3_proc_symlink, .symlink = nfs3_proc_symlink,
.mkdir = nfs3_proc_mkdir, .mkdir = nfs3_proc_mkdir,
......
...@@ -100,6 +100,13 @@ static const umode_t nfs_type2fmt[] = { ...@@ -100,6 +100,13 @@ static const umode_t nfs_type2fmt[] = {
[NF3FIFO] = S_IFIFO, [NF3FIFO] = S_IFIFO,
}; };
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);
}
/* /*
* Common NFS XDR functions as inlines * Common NFS XDR functions as inlines
*/ */
...@@ -119,6 +126,29 @@ xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh) ...@@ -119,6 +126,29 @@ xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
return NULL; return NULL;
} }
static inline __be32 *
xdr_decode_fhandle_stream(struct xdr_stream *xdr, struct nfs_fh *fh)
{
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
fh->size = ntohl(*p++);
if (fh->size <= NFS3_FHSIZE) {
p = xdr_inline_decode(xdr, fh->size);
if (unlikely(!p))
goto out_overflow;
memcpy(fh->data, p, fh->size);
return p + XDR_QUADLEN(fh->size);
}
return NULL;
out_overflow:
print_overflow_msg(__func__, xdr);
return ERR_PTR(-EIO);
}
/* /*
* Encode/decode time. * Encode/decode time.
*/ */
...@@ -240,6 +270,26 @@ xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr) ...@@ -240,6 +270,26 @@ xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
return p; return p;
} }
static inline __be32 *
xdr_decode_post_op_attr_stream(struct xdr_stream *xdr, struct nfs_fattr *fattr)
{
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
if (ntohl(*p++)) {
p = xdr_inline_decode(xdr, 84);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_fattr(p, fattr);
}
return p;
out_overflow:
print_overflow_msg(__func__, xdr);
return ERR_PTR(-EIO);
}
static inline __be32 * static inline __be32 *
xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr) xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
{ {
...@@ -442,12 +492,12 @@ nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args) ...@@ -442,12 +492,12 @@ nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args)
* Encode RENAME arguments * Encode RENAME arguments
*/ */
static int static int
nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs3_renameargs *args) nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args)
{ {
p = xdr_encode_fhandle(p, args->fromfh); p = xdr_encode_fhandle(p, args->old_dir);
p = xdr_encode_array(p, args->fromname, args->fromlen); p = xdr_encode_array(p, args->old_name->name, args->old_name->len);
p = xdr_encode_fhandle(p, args->tofh); p = xdr_encode_fhandle(p, args->new_dir);
p = xdr_encode_array(p, args->toname, args->tolen); p = xdr_encode_array(p, args->new_name->name, args->new_name->len);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0; return 0;
} }
...@@ -504,9 +554,8 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res ...@@ -504,9 +554,8 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res
struct kvec *iov = rcvbuf->head; struct kvec *iov = rcvbuf->head;
struct page **page; struct page **page;
size_t hdrlen; size_t hdrlen;
u32 len, recvd, pglen; u32 recvd, pglen;
int status, nr = 0; int status, nr = 0;
__be32 *entry, *end, *kaddr;
status = ntohl(*p++); status = ntohl(*p++);
/* Decode post_op_attrs */ /* Decode post_op_attrs */
...@@ -536,99 +585,38 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res ...@@ -536,99 +585,38 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res
if (pglen > recvd) if (pglen > recvd)
pglen = recvd; pglen = recvd;
page = rcvbuf->pages; page = rcvbuf->pages;
kaddr = p = kmap_atomic(*page, KM_USER0);
end = (__be32 *)((char *)p + pglen);
entry = p;
/* Make sure the packet actually has a value_follows and EOF entry */
if ((entry + 1) > end)
goto short_pkt;
for (; *p++; nr++) {
if (p + 3 > end)
goto short_pkt;
p += 2; /* inode # */
len = ntohl(*p++); /* string length */
p += XDR_QUADLEN(len) + 2; /* name + cookie */
if (len > NFS3_MAXNAMLEN) {
dprintk("NFS: giant filename in readdir (len 0x%x)!\n",
len);
goto err_unmap;
}
if (res->plus) {
/* post_op_attr */
if (p + 2 > end)
goto short_pkt;
if (*p++) {
p += 21;
if (p + 1 > end)
goto short_pkt;
}
/* post_op_fh3 */
if (*p++) {
if (p + 1 > end)
goto short_pkt;
len = ntohl(*p++);
if (len > NFS3_FHSIZE) {
dprintk("NFS: giant filehandle in "
"readdir (len 0x%x)!\n", len);
goto err_unmap;
}
p += XDR_QUADLEN(len);
}
}
if (p + 2 > end)
goto short_pkt;
entry = p;
}
/*
* Apparently some server sends responses that are a valid size, but
* contain no entries, and have value_follows==0 and EOF==0. For
* those, just set the EOF marker.
*/
if (!nr && entry[1] == 0) {
dprintk("NFS: readdir reply truncated!\n");
entry[1] = 1;
}
out:
kunmap_atomic(kaddr, KM_USER0);
return nr; return nr;
short_pkt:
/*
* When we get a short packet there are 2 possibilities. We can
* return an error, or fix up the response to look like a valid
* response and return what we have so far. If there are no
* entries and the packet was short, then return -EIO. If there
* are valid entries in the response, return them and pretend that
* the call was successful, but incomplete. The caller can retry the
* readdir starting at the last cookie.
*/
entry[0] = entry[1] = 0;
if (!nr)
nr = -errno_NFSERR_IO;
goto out;
err_unmap:
nr = -errno_NFSERR_IO;
goto out;
} }
__be32 * __be32 *
nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus)
{ {
__be32 *p;
struct nfs_entry old = *entry; struct nfs_entry old = *entry;
if (!*p++) { p = xdr_inline_decode(xdr, 4);
if (!*p) if (unlikely(!p))
goto out_overflow;
if (!ntohl(*p++)) {
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
if (!ntohl(*p++))
return ERR_PTR(-EAGAIN); return ERR_PTR(-EAGAIN);
entry->eof = 1; entry->eof = 1;
return ERR_PTR(-EBADCOOKIE); return ERR_PTR(-EBADCOOKIE);
} }
p = xdr_inline_decode(xdr, 12);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, &entry->ino); p = xdr_decode_hyper(p, &entry->ino);
entry->len = ntohl(*p++); entry->len = ntohl(*p++);
p = xdr_inline_decode(xdr, entry->len + 8);
if (unlikely(!p))
goto out_overflow;
entry->name = (const char *) p; entry->name = (const char *) p;
p += XDR_QUADLEN(entry->len); p += XDR_QUADLEN(entry->len);
entry->prev_cookie = entry->cookie; entry->prev_cookie = entry->cookie;
...@@ -636,10 +624,17 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) ...@@ -636,10 +624,17 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
if (plus) { if (plus) {
entry->fattr->valid = 0; entry->fattr->valid = 0;
p = xdr_decode_post_op_attr(p, entry->fattr); p = xdr_decode_post_op_attr_stream(xdr, entry->fattr);
if (IS_ERR(p))
goto out_overflow_exit;
/* In fact, a post_op_fh3: */ /* In fact, a post_op_fh3: */
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
if (*p++) { if (*p++) {
p = xdr_decode_fhandle(p, entry->fh); p = xdr_decode_fhandle_stream(xdr, entry->fh);
if (IS_ERR(p))
goto out_overflow_exit;
/* Ugh -- server reply was truncated */ /* Ugh -- server reply was truncated */
if (p == NULL) { if (p == NULL) {
dprintk("NFS: FH truncated\n"); dprintk("NFS: FH truncated\n");
...@@ -650,8 +645,18 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) ...@@ -650,8 +645,18 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
memset((u8*)(entry->fh), 0, sizeof(*entry->fh)); memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
} }
entry->eof = !p[0] && p[1]; p = xdr_inline_peek(xdr, 8);
if (p != NULL)
entry->eof = !p[0] && p[1];
else
entry->eof = 0;
return p; return p;
out_overflow:
print_overflow_msg(__func__, xdr);
out_overflow_exit:
return ERR_PTR(-EIO);
} }
/* /*
...@@ -824,7 +829,6 @@ nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr) ...@@ -824,7 +829,6 @@ nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
struct kvec *iov = rcvbuf->head; struct kvec *iov = rcvbuf->head;
size_t hdrlen; size_t hdrlen;
u32 len, recvd; u32 len, recvd;
char *kaddr;
int status; int status;
status = ntohl(*p++); status = ntohl(*p++);
...@@ -857,10 +861,7 @@ nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr) ...@@ -857,10 +861,7 @@ nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
return -EIO; return -EIO;
} }
/* NULL terminate the string we got */ xdr_terminate_string(rcvbuf, len);
kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
kaddr[len+rcvbuf->page_base] = '\0';
kunmap_atomic(kaddr, KM_USER0);
return 0; return 0;
} }
...@@ -970,14 +971,14 @@ nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res) ...@@ -970,14 +971,14 @@ nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
* Decode RENAME reply * Decode RENAME reply
*/ */
static int static int
nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs3_renameres *res) nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs_renameres *res)
{ {
int status; int status;
if ((status = ntohl(*p++)) != 0) if ((status = ntohl(*p++)) != 0)
status = nfs_stat_to_errno(status); status = nfs_stat_to_errno(status);
p = xdr_decode_wcc_data(p, res->fromattr); p = xdr_decode_wcc_data(p, res->old_fattr);
p = xdr_decode_wcc_data(p, res->toattr); p = xdr_decode_wcc_data(p, res->new_fattr);
return status; return status;
} }
...@@ -1043,8 +1044,9 @@ nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res) ...@@ -1043,8 +1044,9 @@ nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
res->wtmult = ntohl(*p++); res->wtmult = ntohl(*p++);
res->dtpref = ntohl(*p++); res->dtpref = ntohl(*p++);
p = xdr_decode_hyper(p, &res->maxfilesize); p = xdr_decode_hyper(p, &res->maxfilesize);
p = xdr_decode_time3(p, &res->time_delta);
/* ignore time_delta and properties */ /* ignore properties */
res->lease_time = 0; res->lease_time = 0;
return 0; return 0;
} }
......
...@@ -242,8 +242,6 @@ extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *); ...@@ -242,8 +242,6 @@ extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *);
extern int nfs4_init_clientid(struct nfs_client *, struct rpc_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 nfs41_init_clientid(struct nfs_client *, struct rpc_cred *);
extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait); extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait);
extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *);
extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle);
extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
struct nfs4_fs_locations *fs_locations, struct page *page); struct nfs4_fs_locations *fs_locations, struct page *page);
...@@ -333,7 +331,7 @@ extern void nfs_free_seqid(struct nfs_seqid *seqid); ...@@ -333,7 +331,7 @@ extern void nfs_free_seqid(struct nfs_seqid *seqid);
extern const nfs4_stateid zero_stateid; extern const nfs4_stateid zero_stateid;
/* nfs4xdr.c */ /* nfs4xdr.c */
extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus); extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
extern struct rpc_procinfo nfs4_procedures[]; extern struct rpc_procinfo nfs4_procedures[];
struct nfs4_mount_data; struct nfs4_mount_data;
......
此差异已折叠。
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/ratelimit.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/bitops.h> #include <linux/bitops.h>
...@@ -1063,6 +1064,14 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs ...@@ -1063,6 +1064,14 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs
/* Mark the file as being 'closed' */ /* Mark the file as being 'closed' */
state->state = 0; state->state = 0;
break; 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_ADMIN_REVOKED:
case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_BAD_STATEID: case -NFS4ERR_BAD_STATEID:
...@@ -1138,16 +1147,14 @@ static void nfs4_reclaim_complete(struct nfs_client *clp, ...@@ -1138,16 +1147,14 @@ static void nfs4_reclaim_complete(struct nfs_client *clp,
(void)ops->reclaim_complete(clp); (void)ops->reclaim_complete(clp);
} }
static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp) static int nfs4_state_clear_reclaim_reboot(struct nfs_client *clp)
{ {
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct rb_node *pos; struct rb_node *pos;
struct nfs4_state *state; struct nfs4_state *state;
if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
return; return 0;
nfs4_reclaim_complete(clp, clp->cl_mvops->reboot_recovery_ops);
for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
...@@ -1161,6 +1168,14 @@ static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp) ...@@ -1161,6 +1168,14 @@ static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
} }
nfs_delegation_reap_unclaimed(clp); 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) static void nfs_delegation_clear_all(struct nfs_client *clp)
...@@ -1175,6 +1190,14 @@ static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp) ...@@ -1175,6 +1190,14 @@ static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp)
nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce); 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) static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
{ {
switch (error) { switch (error) {
...@@ -1187,7 +1210,7 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) ...@@ -1187,7 +1210,7 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_LEASE_MOVED: case -NFS4ERR_LEASE_MOVED:
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
nfs4_state_end_reclaim_reboot(clp); nfs4_state_clear_reclaim_reboot(clp);
nfs4_state_start_reclaim_reboot(clp); nfs4_state_start_reclaim_reboot(clp);
break; break;
case -NFS4ERR_EXPIRED: case -NFS4ERR_EXPIRED:
...@@ -1204,6 +1227,10 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) ...@@ -1204,6 +1227,10 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
/* Zero session reset errors */ /* Zero session reset errors */
return 0; return 0;
case -EKEYEXPIRED:
/* Nothing we can do */
nfs4_warn_keyexpired(clp->cl_hostname);
return 0;
} }
return error; return error;
} }
...@@ -1414,9 +1441,10 @@ static void nfs4_set_lease_expired(struct nfs_client *clp, int status) ...@@ -1414,9 +1441,10 @@ static void nfs4_set_lease_expired(struct nfs_client *clp, int status)
case -NFS4ERR_DELAY: case -NFS4ERR_DELAY:
case -NFS4ERR_CLID_INUSE: case -NFS4ERR_CLID_INUSE:
case -EAGAIN: case -EAGAIN:
case -EKEYEXPIRED:
break; break;
case -EKEYEXPIRED:
nfs4_warn_keyexpired(clp->cl_hostname);
case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
* in nfs4_exchange_id */ * in nfs4_exchange_id */
default: default:
......
...@@ -816,7 +816,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const ...@@ -816,7 +816,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
if (iap->ia_valid & ATTR_MODE) if (iap->ia_valid & ATTR_MODE)
len += 4; len += 4;
if (iap->ia_valid & ATTR_UID) { if (iap->ia_valid & ATTR_UID) {
owner_namelen = nfs_map_uid_to_name(server->nfs_client, iap->ia_uid, owner_name); owner_namelen = nfs_map_uid_to_name(server->nfs_client, iap->ia_uid, owner_name, IDMAP_NAMESZ);
if (owner_namelen < 0) { if (owner_namelen < 0) {
dprintk("nfs: couldn't resolve uid %d to string\n", dprintk("nfs: couldn't resolve uid %d to string\n",
iap->ia_uid); iap->ia_uid);
...@@ -828,7 +828,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const ...@@ -828,7 +828,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
len += 4 + (XDR_QUADLEN(owner_namelen) << 2); len += 4 + (XDR_QUADLEN(owner_namelen) << 2);
} }
if (iap->ia_valid & ATTR_GID) { if (iap->ia_valid & ATTR_GID) {
owner_grouplen = nfs_map_gid_to_group(server->nfs_client, iap->ia_gid, owner_group); owner_grouplen = nfs_map_gid_to_group(server->nfs_client, iap->ia_gid, owner_group, IDMAP_NAMESZ);
if (owner_grouplen < 0) { if (owner_grouplen < 0) {
dprintk("nfs: couldn't resolve gid %d to string\n", dprintk("nfs: couldn't resolve gid %d to string\n",
iap->ia_gid); iap->ia_gid);
...@@ -1385,24 +1385,35 @@ static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args, ...@@ -1385,24 +1385,35 @@ static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args,
static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr) 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] = { uint32_t attrs[2] = {0, 0};
FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID, uint32_t dircount = readdir->count >> 1;
FATTR4_WORD1_MOUNTED_ON_FILEID,
};
__be32 *p; __be32 *p;
if (readdir->plus) {
attrs[0] |= FATTR4_WORD0_TYPE|FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE|
FATTR4_WORD0_FSID|FATTR4_WORD0_FILEHANDLE;
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;
}
attrs[0] |= FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID;
attrs[1] |= FATTR4_WORD1_MOUNTED_ON_FILEID;
/* Switch to mounted_on_fileid if the server supports it */
if (readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)
attrs[0] &= ~FATTR4_WORD0_FILEID;
else
attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
p = reserve_space(xdr, 12+NFS4_VERIFIER_SIZE+20); p = reserve_space(xdr, 12+NFS4_VERIFIER_SIZE+20);
*p++ = cpu_to_be32(OP_READDIR); *p++ = cpu_to_be32(OP_READDIR);
p = xdr_encode_hyper(p, readdir->cookie); p = xdr_encode_hyper(p, readdir->cookie);
p = xdr_encode_opaque_fixed(p, readdir->verifier.data, NFS4_VERIFIER_SIZE); p = xdr_encode_opaque_fixed(p, readdir->verifier.data, NFS4_VERIFIER_SIZE);
*p++ = cpu_to_be32(readdir->count >> 1); /* We're not doing readdirplus */ *p++ = cpu_to_be32(dircount);
*p++ = cpu_to_be32(readdir->count); *p++ = cpu_to_be32(readdir->count);
*p++ = cpu_to_be32(2); *p++ = cpu_to_be32(2);
/* Switch to mounted_on_fileid if the server supports it */
if (readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)
attrs[0] &= ~FATTR4_WORD0_FILEID;
else
attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
*p++ = cpu_to_be32(attrs[0] & readdir->bitmask[0]); *p++ = cpu_to_be32(attrs[0] & readdir->bitmask[0]);
*p = cpu_to_be32(attrs[1] & readdir->bitmask[1]); *p = cpu_to_be32(attrs[1] & readdir->bitmask[1]);
hdr->nops++; hdr->nops++;
...@@ -1823,7 +1834,7 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs ...@@ -1823,7 +1834,7 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs
/* /*
* Encode RENAME request * Encode RENAME request
*/ */
static int nfs4_xdr_enc_rename(struct rpc_rqst *req, __be32 *p, const struct nfs4_rename_arg *args) static int nfs4_xdr_enc_rename(struct rpc_rqst *req, __be32 *p, const struct nfs_renameargs *args)
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
...@@ -2676,7 +2687,10 @@ static inline int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, ...@@ -2676,7 +2687,10 @@ static inline int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen,
static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *bitmask) static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *bitmask)
{ {
if (likely(bitmap[0] & FATTR4_WORD0_SUPPORTED_ATTRS)) { if (likely(bitmap[0] & FATTR4_WORD0_SUPPORTED_ATTRS)) {
decode_attr_bitmap(xdr, bitmask); int ret;
ret = decode_attr_bitmap(xdr, bitmask);
if (unlikely(ret < 0))
return ret;
bitmap[0] &= ~FATTR4_WORD0_SUPPORTED_ATTRS; bitmap[0] &= ~FATTR4_WORD0_SUPPORTED_ATTRS;
} else } else
bitmask[0] = bitmask[1] = 0; bitmask[0] = bitmask[1] = 0;
...@@ -2848,6 +2862,56 @@ static int decode_attr_lease_time(struct xdr_stream *xdr, uint32_t *bitmap, uint ...@@ -2848,6 +2862,56 @@ static int decode_attr_lease_time(struct xdr_stream *xdr, uint32_t *bitmap, uint
return -EIO; return -EIO;
} }
static int decode_attr_error(struct xdr_stream *xdr, uint32_t *bitmap)
{
__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;
}
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) static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
{ {
__be32 *p; __be32 *p;
...@@ -3521,6 +3585,24 @@ static int decode_attr_time_metadata(struct xdr_stream *xdr, uint32_t *bitmap, s ...@@ -3521,6 +3585,24 @@ static int decode_attr_time_metadata(struct xdr_stream *xdr, uint32_t *bitmap, s
return status; 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) static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time)
{ {
int status = 0; int status = 0;
...@@ -3744,29 +3826,14 @@ static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf ...@@ -3744,29 +3826,14 @@ static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf
return status; return status;
} }
static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
struct nfs_fattr *fattr, struct nfs_fh *fh,
const struct nfs_server *server, int may_sleep) const struct nfs_server *server, int may_sleep)
{ {
__be32 *savep;
uint32_t attrlen,
bitmap[2] = {0},
type;
int status; int status;
umode_t fmode = 0; umode_t fmode = 0;
uint64_t fileid; uint64_t fileid;
uint32_t type;
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_attr_type(xdr, bitmap, &type); status = decode_attr_type(xdr, bitmap, &type);
if (status < 0) if (status < 0)
...@@ -3792,6 +3859,14 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, ...@@ -3792,6 +3859,14 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
goto xdr_error; goto xdr_error;
fattr->valid |= status; fattr->valid |= status;
status = decode_attr_error(xdr, bitmap);
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); status = decode_attr_fileid(xdr, bitmap, &fattr->fileid);
if (status < 0) if (status < 0)
goto xdr_error; goto xdr_error;
...@@ -3862,12 +3937,46 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, ...@@ -3862,12 +3937,46 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
fattr->valid |= status; 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, const struct nfs_server *server, int may_sleep)
{
__be32 *savep;
uint32_t attrlen,
bitmap[2] = {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, server, may_sleep);
if (status < 0)
goto xdr_error;
status = verify_attr_len(xdr, savep, attrlen); status = verify_attr_len(xdr, savep, attrlen);
xdr_error: xdr_error:
dprintk("%s: xdr returned %d\n", __func__, -status); dprintk("%s: xdr returned %d\n", __func__, -status);
return status; return status;
} }
static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
const struct nfs_server *server, int may_sleep)
{
return decode_getfattr_generic(xdr, fattr, NULL, server, may_sleep);
}
static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo)
{ {
...@@ -3894,6 +4003,9 @@ static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) ...@@ -3894,6 +4003,9 @@ static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo)
if ((status = decode_attr_maxwrite(xdr, bitmap, &fsinfo->wtmax)) != 0) if ((status = decode_attr_maxwrite(xdr, bitmap, &fsinfo->wtmax)) != 0)
goto xdr_error; goto xdr_error;
fsinfo->wtpref = fsinfo->wtmax; fsinfo->wtpref = fsinfo->wtmax;
status = decode_attr_time_delta(xdr, bitmap, &fsinfo->time_delta);
if (status != 0)
goto xdr_error;
status = verify_attr_len(xdr, savep, attrlen); status = verify_attr_len(xdr, savep, attrlen);
xdr_error: xdr_error:
...@@ -3950,13 +4062,13 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl) ...@@ -3950,13 +4062,13 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl)
__be32 *p; __be32 *p;
uint32_t namelen, type; uint32_t namelen, type;
p = xdr_inline_decode(xdr, 32); p = xdr_inline_decode(xdr, 32); /* read 32 bytes */
if (unlikely(!p)) if (unlikely(!p))
goto out_overflow; goto out_overflow;
p = xdr_decode_hyper(p, &offset); p = xdr_decode_hyper(p, &offset); /* read 2 8-byte long words */
p = xdr_decode_hyper(p, &length); p = xdr_decode_hyper(p, &length);
type = be32_to_cpup(p++); type = be32_to_cpup(p++); /* 4 byte read */
if (fl != NULL) { if (fl != NULL) { /* manipulate file lock */
fl->fl_start = (loff_t)offset; fl->fl_start = (loff_t)offset;
fl->fl_end = fl->fl_start + (loff_t)length - 1; fl->fl_end = fl->fl_start + (loff_t)length - 1;
if (length == ~(uint64_t)0) if (length == ~(uint64_t)0)
...@@ -3966,9 +4078,9 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl) ...@@ -3966,9 +4078,9 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl)
fl->fl_type = F_RDLCK; fl->fl_type = F_RDLCK;
fl->fl_pid = 0; fl->fl_pid = 0;
} }
p = xdr_decode_hyper(p, &clientid); p = xdr_decode_hyper(p, &clientid); /* read 8 bytes */
namelen = be32_to_cpup(p); namelen = be32_to_cpup(p); /* read 4 bytes */ /* have read all 32 bytes now */
p = xdr_inline_decode(xdr, namelen); p = xdr_inline_decode(xdr, namelen); /* variable size field */
if (likely(p)) if (likely(p))
return -NFS4ERR_DENIED; return -NFS4ERR_DENIED;
out_overflow: out_overflow:
...@@ -4200,12 +4312,9 @@ static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_ ...@@ -4200,12 +4312,9 @@ static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_
static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir) 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 xdr_buf *rcvbuf = &req->rq_rcv_buf;
struct page *page = *rcvbuf->pages;
struct kvec *iov = rcvbuf->head; struct kvec *iov = rcvbuf->head;
size_t hdrlen; size_t hdrlen;
u32 recvd, pglen = rcvbuf->page_len; u32 recvd, pglen = rcvbuf->page_len;
__be32 *end, *entry, *p, *kaddr;
unsigned int nr = 0;
int status; int status;
status = decode_op_hdr(xdr, OP_READDIR); status = decode_op_hdr(xdr, OP_READDIR);
...@@ -4225,71 +4334,8 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n ...@@ -4225,71 +4334,8 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
pglen = recvd; pglen = recvd;
xdr_read_pages(xdr, pglen); xdr_read_pages(xdr, pglen);
BUG_ON(pglen + readdir->pgbase > PAGE_CACHE_SIZE);
kaddr = p = kmap_atomic(page, KM_USER0);
end = p + ((pglen + readdir->pgbase) >> 2);
entry = p;
/* Make sure the packet actually has a value_follows and EOF entry */
if ((entry + 1) > end)
goto short_pkt;
for (; *p++; nr++) {
u32 len, attrlen, xlen;
if (end - p < 3)
goto short_pkt;
dprintk("cookie = %Lu, ", *((unsigned long long *)p));
p += 2; /* cookie */
len = ntohl(*p++); /* filename length */
if (len > NFS4_MAXNAMLEN) {
dprintk("NFS: giant filename in readdir (len 0x%x)\n",
len);
goto err_unmap;
}
xlen = XDR_QUADLEN(len);
if (end - p < xlen + 1)
goto short_pkt;
dprintk("filename = %*s\n", len, (char *)p);
p += xlen;
len = ntohl(*p++); /* bitmap length */
if (end - p < len + 1)
goto short_pkt;
p += len;
attrlen = XDR_QUADLEN(ntohl(*p++));
if (end - p < attrlen + 2)
goto short_pkt;
p += attrlen; /* attributes */
entry = p;
}
/*
* Apparently some server sends responses that are a valid size, but
* contain no entries, and have value_follows==0 and EOF==0. For
* those, just set the EOF marker.
*/
if (!nr && entry[1] == 0) {
dprintk("NFS: readdir reply truncated!\n");
entry[1] = 1;
}
out:
kunmap_atomic(kaddr, KM_USER0);
return 0; return 0;
short_pkt:
/*
* When we get a short packet there are 2 possibilities. We can
* return an error, or fix up the response to look like a valid
* response and return what we have so far. If there are no
* entries and the packet was short, then return -EIO. If there
* are valid entries in the response, return them and pretend that
* the call was successful, but incomplete. The caller can retry the
* readdir starting at the last cookie.
*/
dprintk("%s: short packet at entry %d\n", __func__, nr);
entry[0] = entry[1] = 0;
if (nr)
goto out;
err_unmap:
kunmap_atomic(kaddr, KM_USER0);
return -errno_NFSERR_IO;
} }
static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
...@@ -4299,7 +4345,6 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) ...@@ -4299,7 +4345,6 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
size_t hdrlen; size_t hdrlen;
u32 len, recvd; u32 len, recvd;
__be32 *p; __be32 *p;
char *kaddr;
int status; int status;
status = decode_op_hdr(xdr, OP_READLINK); status = decode_op_hdr(xdr, OP_READLINK);
...@@ -4330,9 +4375,7 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) ...@@ -4330,9 +4375,7 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
* and and null-terminate the text (the VFS expects * and and null-terminate the text (the VFS expects
* null-termination). * null-termination).
*/ */
kaddr = (char *)kmap_atomic(rcvbuf->pages[0], KM_USER0); xdr_terminate_string(rcvbuf, len);
kaddr[len+rcvbuf->page_base] = '\0';
kunmap_atomic(kaddr, KM_USER0);
return 0; return 0;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr); print_overflow_msg(__func__, xdr);
...@@ -4668,7 +4711,6 @@ static int decode_sequence(struct xdr_stream *xdr, ...@@ -4668,7 +4711,6 @@ static int decode_sequence(struct xdr_stream *xdr,
struct rpc_rqst *rqstp) struct rpc_rqst *rqstp)
{ {
#if defined(CONFIG_NFS_V4_1) #if defined(CONFIG_NFS_V4_1)
struct nfs4_slot *slot;
struct nfs4_sessionid id; struct nfs4_sessionid id;
u32 dummy; u32 dummy;
int status; int status;
...@@ -4700,15 +4742,14 @@ static int decode_sequence(struct xdr_stream *xdr, ...@@ -4700,15 +4742,14 @@ static int decode_sequence(struct xdr_stream *xdr,
goto out_overflow; goto out_overflow;
/* seqid */ /* seqid */
slot = &res->sr_session->fc_slot_table.slots[res->sr_slotid];
dummy = be32_to_cpup(p++); dummy = be32_to_cpup(p++);
if (dummy != slot->seq_nr) { if (dummy != res->sr_slot->seq_nr) {
dprintk("%s Invalid sequence number\n", __func__); dprintk("%s Invalid sequence number\n", __func__);
goto out_err; goto out_err;
} }
/* slot id */ /* slot id */
dummy = be32_to_cpup(p++); dummy = be32_to_cpup(p++);
if (dummy != res->sr_slotid) { if (dummy != res->sr_slot - res->sr_session->fc_slot_table.slots) {
dprintk("%s Invalid slot id\n", __func__); dprintk("%s Invalid slot id\n", __func__);
goto out_err; goto out_err;
} }
...@@ -4873,7 +4914,7 @@ static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, __be32 *p, struct nfs_rem ...@@ -4873,7 +4914,7 @@ static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, __be32 *p, struct nfs_rem
/* /*
* Decode RENAME response * Decode RENAME response
*/ */
static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_rename_res *res) static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, __be32 *p, struct nfs_renameres *res)
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr; struct compound_hdr hdr;
...@@ -5760,23 +5801,35 @@ static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp, uint32_t *p, ...@@ -5760,23 +5801,35 @@ static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp, uint32_t *p,
} }
#endif /* CONFIG_NFS_V4_1 */ #endif /* CONFIG_NFS_V4_1 */
__be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
struct nfs_server *server, int plus)
{ {
uint32_t bitmap[2] = {0}; uint32_t bitmap[2] = {0};
uint32_t len; uint32_t len;
__be32 *p = xdr_inline_decode(xdr, 4);
if (!*p++) { if (unlikely(!p))
if (!*p) goto out_overflow;
if (!ntohl(*p++)) {
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
if (!ntohl(*p++))
return ERR_PTR(-EAGAIN); return ERR_PTR(-EAGAIN);
entry->eof = 1; entry->eof = 1;
return ERR_PTR(-EBADCOOKIE); return ERR_PTR(-EBADCOOKIE);
} }
p = xdr_inline_decode(xdr, 12);
if (unlikely(!p))
goto out_overflow;
entry->prev_cookie = entry->cookie; entry->prev_cookie = entry->cookie;
p = xdr_decode_hyper(p, &entry->cookie); p = xdr_decode_hyper(p, &entry->cookie);
entry->len = ntohl(*p++); entry->len = ntohl(*p++);
p = xdr_inline_decode(xdr, entry->len);
if (unlikely(!p))
goto out_overflow;
entry->name = (const char *) p; entry->name = (const char *) p;
p += XDR_QUADLEN(entry->len);
/* /*
* In case the server doesn't return an inode number, * In case the server doesn't return an inode number,
...@@ -5784,32 +5837,33 @@ __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) ...@@ -5784,32 +5837,33 @@ __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
* since glibc seems to choke on it...) * since glibc seems to choke on it...)
*/ */
entry->ino = 1; entry->ino = 1;
entry->fattr->valid = 0;
len = ntohl(*p++); /* bitmap length */ if (decode_attr_bitmap(xdr, bitmap) < 0)
if (len-- > 0) { goto out_overflow;
bitmap[0] = ntohl(*p++);
if (len-- > 0) { if (decode_attr_length(xdr, &len, &p) < 0)
bitmap[1] = ntohl(*p++); goto out_overflow;
p += len;
} if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh, server, 1) < 0)
} goto out_overflow;
len = XDR_QUADLEN(ntohl(*p++)); /* attribute buffer length */ if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID)
if (len > 0) { entry->ino = entry->fattr->fileid;
if (bitmap[0] & FATTR4_WORD0_RDATTR_ERROR) {
bitmap[0] &= ~FATTR4_WORD0_RDATTR_ERROR; if (verify_attr_len(xdr, p, len) < 0)
/* Ignore the return value of rdattr_error for now */ goto out_overflow;
p++;
len--; p = xdr_inline_peek(xdr, 8);
} if (p != NULL)
if (bitmap[0] == 0 && bitmap[1] == FATTR4_WORD1_MOUNTED_ON_FILEID) entry->eof = !p[0] && p[1];
xdr_decode_hyper(p, &entry->ino); else
else if (bitmap[0] == FATTR4_WORD0_FILEID) entry->eof = 0;
xdr_decode_hyper(p, &entry->ino);
p += len;
}
entry->eof = !p[0] && p[1];
return p; return p;
out_overflow:
print_overflow_msg(__func__, xdr);
return ERR_PTR(-EIO);
} }
/* /*
......
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
* *
* Allow an NFS filesystem to be mounted as root. The way this works is: * 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. * (1) Use the IP autoconfig mechanism to set local IP addresses and routes.
* (2) Handle RPC negotiation with the system which replied to RARP or * (2) Construct the device string and the options string using DHCP
* was reported as a boot server by BOOTP or manually. * option 17 and/or kernel command line options.
* (3) The actual mounting is done later, when init() is running. * (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: * Changes:
...@@ -65,470 +66,243 @@ ...@@ -65,470 +66,243 @@
* Hua Qin : Support for mounting root file system via * Hua Qin : Support for mounting root file system via
* NFS over TCP. * NFS over TCP.
* Fabian Frederick: Option parser rebuilt (using parser lib) * 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/types.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/xprtsock.h>
#include <linux/nfs.h> #include <linux/nfs.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
#include <linux/in.h>
#include <linux/major.h>
#include <linux/utsname.h> #include <linux/utsname.h>
#include <linux/inet.h>
#include <linux/root_dev.h> #include <linux/root_dev.h>
#include <net/ipconfig.h> #include <net/ipconfig.h>
#include <linux/parser.h>
#include "internal.h" #include "internal.h"
/* Define this to allow debugging output */
#undef NFSROOT_DEBUG
#define NFSDBG_FACILITY NFSDBG_ROOT #define NFSDBG_FACILITY NFSDBG_ROOT
/* Default port to use if server is not running a portmapper */
#define NFS_MNT_PORT 627
/* Default path we try to mount. "%s" gets replaced by our IP address */ /* Default path we try to mount. "%s" gets replaced by our IP address */
#define NFS_ROOT "/tftpboot/%s" #define NFS_ROOT "/tftpboot/%s"
/* Parameters passed from the kernel command line */ /* Parameters passed from the kernel command line */
static char nfs_root_name[256] __initdata = ""; static char nfs_root_parms[256] __initdata = "";
/* Text-based mount options passed to super.c */
static char nfs_root_options[256] __initdata = "";
/* Address of NFS server */ /* Address of NFS server */
static __be32 servaddr __initdata = 0; static __be32 servaddr __initdata = htonl(INADDR_NONE);
/* Name of directory to mount */ /* Name of directory to mount */
static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = { 0, }; static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = "";
/* NFS-related data */ /* server:export path string passed to super.c */
static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */ static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = "";
static int nfs_port __initdata = 0; /* Port to connect to for NFS */
static int mount_port __initdata = 0; /* Mount daemon port number */
/***************************************************************************
Parsing of options
***************************************************************************/
enum {
/* Options that take integer arguments */
Opt_port, Opt_rsize, Opt_wsize, Opt_timeo, Opt_retrans, Opt_acregmin,
Opt_acregmax, Opt_acdirmin, Opt_acdirmax,
/* Options that take no arguments */
Opt_soft, Opt_hard, Opt_intr,
Opt_nointr, Opt_posix, Opt_noposix, Opt_cto, Opt_nocto, Opt_ac,
Opt_noac, Opt_lock, Opt_nolock, Opt_v2, Opt_v3, Opt_udp, Opt_tcp,
Opt_acl, Opt_noacl,
/* Error token */
Opt_err
};
static const match_table_t tokens __initconst = {
{Opt_port, "port=%u"},
{Opt_rsize, "rsize=%u"},
{Opt_wsize, "wsize=%u"},
{Opt_timeo, "timeo=%u"},
{Opt_retrans, "retrans=%u"},
{Opt_acregmin, "acregmin=%u"},
{Opt_acregmax, "acregmax=%u"},
{Opt_acdirmin, "acdirmin=%u"},
{Opt_acdirmax, "acdirmax=%u"},
{Opt_soft, "soft"},
{Opt_hard, "hard"},
{Opt_intr, "intr"},
{Opt_nointr, "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_v2, "nfsvers=2"},
{Opt_v2, "v2"},
{Opt_v3, "nfsvers=3"},
{Opt_v3, "v3"},
{Opt_udp, "proto=udp"},
{Opt_udp, "udp"},
{Opt_tcp, "proto=tcp"},
{Opt_tcp, "tcp"},
{Opt_acl, "acl"},
{Opt_noacl, "noacl"},
{Opt_err, NULL}
};
/* /*
* Parse option string. * When the "nfsrootdebug" kernel command line option is specified,
* enable debugging messages for NFSROOT.
*/ */
static int __init nfs_root_debug(char *__unused)
static int __init root_nfs_parse(char *name, char *buf)
{ {
nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT;
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
if (!name)
return 1;
/* Set the NFS remote path */
p = strsep(&name, ",");
if (p[0] != '\0' && strcmp(p, "default") != 0)
strlcpy(buf, p, NFS_MAXPATHLEN);
while ((p = strsep (&name, ",")) != NULL) {
int token;
if (!*p)
continue;
token = match_token(p, tokens, args);
/* %u tokens only. Beware if you add new tokens! */
if (token < Opt_soft && match_int(&args[0], &option))
return 0;
switch (token) {
case Opt_port:
nfs_port = option;
break;
case Opt_rsize:
nfs_data.rsize = option;
break;
case Opt_wsize:
nfs_data.wsize = option;
break;
case Opt_timeo:
nfs_data.timeo = option;
break;
case Opt_retrans:
nfs_data.retrans = option;
break;
case Opt_acregmin:
nfs_data.acregmin = option;
break;
case Opt_acregmax:
nfs_data.acregmax = option;
break;
case Opt_acdirmin:
nfs_data.acdirmin = option;
break;
case Opt_acdirmax:
nfs_data.acdirmax = option;
break;
case Opt_soft:
nfs_data.flags |= NFS_MOUNT_SOFT;
break;
case Opt_hard:
nfs_data.flags &= ~NFS_MOUNT_SOFT;
break;
case Opt_intr:
case Opt_nointr:
break;
case Opt_posix:
nfs_data.flags |= NFS_MOUNT_POSIX;
break;
case Opt_noposix:
nfs_data.flags &= ~NFS_MOUNT_POSIX;
break;
case Opt_cto:
nfs_data.flags &= ~NFS_MOUNT_NOCTO;
break;
case Opt_nocto:
nfs_data.flags |= NFS_MOUNT_NOCTO;
break;
case Opt_ac:
nfs_data.flags &= ~NFS_MOUNT_NOAC;
break;
case Opt_noac:
nfs_data.flags |= NFS_MOUNT_NOAC;
break;
case Opt_lock:
nfs_data.flags &= ~NFS_MOUNT_NONLM;
break;
case Opt_nolock:
nfs_data.flags |= NFS_MOUNT_NONLM;
break;
case Opt_v2:
nfs_data.flags &= ~NFS_MOUNT_VER3;
break;
case Opt_v3:
nfs_data.flags |= NFS_MOUNT_VER3;
break;
case Opt_udp:
nfs_data.flags &= ~NFS_MOUNT_TCP;
break;
case Opt_tcp:
nfs_data.flags |= NFS_MOUNT_TCP;
break;
case Opt_acl:
nfs_data.flags &= ~NFS_MOUNT_NOACL;
break;
case Opt_noacl:
nfs_data.flags |= NFS_MOUNT_NOACL;
break;
default:
printk(KERN_WARNING "Root-NFS: unknown "
"option: %s\n", p);
return 0;
}
}
return 1; return 1;
} }
__setup("nfsrootdebug", nfs_root_debug);
/* /*
* Prepare the NFS data structure and parse all options. * 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 root_nfs_name(char *name) static int __init nfs_root_setup(char *line)
{ {
static char buf[NFS_MAXPATHLEN] __initdata; ROOT_DEV = Root_NFS;
char *cp;
if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) {
/* Set some default values */ strlcpy(nfs_root_parms, line, sizeof(nfs_root_parms));
memset(&nfs_data, 0, sizeof(nfs_data)); } else {
nfs_port = -1; size_t n = strlen(line) + sizeof(NFS_ROOT) - 1;
nfs_data.version = NFS_MOUNT_VERSION; if (n >= sizeof(nfs_root_parms))
nfs_data.flags = NFS_MOUNT_NONLM; /* No lockd in nfs root yet */ line[sizeof(nfs_root_parms) - sizeof(NFS_ROOT) - 2] = '\0';
nfs_data.rsize = NFS_DEF_FILE_IO_SIZE; sprintf(nfs_root_parms, NFS_ROOT, line);
nfs_data.wsize = NFS_DEF_FILE_IO_SIZE;
nfs_data.acregmin = NFS_DEF_ACREGMIN;
nfs_data.acregmax = NFS_DEF_ACREGMAX;
nfs_data.acdirmin = NFS_DEF_ACDIRMIN;
nfs_data.acdirmax = NFS_DEF_ACDIRMAX;
strcpy(buf, NFS_ROOT);
/* Process options received from the remote server */
root_nfs_parse(root_server_path, buf);
/* Override them by options set on kernel command-line */
root_nfs_parse(name, buf);
cp = utsname()->nodename;
if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
return -1;
} }
sprintf(nfs_export_path, buf, cp);
/*
* 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; return 1;
} }
__setup("nfsroot=", nfs_root_setup);
/* static int __init root_nfs_copy(char *dest, const char *src,
* Get NFS server address. const size_t destlen)
*/
static int __init root_nfs_addr(void)
{ {
if ((servaddr = root_server_addr) == htonl(INADDR_NONE)) { if (strlcpy(dest, src, destlen) > destlen)
printk(KERN_ERR "Root-NFS: No NFS server available, giving up.\n");
return -1; return -1;
} return 0;
}
snprintf(nfs_data.hostname, sizeof(nfs_data.hostname), static int __init root_nfs_cat(char *dest, const char *src,
"%pI4", &servaddr); const size_t destlen)
{
if (strlcat(dest, src, destlen) > destlen)
return -1;
return 0; return 0;
} }
/* /*
* Tell the user what's going on. * Parse out root export path and mount options from
* passed-in string @incoming.
*
* Copy the export path into @exppath.
*/ */
#ifdef NFSROOT_DEBUG static int __init root_nfs_parse_options(char *incoming, char *exppath,
static void __init root_nfs_print(void) const size_t exppathlen)
{ {
printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n", char *p;
nfs_export_path, nfs_data.hostname);
printk(KERN_NOTICE "Root-NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
printk(KERN_NOTICE "Root-NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
nfs_data.acregmin, nfs_data.acregmax,
nfs_data.acdirmin, nfs_data.acdirmax);
printk(KERN_NOTICE "Root-NFS: nfsd port = %d, mountd port = %d, flags = %08x\n",
nfs_port, mount_port, nfs_data.flags);
}
#endif
static int __init root_nfs_init(void) /*
{ * Set the NFS remote path
#ifdef NFSROOT_DEBUG */
nfs_debug |= NFSDBG_ROOT; p = strsep(&incoming, ",");
#endif if (*p != '\0' && strcmp(p, "default") != 0)
if (root_nfs_copy(exppath, p, exppathlen))
return -1;
/* /*
* Decode the root directory path name and NFS options from * @incoming now points to the rest of the string; if it
* the kernel command line. This has to go here in order to * contains something, append it to our root options buffer
* be able to use the client IP address for the remote root
* directory (necessary for pure RARP booting).
*/ */
if (root_nfs_name(nfs_root_name) < 0 || if (incoming != NULL && *incoming != '\0')
root_nfs_addr() < 0) if (root_nfs_cat(nfs_root_options, incoming,
return -1; sizeof(nfs_root_options)))
return -1;
#ifdef NFSROOT_DEBUG /*
root_nfs_print(); * Possibly prepare for more options to be appended
#endif */
if (nfs_root_options[0] != '\0' &&
nfs_root_options[strlen(nfs_root_options)] != ',')
if (root_nfs_cat(nfs_root_options, ",",
sizeof(nfs_root_options)))
return -1;
return 0; return 0;
} }
/* /*
* Parse NFS server and directory information passed on the kernel * Decode the export directory path name and NFS options from
* command line. * 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 nfs_root_setup(char *line) static int __init root_nfs_data(char *cmdline)
{ {
ROOT_DEV = Root_NFS; char addr_option[sizeof("nolock,addr=") + INET_ADDRSTRLEN + 1];
if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { int len, retval = -1;
strlcpy(nfs_root_name, line, sizeof(nfs_root_name)); char *tmp = NULL;
} else { const size_t tmplen = sizeof(nfs_export_path);
int n = strlen(line) + sizeof(NFS_ROOT) - 1;
if (n >= sizeof(nfs_root_name)) tmp = kzalloc(tmplen, GFP_KERNEL);
line[sizeof(nfs_root_name) - sizeof(NFS_ROOT) - 2] = '\0'; if (tmp == NULL)
sprintf(nfs_root_name, NFS_ROOT, line); 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;
} }
root_server_addr = root_nfs_parse_addr(nfs_root_name);
return 1;
}
__setup("nfsroot=", nfs_root_setup); if (cmdline[0] != '\0') {
dprintk("Root-NFS: nfsroot=%s\n", cmdline);
/*************************************************************************** if (root_nfs_parse_options(cmdline, tmp, tmplen))
goto out_optionstoolong;
Routines to actually mount the root directory }
***************************************************************************/ /*
* Append mandatory options for nfsroot so they override
* what has come before
*/
snprintf(addr_option, sizeof(addr_option), "nolock,addr=%pI4",
&servaddr);
if (root_nfs_cat(nfs_root_options, addr_option,
sizeof(nfs_root_options)))
goto out_optionstoolong;
/* /*
* Construct sockaddr_in from address and port number. * Set up nfs_root_device. For NFS mounts, this looks like
*/ *
static inline void * server:/path
set_sockaddr(struct sockaddr_in *sin, __be32 addr, __be16 port) *
{ * At this point, utsname()->nodename contains our local
sin->sin_family = AF_INET; * IP address or hostname, set by ipconfig. If "%s" exists
sin->sin_addr.s_addr = addr; * in tmp, substitute the nodename, then shovel the whole
sin->sin_port = port; * 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;
* Query server portmapper for the port of a daemon program.
*/
static int __init root_nfs_getport(int program, int version, int proto)
{
struct sockaddr_in sin;
printk(KERN_NOTICE "Looking up port of RPC %d/%d on %pI4\n", out:
program, version, &servaddr); kfree(tmp);
set_sockaddr(&sin, servaddr, 0); return retval;
return rpcb_getport_sync(&sin, program, version, proto); 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
* Use portmapper to find mountd and nfsd port numbers if not overriden * @root_device: OUT: address of string containing NFSROOT device
* by the user. Use defaults if portmapper is not available. * @root_data: OUT: address of string containing NFSROOT mount options
* XXX: Is there any nfs server with no portmapper? *
* Returns zero and sets @root_device and @root_data if successful,
* otherwise -1 is returned.
*/ */
static int __init root_nfs_ports(void) int __init nfs_root_data(char **root_device, char **root_data)
{ {
int port; servaddr = root_server_addr;
int nfsd_ver, mountd_ver; if (servaddr == htonl(INADDR_NONE)) {
int nfsd_port, mountd_port; printk(KERN_ERR "Root-NFS: no NFS server address\n");
int proto; return -1;
if (nfs_data.flags & NFS_MOUNT_VER3) {
nfsd_ver = NFS3_VERSION;
mountd_ver = NFS_MNT3_VERSION;
nfsd_port = NFS_PORT;
mountd_port = NFS_MNT_PORT;
} else {
nfsd_ver = NFS2_VERSION;
mountd_ver = NFS_MNT_VERSION;
nfsd_port = NFS_PORT;
mountd_port = NFS_MNT_PORT;
}
proto = (nfs_data.flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;
if (nfs_port < 0) {
if ((port = root_nfs_getport(NFS_PROGRAM, nfsd_ver, proto)) < 0) {
printk(KERN_ERR "Root-NFS: Unable to get nfsd port "
"number from server, using default\n");
port = nfsd_port;
}
nfs_port = port;
dprintk("Root-NFS: Portmapper on server returned %d "
"as nfsd port\n", port);
} }
if ((port = root_nfs_getport(NFS_MNT_PROGRAM, mountd_ver, proto)) < 0) { if (root_nfs_data(nfs_root_parms) < 0)
printk(KERN_ERR "Root-NFS: Unable to get mountd port " return -1;
"number from server, using default\n");
port = mountd_port;
}
mount_port = port;
dprintk("Root-NFS: mountd port is %d\n", port);
*root_device = nfs_root_device;
*root_data = nfs_root_options;
return 0; return 0;
} }
/*
* Get a file handle from the server for the directory which is to be
* mounted.
*/
static int __init root_nfs_get_handle(void)
{
struct sockaddr_in sin;
unsigned int auth_flav_len = 0;
struct nfs_mount_request request = {
.sap = (struct sockaddr *)&sin,
.salen = sizeof(sin),
.dirpath = nfs_export_path,
.version = (nfs_data.flags & NFS_MOUNT_VER3) ?
NFS_MNT3_VERSION : NFS_MNT_VERSION,
.protocol = (nfs_data.flags & NFS_MOUNT_TCP) ?
XPRT_TRANSPORT_TCP : XPRT_TRANSPORT_UDP,
.auth_flav_len = &auth_flav_len,
};
int status = -ENOMEM;
request.fh = nfs_alloc_fhandle();
if (!request.fh)
goto out;
set_sockaddr(&sin, servaddr, htons(mount_port));
status = nfs_mount(&request);
if (status < 0)
printk(KERN_ERR "Root-NFS: Server returned error %d "
"while mounting %s\n", status, nfs_export_path);
else {
nfs_data.root.size = request.fh->size;
memcpy(&nfs_data.root.data, request.fh->data, request.fh->size);
}
nfs_free_fhandle(request.fh);
out:
return status;
}
/*
* Get the NFS port numbers and file handle, and return the prepared 'data'
* argument for mount() if everything went OK. Return NULL otherwise.
*/
void * __init nfs_root_data(void)
{
if (root_nfs_init() < 0
|| root_nfs_ports() < 0
|| root_nfs_get_handle() < 0)
return NULL;
set_sockaddr((struct sockaddr_in *) &nfs_data.addr, servaddr, htons(nfs_port));
return (void*)&nfs_data;
}
...@@ -258,7 +258,7 @@ static void nfs_free_createdata(const struct nfs_createdata *data) ...@@ -258,7 +258,7 @@ static void nfs_free_createdata(const struct nfs_createdata *data)
static int static int
nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags, struct nameidata *nd) int flags, struct nfs_open_context *ctx)
{ {
struct nfs_createdata *data; struct nfs_createdata *data;
struct rpc_message msg = { struct rpc_message msg = {
...@@ -365,17 +365,32 @@ static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir) ...@@ -365,17 +365,32 @@ static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir)
return 1; return 1;
} }
static void
nfs_proc_rename_setup(struct rpc_message *msg, struct inode *dir)
{
msg->rpc_proc = &nfs_procedures[NFSPROC_RENAME];
}
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 static int
nfs_proc_rename(struct inode *old_dir, struct qstr *old_name, nfs_proc_rename(struct inode *old_dir, struct qstr *old_name,
struct inode *new_dir, struct qstr *new_name) struct inode *new_dir, struct qstr *new_name)
{ {
struct nfs_renameargs arg = { struct nfs_renameargs arg = {
.fromfh = NFS_FH(old_dir), .old_dir = NFS_FH(old_dir),
.fromname = old_name->name, .old_name = old_name,
.fromlen = old_name->len, .new_dir = NFS_FH(new_dir),
.tofh = NFS_FH(new_dir), .new_name = new_name,
.toname = new_name->name,
.tolen = new_name->len
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs_procedures[NFSPROC_RENAME], .rpc_proc = &nfs_procedures[NFSPROC_RENAME],
...@@ -519,14 +534,14 @@ nfs_proc_rmdir(struct inode *dir, struct qstr *name) ...@@ -519,14 +534,14 @@ nfs_proc_rmdir(struct inode *dir, struct qstr *name)
*/ */
static int static int
nfs_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, nfs_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
u64 cookie, struct page *page, unsigned int count, int plus) u64 cookie, struct page **pages, unsigned int count, int plus)
{ {
struct inode *dir = dentry->d_inode; struct inode *dir = dentry->d_inode;
struct nfs_readdirargs arg = { struct nfs_readdirargs arg = {
.fh = NFS_FH(dir), .fh = NFS_FH(dir),
.cookie = cookie, .cookie = cookie,
.count = count, .count = count,
.pages = &page, .pages = pages,
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs_procedures[NFSPROC_READDIR], .rpc_proc = &nfs_procedures[NFSPROC_READDIR],
...@@ -705,6 +720,8 @@ const struct nfs_rpc_ops nfs_v2_clientops = { ...@@ -705,6 +720,8 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
.unlink_setup = nfs_proc_unlink_setup, .unlink_setup = nfs_proc_unlink_setup,
.unlink_done = nfs_proc_unlink_done, .unlink_done = nfs_proc_unlink_done,
.rename = nfs_proc_rename, .rename = nfs_proc_rename,
.rename_setup = nfs_proc_rename_setup,
.rename_done = nfs_proc_rename_done,
.link = nfs_proc_link, .link = nfs_proc_link,
.symlink = nfs_proc_symlink, .symlink = nfs_proc_symlink,
.mkdir = nfs_proc_mkdir, .mkdir = nfs_proc_mkdir,
......
...@@ -46,7 +46,6 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) ...@@ -46,7 +46,6 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
memset(p, 0, sizeof(*p)); memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages); INIT_LIST_HEAD(&p->pages);
p->npages = pagecount; p->npages = pagecount;
p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
if (pagecount <= ARRAY_SIZE(p->page_array)) if (pagecount <= ARRAY_SIZE(p->page_array))
p->pagevec = p->page_array; p->pagevec = p->page_array;
else { else {
......
...@@ -100,6 +100,7 @@ enum { ...@@ -100,6 +100,7 @@ enum {
Opt_addr, Opt_mountaddr, Opt_clientaddr, Opt_addr, Opt_mountaddr, Opt_clientaddr,
Opt_lookupcache, Opt_lookupcache,
Opt_fscache_uniq, Opt_fscache_uniq,
Opt_local_lock,
/* Special mount options */ /* Special mount options */
Opt_userspace, Opt_deprecated, Opt_sloppy, Opt_userspace, Opt_deprecated, Opt_sloppy,
...@@ -171,6 +172,7 @@ static const match_table_t nfs_mount_option_tokens = { ...@@ -171,6 +172,7 @@ static const match_table_t nfs_mount_option_tokens = {
{ Opt_lookupcache, "lookupcache=%s" }, { Opt_lookupcache, "lookupcache=%s" },
{ Opt_fscache_uniq, "fsc=%s" }, { Opt_fscache_uniq, "fsc=%s" },
{ Opt_local_lock, "local_lock=%s" },
{ Opt_err, NULL } { Opt_err, NULL }
}; };
...@@ -236,6 +238,22 @@ static match_table_t nfs_lookupcache_tokens = { ...@@ -236,6 +238,22 @@ static match_table_t nfs_lookupcache_tokens = {
{ Opt_lookupcache_err, NULL } { 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 }
};
static void nfs_umount_begin(struct super_block *); static void nfs_umount_begin(struct super_block *);
static int nfs_statfs(struct dentry *, struct kstatfs *); static int nfs_statfs(struct dentry *, struct kstatfs *);
...@@ -622,6 +640,7 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, ...@@ -622,6 +640,7 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
const struct proc_nfs_info *nfs_infop; const struct proc_nfs_info *nfs_infop;
struct nfs_client *clp = nfss->nfs_client; struct nfs_client *clp = nfss->nfs_client;
u32 version = clp->rpc_ops->version; u32 version = clp->rpc_ops->version;
int local_flock, local_fcntl;
seq_printf(m, ",vers=%u", version); seq_printf(m, ",vers=%u", version);
seq_printf(m, ",rsize=%u", nfss->rsize); seq_printf(m, ",rsize=%u", nfss->rsize);
...@@ -670,6 +689,18 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, ...@@ -670,6 +689,18 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
else else
seq_printf(m, ",lookupcache=pos"); 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");
} }
/* /*
...@@ -1017,9 +1048,13 @@ static int nfs_parse_mount_options(char *raw, ...@@ -1017,9 +1048,13 @@ static int nfs_parse_mount_options(char *raw,
break; break;
case Opt_lock: case Opt_lock:
mnt->flags &= ~NFS_MOUNT_NONLM; mnt->flags &= ~NFS_MOUNT_NONLM;
mnt->flags &= ~(NFS_MOUNT_LOCAL_FLOCK |
NFS_MOUNT_LOCAL_FCNTL);
break; break;
case Opt_nolock: case Opt_nolock:
mnt->flags |= NFS_MOUNT_NONLM; mnt->flags |= NFS_MOUNT_NONLM;
mnt->flags |= (NFS_MOUNT_LOCAL_FLOCK |
NFS_MOUNT_LOCAL_FCNTL);
break; break;
case Opt_v2: case Opt_v2:
mnt->flags &= ~NFS_MOUNT_VER3; mnt->flags &= ~NFS_MOUNT_VER3;
...@@ -1420,6 +1455,34 @@ static int nfs_parse_mount_options(char *raw, ...@@ -1420,6 +1455,34 @@ static int nfs_parse_mount_options(char *raw,
mnt->fscache_uniq = string; mnt->fscache_uniq = string;
mnt->options |= NFS_OPTION_FSCACHE; mnt->options |= NFS_OPTION_FSCACHE;
break; 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 * Special options
...@@ -1825,6 +1888,12 @@ static int nfs_validate_mount_data(void *options, ...@@ -1825,6 +1888,12 @@ static int nfs_validate_mount_data(void *options,
if (!args->nfs_server.hostname) if (!args->nfs_server.hostname)
goto out_nomem; 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 * The legacy version 6 binary mount data from userspace has a
* field used only to transport selinux information into the * field used only to transport selinux information into the
...@@ -2441,7 +2510,8 @@ static void nfs4_fill_super(struct super_block *sb) ...@@ -2441,7 +2510,8 @@ static void nfs4_fill_super(struct super_block *sb)
static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args) static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args)
{ {
args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3); 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, static int nfs4_validate_text_mount_data(void *options,
......
...@@ -32,6 +32,7 @@ static ctl_table nfs_cb_sysctls[] = { ...@@ -32,6 +32,7 @@ static ctl_table nfs_cb_sysctls[] = {
.extra1 = (int *)&nfs_set_port_min, .extra1 = (int *)&nfs_set_port_min,
.extra2 = (int *)&nfs_set_port_max, .extra2 = (int *)&nfs_set_port_max,
}, },
#ifndef CONFIG_NFS_USE_NEW_IDMAPPER
{ {
.procname = "idmap_cache_timeout", .procname = "idmap_cache_timeout",
.data = &nfs_idmap_cache_timeout, .data = &nfs_idmap_cache_timeout,
...@@ -39,6 +40,7 @@ static ctl_table nfs_cb_sysctls[] = { ...@@ -39,6 +40,7 @@ static ctl_table nfs_cb_sysctls[] = {
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec_jiffies, .proc_handler = proc_dointvec_jiffies,
}, },
#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */
#endif #endif
{ {
.procname = "nfs_mountpoint_timeout", .procname = "nfs_mountpoint_timeout",
......
此差异已折叠。
...@@ -55,7 +55,6 @@ struct nfs_write_data *nfs_commitdata_alloc(void) ...@@ -55,7 +55,6 @@ struct nfs_write_data *nfs_commitdata_alloc(void)
if (p) { if (p) {
memset(p, 0, sizeof(*p)); memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages); INIT_LIST_HEAD(&p->pages);
p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
} }
return p; return p;
} }
...@@ -75,7 +74,6 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) ...@@ -75,7 +74,6 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
memset(p, 0, sizeof(*p)); memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages); INIT_LIST_HEAD(&p->pages);
p->npages = pagecount; p->npages = pagecount;
p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
if (pagecount <= ARRAY_SIZE(p->page_array)) if (pagecount <= ARRAY_SIZE(p->page_array))
p->pagevec = p->page_array; p->pagevec = p->page_array;
else { else {
...@@ -1433,15 +1431,17 @@ static int nfs_commit_unstable_pages(struct inode *inode, struct writeback_contr ...@@ -1433,15 +1431,17 @@ static int nfs_commit_unstable_pages(struct inode *inode, struct writeback_contr
int flags = FLUSH_SYNC; int flags = FLUSH_SYNC;
int ret = 0; int ret = 0;
/* Don't commit yet if this is a non-blocking flush and there are if (wbc->sync_mode == WB_SYNC_NONE) {
* lots of outstanding writes for this mapping. /* Don't commit yet if this is a non-blocking flush and there
*/ * are a lot of outstanding writes for this mapping.
if (wbc->sync_mode == WB_SYNC_NONE && */
nfsi->ncommit <= (nfsi->npages >> 1)) if (nfsi->ncommit <= (nfsi->npages >> 1))
goto out_mark_dirty; goto out_mark_dirty;
if (wbc->nonblocking || wbc->for_background) /* don't wait for the COMMIT response */
flags = 0; flags = 0;
}
ret = nfs_commit_inode(inode, flags); ret = nfs_commit_inode(inode, flags);
if (ret >= 0) { if (ret >= 0) {
if (wbc->sync_mode == WB_SYNC_NONE) { if (wbc->sync_mode == WB_SYNC_NONE) {
......
...@@ -360,10 +360,13 @@ extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr); ...@@ -360,10 +360,13 @@ extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr);
extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
extern void put_nfs_open_context(struct nfs_open_context *ctx); extern void put_nfs_open_context(struct nfs_open_context *ctx);
extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode); extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode);
extern struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct rpc_cred *cred, fmode_t f_mode);
extern void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx);
extern struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx); extern struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx);
extern void nfs_put_lock_context(struct nfs_lock_context *l_ctx); extern void nfs_put_lock_context(struct nfs_lock_context *l_ctx);
extern u64 nfs_compat_user_ino64(u64 fileid); extern u64 nfs_compat_user_ino64(u64 fileid);
extern void nfs_fattr_init(struct nfs_fattr *fattr); extern void nfs_fattr_init(struct nfs_fattr *fattr);
extern unsigned long nfs_inc_attr_generation_counter(void);
extern struct nfs_fattr *nfs_alloc_fattr(void); extern struct nfs_fattr *nfs_alloc_fattr(void);
...@@ -379,9 +382,12 @@ static inline void nfs_free_fhandle(const struct nfs_fh *fh) ...@@ -379,9 +382,12 @@ static inline void nfs_free_fhandle(const struct nfs_fh *fh)
kfree(fh); kfree(fh);
} }
/*
* linux/fs/nfs/nfsroot.c
*/
extern int nfs_root_data(char **root_device, char **root_data); /*__init*/
/* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */ /* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */
extern __be32 root_nfs_parse_addr(char *name); /*__init*/ extern __be32 root_nfs_parse_addr(char *name); /*__init*/
extern unsigned long nfs_inc_attr_generation_counter(void);
/* /*
* linux/fs/nfs/file.c * linux/fs/nfs/file.c
...@@ -479,10 +485,10 @@ extern void nfs_release_automount_timer(void); ...@@ -479,10 +485,10 @@ extern void nfs_release_automount_timer(void);
/* /*
* linux/fs/nfs/unlink.c * linux/fs/nfs/unlink.c
*/ */
extern int nfs_async_unlink(struct inode *dir, struct dentry *dentry);
extern void nfs_complete_unlink(struct dentry *dentry, struct inode *); extern void nfs_complete_unlink(struct dentry *dentry, struct inode *);
extern void nfs_block_sillyrename(struct dentry *dentry); extern void nfs_block_sillyrename(struct dentry *dentry);
extern void nfs_unblock_sillyrename(struct dentry *dentry); extern void nfs_unblock_sillyrename(struct dentry *dentry);
extern int nfs_sillyrename(struct inode *dir, struct dentry *dentry);
/* /*
* linux/fs/nfs/write.c * linux/fs/nfs/write.c
...@@ -584,10 +590,6 @@ nfs_fileid_to_ino_t(u64 fileid) ...@@ -584,10 +590,6 @@ nfs_fileid_to_ino_t(u64 fileid)
return ino; return ino;
} }
/* NFS root */
extern void * nfs_root_data(void);
#define nfs_wait_event(clnt, wq, condition) \ #define nfs_wait_event(clnt, wq, condition) \
({ \ ({ \
int __retval = wait_event_killable(wq, condition); \ int __retval = wait_event_killable(wq, condition); \
......
...@@ -124,6 +124,7 @@ struct nfs_server { ...@@ -124,6 +124,7 @@ struct nfs_server {
struct nfs_fsid fsid; struct nfs_fsid fsid;
__u64 maxfilesize; /* maximum file size */ __u64 maxfilesize; /* maximum file size */
struct timespec time_delta; /* smallest time granularity */
unsigned long mount_time; /* when this fs was mounted */ unsigned long mount_time; /* when this fs was mounted */
dev_t s_dev; /* superblock dev numbers */ dev_t s_dev; /* superblock dev numbers */
......
...@@ -66,13 +66,40 @@ struct idmap_msg { ...@@ -66,13 +66,40 @@ struct idmap_msg {
/* Forward declaration to make this header independent of others */ /* Forward declaration to make this header independent of others */
struct nfs_client; struct nfs_client;
#ifdef CONFIG_NFS_USE_NEW_IDMAPPER
int nfs_idmap_init(void);
void nfs_idmap_quit(void);
static inline int nfs_idmap_new(struct nfs_client *clp)
{
return 0;
}
static inline void nfs_idmap_delete(struct nfs_client *clp)
{
}
#else /* CONFIG_NFS_USE_NEW_IDMAPPER not set */
static inline int nfs_idmap_init(void)
{
return 0;
}
static inline void nfs_idmap_quit(void)
{
}
int nfs_idmap_new(struct nfs_client *); int nfs_idmap_new(struct nfs_client *);
void nfs_idmap_delete(struct nfs_client *); void nfs_idmap_delete(struct nfs_client *);
#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */
int nfs_map_name_to_uid(struct nfs_client *, const char *, size_t, __u32 *); int nfs_map_name_to_uid(struct nfs_client *, const char *, size_t, __u32 *);
int nfs_map_group_to_gid(struct nfs_client *, const char *, size_t, __u32 *); int nfs_map_group_to_gid(struct nfs_client *, const char *, size_t, __u32 *);
int nfs_map_uid_to_name(struct nfs_client *, __u32, char *); int nfs_map_uid_to_name(struct nfs_client *, __u32, char *, size_t);
int nfs_map_gid_to_group(struct nfs_client *, __u32, char *); int nfs_map_gid_to_group(struct nfs_client *, __u32, char *, size_t);
extern unsigned int nfs_idmap_cache_timeout; extern unsigned int nfs_idmap_cache_timeout;
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -71,4 +71,7 @@ struct nfs_mount_data { ...@@ -71,4 +71,7 @@ struct nfs_mount_data {
#define NFS_MOUNT_NORESVPORT 0x40000 #define NFS_MOUNT_NORESVPORT 0x40000
#define NFS_MOUNT_LEGACY_INTERFACE 0x80000 #define NFS_MOUNT_LEGACY_INTERFACE 0x80000
#define NFS_MOUNT_LOCAL_FLOCK 0x100000
#define NFS_MOUNT_LOCAL_FCNTL 0x200000
#endif #endif
此差异已折叠。
...@@ -137,7 +137,6 @@ int rpcb_register(u32, u32, int, unsigned short); ...@@ -137,7 +137,6 @@ int rpcb_register(u32, u32, int, unsigned short);
int rpcb_v4_register(const u32 program, const u32 version, int rpcb_v4_register(const u32 program, const u32 version,
const struct sockaddr *address, const struct sockaddr *address,
const char *netid); const char *netid);
int rpcb_getport_sync(struct sockaddr_in *, u32, u32, int);
void rpcb_getport_async(struct rpc_task *); void rpcb_getport_async(struct rpc_task *);
void rpc_call_start(struct rpc_task *); void rpc_call_start(struct rpc_task *);
......
...@@ -108,6 +108,7 @@ void xdr_encode_pages(struct xdr_buf *, struct page **, unsigned int, ...@@ -108,6 +108,7 @@ void xdr_encode_pages(struct xdr_buf *, struct page **, unsigned int,
unsigned int); unsigned int);
void xdr_inline_pages(struct xdr_buf *, unsigned int, void xdr_inline_pages(struct xdr_buf *, unsigned int,
struct page **, unsigned int, unsigned int); struct page **, unsigned int, unsigned int);
void xdr_terminate_string(struct xdr_buf *, const u32);
static inline __be32 *xdr_encode_array(__be32 *p, const void *s, unsigned int len) static inline __be32 *xdr_encode_array(__be32 *p, const void *s, unsigned int len)
{ {
...@@ -200,6 +201,7 @@ extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes); ...@@ -200,6 +201,7 @@ extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
unsigned int base, unsigned int len); unsigned int base, unsigned int len);
extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
extern __be32 *xdr_inline_peek(struct xdr_stream *xdr, size_t nbytes);
extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes); extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes);
extern void xdr_read_pages(struct xdr_stream *xdr, unsigned int len); extern void xdr_read_pages(struct xdr_stream *xdr, unsigned int len);
extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len); extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册