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 ...
This commit is contained in:
commit
74eb94b218
|
@ -14,3 +14,5 @@ nfsroot.txt
|
|||
- short guide on setting up a diskless box with NFS root filesystem.
|
||||
rpc-cache.txt
|
||||
- introduction to the caching mechanisms in the sunrpc layer.
|
||||
idmapper.txt
|
||||
- information for configuring request-keys to be used by idmapper
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
=========
|
||||
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>
|
|||
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
|
||||
|
|
|
@ -1541,12 +1541,15 @@ and is between 256 and 4096 characters. It is defined in the file
|
|||
1 to enable accounting
|
||||
Default value is 0.
|
||||
|
||||
nfsaddrs= [NFS]
|
||||
nfsaddrs= [NFS] Deprecated. Use ip= instead.
|
||||
See Documentation/filesystems/nfs/nfsroot.txt.
|
||||
|
||||
nfsroot= [NFS] nfs root filesystem for disk-less boxes.
|
||||
See Documentation/filesystems/nfs/nfsroot.txt.
|
||||
|
||||
nfsrootdebug [NFS] enable nfsroot debugging messages.
|
||||
See Documentation/filesystems/nfs/nfsroot.txt.
|
||||
|
||||
nfs.callback_tcpport=
|
||||
[NFS] set the TCP port on which the NFSv4 callback
|
||||
channel should listen.
|
||||
|
|
|
@ -42,6 +42,7 @@ struct nlm_wait {
|
|||
};
|
||||
|
||||
static LIST_HEAD(nlm_blocked);
|
||||
static DEFINE_SPINLOCK(nlm_blocked_lock);
|
||||
|
||||
/**
|
||||
* 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 *
|
|||
block->b_lock = fl;
|
||||
init_waitqueue_head(&block->b_wait);
|
||||
block->b_status = nlm_lck_blocked;
|
||||
|
||||
spin_lock(&nlm_blocked_lock);
|
||||
list_add(&block->b_list, &nlm_blocked);
|
||||
spin_unlock(&nlm_blocked_lock);
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
@ -106,7 +110,9 @@ void nlmclnt_finish_block(struct nlm_wait *block)
|
|||
{
|
||||
if (block == NULL)
|
||||
return;
|
||||
spin_lock(&nlm_blocked_lock);
|
||||
list_del(&block->b_list);
|
||||
spin_unlock(&nlm_blocked_lock);
|
||||
kfree(block);
|
||||
}
|
||||
|
||||
|
@ -154,6 +160,7 @@ __be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
|
|||
* Look up blocked request based on arguments.
|
||||
* Warning: must not use cookie to match it!
|
||||
*/
|
||||
spin_lock(&nlm_blocked_lock);
|
||||
list_for_each_entry(block, &nlm_blocked, b_list) {
|
||||
struct file_lock *fl_blocked = block->b_lock;
|
||||
|
||||
|
@ -178,6 +185,7 @@ __be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
|
|||
wake_up(&block->b_wait);
|
||||
res = nlm_granted;
|
||||
}
|
||||
spin_unlock(&nlm_blocked_lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -216,10 +224,6 @@ reclaimer(void *ptr)
|
|||
allow_signal(SIGKILL);
|
||||
|
||||
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 */
|
||||
|
||||
dprintk("lockd: reclaiming locks for host %s\n", host->h_name);
|
||||
|
@ -260,16 +264,17 @@ restart:
|
|||
dprintk("NLM: done reclaiming locks for host %s\n", host->h_name);
|
||||
|
||||
/* 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) {
|
||||
if (block->b_host == host) {
|
||||
block->b_status = nlm_lck_denied_grace_period;
|
||||
wake_up(&block->b_wait);
|
||||
}
|
||||
}
|
||||
spin_unlock(&nlm_blocked_lock);
|
||||
|
||||
/* Release host handle after use */
|
||||
nlm_release_host(host);
|
||||
lockd_down();
|
||||
unlock_kernel();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -166,7 +166,6 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
|
|||
/* Set up the argument struct */
|
||||
nlmclnt_setlockargs(call, fl);
|
||||
|
||||
lock_kernel();
|
||||
if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
|
||||
if (fl->fl_type != F_UNLCK) {
|
||||
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)
|
|||
status = nlmclnt_test(call, fl);
|
||||
else
|
||||
status = -EINVAL;
|
||||
|
||||
fl->fl_ops->fl_release_private(fl);
|
||||
fl->fl_ops = NULL;
|
||||
unlock_kernel();
|
||||
|
||||
dprintk("lockd: clnt proc returns %d\n", status);
|
||||
return status;
|
||||
|
@ -226,9 +223,7 @@ void nlm_release_call(struct nlm_rqst *call)
|
|||
|
||||
static void nlmclnt_rpc_release(void *data)
|
||||
{
|
||||
lock_kernel();
|
||||
nlm_release_call(data);
|
||||
unlock_kernel();
|
||||
}
|
||||
|
||||
static int nlm_wait_on_grace(wait_queue_head_t *queue)
|
||||
|
@ -448,14 +443,18 @@ out:
|
|||
|
||||
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.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);
|
||||
spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock);
|
||||
}
|
||||
|
||||
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);
|
||||
spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock);
|
||||
nlm_put_lockowner(fl->fl_u.nfs_fl.owner);
|
||||
}
|
||||
|
||||
|
@ -721,9 +720,7 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
|
|||
die:
|
||||
return;
|
||||
retry_rebind:
|
||||
lock_kernel();
|
||||
nlm_rebind_host(req->a_host);
|
||||
unlock_kernel();
|
||||
retry_unlock:
|
||||
rpc_restart_call(task);
|
||||
}
|
||||
|
@ -801,9 +798,7 @@ retry_cancel:
|
|||
/* Don't ever retry more than 3 times */
|
||||
if (req->a_retries++ >= NLMCLNT_MAX_RETRIES)
|
||||
goto die;
|
||||
lock_kernel();
|
||||
nlm_rebind_host(req->a_host);
|
||||
unlock_kernel();
|
||||
rpc_restart_call(task);
|
||||
rpc_delay(task, 30 * HZ);
|
||||
}
|
||||
|
|
|
@ -118,3 +118,14 @@ config NFS_USE_KERNEL_DNS
|
|||
select DNS_RESOLVER
|
||||
select KEYS
|
||||
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,
|
|||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -657,7 +658,8 @@ static int nfs_start_lockd(struct nfs_server *server)
|
|||
|
||||
if (nlm_init.nfs_version > 3)
|
||||
return 0;
|
||||
if (server->flags & NFS_MOUNT_NONLM)
|
||||
if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) &&
|
||||
(server->flags & NFS_MOUNT_LOCAL_FCNTL))
|
||||
return 0;
|
||||
|
||||
switch (clp->cl_proto) {
|
||||
|
@ -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->dtsize = nfs_block_size(fsinfo->dtpref, NULL);
|
||||
if (server->dtsize > PAGE_CACHE_SIZE)
|
||||
server->dtsize = PAGE_CACHE_SIZE;
|
||||
if (server->dtsize > PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES)
|
||||
server->dtsize = PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES;
|
||||
if (server->dtsize > server->rsize)
|
||||
server->dtsize = server->rsize;
|
||||
|
||||
|
@ -913,6 +915,8 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *
|
|||
|
||||
server->maxfilesize = fsinfo->maxfilesize;
|
||||
|
||||
server->time_delta = fsinfo->time_delta;
|
||||
|
||||
/* We're airborne Set socket buffersize */
|
||||
rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100);
|
||||
}
|
||||
|
@ -1356,8 +1360,9 @@ static int nfs4_init_server(struct nfs_server *server,
|
|||
|
||||
/* Initialise the client representation from the mount data */
|
||||
server->flags = data->flags;
|
||||
server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|
|
||||
NFS_CAP_POSIX_LOCK;
|
||||
server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK;
|
||||
if (!(data->flags & NFS_MOUNT_NORDIRPLUS))
|
||||
server->caps |= NFS_CAP_READDIRPLUS;
|
||||
server->options = data->options;
|
||||
|
||||
/* Get a client record */
|
||||
|
|
1043
fs/nfs/dir.c
1043
fs/nfs/dir.c
File diff suppressed because it is too large
Load Diff
|
@ -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 dentry *dentry = filp->f_path.dentry;
|
||||
unsigned pagelen;
|
||||
int ret = -EINVAL;
|
||||
int ret = VM_FAULT_NOPAGE;
|
||||
struct address_space *mapping;
|
||||
|
||||
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)
|
|||
if (mapping != dentry->d_inode->i_mapping)
|
||||
goto out_unlock;
|
||||
|
||||
ret = 0;
|
||||
pagelen = nfs_page_length(page);
|
||||
if (pagelen == 0)
|
||||
goto out_unlock;
|
||||
|
||||
ret = nfs_flush_incompatible(filp, page);
|
||||
if (ret != 0)
|
||||
goto out_unlock;
|
||||
ret = VM_FAULT_LOCKED;
|
||||
if (nfs_flush_incompatible(filp, page) == 0 &&
|
||||
nfs_updatepage(filp, page, 0, pagelen) == 0)
|
||||
goto out;
|
||||
|
||||
ret = nfs_updatepage(filp, page, 0, pagelen);
|
||||
ret = VM_FAULT_SIGBUS;
|
||||
out_unlock:
|
||||
if (!ret)
|
||||
return VM_FAULT_LOCKED;
|
||||
unlock_page(page);
|
||||
return VM_FAULT_SIGBUS;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
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,
|
|||
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;
|
||||
int status = 0;
|
||||
|
@ -699,7 +699,7 @@ static int do_getlk(struct file *filp, int cmd, struct file_lock *fl)
|
|||
if (nfs_have_delegation(inode, FMODE_READ))
|
||||
goto out_noconflict;
|
||||
|
||||
if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)
|
||||
if (is_local)
|
||||
goto out_noconflict;
|
||||
|
||||
status = NFS_PROTO(inode)->lock(filp, cmd, fl);
|
||||
|
@ -726,7 +726,8 @@ static int do_vfs_lock(struct file *file, struct file_lock *fl)
|
|||
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;
|
||||
int status;
|
||||
|
@ -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
|
||||
* 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);
|
||||
else
|
||||
status = do_vfs_lock(filp, fl);
|
||||
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;
|
||||
int status;
|
||||
|
@ -762,20 +772,31 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
|
|||
if (status != 0)
|
||||
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);
|
||||
else
|
||||
status = do_vfs_lock(filp, fl);
|
||||
if (status < 0)
|
||||
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.
|
||||
*/
|
||||
nfs_sync_mapping(filp->f_mapping);
|
||||
if (!nfs_have_delegation(inode, FMODE_READ))
|
||||
nfs_zap_caches(inode);
|
||||
if (!nfs_have_delegation(inode, FMODE_READ)) {
|
||||
if (is_time_granular(&NFS_SERVER(inode)->time_delta))
|
||||
__nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
||||
else
|
||||
nfs_zap_caches(inode);
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
@ -787,6 +808,7 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
|
|||
{
|
||||
struct inode *inode = filp->f_mapping->host;
|
||||
int ret = -ENOLCK;
|
||||
int is_local = 0;
|
||||
|
||||
dprintk("NFS: lock(%s/%s, t=%x, fl=%x, r=%lld:%lld)\n",
|
||||
filp->f_path.dentry->d_parent->d_name.name,
|
||||
|
@ -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)
|
||||
goto out_err;
|
||||
|
||||
if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FCNTL)
|
||||
is_local = 1;
|
||||
|
||||
if (NFS_PROTO(inode)->lock_check_bounds != NULL) {
|
||||
ret = NFS_PROTO(inode)->lock_check_bounds(fl);
|
||||
if (ret < 0)
|
||||
|
@ -807,11 +832,11 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
|
|||
}
|
||||
|
||||
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)
|
||||
ret = do_unlk(filp, cmd, fl);
|
||||
ret = do_unlk(filp, cmd, fl, is_local);
|
||||
else
|
||||
ret = do_setlk(filp, cmd, fl);
|
||||
ret = do_setlk(filp, cmd, fl, is_local);
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
|
@ -821,6 +846,9 @@ out_err:
|
|||
*/
|
||||
static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
|
||||
{
|
||||
struct inode *inode = filp->f_mapping->host;
|
||||
int is_local = 0;
|
||||
|
||||
dprintk("NFS: flock(%s/%s, t=%x, fl=%x)\n",
|
||||
filp->f_path.dentry->d_parent->d_name.name,
|
||||
filp->f_path.dentry->d_name.name,
|
||||
|
@ -829,14 +857,17 @@ static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
|
|||
if (!(fl->fl_flags & FL_FLOCK))
|
||||
return -ENOLCK;
|
||||
|
||||
if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FLOCK)
|
||||
is_local = 1;
|
||||
|
||||
/* We're simulating flock() locks using posix locks on the server */
|
||||
fl->fl_owner = (fl_owner_t)filp;
|
||||
fl->fl_start = 0;
|
||||
fl->fl_end = OFFSET_MAX;
|
||||
|
||||
if (fl->fl_type == F_UNLCK)
|
||||
return do_unlk(filp, cmd, fl);
|
||||
return do_setlk(filp, cmd, fl);
|
||||
return do_unlk(filp, cmd, fl, is_local);
|
||||
return do_setlk(filp, cmd, fl, is_local);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
211
fs/nfs/idmap.c
211
fs/nfs/idmap.c
|
@ -34,6 +34,212 @@
|
|||
* 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/mutex.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
|
|||
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;
|
||||
|
||||
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;
|
||||
|
||||
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)
|
|||
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
|
||||
* instead of inode number.
|
||||
|
@ -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)) {
|
||||
inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
|
||||
inode->i_fop = &nfs_dir_operations;
|
||||
if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS)
|
||||
&& fattr->size <= NFS_LIMIT_READDIRPLUS)
|
||||
if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS))
|
||||
set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
|
||||
/* Deal with crossing mountpoints */
|
||||
if ((fattr->valid & NFS_ATTR_FATTR_FSID)
|
||||
|
@ -623,7 +619,7 @@ void nfs_close_context(struct nfs_open_context *ctx, int is_sync)
|
|||
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;
|
||||
|
||||
|
@ -633,11 +629,13 @@ static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct
|
|||
path_get(&ctx->path);
|
||||
ctx->cred = get_rpccred(cred);
|
||||
ctx->state = NULL;
|
||||
ctx->mode = f_mode;
|
||||
ctx->flags = 0;
|
||||
ctx->error = 0;
|
||||
ctx->dir_cookie = 0;
|
||||
nfs_init_lock_context(&ctx->lock_context);
|
||||
ctx->lock_context.open_context = ctx;
|
||||
INIT_LIST_HEAD(&ctx->list);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
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;
|
||||
list_del(&ctx->list);
|
||||
spin_unlock(&inode->i_lock);
|
||||
NFS_PROTO(inode)->close_context(ctx, is_sync);
|
||||
if (inode != NULL)
|
||||
NFS_PROTO(inode)->close_context(ctx, is_sync);
|
||||
if (ctx->cred != NULL)
|
||||
put_rpccred(ctx->cred);
|
||||
path_put(&ctx->path);
|
||||
|
@ -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
|
||||
* 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 nfs_inode *nfsi = NFS_I(inode);
|
||||
|
@ -730,11 +732,10 @@ int nfs_open(struct inode *inode, struct file *filp)
|
|||
cred = rpc_lookup_cred();
|
||||
if (IS_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);
|
||||
if (ctx == NULL)
|
||||
return -ENOMEM;
|
||||
ctx->mode = filp->f_mode;
|
||||
nfs_file_set_open_context(filp, ctx);
|
||||
put_nfs_open_context(ctx);
|
||||
nfs_fscache_set_inode_cookie(inode, filp);
|
||||
|
@ -1493,7 +1494,7 @@ static int nfsiod_start(void)
|
|||
{
|
||||
struct workqueue_struct *wq;
|
||||
dprintk("RPC: creating workqueue nfsiod\n");
|
||||
wq = create_singlethread_workqueue("nfsiod");
|
||||
wq = alloc_workqueue("nfsiod", WQ_RESCUER, 0);
|
||||
if (wq == NULL)
|
||||
return -ENOMEM;
|
||||
nfsiod_workqueue = wq;
|
||||
|
@ -1521,6 +1522,10 @@ static int __init init_nfs_fs(void)
|
|||
{
|
||||
int err;
|
||||
|
||||
err = nfs_idmap_init();
|
||||
if (err < 0)
|
||||
goto out9;
|
||||
|
||||
err = nfs_dns_resolver_init();
|
||||
if (err < 0)
|
||||
goto out8;
|
||||
|
@ -1585,6 +1590,8 @@ out6:
|
|||
out7:
|
||||
nfs_dns_resolver_destroy();
|
||||
out8:
|
||||
nfs_idmap_quit();
|
||||
out9:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1597,6 +1604,7 @@ static void __exit exit_nfs_fs(void)
|
|||
nfs_destroy_nfspagecache();
|
||||
nfs_fscache_unregister();
|
||||
nfs_dns_resolver_destroy();
|
||||
nfs_idmap_quit();
|
||||
#ifdef CONFIG_PROC_FS
|
||||
rpc_proc_unregister("nfs");
|
||||
#endif
|
||||
|
|
|
@ -62,6 +62,12 @@ struct nfs_clone_mount {
|
|||
*/
|
||||
#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
|
||||
*/
|
||||
|
@ -181,15 +187,15 @@ extern void nfs_destroy_directcache(void);
|
|||
/* nfs2xdr.c */
|
||||
extern int nfs_stat_to_errno(int);
|
||||
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 */
|
||||
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 */
|
||||
#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
|
||||
#ifdef CONFIG_NFS_V4_1
|
||||
extern const u32 nfs41_maxread_overhead;
|
||||
|
|
|
@ -436,7 +436,7 @@ static int decode_auth_flavors(struct xdr_stream *xdr, struct mountres *res)
|
|||
|
||||
for (i = 0; i < entries; i++) {
|
||||
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;
|
||||
|
||||
|
|
107
fs/nfs/nfs2xdr.c
107
fs/nfs/nfs2xdr.c
|
@ -337,10 +337,10 @@ nfs_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs_createargs *args)
|
|||
static int
|
||||
nfs_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args)
|
||||
{
|
||||
p = xdr_encode_fhandle(p, args->fromfh);
|
||||
p = xdr_encode_array(p, args->fromname, args->fromlen);
|
||||
p = xdr_encode_fhandle(p, args->tofh);
|
||||
p = xdr_encode_array(p, args->toname, args->tolen);
|
||||
p = xdr_encode_fhandle(p, args->old_dir);
|
||||
p = xdr_encode_array(p, args->old_name->name, args->old_name->len);
|
||||
p = xdr_encode_fhandle(p, args->new_dir);
|
||||
p = xdr_encode_array(p, args->new_name->name, args->new_name->len);
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
@ -423,9 +423,7 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
|
|||
struct page **page;
|
||||
size_t hdrlen;
|
||||
unsigned int pglen, recvd;
|
||||
u32 len;
|
||||
int status, nr = 0;
|
||||
__be32 *end, *entry, *kaddr;
|
||||
|
||||
if ((status = ntohl(*p++)))
|
||||
return nfs_stat_to_errno(status);
|
||||
|
@ -445,80 +443,59 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
|
|||
if (pglen > recvd)
|
||||
pglen = recvd;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
__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++) {
|
||||
if (!*p)
|
||||
__be32 *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);
|
||||
entry->eof = 1;
|
||||
return ERR_PTR(-EBADCOOKIE);
|
||||
}
|
||||
|
||||
p = xdr_inline_decode(xdr, 8);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
|
||||
entry->ino = 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;
|
||||
p += XDR_QUADLEN(entry->len);
|
||||
entry->prev_cookie = entry->cookie;
|
||||
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;
|
||||
|
||||
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)
|
|||
struct kvec *iov = rcvbuf->head;
|
||||
size_t hdrlen;
|
||||
u32 len, recvd;
|
||||
char *kaddr;
|
||||
int status;
|
||||
|
||||
if ((status = ntohl(*p++)))
|
||||
|
@ -623,10 +599,7 @@ nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
/* NULL terminate the string we got */
|
||||
kaddr = (char *)kmap_atomic(rcvbuf->pages[0], KM_USER0);
|
||||
kaddr[len+rcvbuf->page_base] = '\0';
|
||||
kunmap_atomic(kaddr, KM_USER0);
|
||||
xdr_terminate_string(rcvbuf, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -313,7 +313,7 @@ static void nfs3_free_createdata(struct nfs3_createdata *data)
|
|||
*/
|
||||
static int
|
||||
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;
|
||||
mode_t mode = sattr->ia_mode;
|
||||
|
@ -438,19 +438,38 @@ nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir)
|
|||
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
|
||||
nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name,
|
||||
struct inode *new_dir, struct qstr *new_name)
|
||||
{
|
||||
struct nfs3_renameargs arg = {
|
||||
.fromfh = NFS_FH(old_dir),
|
||||
.fromname = old_name->name,
|
||||
.fromlen = old_name->len,
|
||||
.tofh = NFS_FH(new_dir),
|
||||
.toname = new_name->name,
|
||||
.tolen = new_name->len
|
||||
struct nfs_renameargs arg = {
|
||||
.old_dir = NFS_FH(old_dir),
|
||||
.old_name = old_name,
|
||||
.new_dir = NFS_FH(new_dir),
|
||||
.new_name = new_name,
|
||||
};
|
||||
struct nfs3_renameres res;
|
||||
struct nfs_renameres res;
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs3_procedures[NFS3PROC_RENAME],
|
||||
.rpc_argp = &arg,
|
||||
|
@ -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);
|
||||
|
||||
res.fromattr = nfs_alloc_fattr();
|
||||
res.toattr = nfs_alloc_fattr();
|
||||
if (res.fromattr == NULL || res.toattr == NULL)
|
||||
res.old_fattr = nfs_alloc_fattr();
|
||||
res.new_fattr = nfs_alloc_fattr();
|
||||
if (res.old_fattr == NULL || res.new_fattr == NULL)
|
||||
goto out;
|
||||
|
||||
status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0);
|
||||
nfs_post_op_update_inode(old_dir, res.fromattr);
|
||||
nfs_post_op_update_inode(new_dir, res.toattr);
|
||||
nfs_post_op_update_inode(old_dir, res.old_fattr);
|
||||
nfs_post_op_update_inode(new_dir, res.new_fattr);
|
||||
out:
|
||||
nfs_free_fattr(res.toattr);
|
||||
nfs_free_fattr(res.fromattr);
|
||||
nfs_free_fattr(res.old_fattr);
|
||||
nfs_free_fattr(res.new_fattr);
|
||||
dprintk("NFS reply rename: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
@ -611,7 +630,7 @@ out:
|
|||
*/
|
||||
static int
|
||||
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;
|
||||
__be32 *verf = NFS_COOKIEVERF(dir);
|
||||
|
@ -621,7 +640,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
|
|||
.verf = {verf[0], verf[1]},
|
||||
.plus = plus,
|
||||
.count = count,
|
||||
.pages = &page
|
||||
.pages = pages
|
||||
};
|
||||
struct nfs3_readdirres res = {
|
||||
.verf = verf,
|
||||
|
@ -652,7 +671,8 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
|
|||
|
||||
nfs_free_fattr(res.dir_attr);
|
||||
out:
|
||||
dprintk("NFS reply readdir: %d\n", status);
|
||||
dprintk("NFS reply readdir%s: %d\n",
|
||||
plus? "plus" : "", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -722,7 +742,7 @@ nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
|
|||
dprintk("NFS call fsstat\n");
|
||||
nfs_fattr_init(stat->fattr);
|
||||
status = rpc_call_sync(server->client, &msg, 0);
|
||||
dprintk("NFS reply statfs: %d\n", status);
|
||||
dprintk("NFS reply fsstat: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -844,6 +864,8 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
|
|||
.unlink_setup = nfs3_proc_unlink_setup,
|
||||
.unlink_done = nfs3_proc_unlink_done,
|
||||
.rename = nfs3_proc_rename,
|
||||
.rename_setup = nfs3_proc_rename_setup,
|
||||
.rename_done = nfs3_proc_rename_done,
|
||||
.link = nfs3_proc_link,
|
||||
.symlink = nfs3_proc_symlink,
|
||||
.mkdir = nfs3_proc_mkdir,
|
||||
|
|
196
fs/nfs/nfs3xdr.c
196
fs/nfs/nfs3xdr.c
|
@ -100,6 +100,13 @@ static const umode_t nfs_type2fmt[] = {
|
|||
[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
|
||||
*/
|
||||
|
@ -119,6 +126,29 @@ xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
|
|||
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.
|
||||
*/
|
||||
|
@ -240,6 +270,26 @@ xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
|
|||
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 *
|
||||
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)
|
|||
* Encode RENAME arguments
|
||||
*/
|
||||
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_array(p, args->fromname, args->fromlen);
|
||||
p = xdr_encode_fhandle(p, args->tofh);
|
||||
p = xdr_encode_array(p, args->toname, args->tolen);
|
||||
p = xdr_encode_fhandle(p, args->old_dir);
|
||||
p = xdr_encode_array(p, args->old_name->name, args->old_name->len);
|
||||
p = xdr_encode_fhandle(p, args->new_dir);
|
||||
p = xdr_encode_array(p, args->new_name->name, args->new_name->len);
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
@ -504,9 +554,8 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res
|
|||
struct kvec *iov = rcvbuf->head;
|
||||
struct page **page;
|
||||
size_t hdrlen;
|
||||
u32 len, recvd, pglen;
|
||||
u32 recvd, pglen;
|
||||
int status, nr = 0;
|
||||
__be32 *entry, *end, *kaddr;
|
||||
|
||||
status = ntohl(*p++);
|
||||
/* Decode post_op_attrs */
|
||||
|
@ -536,99 +585,38 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res
|
|||
if (pglen > recvd)
|
||||
pglen = recvd;
|
||||
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;
|
||||
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 *
|
||||
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;
|
||||
|
||||
if (!*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);
|
||||
entry->eof = 1;
|
||||
return ERR_PTR(-EBADCOOKIE);
|
||||
}
|
||||
|
||||
p = xdr_inline_decode(xdr, 12);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
p = xdr_decode_hyper(p, &entry->ino);
|
||||
entry->len = ntohl(*p++);
|
||||
|
||||
p = xdr_inline_decode(xdr, entry->len + 8);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
entry->name = (const char *) p;
|
||||
p += XDR_QUADLEN(entry->len);
|
||||
entry->prev_cookie = entry->cookie;
|
||||
|
@ -636,10 +624,17 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
|
|||
|
||||
if (plus) {
|
||||
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: */
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
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 */
|
||||
if (p == NULL) {
|
||||
dprintk("NFS: FH truncated\n");
|
||||
|
@ -650,8 +645,18 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
|
|||
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;
|
||||
|
||||
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)
|
|||
struct kvec *iov = rcvbuf->head;
|
||||
size_t hdrlen;
|
||||
u32 len, recvd;
|
||||
char *kaddr;
|
||||
int status;
|
||||
|
||||
status = ntohl(*p++);
|
||||
|
@ -857,10 +861,7 @@ nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
/* NULL terminate the string we got */
|
||||
kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
|
||||
kaddr[len+rcvbuf->page_base] = '\0';
|
||||
kunmap_atomic(kaddr, KM_USER0);
|
||||
xdr_terminate_string(rcvbuf, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -970,14 +971,14 @@ nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
|
|||
* Decode RENAME reply
|
||||
*/
|
||||
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;
|
||||
|
||||
if ((status = ntohl(*p++)) != 0)
|
||||
status = nfs_stat_to_errno(status);
|
||||
p = xdr_decode_wcc_data(p, res->fromattr);
|
||||
p = xdr_decode_wcc_data(p, res->toattr);
|
||||
p = xdr_decode_wcc_data(p, res->old_fattr);
|
||||
p = xdr_decode_wcc_data(p, res->new_fattr);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -1043,8 +1044,9 @@ nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
|
|||
res->wtmult = ntohl(*p++);
|
||||
res->dtpref = ntohl(*p++);
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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 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 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_proc_fs_locations(struct inode *dir, const struct qstr *name,
|
||||
struct nfs4_fs_locations *fs_locations, struct page *page);
|
||||
|
@ -333,7 +331,7 @@ extern void nfs_free_seqid(struct nfs_seqid *seqid);
|
|||
extern const nfs4_stateid zero_stateid;
|
||||
|
||||
/* 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[];
|
||||
|
||||
struct nfs4_mount_data;
|
||||
|
|
|
@ -129,7 +129,7 @@ const u32 nfs4_fsinfo_bitmap[2] = { FATTR4_WORD0_MAXFILESIZE
|
|||
| FATTR4_WORD0_MAXREAD
|
||||
| FATTR4_WORD0_MAXWRITE
|
||||
| FATTR4_WORD0_LEASE_TIME,
|
||||
0
|
||||
FATTR4_WORD1_TIME_DELTA
|
||||
};
|
||||
|
||||
const u32 nfs4_fs_locations_bitmap[2] = {
|
||||
|
@ -255,9 +255,6 @@ static int nfs4_handle_exception(const struct nfs_server *server, int errorcode,
|
|||
nfs4_state_mark_reclaim_nograce(clp, state);
|
||||
goto do_state_recovery;
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
if (state == NULL)
|
||||
break;
|
||||
nfs4_state_mark_reclaim_reboot(clp, state);
|
||||
case -NFS4ERR_STALE_CLIENTID:
|
||||
case -NFS4ERR_EXPIRED:
|
||||
goto do_state_recovery;
|
||||
|
@ -334,10 +331,12 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp
|
|||
* Must be called while holding tbl->slot_tbl_lock
|
||||
*/
|
||||
static void
|
||||
nfs4_free_slot(struct nfs4_slot_table *tbl, u8 free_slotid)
|
||||
nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *free_slot)
|
||||
{
|
||||
int free_slotid = free_slot - tbl->slots;
|
||||
int slotid = free_slotid;
|
||||
|
||||
BUG_ON(slotid < 0 || slotid >= NFS4_MAX_SLOT_TABLE);
|
||||
/* clear used bit in bitmap */
|
||||
__clear_bit(slotid, tbl->used_slots);
|
||||
|
||||
|
@ -379,7 +378,7 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
|
|||
struct nfs4_slot_table *tbl;
|
||||
|
||||
tbl = &res->sr_session->fc_slot_table;
|
||||
if (res->sr_slotid == NFS4_MAX_SLOT_TABLE) {
|
||||
if (!res->sr_slot) {
|
||||
/* just wake up the next guy waiting since
|
||||
* we may have not consumed a slot after all */
|
||||
dprintk("%s: No slot\n", __func__);
|
||||
|
@ -387,17 +386,15 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
|
|||
}
|
||||
|
||||
spin_lock(&tbl->slot_tbl_lock);
|
||||
nfs4_free_slot(tbl, res->sr_slotid);
|
||||
nfs4_free_slot(tbl, res->sr_slot);
|
||||
nfs41_check_drain_session_complete(res->sr_session);
|
||||
spin_unlock(&tbl->slot_tbl_lock);
|
||||
res->sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
res->sr_slot = NULL;
|
||||
}
|
||||
|
||||
static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
|
||||
{
|
||||
unsigned long timestamp;
|
||||
struct nfs4_slot_table *tbl;
|
||||
struct nfs4_slot *slot;
|
||||
struct nfs_client *clp;
|
||||
|
||||
/*
|
||||
|
@ -410,17 +407,14 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *
|
|||
res->sr_status = NFS_OK;
|
||||
|
||||
/* -ERESTARTSYS can result in skipping nfs41_sequence_setup */
|
||||
if (res->sr_slotid == NFS4_MAX_SLOT_TABLE)
|
||||
if (!res->sr_slot)
|
||||
goto out;
|
||||
|
||||
tbl = &res->sr_session->fc_slot_table;
|
||||
slot = tbl->slots + res->sr_slotid;
|
||||
|
||||
/* Check the SEQUENCE operation status */
|
||||
switch (res->sr_status) {
|
||||
case 0:
|
||||
/* Update the slot's sequence and clientid lease timer */
|
||||
++slot->seq_nr;
|
||||
++res->sr_slot->seq_nr;
|
||||
timestamp = res->sr_renewal_time;
|
||||
clp = res->sr_session->clp;
|
||||
do_renew_lease(clp, timestamp);
|
||||
|
@ -433,12 +427,14 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *
|
|||
* returned NFS4ERR_DELAY as per Section 2.10.6.2
|
||||
* of RFC5661.
|
||||
*/
|
||||
dprintk("%s: slot=%d seq=%d: Operation in progress\n",
|
||||
__func__, res->sr_slotid, slot->seq_nr);
|
||||
dprintk("%s: slot=%ld seq=%d: Operation in progress\n",
|
||||
__func__,
|
||||
res->sr_slot - res->sr_session->fc_slot_table.slots,
|
||||
res->sr_slot->seq_nr);
|
||||
goto out_retry;
|
||||
default:
|
||||
/* Just update the slot sequence no. */
|
||||
++slot->seq_nr;
|
||||
++res->sr_slot->seq_nr;
|
||||
}
|
||||
out:
|
||||
/* The session may be reset by one of the error handlers. */
|
||||
|
@ -505,10 +501,9 @@ static int nfs41_setup_sequence(struct nfs4_session *session,
|
|||
|
||||
dprintk("--> %s\n", __func__);
|
||||
/* slot already allocated? */
|
||||
if (res->sr_slotid != NFS4_MAX_SLOT_TABLE)
|
||||
if (res->sr_slot != NULL)
|
||||
return 0;
|
||||
|
||||
res->sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
tbl = &session->fc_slot_table;
|
||||
|
||||
spin_lock(&tbl->slot_tbl_lock);
|
||||
|
@ -550,7 +545,7 @@ static int nfs41_setup_sequence(struct nfs4_session *session,
|
|||
dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr);
|
||||
|
||||
res->sr_session = session;
|
||||
res->sr_slotid = slotid;
|
||||
res->sr_slot = slot;
|
||||
res->sr_renewal_time = jiffies;
|
||||
res->sr_status_flags = 0;
|
||||
/*
|
||||
|
@ -576,8 +571,9 @@ int nfs4_setup_sequence(const struct nfs_server *server,
|
|||
goto out;
|
||||
}
|
||||
|
||||
dprintk("--> %s clp %p session %p sr_slotid %d\n",
|
||||
__func__, session->clp, session, res->sr_slotid);
|
||||
dprintk("--> %s clp %p session %p sr_slot %ld\n",
|
||||
__func__, session->clp, session, res->sr_slot ?
|
||||
res->sr_slot - session->fc_slot_table.slots : -1);
|
||||
|
||||
ret = nfs41_setup_sequence(session, args, res, cache_reply,
|
||||
task);
|
||||
|
@ -650,7 +646,7 @@ static int nfs4_call_sync_sequence(struct nfs_server *server,
|
|||
.callback_data = &data
|
||||
};
|
||||
|
||||
res->sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
res->sr_slot = NULL;
|
||||
if (privileged)
|
||||
task_setup.callback_ops = &nfs41_call_priv_sync_ops;
|
||||
task = rpc_run_task(&task_setup);
|
||||
|
@ -735,7 +731,6 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)
|
|||
p->o_res.server = p->o_arg.server;
|
||||
nfs_fattr_init(&p->f_attr);
|
||||
nfs_fattr_init(&p->dir_attr);
|
||||
p->o_res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
}
|
||||
|
||||
static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
|
||||
|
@ -1120,6 +1115,7 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
|
|||
clear_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
smp_rmb();
|
||||
if (state->n_rdwr != 0) {
|
||||
clear_bit(NFS_O_RDWR_STATE, &state->flags);
|
||||
ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
@ -1127,6 +1123,7 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
|
|||
return -ESTALE;
|
||||
}
|
||||
if (state->n_wronly != 0) {
|
||||
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
|
||||
ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
@ -1134,6 +1131,7 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
|
|||
return -ESTALE;
|
||||
}
|
||||
if (state->n_rdonly != 0) {
|
||||
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
|
||||
ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
@ -1188,7 +1186,7 @@ static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
|
|||
int err;
|
||||
do {
|
||||
err = _nfs4_do_open_reclaim(ctx, state);
|
||||
if (err != -NFS4ERR_DELAY && err != -EKEYEXPIRED)
|
||||
if (err != -NFS4ERR_DELAY)
|
||||
break;
|
||||
nfs4_handle_exception(server, err, &exception);
|
||||
} while (exception.retry);
|
||||
|
@ -1258,6 +1256,13 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
|
|||
case -NFS4ERR_ADMIN_REVOKED:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
nfs4_state_mark_reclaim_nograce(server->nfs_client, state);
|
||||
case -EKEYEXPIRED:
|
||||
/*
|
||||
* User RPCSEC_GSS context has expired.
|
||||
* We cannot recover this stateid now, so
|
||||
* skip it and allow recovery thread to
|
||||
* proceed.
|
||||
*/
|
||||
case -ENOMEM:
|
||||
err = 0;
|
||||
goto out;
|
||||
|
@ -1605,7 +1610,6 @@ static int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state
|
|||
goto out;
|
||||
case -NFS4ERR_GRACE:
|
||||
case -NFS4ERR_DELAY:
|
||||
case -EKEYEXPIRED:
|
||||
nfs4_handle_exception(server, err, &exception);
|
||||
err = 0;
|
||||
}
|
||||
|
@ -1975,7 +1979,6 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, i
|
|||
calldata->res.fattr = &calldata->fattr;
|
||||
calldata->res.seqid = calldata->arg.seqid;
|
||||
calldata->res.server = server;
|
||||
calldata->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
path_get(path);
|
||||
calldata->path = *path;
|
||||
|
||||
|
@ -1998,120 +2001,17 @@ out:
|
|||
return status;
|
||||
}
|
||||
|
||||
static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state, fmode_t fmode)
|
||||
static struct inode *
|
||||
nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr)
|
||||
{
|
||||
struct file *filp;
|
||||
int ret;
|
||||
|
||||
/* If the open_intent is for execute, we have an extra check to make */
|
||||
if (fmode & FMODE_EXEC) {
|
||||
ret = nfs_may_open(state->inode,
|
||||
state->owner->so_cred,
|
||||
nd->intent.open.flags);
|
||||
if (ret < 0)
|
||||
goto out_close;
|
||||
}
|
||||
filp = lookup_instantiate_filp(nd, path->dentry, NULL);
|
||||
if (!IS_ERR(filp)) {
|
||||
struct nfs_open_context *ctx;
|
||||
ctx = nfs_file_open_context(filp);
|
||||
ctx->state = state;
|
||||
return 0;
|
||||
}
|
||||
ret = PTR_ERR(filp);
|
||||
out_close:
|
||||
nfs4_close_sync(path, state, fmode & (FMODE_READ|FMODE_WRITE));
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct dentry *
|
||||
nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct path path = {
|
||||
.mnt = nd->path.mnt,
|
||||
.dentry = dentry,
|
||||
};
|
||||
struct dentry *parent;
|
||||
struct iattr attr;
|
||||
struct rpc_cred *cred;
|
||||
struct nfs4_state *state;
|
||||
struct dentry *res;
|
||||
int open_flags = nd->intent.open.flags;
|
||||
fmode_t fmode = open_flags & (FMODE_READ | FMODE_WRITE | FMODE_EXEC);
|
||||
|
||||
if (nd->flags & LOOKUP_CREATE) {
|
||||
attr.ia_mode = nd->intent.open.create_mode;
|
||||
attr.ia_valid = ATTR_MODE;
|
||||
if (!IS_POSIXACL(dir))
|
||||
attr.ia_mode &= ~current_umask();
|
||||
} else {
|
||||
open_flags &= ~O_EXCL;
|
||||
attr.ia_valid = 0;
|
||||
BUG_ON(open_flags & O_CREAT);
|
||||
}
|
||||
|
||||
cred = rpc_lookup_cred();
|
||||
if (IS_ERR(cred))
|
||||
return (struct dentry *)cred;
|
||||
parent = dentry->d_parent;
|
||||
/* Protect against concurrent sillydeletes */
|
||||
nfs_block_sillyrename(parent);
|
||||
state = nfs4_do_open(dir, &path, fmode, open_flags, &attr, cred);
|
||||
put_rpccred(cred);
|
||||
if (IS_ERR(state)) {
|
||||
if (PTR_ERR(state) == -ENOENT) {
|
||||
d_add(dentry, NULL);
|
||||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
||||
}
|
||||
nfs_unblock_sillyrename(parent);
|
||||
return (struct dentry *)state;
|
||||
}
|
||||
res = d_add_unique(dentry, igrab(state->inode));
|
||||
if (res != NULL)
|
||||
path.dentry = res;
|
||||
nfs_set_verifier(path.dentry, nfs_save_change_attribute(dir));
|
||||
nfs_unblock_sillyrename(parent);
|
||||
nfs4_intent_set_file(nd, &path, state, fmode);
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, struct nameidata *nd)
|
||||
{
|
||||
struct path path = {
|
||||
.mnt = nd->path.mnt,
|
||||
.dentry = dentry,
|
||||
};
|
||||
struct rpc_cred *cred;
|
||||
struct nfs4_state *state;
|
||||
fmode_t fmode = openflags & (FMODE_READ | FMODE_WRITE);
|
||||
|
||||
cred = rpc_lookup_cred();
|
||||
if (IS_ERR(cred))
|
||||
return PTR_ERR(cred);
|
||||
state = nfs4_do_open(dir, &path, fmode, openflags, NULL, cred);
|
||||
put_rpccred(cred);
|
||||
if (IS_ERR(state)) {
|
||||
switch (PTR_ERR(state)) {
|
||||
case -EPERM:
|
||||
case -EACCES:
|
||||
case -EDQUOT:
|
||||
case -ENOSPC:
|
||||
case -EROFS:
|
||||
return PTR_ERR(state);
|
||||
default:
|
||||
goto out_drop;
|
||||
}
|
||||
}
|
||||
if (state->inode == dentry->d_inode) {
|
||||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
||||
nfs4_intent_set_file(nd, &path, state, fmode);
|
||||
return 1;
|
||||
}
|
||||
nfs4_close_sync(&path, state, fmode);
|
||||
out_drop:
|
||||
d_drop(dentry);
|
||||
return 0;
|
||||
state = nfs4_do_open(dir, &ctx->path, ctx->mode, open_flags, attr, ctx->cred);
|
||||
if (IS_ERR(state))
|
||||
return ERR_CAST(state);
|
||||
ctx->state = state;
|
||||
return igrab(state->inode);
|
||||
}
|
||||
|
||||
static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
|
||||
|
@ -2568,36 +2468,34 @@ static int nfs4_proc_readlink(struct inode *inode, struct page *page,
|
|||
|
||||
static int
|
||||
nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
|
||||
int flags, struct nameidata *nd)
|
||||
int flags, struct nfs_open_context *ctx)
|
||||
{
|
||||
struct path path = {
|
||||
.mnt = nd->path.mnt,
|
||||
struct path my_path = {
|
||||
.dentry = dentry,
|
||||
};
|
||||
struct path *path = &my_path;
|
||||
struct nfs4_state *state;
|
||||
struct rpc_cred *cred;
|
||||
fmode_t fmode = flags & (FMODE_READ | FMODE_WRITE);
|
||||
struct rpc_cred *cred = NULL;
|
||||
fmode_t fmode = 0;
|
||||
int status = 0;
|
||||
|
||||
cred = rpc_lookup_cred();
|
||||
if (IS_ERR(cred)) {
|
||||
status = PTR_ERR(cred);
|
||||
goto out;
|
||||
if (ctx != NULL) {
|
||||
cred = ctx->cred;
|
||||
path = &ctx->path;
|
||||
fmode = ctx->mode;
|
||||
}
|
||||
state = nfs4_do_open(dir, &path, fmode, flags, sattr, cred);
|
||||
state = nfs4_do_open(dir, path, fmode, flags, sattr, cred);
|
||||
d_drop(dentry);
|
||||
if (IS_ERR(state)) {
|
||||
status = PTR_ERR(state);
|
||||
goto out_putcred;
|
||||
goto out;
|
||||
}
|
||||
d_add(dentry, igrab(state->inode));
|
||||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
||||
if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0)
|
||||
status = nfs4_intent_set_file(nd, &path, state, fmode);
|
||||
if (ctx != NULL)
|
||||
ctx->state = state;
|
||||
else
|
||||
nfs4_close_sync(&path, state, fmode);
|
||||
out_putcred:
|
||||
put_rpccred(cred);
|
||||
nfs4_close_sync(path, state, fmode);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
@ -2655,6 +2553,7 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir)
|
|||
|
||||
args->bitmask = server->cache_consistency_bitmask;
|
||||
res->server = server;
|
||||
res->seq_res.sr_slot = NULL;
|
||||
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
|
||||
}
|
||||
|
||||
|
@ -2671,18 +2570,46 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void nfs4_proc_rename_setup(struct rpc_message *msg, struct inode *dir)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(dir);
|
||||
struct nfs_renameargs *arg = msg->rpc_argp;
|
||||
struct nfs_renameres *res = msg->rpc_resp;
|
||||
|
||||
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME];
|
||||
arg->bitmask = server->attr_bitmask;
|
||||
res->server = server;
|
||||
}
|
||||
|
||||
static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir,
|
||||
struct inode *new_dir)
|
||||
{
|
||||
struct nfs_renameres *res = task->tk_msg.rpc_resp;
|
||||
|
||||
if (!nfs4_sequence_done(task, &res->seq_res))
|
||||
return 0;
|
||||
if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
|
||||
return 0;
|
||||
|
||||
update_changeattr(old_dir, &res->old_cinfo);
|
||||
nfs_post_op_update_inode(old_dir, res->old_fattr);
|
||||
update_changeattr(new_dir, &res->new_cinfo);
|
||||
nfs_post_op_update_inode(new_dir, res->new_fattr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
|
||||
struct inode *new_dir, struct qstr *new_name)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(old_dir);
|
||||
struct nfs4_rename_arg arg = {
|
||||
struct nfs_renameargs arg = {
|
||||
.old_dir = NFS_FH(old_dir),
|
||||
.new_dir = NFS_FH(new_dir),
|
||||
.old_name = old_name,
|
||||
.new_name = new_name,
|
||||
.bitmask = server->attr_bitmask,
|
||||
};
|
||||
struct nfs4_rename_res res = {
|
||||
struct nfs_renameres res = {
|
||||
.server = server,
|
||||
};
|
||||
struct rpc_message msg = {
|
||||
|
@ -2896,15 +2823,16 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
|
|||
}
|
||||
|
||||
static int _nfs4_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 nfs4_readdir_arg args = {
|
||||
.fh = NFS_FH(dir),
|
||||
.pages = &page,
|
||||
.pages = pages,
|
||||
.pgbase = 0,
|
||||
.count = count,
|
||||
.bitmask = NFS_SERVER(dentry->d_inode)->attr_bitmask,
|
||||
.plus = plus,
|
||||
};
|
||||
struct nfs4_readdir_res res;
|
||||
struct rpc_message msg = {
|
||||
|
@ -2932,14 +2860,14 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
|
|||
}
|
||||
|
||||
static int nfs4_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 nfs4_exception exception = { };
|
||||
int err;
|
||||
do {
|
||||
err = nfs4_handle_exception(NFS_SERVER(dentry->d_inode),
|
||||
_nfs4_proc_readdir(dentry, cred, cookie,
|
||||
page, count, plus),
|
||||
pages, count, plus),
|
||||
&exception);
|
||||
} while (exception.retry);
|
||||
return err;
|
||||
|
@ -3490,9 +3418,6 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
|
|||
nfs4_state_mark_reclaim_nograce(clp, state);
|
||||
goto do_state_recovery;
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
if (state == NULL)
|
||||
break;
|
||||
nfs4_state_mark_reclaim_reboot(clp, state);
|
||||
case -NFS4ERR_STALE_CLIENTID:
|
||||
case -NFS4ERR_EXPIRED:
|
||||
goto do_state_recovery;
|
||||
|
@ -3626,7 +3551,6 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
|
|||
case -NFS4ERR_RESOURCE:
|
||||
/* The IBM lawyers misread another document! */
|
||||
case -NFS4ERR_DELAY:
|
||||
case -EKEYEXPIRED:
|
||||
err = nfs4_delay(clp->cl_rpcclient, &timeout);
|
||||
}
|
||||
} while (err == 0);
|
||||
|
@ -3721,7 +3645,6 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
|
|||
memcpy(&data->stateid, stateid, sizeof(data->stateid));
|
||||
data->res.fattr = &data->fattr;
|
||||
data->res.server = server;
|
||||
data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
nfs_fattr_init(data->res.fattr);
|
||||
data->timestamp = jiffies;
|
||||
data->rpc_status = 0;
|
||||
|
@ -3874,7 +3797,6 @@ static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl,
|
|||
p->arg.fl = &p->fl;
|
||||
p->arg.seqid = seqid;
|
||||
p->res.seqid = seqid;
|
||||
p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
p->arg.stateid = &lsp->ls_stateid;
|
||||
p->lsp = lsp;
|
||||
atomic_inc(&lsp->ls_count);
|
||||
|
@ -4054,7 +3976,6 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
|
|||
p->arg.lock_owner.clientid = server->nfs_client->cl_clientid;
|
||||
p->arg.lock_owner.id = lsp->ls_id.id;
|
||||
p->res.lock_seqid = p->arg.lock_seqid;
|
||||
p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
p->lsp = lsp;
|
||||
p->server = server;
|
||||
atomic_inc(&lsp->ls_count);
|
||||
|
@ -4241,7 +4162,7 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request
|
|||
if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
|
||||
return 0;
|
||||
err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_RECLAIM);
|
||||
if (err != -NFS4ERR_DELAY && err != -EKEYEXPIRED)
|
||||
if (err != -NFS4ERR_DELAY)
|
||||
break;
|
||||
nfs4_handle_exception(server, err, &exception);
|
||||
} while (exception.retry);
|
||||
|
@ -4266,7 +4187,6 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
|
|||
goto out;
|
||||
case -NFS4ERR_GRACE:
|
||||
case -NFS4ERR_DELAY:
|
||||
case -EKEYEXPIRED:
|
||||
nfs4_handle_exception(server, err, &exception);
|
||||
err = 0;
|
||||
}
|
||||
|
@ -4412,13 +4332,21 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
|
|||
nfs4_state_mark_reclaim_nograce(server->nfs_client, state);
|
||||
err = 0;
|
||||
goto out;
|
||||
case -EKEYEXPIRED:
|
||||
/*
|
||||
* User RPCSEC_GSS context has expired.
|
||||
* We cannot recover this stateid now, so
|
||||
* skip it and allow recovery thread to
|
||||
* proceed.
|
||||
*/
|
||||
err = 0;
|
||||
goto out;
|
||||
case -ENOMEM:
|
||||
case -NFS4ERR_DENIED:
|
||||
/* kill_proc(fl->fl_pid, SIGLOST, 1); */
|
||||
err = 0;
|
||||
goto out;
|
||||
case -NFS4ERR_DELAY:
|
||||
case -EKEYEXPIRED:
|
||||
break;
|
||||
}
|
||||
err = nfs4_handle_exception(server, err, &exception);
|
||||
|
@ -4647,7 +4575,6 @@ static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata)
|
|||
switch (task->tk_status) {
|
||||
case -NFS4ERR_DELAY:
|
||||
case -NFS4ERR_GRACE:
|
||||
case -EKEYEXPIRED:
|
||||
dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status);
|
||||
rpc_delay(task, NFS4_POLL_RETRY_MIN);
|
||||
task->tk_status = 0;
|
||||
|
@ -4687,7 +4614,6 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
|
|||
};
|
||||
int status;
|
||||
|
||||
res.lr_seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
dprintk("--> %s\n", __func__);
|
||||
task = rpc_run_task(&task_setup);
|
||||
|
||||
|
@ -5111,7 +5037,6 @@ static int nfs41_sequence_handle_errors(struct rpc_task *task, struct nfs_client
|
|||
{
|
||||
switch(task->tk_status) {
|
||||
case -NFS4ERR_DELAY:
|
||||
case -EKEYEXPIRED:
|
||||
rpc_delay(task, NFS4_POLL_RETRY_MAX);
|
||||
return -EAGAIN;
|
||||
default:
|
||||
|
@ -5180,12 +5105,11 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_
|
|||
|
||||
if (!atomic_inc_not_zero(&clp->cl_count))
|
||||
return ERR_PTR(-EIO);
|
||||
calldata = kmalloc(sizeof(*calldata), GFP_NOFS);
|
||||
calldata = kzalloc(sizeof(*calldata), GFP_NOFS);
|
||||
if (calldata == NULL) {
|
||||
nfs_put_client(clp);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
calldata->res.sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
msg.rpc_argp = &calldata->args;
|
||||
msg.rpc_resp = &calldata->res;
|
||||
calldata->clp = clp;
|
||||
|
@ -5254,7 +5178,6 @@ static int nfs41_reclaim_complete_handle_errors(struct rpc_task *task, struct nf
|
|||
case -NFS4ERR_WRONG_CRED: /* What to do here? */
|
||||
break;
|
||||
case -NFS4ERR_DELAY:
|
||||
case -EKEYEXPIRED:
|
||||
rpc_delay(task, NFS4_POLL_RETRY_MAX);
|
||||
return -EAGAIN;
|
||||
default:
|
||||
|
@ -5317,7 +5240,6 @@ static int nfs41_proc_reclaim_complete(struct nfs_client *clp)
|
|||
goto out;
|
||||
calldata->clp = clp;
|
||||
calldata->arg.one_fs = 0;
|
||||
calldata->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
|
||||
msg.rpc_argp = &calldata->arg;
|
||||
msg.rpc_resp = &calldata->res;
|
||||
|
@ -5443,6 +5365,8 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
|
|||
.unlink_setup = nfs4_proc_unlink_setup,
|
||||
.unlink_done = nfs4_proc_unlink_done,
|
||||
.rename = nfs4_proc_rename,
|
||||
.rename_setup = nfs4_proc_rename_setup,
|
||||
.rename_done = nfs4_proc_rename_done,
|
||||
.link = nfs4_proc_link,
|
||||
.symlink = nfs4_proc_symlink,
|
||||
.mkdir = nfs4_proc_mkdir,
|
||||
|
@ -5463,6 +5387,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
|
|||
.lock = nfs4_proc_lock,
|
||||
.clear_acl_cache = nfs4_zap_acl_attr,
|
||||
.close_context = nfs4_close_context,
|
||||
.open_context = nfs4_atomic_open,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include <linux/kthread.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
|
@ -1063,6 +1064,14 @@ restart:
|
|||
/* Mark the file as being 'closed' */
|
||||
state->state = 0;
|
||||
break;
|
||||
case -EKEYEXPIRED:
|
||||
/*
|
||||
* User RPCSEC_GSS context has expired.
|
||||
* We cannot recover this stateid now, so
|
||||
* skip it and allow recovery thread to
|
||||
* proceed.
|
||||
*/
|
||||
break;
|
||||
case -NFS4ERR_ADMIN_REVOKED:
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
|
@ -1138,16 +1147,14 @@ static void nfs4_reclaim_complete(struct nfs_client *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 rb_node *pos;
|
||||
struct nfs4_state *state;
|
||||
|
||||
if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
|
||||
return;
|
||||
|
||||
nfs4_reclaim_complete(clp, clp->cl_mvops->reboot_recovery_ops);
|
||||
return 0;
|
||||
|
||||
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);
|
||||
|
@ -1161,6 +1168,14 @@ static void nfs4_state_end_reclaim_reboot(struct nfs_client *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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
static void nfs4_warn_keyexpired(const char *s)
|
||||
{
|
||||
printk_ratelimited(KERN_WARNING "Error: state manager"
|
||||
" encountered RPCSEC_GSS session"
|
||||
" expired against NFSv4 server %s.\n",
|
||||
s);
|
||||
}
|
||||
|
||||
static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
|
||||
{
|
||||
switch (error) {
|
||||
|
@ -1187,7 +1210,7 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
|
|||
case -NFS4ERR_STALE_CLIENTID:
|
||||
case -NFS4ERR_LEASE_MOVED:
|
||||
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);
|
||||
break;
|
||||
case -NFS4ERR_EXPIRED:
|
||||
|
@ -1204,6 +1227,10 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
|
|||
set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
|
||||
/* Zero session reset errors */
|
||||
return 0;
|
||||
case -EKEYEXPIRED:
|
||||
/* Nothing we can do */
|
||||
nfs4_warn_keyexpired(clp->cl_hostname);
|
||||
return 0;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
@ -1414,9 +1441,10 @@ static void nfs4_set_lease_expired(struct nfs_client *clp, int status)
|
|||
case -NFS4ERR_DELAY:
|
||||
case -NFS4ERR_CLID_INUSE:
|
||||
case -EAGAIN:
|
||||
case -EKEYEXPIRED:
|
||||
break;
|
||||
|
||||
case -EKEYEXPIRED:
|
||||
nfs4_warn_keyexpired(clp->cl_hostname);
|
||||
case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
|
||||
* in nfs4_exchange_id */
|
||||
default:
|
||||
|
|
340
fs/nfs/nfs4xdr.c
340
fs/nfs/nfs4xdr.c
|
@ -816,7 +816,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
|
|||
if (iap->ia_valid & ATTR_MODE)
|
||||
len += 4;
|
||||
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) {
|
||||
dprintk("nfs: couldn't resolve uid %d to string\n",
|
||||
iap->ia_uid);
|
||||
|
@ -828,7 +828,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
|
|||
len += 4 + (XDR_QUADLEN(owner_namelen) << 2);
|
||||
}
|
||||
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) {
|
||||
dprintk("nfs: couldn't resolve gid %d to string\n",
|
||||
iap->ia_gid);
|
||||
|
@ -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)
|
||||
{
|
||||
uint32_t attrs[2] = {
|
||||
FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID,
|
||||
FATTR4_WORD1_MOUNTED_ON_FILEID,
|
||||
};
|
||||
uint32_t attrs[2] = {0, 0};
|
||||
uint32_t dircount = readdir->count >> 1;
|
||||
__be32 *p;
|
||||
|
||||
p = reserve_space(xdr, 12+NFS4_VERIFIER_SIZE+20);
|
||||
*p++ = cpu_to_be32(OP_READDIR);
|
||||
p = xdr_encode_hyper(p, readdir->cookie);
|
||||
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(readdir->count);
|
||||
*p++ = cpu_to_be32(2);
|
||||
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++ = cpu_to_be32(OP_READDIR);
|
||||
p = xdr_encode_hyper(p, readdir->cookie);
|
||||
p = xdr_encode_opaque_fixed(p, readdir->verifier.data, NFS4_VERIFIER_SIZE);
|
||||
*p++ = cpu_to_be32(dircount);
|
||||
*p++ = cpu_to_be32(readdir->count);
|
||||
*p++ = cpu_to_be32(2);
|
||||
|
||||
*p++ = cpu_to_be32(attrs[0] & readdir->bitmask[0]);
|
||||
*p = cpu_to_be32(attrs[1] & readdir->bitmask[1]);
|
||||
hdr->nops++;
|
||||
|
@ -1823,7 +1834,7 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs
|
|||
/*
|
||||
* 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 compound_hdr hdr = {
|
||||
|
@ -2676,7 +2687,10 @@ out_overflow:
|
|||
static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *bitmask)
|
||||
{
|
||||
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;
|
||||
} else
|
||||
bitmask[0] = bitmask[1] = 0;
|
||||
|
@ -2848,6 +2862,56 @@ out_overflow:
|
|||
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)
|
||||
{
|
||||
__be32 *p;
|
||||
|
@ -3521,6 +3585,24 @@ static int decode_attr_time_metadata(struct xdr_stream *xdr, uint32_t *bitmap, s
|
|||
return status;
|
||||
}
|
||||
|
||||
static int decode_attr_time_delta(struct xdr_stream *xdr, uint32_t *bitmap,
|
||||
struct timespec *time)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
time->tv_sec = 0;
|
||||
time->tv_nsec = 0;
|
||||
if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_DELTA - 1U)))
|
||||
return -EIO;
|
||||
if (likely(bitmap[1] & FATTR4_WORD1_TIME_DELTA)) {
|
||||
status = decode_attr_time(xdr, time);
|
||||
bitmap[1] &= ~FATTR4_WORD1_TIME_DELTA;
|
||||
}
|
||||
dprintk("%s: time_delta=%ld %ld\n", __func__, (long)time->tv_sec,
|
||||
(long)time->tv_nsec);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time)
|
||||
{
|
||||
int status = 0;
|
||||
|
@ -3744,29 +3826,14 @@ xdr_error:
|
|||
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)
|
||||
{
|
||||
__be32 *savep;
|
||||
uint32_t attrlen,
|
||||
bitmap[2] = {0},
|
||||
type;
|
||||
int status;
|
||||
umode_t fmode = 0;
|
||||
uint64_t fileid;
|
||||
|
||||
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;
|
||||
|
||||
uint32_t type;
|
||||
|
||||
status = decode_attr_type(xdr, bitmap, &type);
|
||||
if (status < 0)
|
||||
|
@ -3792,6 +3859,14 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
|
|||
goto xdr_error;
|
||||
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);
|
||||
if (status < 0)
|
||||
goto xdr_error;
|
||||
|
@ -3862,12 +3937,46 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
|
|||
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);
|
||||
xdr_error:
|
||||
dprintk("%s: xdr returned %d\n", __func__, -status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
|
||||
const struct nfs_server *server, 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)
|
||||
{
|
||||
|
@ -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)
|
||||
goto xdr_error;
|
||||
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);
|
||||
xdr_error:
|
||||
|
@ -3950,13 +4062,13 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl)
|
|||
__be32 *p;
|
||||
uint32_t namelen, type;
|
||||
|
||||
p = xdr_inline_decode(xdr, 32);
|
||||
p = xdr_inline_decode(xdr, 32); /* read 32 bytes */
|
||||
if (unlikely(!p))
|
||||
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);
|
||||
type = be32_to_cpup(p++);
|
||||
if (fl != NULL) {
|
||||
type = be32_to_cpup(p++); /* 4 byte read */
|
||||
if (fl != NULL) { /* manipulate file lock */
|
||||
fl->fl_start = (loff_t)offset;
|
||||
fl->fl_end = fl->fl_start + (loff_t)length - 1;
|
||||
if (length == ~(uint64_t)0)
|
||||
|
@ -3966,9 +4078,9 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl)
|
|||
fl->fl_type = F_RDLCK;
|
||||
fl->fl_pid = 0;
|
||||
}
|
||||
p = xdr_decode_hyper(p, &clientid);
|
||||
namelen = be32_to_cpup(p);
|
||||
p = xdr_inline_decode(xdr, namelen);
|
||||
p = xdr_decode_hyper(p, &clientid); /* read 8 bytes */
|
||||
namelen = be32_to_cpup(p); /* read 4 bytes */ /* have read all 32 bytes now */
|
||||
p = xdr_inline_decode(xdr, namelen); /* variable size field */
|
||||
if (likely(p))
|
||||
return -NFS4ERR_DENIED;
|
||||
out_overflow:
|
||||
|
@ -4200,12 +4312,9 @@ out_overflow:
|
|||
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 page *page = *rcvbuf->pages;
|
||||
struct kvec *iov = rcvbuf->head;
|
||||
size_t hdrlen;
|
||||
u32 recvd, pglen = rcvbuf->page_len;
|
||||
__be32 *end, *entry, *p, *kaddr;
|
||||
unsigned int nr = 0;
|
||||
int status;
|
||||
|
||||
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
|
|||
pglen = recvd;
|
||||
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;
|
||||
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)
|
||||
|
@ -4299,7 +4345,6 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
|
|||
size_t hdrlen;
|
||||
u32 len, recvd;
|
||||
__be32 *p;
|
||||
char *kaddr;
|
||||
int status;
|
||||
|
||||
status = decode_op_hdr(xdr, OP_READLINK);
|
||||
|
@ -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
|
||||
* null-termination).
|
||||
*/
|
||||
kaddr = (char *)kmap_atomic(rcvbuf->pages[0], KM_USER0);
|
||||
kaddr[len+rcvbuf->page_base] = '\0';
|
||||
kunmap_atomic(kaddr, KM_USER0);
|
||||
xdr_terminate_string(rcvbuf, len);
|
||||
return 0;
|
||||
out_overflow:
|
||||
print_overflow_msg(__func__, xdr);
|
||||
|
@ -4668,7 +4711,6 @@ static int decode_sequence(struct xdr_stream *xdr,
|
|||
struct rpc_rqst *rqstp)
|
||||
{
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
struct nfs4_slot *slot;
|
||||
struct nfs4_sessionid id;
|
||||
u32 dummy;
|
||||
int status;
|
||||
|
@ -4700,15 +4742,14 @@ static int decode_sequence(struct xdr_stream *xdr,
|
|||
goto out_overflow;
|
||||
|
||||
/* seqid */
|
||||
slot = &res->sr_session->fc_slot_table.slots[res->sr_slotid];
|
||||
dummy = be32_to_cpup(p++);
|
||||
if (dummy != slot->seq_nr) {
|
||||
if (dummy != res->sr_slot->seq_nr) {
|
||||
dprintk("%s Invalid sequence number\n", __func__);
|
||||
goto out_err;
|
||||
}
|
||||
/* slot id */
|
||||
dummy = be32_to_cpup(p++);
|
||||
if (dummy != res->sr_slotid) {
|
||||
if (dummy != res->sr_slot - res->sr_session->fc_slot_table.slots) {
|
||||
dprintk("%s Invalid slot id\n", __func__);
|
||||
goto out_err;
|
||||
}
|
||||
|
@ -4873,7 +4914,7 @@ out:
|
|||
/*
|
||||
* 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 compound_hdr hdr;
|
||||
|
@ -5760,23 +5801,35 @@ static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp, uint32_t *p,
|
|||
}
|
||||
#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 len;
|
||||
|
||||
if (!*p++) {
|
||||
if (!*p)
|
||||
__be32 *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);
|
||||
entry->eof = 1;
|
||||
return ERR_PTR(-EBADCOOKIE);
|
||||
}
|
||||
|
||||
p = xdr_inline_decode(xdr, 12);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
entry->prev_cookie = entry->cookie;
|
||||
p = xdr_decode_hyper(p, &entry->cookie);
|
||||
entry->len = ntohl(*p++);
|
||||
|
||||
p = xdr_inline_decode(xdr, entry->len);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
entry->name = (const char *) p;
|
||||
p += XDR_QUADLEN(entry->len);
|
||||
|
||||
/*
|
||||
* 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)
|
|||
* since glibc seems to choke on it...)
|
||||
*/
|
||||
entry->ino = 1;
|
||||
entry->fattr->valid = 0;
|
||||
|
||||
len = ntohl(*p++); /* bitmap length */
|
||||
if (len-- > 0) {
|
||||
bitmap[0] = ntohl(*p++);
|
||||
if (len-- > 0) {
|
||||
bitmap[1] = ntohl(*p++);
|
||||
p += len;
|
||||
}
|
||||
}
|
||||
len = XDR_QUADLEN(ntohl(*p++)); /* attribute buffer length */
|
||||
if (len > 0) {
|
||||
if (bitmap[0] & FATTR4_WORD0_RDATTR_ERROR) {
|
||||
bitmap[0] &= ~FATTR4_WORD0_RDATTR_ERROR;
|
||||
/* Ignore the return value of rdattr_error for now */
|
||||
p++;
|
||||
len--;
|
||||
}
|
||||
if (bitmap[0] == 0 && bitmap[1] == FATTR4_WORD1_MOUNTED_ON_FILEID)
|
||||
xdr_decode_hyper(p, &entry->ino);
|
||||
else if (bitmap[0] == FATTR4_WORD0_FILEID)
|
||||
xdr_decode_hyper(p, &entry->ino);
|
||||
p += len;
|
||||
}
|
||||
if (decode_attr_bitmap(xdr, bitmap) < 0)
|
||||
goto out_overflow;
|
||||
|
||||
if (decode_attr_length(xdr, &len, &p) < 0)
|
||||
goto out_overflow;
|
||||
|
||||
if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh, server, 1) < 0)
|
||||
goto out_overflow;
|
||||
if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID)
|
||||
entry->ino = entry->fattr->fileid;
|
||||
|
||||
if (verify_attr_len(xdr, p, len) < 0)
|
||||
goto out_overflow;
|
||||
|
||||
p = xdr_inline_peek(xdr, 8);
|
||||
if (p != NULL)
|
||||
entry->eof = !p[0] && p[1];
|
||||
else
|
||||
entry->eof = 0;
|
||||
|
||||
entry->eof = !p[0] && p[1];
|
||||
return p;
|
||||
|
||||
out_overflow:
|
||||
print_overflow_msg(__func__, xdr);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
574
fs/nfs/nfsroot.c
574
fs/nfs/nfsroot.c
|
@ -3,9 +3,10 @@
|
|||
*
|
||||
* Allow an NFS filesystem to be mounted as root. The way this works is:
|
||||
* (1) Use the IP autoconfig mechanism to set local IP addresses and routes.
|
||||
* (2) Handle RPC negotiation with the system which replied to RARP or
|
||||
* was reported as a boot server by BOOTP or manually.
|
||||
* (3) The actual mounting is done later, when init() is running.
|
||||
* (2) Construct the device string and the options string using DHCP
|
||||
* option 17 and/or kernel command line options.
|
||||
* (3) When mount_root() sets up the root file system, pass these strings
|
||||
* to the NFS client's regular mount interface via sys_mount().
|
||||
*
|
||||
*
|
||||
* Changes:
|
||||
|
@ -65,470 +66,243 @@
|
|||
* Hua Qin : Support for mounting root file system via
|
||||
* NFS over TCP.
|
||||
* Fabian Frederick: Option parser rebuilt (using parser lib)
|
||||
*/
|
||||
* Chuck Lever : Use super.c's text-based mount option parsing
|
||||
* Chuck Lever : Add "nfsrootdebug".
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/xprtsock.h>
|
||||
#include <linux/nfs.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/inet.h>
|
||||
#include <linux/root_dev.h>
|
||||
#include <net/ipconfig.h>
|
||||
#include <linux/parser.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/* Define this to allow debugging output */
|
||||
#undef NFSROOT_DEBUG
|
||||
#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 */
|
||||
#define NFS_ROOT "/tftpboot/%s"
|
||||
|
||||
/* 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 */
|
||||
static __be32 servaddr __initdata = 0;
|
||||
static __be32 servaddr __initdata = htonl(INADDR_NONE);
|
||||
|
||||
/* 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 */
|
||||
static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */
|
||||
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}
|
||||
|
||||
};
|
||||
/* server:export path string passed to super.c */
|
||||
static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = "";
|
||||
|
||||
/*
|
||||
* Parse option string.
|
||||
* When the "nfsrootdebug" kernel command line option is specified,
|
||||
* enable debugging messages for NFSROOT.
|
||||
*/
|
||||
|
||||
static int __init root_nfs_parse(char *name, char *buf)
|
||||
static int __init nfs_root_debug(char *__unused)
|
||||
{
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare the NFS data structure and parse all options.
|
||||
*/
|
||||
static int __init root_nfs_name(char *name)
|
||||
{
|
||||
static char buf[NFS_MAXPATHLEN] __initdata;
|
||||
char *cp;
|
||||
|
||||
/* Set some default values */
|
||||
memset(&nfs_data, 0, sizeof(nfs_data));
|
||||
nfs_port = -1;
|
||||
nfs_data.version = NFS_MOUNT_VERSION;
|
||||
nfs_data.flags = NFS_MOUNT_NONLM; /* No lockd in nfs root yet */
|
||||
nfs_data.rsize = NFS_DEF_FILE_IO_SIZE;
|
||||
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);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get NFS server address.
|
||||
*/
|
||||
static int __init root_nfs_addr(void)
|
||||
{
|
||||
if ((servaddr = root_server_addr) == htonl(INADDR_NONE)) {
|
||||
printk(KERN_ERR "Root-NFS: No NFS server available, giving up.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(nfs_data.hostname, sizeof(nfs_data.hostname),
|
||||
"%pI4", &servaddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tell the user what's going on.
|
||||
*/
|
||||
#ifdef NFSROOT_DEBUG
|
||||
static void __init root_nfs_print(void)
|
||||
{
|
||||
printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n",
|
||||
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)
|
||||
{
|
||||
#ifdef NFSROOT_DEBUG
|
||||
nfs_debug |= NFSDBG_ROOT;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Decode the root directory path name and NFS options from
|
||||
* the kernel command line. This has to go here in order to
|
||||
* 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 ||
|
||||
root_nfs_addr() < 0)
|
||||
return -1;
|
||||
|
||||
#ifdef NFSROOT_DEBUG
|
||||
root_nfs_print();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__setup("nfsrootdebug", nfs_root_debug);
|
||||
|
||||
/*
|
||||
* Parse NFS server and directory information passed on the kernel
|
||||
* command line.
|
||||
*
|
||||
* nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
|
||||
*
|
||||
* If there is a "%s" token in the <root-dir> string, it is replaced
|
||||
* by the ASCII-representation of the client's IP address.
|
||||
*/
|
||||
static int __init nfs_root_setup(char *line)
|
||||
{
|
||||
ROOT_DEV = Root_NFS;
|
||||
|
||||
if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) {
|
||||
strlcpy(nfs_root_name, line, sizeof(nfs_root_name));
|
||||
strlcpy(nfs_root_parms, line, sizeof(nfs_root_parms));
|
||||
} else {
|
||||
int n = strlen(line) + sizeof(NFS_ROOT) - 1;
|
||||
if (n >= sizeof(nfs_root_name))
|
||||
line[sizeof(nfs_root_name) - sizeof(NFS_ROOT) - 2] = '\0';
|
||||
sprintf(nfs_root_name, NFS_ROOT, line);
|
||||
size_t n = strlen(line) + sizeof(NFS_ROOT) - 1;
|
||||
if (n >= sizeof(nfs_root_parms))
|
||||
line[sizeof(nfs_root_parms) - sizeof(NFS_ROOT) - 2] = '\0';
|
||||
sprintf(nfs_root_parms, NFS_ROOT, line);
|
||||
}
|
||||
root_server_addr = root_nfs_parse_addr(nfs_root_name);
|
||||
|
||||
/*
|
||||
* Extract the IP address of the NFS server containing our
|
||||
* root file system, if one was specified.
|
||||
*
|
||||
* Note: root_nfs_parse_addr() removes the server-ip from
|
||||
* nfs_root_parms, if it exists.
|
||||
*/
|
||||
root_server_addr = root_nfs_parse_addr(nfs_root_parms);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("nfsroot=", nfs_root_setup);
|
||||
|
||||
/***************************************************************************
|
||||
|
||||
Routines to actually mount the root directory
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
/*
|
||||
* Construct sockaddr_in from address and port number.
|
||||
*/
|
||||
static inline void
|
||||
set_sockaddr(struct sockaddr_in *sin, __be32 addr, __be16 port)
|
||||
static int __init root_nfs_copy(char *dest, const char *src,
|
||||
const size_t destlen)
|
||||
{
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_addr.s_addr = addr;
|
||||
sin->sin_port = port;
|
||||
if (strlcpy(dest, src, destlen) > destlen)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init root_nfs_cat(char *dest, const char *src,
|
||||
const size_t destlen)
|
||||
{
|
||||
if (strlcat(dest, src, destlen) > destlen)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Query server portmapper for the port of a daemon program.
|
||||
* Parse out root export path and mount options from
|
||||
* passed-in string @incoming.
|
||||
*
|
||||
* Copy the export path into @exppath.
|
||||
*/
|
||||
static int __init root_nfs_getport(int program, int version, int proto)
|
||||
static int __init root_nfs_parse_options(char *incoming, char *exppath,
|
||||
const size_t exppathlen)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
char *p;
|
||||
|
||||
printk(KERN_NOTICE "Looking up port of RPC %d/%d on %pI4\n",
|
||||
program, version, &servaddr);
|
||||
set_sockaddr(&sin, servaddr, 0);
|
||||
return rpcb_getport_sync(&sin, program, version, proto);
|
||||
}
|
||||
/*
|
||||
* Set the NFS remote path
|
||||
*/
|
||||
p = strsep(&incoming, ",");
|
||||
if (*p != '\0' && strcmp(p, "default") != 0)
|
||||
if (root_nfs_copy(exppath, p, exppathlen))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* @incoming now points to the rest of the string; if it
|
||||
* contains something, append it to our root options buffer
|
||||
*/
|
||||
if (incoming != NULL && *incoming != '\0')
|
||||
if (root_nfs_cat(nfs_root_options, incoming,
|
||||
sizeof(nfs_root_options)))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Use portmapper to find mountd and nfsd port numbers if not overriden
|
||||
* by the user. Use defaults if portmapper is not available.
|
||||
* XXX: Is there any nfs server with no portmapper?
|
||||
*/
|
||||
static int __init root_nfs_ports(void)
|
||||
{
|
||||
int port;
|
||||
int nfsd_ver, mountd_ver;
|
||||
int nfsd_port, mountd_port;
|
||||
int proto;
|
||||
|
||||
if (nfs_data.flags & NFS_MOUNT_VER3) {
|
||||
nfsd_ver = NFS3_VERSION;
|
||||
mountd_ver = NFS_MNT3_VERSION;
|
||||
nfsd_port = NFS_PORT;
|
||||
mountd_port = NFS_MNT_PORT;
|
||||
} else {
|
||||
nfsd_ver = NFS2_VERSION;
|
||||
mountd_ver = NFS_MNT_VERSION;
|
||||
nfsd_port = NFS_PORT;
|
||||
mountd_port = NFS_MNT_PORT;
|
||||
}
|
||||
|
||||
proto = (nfs_data.flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;
|
||||
|
||||
if (nfs_port < 0) {
|
||||
if ((port = root_nfs_getport(NFS_PROGRAM, 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) {
|
||||
printk(KERN_ERR "Root-NFS: Unable to get mountd port "
|
||||
"number from server, using default\n");
|
||||
port = mountd_port;
|
||||
}
|
||||
mount_port = port;
|
||||
dprintk("Root-NFS: mountd port is %d\n", port);
|
||||
/*
|
||||
* Possibly prepare for more options to be appended
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get a file handle from the server for the directory which is to be
|
||||
* mounted.
|
||||
* Decode the export directory path name and NFS options from
|
||||
* the kernel command line. This has to be done late in order to
|
||||
* use a dynamically acquired client IP address for the remote
|
||||
* root directory path.
|
||||
*
|
||||
* Returns zero if successful; otherwise -1 is returned.
|
||||
*/
|
||||
static int __init root_nfs_get_handle(void)
|
||||
static int __init root_nfs_data(char *cmdline)
|
||||
{
|
||||
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;
|
||||
char addr_option[sizeof("nolock,addr=") + INET_ADDRSTRLEN + 1];
|
||||
int len, retval = -1;
|
||||
char *tmp = NULL;
|
||||
const size_t tmplen = sizeof(nfs_export_path);
|
||||
|
||||
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);
|
||||
tmp = kzalloc(tmplen, GFP_KERNEL);
|
||||
if (tmp == NULL)
|
||||
goto out_nomem;
|
||||
strcpy(tmp, NFS_ROOT);
|
||||
|
||||
if (root_server_path[0] != '\0') {
|
||||
dprintk("Root-NFS: DHCPv4 option 17: %s\n",
|
||||
root_server_path);
|
||||
if (root_nfs_parse_options(root_server_path, tmp, tmplen))
|
||||
goto out_optionstoolong;
|
||||
}
|
||||
nfs_free_fhandle(request.fh);
|
||||
|
||||
if (cmdline[0] != '\0') {
|
||||
dprintk("Root-NFS: nfsroot=%s\n", cmdline);
|
||||
if (root_nfs_parse_options(cmdline, tmp, tmplen))
|
||||
goto out_optionstoolong;
|
||||
}
|
||||
|
||||
/*
|
||||
* Append mandatory options for nfsroot so they override
|
||||
* what has come before
|
||||
*/
|
||||
snprintf(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;
|
||||
|
||||
/*
|
||||
* Set up nfs_root_device. For NFS mounts, this looks like
|
||||
*
|
||||
* server:/path
|
||||
*
|
||||
* At this point, utsname()->nodename contains our local
|
||||
* IP address or hostname, set by ipconfig. If "%s" exists
|
||||
* in tmp, substitute the nodename, then shovel the whole
|
||||
* mess into nfs_root_device.
|
||||
*/
|
||||
len = snprintf(nfs_export_path, sizeof(nfs_export_path),
|
||||
tmp, utsname()->nodename);
|
||||
if (len > (int)sizeof(nfs_export_path))
|
||||
goto out_devnametoolong;
|
||||
len = snprintf(nfs_root_device, sizeof(nfs_root_device),
|
||||
"%pI4:%s", &servaddr, nfs_export_path);
|
||||
if (len > (int)sizeof(nfs_root_device))
|
||||
goto out_devnametoolong;
|
||||
|
||||
retval = 0;
|
||||
|
||||
out:
|
||||
return status;
|
||||
kfree(tmp);
|
||||
return retval;
|
||||
out_nomem:
|
||||
printk(KERN_ERR "Root-NFS: could not allocate memory\n");
|
||||
goto out;
|
||||
out_optionstoolong:
|
||||
printk(KERN_ERR "Root-NFS: mount options string too long\n");
|
||||
goto out;
|
||||
out_devnametoolong:
|
||||
printk(KERN_ERR "Root-NFS: root device name too long.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the NFS port numbers and file handle, and return the prepared 'data'
|
||||
* argument for mount() if everything went OK. Return NULL otherwise.
|
||||
/**
|
||||
* nfs_root_data - Return prepared 'data' for NFSROOT mount
|
||||
* @root_device: OUT: address of string containing NFSROOT device
|
||||
* @root_data: OUT: address of string containing NFSROOT mount options
|
||||
*
|
||||
* Returns zero and sets @root_device and @root_data if successful,
|
||||
* otherwise -1 is returned.
|
||||
*/
|
||||
void * __init nfs_root_data(void)
|
||||
int __init nfs_root_data(char **root_device, char **root_data)
|
||||
{
|
||||
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;
|
||||
servaddr = root_server_addr;
|
||||
if (servaddr == htonl(INADDR_NONE)) {
|
||||
printk(KERN_ERR "Root-NFS: no NFS server address\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (root_nfs_data(nfs_root_parms) < 0)
|
||||
return -1;
|
||||
|
||||
*root_device = nfs_root_device;
|
||||
*root_data = nfs_root_options;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -258,7 +258,7 @@ static void nfs_free_createdata(const struct nfs_createdata *data)
|
|||
|
||||
static int
|
||||
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 rpc_message msg = {
|
||||
|
@ -365,17 +365,32 @@ static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir)
|
|||
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
|
||||
nfs_proc_rename(struct inode *old_dir, struct qstr *old_name,
|
||||
struct inode *new_dir, struct qstr *new_name)
|
||||
{
|
||||
struct nfs_renameargs arg = {
|
||||
.fromfh = NFS_FH(old_dir),
|
||||
.fromname = old_name->name,
|
||||
.fromlen = old_name->len,
|
||||
.tofh = NFS_FH(new_dir),
|
||||
.toname = new_name->name,
|
||||
.tolen = new_name->len
|
||||
.old_dir = NFS_FH(old_dir),
|
||||
.old_name = old_name,
|
||||
.new_dir = NFS_FH(new_dir),
|
||||
.new_name = new_name,
|
||||
};
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs_procedures[NFSPROC_RENAME],
|
||||
|
@ -519,14 +534,14 @@ nfs_proc_rmdir(struct inode *dir, struct qstr *name)
|
|||
*/
|
||||
static int
|
||||
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 nfs_readdirargs arg = {
|
||||
.fh = NFS_FH(dir),
|
||||
.cookie = cookie,
|
||||
.count = count,
|
||||
.pages = &page,
|
||||
.pages = pages,
|
||||
};
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs_procedures[NFSPROC_READDIR],
|
||||
|
@ -705,6 +720,8 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
|
|||
.unlink_setup = nfs_proc_unlink_setup,
|
||||
.unlink_done = nfs_proc_unlink_done,
|
||||
.rename = nfs_proc_rename,
|
||||
.rename_setup = nfs_proc_rename_setup,
|
||||
.rename_done = nfs_proc_rename_done,
|
||||
.link = nfs_proc_link,
|
||||
.symlink = nfs_proc_symlink,
|
||||
.mkdir = nfs_proc_mkdir,
|
||||
|
|
|
@ -46,7 +46,6 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
|
|||
memset(p, 0, sizeof(*p));
|
||||
INIT_LIST_HEAD(&p->pages);
|
||||
p->npages = pagecount;
|
||||
p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
if (pagecount <= ARRAY_SIZE(p->page_array))
|
||||
p->pagevec = p->page_array;
|
||||
else {
|
||||
|
|
|
@ -100,6 +100,7 @@ enum {
|
|||
Opt_addr, Opt_mountaddr, Opt_clientaddr,
|
||||
Opt_lookupcache,
|
||||
Opt_fscache_uniq,
|
||||
Opt_local_lock,
|
||||
|
||||
/* Special mount options */
|
||||
Opt_userspace, Opt_deprecated, Opt_sloppy,
|
||||
|
@ -171,6 +172,7 @@ static const match_table_t nfs_mount_option_tokens = {
|
|||
|
||||
{ Opt_lookupcache, "lookupcache=%s" },
|
||||
{ Opt_fscache_uniq, "fsc=%s" },
|
||||
{ Opt_local_lock, "local_lock=%s" },
|
||||
|
||||
{ Opt_err, NULL }
|
||||
};
|
||||
|
@ -236,6 +238,22 @@ static match_table_t nfs_lookupcache_tokens = {
|
|||
{ 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 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,
|
|||
const struct proc_nfs_info *nfs_infop;
|
||||
struct nfs_client *clp = nfss->nfs_client;
|
||||
u32 version = clp->rpc_ops->version;
|
||||
int local_flock, local_fcntl;
|
||||
|
||||
seq_printf(m, ",vers=%u", version);
|
||||
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,
|
|||
else
|
||||
seq_printf(m, ",lookupcache=pos");
|
||||
}
|
||||
|
||||
local_flock = nfss->flags & NFS_MOUNT_LOCAL_FLOCK;
|
||||
local_fcntl = nfss->flags & NFS_MOUNT_LOCAL_FCNTL;
|
||||
|
||||
if (!local_flock && !local_fcntl)
|
||||
seq_printf(m, ",local_lock=none");
|
||||
else if (local_flock && local_fcntl)
|
||||
seq_printf(m, ",local_lock=all");
|
||||
else if (local_flock)
|
||||
seq_printf(m, ",local_lock=flock");
|
||||
else
|
||||
seq_printf(m, ",local_lock=posix");
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1017,9 +1048,13 @@ static int nfs_parse_mount_options(char *raw,
|
|||
break;
|
||||
case Opt_lock:
|
||||
mnt->flags &= ~NFS_MOUNT_NONLM;
|
||||
mnt->flags &= ~(NFS_MOUNT_LOCAL_FLOCK |
|
||||
NFS_MOUNT_LOCAL_FCNTL);
|
||||
break;
|
||||
case Opt_nolock:
|
||||
mnt->flags |= NFS_MOUNT_NONLM;
|
||||
mnt->flags |= (NFS_MOUNT_LOCAL_FLOCK |
|
||||
NFS_MOUNT_LOCAL_FCNTL);
|
||||
break;
|
||||
case Opt_v2:
|
||||
mnt->flags &= ~NFS_MOUNT_VER3;
|
||||
|
@ -1420,6 +1455,34 @@ static int nfs_parse_mount_options(char *raw,
|
|||
mnt->fscache_uniq = string;
|
||||
mnt->options |= NFS_OPTION_FSCACHE;
|
||||
break;
|
||||
case Opt_local_lock:
|
||||
string = match_strdup(args);
|
||||
if (string == NULL)
|
||||
goto out_nomem;
|
||||
token = match_token(string, nfs_local_lock_tokens,
|
||||
args);
|
||||
kfree(string);
|
||||
switch (token) {
|
||||
case Opt_local_lock_all:
|
||||
mnt->flags |= (NFS_MOUNT_LOCAL_FLOCK |
|
||||
NFS_MOUNT_LOCAL_FCNTL);
|
||||
break;
|
||||
case Opt_local_lock_flock:
|
||||
mnt->flags |= NFS_MOUNT_LOCAL_FLOCK;
|
||||
break;
|
||||
case Opt_local_lock_posix:
|
||||
mnt->flags |= NFS_MOUNT_LOCAL_FCNTL;
|
||||
break;
|
||||
case Opt_local_lock_none:
|
||||
mnt->flags &= ~(NFS_MOUNT_LOCAL_FLOCK |
|
||||
NFS_MOUNT_LOCAL_FCNTL);
|
||||
break;
|
||||
default:
|
||||
dfprintk(MOUNT, "NFS: invalid "
|
||||
"local_lock argument\n");
|
||||
return 0;
|
||||
};
|
||||
break;
|
||||
|
||||
/*
|
||||
* Special options
|
||||
|
@ -1825,6 +1888,12 @@ static int nfs_validate_mount_data(void *options,
|
|||
if (!args->nfs_server.hostname)
|
||||
goto out_nomem;
|
||||
|
||||
if (!(data->flags & NFS_MOUNT_NONLM))
|
||||
args->flags &= ~(NFS_MOUNT_LOCAL_FLOCK|
|
||||
NFS_MOUNT_LOCAL_FCNTL);
|
||||
else
|
||||
args->flags |= (NFS_MOUNT_LOCAL_FLOCK|
|
||||
NFS_MOUNT_LOCAL_FCNTL);
|
||||
/*
|
||||
* The legacy version 6 binary mount data from userspace has a
|
||||
* field used only to transport selinux information into the
|
||||
|
@ -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)
|
||||
{
|
||||
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,
|
||||
|
|
|
@ -32,6 +32,7 @@ static ctl_table nfs_cb_sysctls[] = {
|
|||
.extra1 = (int *)&nfs_set_port_min,
|
||||
.extra2 = (int *)&nfs_set_port_max,
|
||||
},
|
||||
#ifndef CONFIG_NFS_USE_NEW_IDMAPPER
|
||||
{
|
||||
.procname = "idmap_cache_timeout",
|
||||
.data = &nfs_idmap_cache_timeout,
|
||||
|
@ -39,6 +40,7 @@ static ctl_table nfs_cb_sysctls[] = {
|
|||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_jiffies,
|
||||
},
|
||||
#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */
|
||||
#endif
|
||||
{
|
||||
.procname = "nfs_mountpoint_timeout",
|
||||
|
|
259
fs/nfs/unlink.c
259
fs/nfs/unlink.c
|
@ -13,9 +13,12 @@
|
|||
#include <linux/nfs_fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "nfs4_fs.h"
|
||||
#include "iostat.h"
|
||||
#include "delegation.h"
|
||||
|
||||
struct nfs_unlinkdata {
|
||||
struct hlist_node list;
|
||||
|
@ -244,7 +247,7 @@ void nfs_unblock_sillyrename(struct dentry *dentry)
|
|||
* @dir: parent directory of dentry
|
||||
* @dentry: dentry to unlink
|
||||
*/
|
||||
int
|
||||
static int
|
||||
nfs_async_unlink(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct nfs_unlinkdata *data;
|
||||
|
@ -259,7 +262,6 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry)
|
|||
status = PTR_ERR(data->cred);
|
||||
goto out_free;
|
||||
}
|
||||
data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
data->res.dir_attr = &data->dir_attr;
|
||||
|
||||
status = -EBUSY;
|
||||
|
@ -303,3 +305,256 @@ nfs_complete_unlink(struct dentry *dentry, struct inode *inode)
|
|||
if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data)))
|
||||
nfs_free_unlinkdata(data);
|
||||
}
|
||||
|
||||
/* Cancel a queued async unlink. Called when a sillyrename run fails. */
|
||||
static void
|
||||
nfs_cancel_async_unlink(struct dentry *dentry)
|
||||
{
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
|
||||
struct nfs_unlinkdata *data = dentry->d_fsdata;
|
||||
|
||||
dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
nfs_free_unlinkdata(data);
|
||||
return;
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
struct nfs_renamedata {
|
||||
struct nfs_renameargs args;
|
||||
struct nfs_renameres res;
|
||||
struct rpc_cred *cred;
|
||||
struct inode *old_dir;
|
||||
struct dentry *old_dentry;
|
||||
struct nfs_fattr old_fattr;
|
||||
struct inode *new_dir;
|
||||
struct dentry *new_dentry;
|
||||
struct nfs_fattr new_fattr;
|
||||
};
|
||||
|
||||
/**
|
||||
* nfs_async_rename_done - Sillyrename post-processing
|
||||
* @task: rpc_task of the sillyrename
|
||||
* @calldata: nfs_renamedata for the sillyrename
|
||||
*
|
||||
* Do the directory attribute updates and the d_move
|
||||
*/
|
||||
static void nfs_async_rename_done(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs_renamedata *data = calldata;
|
||||
struct inode *old_dir = data->old_dir;
|
||||
struct inode *new_dir = data->new_dir;
|
||||
|
||||
if (!NFS_PROTO(old_dir)->rename_done(task, old_dir, new_dir)) {
|
||||
nfs_restart_rpc(task, NFS_SERVER(old_dir)->nfs_client);
|
||||
return;
|
||||
}
|
||||
|
||||
if (task->tk_status != 0) {
|
||||
nfs_cancel_async_unlink(data->old_dentry);
|
||||
return;
|
||||
}
|
||||
|
||||
nfs_set_verifier(data->old_dentry, nfs_save_change_attribute(old_dir));
|
||||
d_move(data->old_dentry, data->new_dentry);
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_async_rename_release - Release the sillyrename data.
|
||||
* @calldata: the struct nfs_renamedata to be released
|
||||
*/
|
||||
static void nfs_async_rename_release(void *calldata)
|
||||
{
|
||||
struct nfs_renamedata *data = calldata;
|
||||
struct super_block *sb = data->old_dir->i_sb;
|
||||
|
||||
if (data->old_dentry->d_inode)
|
||||
nfs_mark_for_revalidate(data->old_dentry->d_inode);
|
||||
|
||||
dput(data->old_dentry);
|
||||
dput(data->new_dentry);
|
||||
iput(data->old_dir);
|
||||
iput(data->new_dir);
|
||||
nfs_sb_deactive(sb);
|
||||
put_rpccred(data->cred);
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
static void nfs_rename_prepare(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs_renamedata *data = calldata;
|
||||
struct nfs_server *server = NFS_SERVER(data->old_dir);
|
||||
|
||||
if (nfs4_setup_sequence(server, &data->args.seq_args,
|
||||
&data->res.seq_res, 1, task))
|
||||
return;
|
||||
rpc_call_start(task);
|
||||
}
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
|
||||
static const struct rpc_call_ops nfs_rename_ops = {
|
||||
.rpc_call_done = nfs_async_rename_done,
|
||||
.rpc_release = nfs_async_rename_release,
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
.rpc_call_prepare = nfs_rename_prepare,
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
};
|
||||
|
||||
/**
|
||||
* nfs_async_rename - perform an asynchronous rename operation
|
||||
* @old_dir: directory that currently holds the dentry to be renamed
|
||||
* @new_dir: target directory for the rename
|
||||
* @old_dentry: original dentry to be renamed
|
||||
* @new_dentry: dentry to which the old_dentry should be renamed
|
||||
*
|
||||
* It's expected that valid references to the dentries and inodes are held
|
||||
*/
|
||||
static struct rpc_task *
|
||||
nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
|
||||
struct dentry *old_dentry, struct dentry *new_dentry)
|
||||
{
|
||||
struct nfs_renamedata *data;
|
||||
struct rpc_message msg = { };
|
||||
struct rpc_task_setup task_setup_data = {
|
||||
.rpc_message = &msg,
|
||||
.callback_ops = &nfs_rename_ops,
|
||||
.workqueue = nfsiod_workqueue,
|
||||
.rpc_client = NFS_CLIENT(old_dir),
|
||||
.flags = RPC_TASK_ASYNC,
|
||||
};
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
task_setup_data.callback_data = data,
|
||||
|
||||
data->cred = rpc_lookup_cred();
|
||||
if (IS_ERR(data->cred)) {
|
||||
struct rpc_task *task = ERR_CAST(data->cred);
|
||||
kfree(data);
|
||||
return task;
|
||||
}
|
||||
|
||||
msg.rpc_argp = &data->args;
|
||||
msg.rpc_resp = &data->res;
|
||||
msg.rpc_cred = data->cred;
|
||||
|
||||
/* set up nfs_renamedata */
|
||||
data->old_dir = old_dir;
|
||||
atomic_inc(&old_dir->i_count);
|
||||
data->new_dir = new_dir;
|
||||
atomic_inc(&new_dir->i_count);
|
||||
data->old_dentry = dget(old_dentry);
|
||||
data->new_dentry = dget(new_dentry);
|
||||
nfs_fattr_init(&data->old_fattr);
|
||||
nfs_fattr_init(&data->new_fattr);
|
||||
|
||||
/* set up nfs_renameargs */
|
||||
data->args.old_dir = NFS_FH(old_dir);
|
||||
data->args.old_name = &old_dentry->d_name;
|
||||
data->args.new_dir = NFS_FH(new_dir);
|
||||
data->args.new_name = &new_dentry->d_name;
|
||||
|
||||
/* set up nfs_renameres */
|
||||
data->res.old_fattr = &data->old_fattr;
|
||||
data->res.new_fattr = &data->new_fattr;
|
||||
|
||||
nfs_sb_active(old_dir->i_sb);
|
||||
|
||||
NFS_PROTO(data->old_dir)->rename_setup(&msg, old_dir);
|
||||
|
||||
return rpc_run_task(&task_setup_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_sillyrename - Perform a silly-rename of a dentry
|
||||
* @dir: inode of directory that contains dentry
|
||||
* @dentry: dentry to be sillyrenamed
|
||||
*
|
||||
* NFSv2/3 is stateless and the server doesn't know when the client is
|
||||
* holding a file open. To prevent application problems when a file is
|
||||
* unlinked while it's still open, the client performs a "silly-rename".
|
||||
* That is, it renames the file to a hidden file in the same directory,
|
||||
* and only performs the unlink once the last reference to it is put.
|
||||
*
|
||||
* The final cleanup is done during dentry_iput.
|
||||
*/
|
||||
int
|
||||
nfs_sillyrename(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
static unsigned int sillycounter;
|
||||
const int fileidsize = sizeof(NFS_FILEID(dentry->d_inode))*2;
|
||||
const int countersize = sizeof(sillycounter)*2;
|
||||
const int slen = sizeof(".nfs")+fileidsize+countersize-1;
|
||||
char silly[slen+1];
|
||||
struct dentry *sdentry;
|
||||
struct rpc_task *task;
|
||||
int error = -EIO;
|
||||
|
||||
dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
|
||||
dentry->d_parent->d_name.name, dentry->d_name.name,
|
||||
atomic_read(&dentry->d_count));
|
||||
nfs_inc_stats(dir, NFSIOS_SILLYRENAME);
|
||||
|
||||
/*
|
||||
* We don't allow a dentry to be silly-renamed twice.
|
||||
*/
|
||||
error = -EBUSY;
|
||||
if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
|
||||
goto out;
|
||||
|
||||
sprintf(silly, ".nfs%*.*Lx",
|
||||
fileidsize, fileidsize,
|
||||
(unsigned long long)NFS_FILEID(dentry->d_inode));
|
||||
|
||||
/* Return delegation in anticipation of the rename */
|
||||
nfs_inode_return_delegation(dentry->d_inode);
|
||||
|
||||
sdentry = NULL;
|
||||
do {
|
||||
char *suffix = silly + slen - countersize;
|
||||
|
||||
dput(sdentry);
|
||||
sillycounter++;
|
||||
sprintf(suffix, "%*.*x", countersize, countersize, sillycounter);
|
||||
|
||||
dfprintk(VFS, "NFS: trying to rename %s to %s\n",
|
||||
dentry->d_name.name, silly);
|
||||
|
||||
sdentry = lookup_one_len(silly, dentry->d_parent, slen);
|
||||
/*
|
||||
* N.B. Better to return EBUSY here ... it could be
|
||||
* dangerous to delete the file while it's in use.
|
||||
*/
|
||||
if (IS_ERR(sdentry))
|
||||
goto out;
|
||||
} while (sdentry->d_inode != NULL); /* need negative lookup */
|
||||
|
||||
/* queue unlink first. Can't do this from rpc_release as it
|
||||
* has to allocate memory
|
||||
*/
|
||||
error = nfs_async_unlink(dir, dentry);
|
||||
if (error)
|
||||
goto out_dput;
|
||||
|
||||
/* run the rename task, undo unlink if it fails */
|
||||
task = nfs_async_rename(dir, dir, dentry, sdentry);
|
||||
if (IS_ERR(task)) {
|
||||
error = -EBUSY;
|
||||
nfs_cancel_async_unlink(dentry);
|
||||
goto out_dput;
|
||||
}
|
||||
|
||||
/* wait for the RPC task to complete, unless a SIGKILL intervenes */
|
||||
error = rpc_wait_for_completion_task(task);
|
||||
if (error == 0)
|
||||
error = task->tk_status;
|
||||
rpc_put_task(task);
|
||||
out_dput:
|
||||
dput(sdentry);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@ struct nfs_write_data *nfs_commitdata_alloc(void)
|
|||
if (p) {
|
||||
memset(p, 0, sizeof(*p));
|
||||
INIT_LIST_HEAD(&p->pages);
|
||||
p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
@ -75,7 +74,6 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
|
|||
memset(p, 0, sizeof(*p));
|
||||
INIT_LIST_HEAD(&p->pages);
|
||||
p->npages = pagecount;
|
||||
p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
|
||||
if (pagecount <= ARRAY_SIZE(p->page_array))
|
||||
p->pagevec = p->page_array;
|
||||
else {
|
||||
|
@ -1433,15 +1431,17 @@ static int nfs_commit_unstable_pages(struct inode *inode, struct writeback_contr
|
|||
int flags = FLUSH_SYNC;
|
||||
int ret = 0;
|
||||
|
||||
/* Don't commit yet if this is a non-blocking flush and there are
|
||||
* lots of outstanding writes for this mapping.
|
||||
*/
|
||||
if (wbc->sync_mode == WB_SYNC_NONE &&
|
||||
nfsi->ncommit <= (nfsi->npages >> 1))
|
||||
goto out_mark_dirty;
|
||||
if (wbc->sync_mode == WB_SYNC_NONE) {
|
||||
/* Don't commit yet if this is a non-blocking flush and there
|
||||
* are a lot of outstanding writes for this mapping.
|
||||
*/
|
||||
if (nfsi->ncommit <= (nfsi->npages >> 1))
|
||||
goto out_mark_dirty;
|
||||
|
||||
if (wbc->nonblocking || wbc->for_background)
|
||||
/* don't wait for the COMMIT response */
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
ret = nfs_commit_inode(inode, flags);
|
||||
if (ret >= 0) {
|
||||
if (wbc->sync_mode == WB_SYNC_NONE) {
|
||||
|
|
|
@ -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 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 *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 void nfs_put_lock_context(struct nfs_lock_context *l_ctx);
|
||||
extern u64 nfs_compat_user_ino64(u64 fileid);
|
||||
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);
|
||||
|
||||
|
@ -379,9 +382,12 @@ static inline void nfs_free_fhandle(const struct nfs_fh *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. */
|
||||
extern __be32 root_nfs_parse_addr(char *name); /*__init*/
|
||||
extern unsigned long nfs_inc_attr_generation_counter(void);
|
||||
|
||||
/*
|
||||
* linux/fs/nfs/file.c
|
||||
|
@ -479,10 +485,10 @@ extern void nfs_release_automount_timer(void);
|
|||
/*
|
||||
* 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_block_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
|
||||
|
@ -584,10 +590,6 @@ nfs_fileid_to_ino_t(u64 fileid)
|
|||
return ino;
|
||||
}
|
||||
|
||||
/* NFS root */
|
||||
|
||||
extern void * nfs_root_data(void);
|
||||
|
||||
#define nfs_wait_event(clnt, wq, condition) \
|
||||
({ \
|
||||
int __retval = wait_event_killable(wq, condition); \
|
||||
|
|
|
@ -124,6 +124,7 @@ struct nfs_server {
|
|||
|
||||
struct nfs_fsid fsid;
|
||||
__u64 maxfilesize; /* maximum file size */
|
||||
struct timespec time_delta; /* smallest time granularity */
|
||||
unsigned long mount_time; /* when this fs was mounted */
|
||||
dev_t s_dev; /* superblock dev numbers */
|
||||
|
||||
|
|
|
@ -66,13 +66,40 @@ struct idmap_msg {
|
|||
/* Forward declaration to make this header independent of others */
|
||||
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 *);
|
||||
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_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_gid_to_group(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 *, size_t);
|
||||
|
||||
extern unsigned int nfs_idmap_cache_timeout;
|
||||
#endif /* __KERNEL__ */
|
||||
|
|
|
@ -71,4 +71,7 @@ struct nfs_mount_data {
|
|||
#define NFS_MOUNT_NORESVPORT 0x40000
|
||||
#define NFS_MOUNT_LEGACY_INTERFACE 0x80000
|
||||
|
||||
#define NFS_MOUNT_LOCAL_FLOCK 0x100000
|
||||
#define NFS_MOUNT_LOCAL_FCNTL 0x200000
|
||||
|
||||
#endif
|
||||
|
|
|
@ -112,6 +112,7 @@ struct nfs_fsinfo {
|
|||
__u32 wtmult; /* writes should be multiple of this */
|
||||
__u32 dtpref; /* pref. readdir transfer size */
|
||||
__u64 maxfilesize;
|
||||
struct timespec time_delta; /* server time granularity */
|
||||
__u32 lease_time; /* in seconds */
|
||||
};
|
||||
|
||||
|
@ -170,7 +171,7 @@ struct nfs4_sequence_args {
|
|||
|
||||
struct nfs4_sequence_res {
|
||||
struct nfs4_session *sr_session;
|
||||
u8 sr_slotid; /* slot used to send request */
|
||||
struct nfs4_slot *sr_slot; /* slot used to send request */
|
||||
int sr_status; /* sequence operation status */
|
||||
unsigned long sr_renewal_time;
|
||||
u32 sr_status_flags;
|
||||
|
@ -399,6 +400,27 @@ struct nfs_removeres {
|
|||
struct nfs4_sequence_res seq_res;
|
||||
};
|
||||
|
||||
/*
|
||||
* Common arguments to the rename call
|
||||
*/
|
||||
struct nfs_renameargs {
|
||||
const struct nfs_fh *old_dir;
|
||||
const struct nfs_fh *new_dir;
|
||||
const struct qstr *old_name;
|
||||
const struct qstr *new_name;
|
||||
const u32 *bitmask;
|
||||
struct nfs4_sequence_args seq_args;
|
||||
};
|
||||
|
||||
struct nfs_renameres {
|
||||
const struct nfs_server *server;
|
||||
struct nfs4_change_info old_cinfo;
|
||||
struct nfs_fattr *old_fattr;
|
||||
struct nfs4_change_info new_cinfo;
|
||||
struct nfs_fattr *new_fattr;
|
||||
struct nfs4_sequence_res seq_res;
|
||||
};
|
||||
|
||||
/*
|
||||
* Argument struct for decode_entry function
|
||||
*/
|
||||
|
@ -434,15 +456,6 @@ struct nfs_createargs {
|
|||
struct iattr * sattr;
|
||||
};
|
||||
|
||||
struct nfs_renameargs {
|
||||
struct nfs_fh * fromfh;
|
||||
const char * fromname;
|
||||
unsigned int fromlen;
|
||||
struct nfs_fh * tofh;
|
||||
const char * toname;
|
||||
unsigned int tolen;
|
||||
};
|
||||
|
||||
struct nfs_setattrargs {
|
||||
struct nfs_fh * fh;
|
||||
nfs4_stateid stateid;
|
||||
|
@ -586,15 +599,6 @@ struct nfs3_mknodargs {
|
|||
dev_t rdev;
|
||||
};
|
||||
|
||||
struct nfs3_renameargs {
|
||||
struct nfs_fh * fromfh;
|
||||
const char * fromname;
|
||||
unsigned int fromlen;
|
||||
struct nfs_fh * tofh;
|
||||
const char * toname;
|
||||
unsigned int tolen;
|
||||
};
|
||||
|
||||
struct nfs3_linkargs {
|
||||
struct nfs_fh * fromfh;
|
||||
struct nfs_fh * tofh;
|
||||
|
@ -629,11 +633,6 @@ struct nfs3_readlinkargs {
|
|||
struct page ** pages;
|
||||
};
|
||||
|
||||
struct nfs3_renameres {
|
||||
struct nfs_fattr * fromattr;
|
||||
struct nfs_fattr * toattr;
|
||||
};
|
||||
|
||||
struct nfs3_linkres {
|
||||
struct nfs_fattr * dir_attr;
|
||||
struct nfs_fattr * fattr;
|
||||
|
@ -780,6 +779,7 @@ struct nfs4_readdir_arg {
|
|||
struct page ** pages; /* zero-copy data */
|
||||
unsigned int pgbase; /* zero-copy data */
|
||||
const u32 * bitmask;
|
||||
int plus;
|
||||
struct nfs4_sequence_args seq_args;
|
||||
};
|
||||
|
||||
|
@ -801,24 +801,6 @@ struct nfs4_readlink_res {
|
|||
struct nfs4_sequence_res seq_res;
|
||||
};
|
||||
|
||||
struct nfs4_rename_arg {
|
||||
const struct nfs_fh * old_dir;
|
||||
const struct nfs_fh * new_dir;
|
||||
const struct qstr * old_name;
|
||||
const struct qstr * new_name;
|
||||
const u32 * bitmask;
|
||||
struct nfs4_sequence_args seq_args;
|
||||
};
|
||||
|
||||
struct nfs4_rename_res {
|
||||
const struct nfs_server * server;
|
||||
struct nfs4_change_info old_cinfo;
|
||||
struct nfs_fattr * old_fattr;
|
||||
struct nfs4_change_info new_cinfo;
|
||||
struct nfs_fattr * new_fattr;
|
||||
struct nfs4_sequence_res seq_res;
|
||||
};
|
||||
|
||||
#define NFS4_SETCLIENTID_NAMELEN (127)
|
||||
struct nfs4_setclientid {
|
||||
const nfs4_verifier * sc_verifier;
|
||||
|
@ -1032,19 +1014,21 @@ struct nfs_rpc_ops {
|
|||
int (*readlink)(struct inode *, struct page *, unsigned int,
|
||||
unsigned int);
|
||||
int (*create) (struct inode *, struct dentry *,
|
||||
struct iattr *, int, struct nameidata *);
|
||||
struct iattr *, int, struct nfs_open_context *);
|
||||
int (*remove) (struct inode *, struct qstr *);
|
||||
void (*unlink_setup) (struct rpc_message *, struct inode *dir);
|
||||
int (*unlink_done) (struct rpc_task *, struct inode *);
|
||||
int (*rename) (struct inode *, struct qstr *,
|
||||
struct inode *, struct qstr *);
|
||||
void (*rename_setup) (struct rpc_message *msg, struct inode *dir);
|
||||
int (*rename_done) (struct rpc_task *task, struct inode *old_dir, struct inode *new_dir);
|
||||
int (*link) (struct inode *, struct inode *, struct qstr *);
|
||||
int (*symlink) (struct inode *, struct dentry *, struct page *,
|
||||
unsigned int, struct iattr *);
|
||||
int (*mkdir) (struct inode *, struct dentry *, struct iattr *);
|
||||
int (*rmdir) (struct inode *, struct qstr *);
|
||||
int (*readdir) (struct dentry *, struct rpc_cred *,
|
||||
u64, struct page *, unsigned int, int);
|
||||
u64, struct page **, unsigned int, int);
|
||||
int (*mknod) (struct inode *, struct dentry *, struct iattr *,
|
||||
dev_t);
|
||||
int (*statfs) (struct nfs_server *, struct nfs_fh *,
|
||||
|
@ -1054,7 +1038,7 @@ struct nfs_rpc_ops {
|
|||
int (*pathconf) (struct nfs_server *, struct nfs_fh *,
|
||||
struct nfs_pathconf *);
|
||||
int (*set_capabilities)(struct nfs_server *, struct nfs_fh *);
|
||||
__be32 *(*decode_dirent)(__be32 *, struct nfs_entry *, int plus);
|
||||
__be32 *(*decode_dirent)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int plus);
|
||||
void (*read_setup) (struct nfs_read_data *, struct rpc_message *);
|
||||
int (*read_done) (struct rpc_task *, struct nfs_read_data *);
|
||||
void (*write_setup) (struct nfs_write_data *, struct rpc_message *);
|
||||
|
@ -1065,6 +1049,10 @@ struct nfs_rpc_ops {
|
|||
int (*lock_check_bounds)(const struct file_lock *);
|
||||
void (*clear_acl_cache)(struct inode *);
|
||||
void (*close_context)(struct nfs_open_context *ctx, int);
|
||||
struct inode * (*open_context) (struct inode *dir,
|
||||
struct nfs_open_context *ctx,
|
||||
int open_flags,
|
||||
struct iattr *iattr);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -137,7 +137,6 @@ int rpcb_register(u32, u32, int, unsigned short);
|
|||
int rpcb_v4_register(const u32 program, const u32 version,
|
||||
const struct sockaddr *address,
|
||||
const char *netid);
|
||||
int rpcb_getport_sync(struct sockaddr_in *, u32, u32, int);
|
||||
void rpcb_getport_async(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,
|
|||
unsigned int);
|
||||
void xdr_inline_pages(struct xdr_buf *, 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)
|
||||
{
|
||||
|
@ -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,
|
||||
unsigned int base, unsigned int len);
|
||||
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 void xdr_read_pages(struct xdr_stream *xdr, unsigned int len);
|
||||
extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len);
|
||||
|
|
|
@ -361,13 +361,13 @@ out:
|
|||
#ifdef CONFIG_ROOT_NFS
|
||||
static int __init mount_nfs_root(void)
|
||||
{
|
||||
void *data = nfs_root_data();
|
||||
char *root_dev, *root_data;
|
||||
|
||||
create_dev("/dev/root", ROOT_DEV);
|
||||
if (data &&
|
||||
do_mount_root("/dev/root", "nfs", root_mountflags, data) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
if (nfs_root_data(&root_dev, &root_data) != 0)
|
||||
return 0;
|
||||
if (do_mount_root(root_dev, "nfs", root_mountflags, root_data) != 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -595,7 +595,7 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
|
|||
int
|
||||
rpcauth_refreshcred(struct rpc_task *task)
|
||||
{
|
||||
struct rpc_cred *cred = task->tk_rqstp->rq_cred;
|
||||
struct rpc_cred *cred;
|
||||
int err;
|
||||
|
||||
cred = task->tk_rqstp->rq_cred;
|
||||
|
|
|
@ -1675,7 +1675,7 @@ rpc_verify_header(struct rpc_task *task)
|
|||
rpcauth_invalcred(task);
|
||||
/* Ensure we obtain a new XID! */
|
||||
xprt_release(task);
|
||||
task->tk_action = call_refresh;
|
||||
task->tk_action = call_reserve;
|
||||
goto out_retry;
|
||||
case RPC_AUTH_BADCRED:
|
||||
case RPC_AUTH_BADVERF:
|
||||
|
|
|
@ -211,8 +211,9 @@ static int rpcb_create_local(void)
|
|||
*/
|
||||
clnt4 = rpc_bind_new_program(clnt, &rpcb_program, RPCBVERS_4);
|
||||
if (IS_ERR(clnt4)) {
|
||||
dprintk("RPC: failed to create local rpcbind v4 "
|
||||
"cleint (errno %ld).\n", PTR_ERR(clnt4));
|
||||
dprintk("RPC: failed to bind second program to "
|
||||
"rpcbind v4 client (errno %ld).\n",
|
||||
PTR_ERR(clnt4));
|
||||
clnt4 = NULL;
|
||||
}
|
||||
|
||||
|
@ -475,57 +476,6 @@ int rpcb_v4_register(const u32 program, const u32 version,
|
|||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpcb_getport_sync - obtain the port for an RPC service on a given host
|
||||
* @sin: address of remote peer
|
||||
* @prog: RPC program number to bind
|
||||
* @vers: RPC version number to bind
|
||||
* @prot: transport protocol to use to make this request
|
||||
*
|
||||
* Return value is the requested advertised port number,
|
||||
* or a negative errno value.
|
||||
*
|
||||
* Called from outside the RPC client in a synchronous task context.
|
||||
* Uses default timeout parameters specified by underlying transport.
|
||||
*
|
||||
* XXX: Needs to support IPv6
|
||||
*/
|
||||
int rpcb_getport_sync(struct sockaddr_in *sin, u32 prog, u32 vers, int prot)
|
||||
{
|
||||
struct rpcbind_args map = {
|
||||
.r_prog = prog,
|
||||
.r_vers = vers,
|
||||
.r_prot = prot,
|
||||
.r_port = 0,
|
||||
};
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &rpcb_procedures2[RPCBPROC_GETPORT],
|
||||
.rpc_argp = &map,
|
||||
.rpc_resp = &map,
|
||||
};
|
||||
struct rpc_clnt *rpcb_clnt;
|
||||
int status;
|
||||
|
||||
dprintk("RPC: %s(%pI4, %u, %u, %d)\n",
|
||||
__func__, &sin->sin_addr.s_addr, prog, vers, prot);
|
||||
|
||||
rpcb_clnt = rpcb_create(NULL, (struct sockaddr *)sin,
|
||||
sizeof(*sin), prot, RPCBVERS_2);
|
||||
if (IS_ERR(rpcb_clnt))
|
||||
return PTR_ERR(rpcb_clnt);
|
||||
|
||||
status = rpc_call_sync(rpcb_clnt, &msg, 0);
|
||||
rpc_shutdown_client(rpcb_clnt);
|
||||
|
||||
if (status >= 0) {
|
||||
if (map.r_port != 0)
|
||||
return map.r_port;
|
||||
status = -EACCES;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpcb_getport_sync);
|
||||
|
||||
static struct rpc_task *rpcb_call_async(struct rpc_clnt *rpcb_clnt, struct rpcbind_args *map, struct rpc_procinfo *proc)
|
||||
{
|
||||
struct rpc_message msg = {
|
||||
|
|
|
@ -908,7 +908,7 @@ static int rpciod_start(void)
|
|||
* Create the rpciod thread and wait for it to start.
|
||||
*/
|
||||
dprintk("RPC: creating workqueue rpciod\n");
|
||||
wq = create_workqueue("rpciod");
|
||||
wq = alloc_workqueue("rpciod", WQ_RESCUER, 0);
|
||||
rpciod_workqueue = wq;
|
||||
return rpciod_workqueue != NULL;
|
||||
}
|
||||
|
|
|
@ -111,6 +111,23 @@ xdr_decode_string_inplace(__be32 *p, char **sp,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(xdr_decode_string_inplace);
|
||||
|
||||
/**
|
||||
* xdr_terminate_string - '\0'-terminate a string residing in an xdr_buf
|
||||
* @buf: XDR buffer where string resides
|
||||
* @len: length of string, in bytes
|
||||
*
|
||||
*/
|
||||
void
|
||||
xdr_terminate_string(struct xdr_buf *buf, const u32 len)
|
||||
{
|
||||
char *kaddr;
|
||||
|
||||
kaddr = kmap_atomic(buf->pages[0], KM_USER0);
|
||||
kaddr[buf->page_base + len] = '\0';
|
||||
kunmap_atomic(kaddr, KM_USER0);
|
||||
}
|
||||
EXPORT_SYMBOL(xdr_terminate_string);
|
||||
|
||||
void
|
||||
xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
|
||||
unsigned int len)
|
||||
|
@ -395,24 +412,29 @@ xdr_shrink_pagelen(struct xdr_buf *buf, size_t len)
|
|||
{
|
||||
struct kvec *tail;
|
||||
size_t copy;
|
||||
char *p;
|
||||
unsigned int pglen = buf->page_len;
|
||||
unsigned int tailbuf_len;
|
||||
|
||||
tail = buf->tail;
|
||||
BUG_ON (len > pglen);
|
||||
|
||||
tailbuf_len = buf->buflen - buf->head->iov_len - buf->page_len;
|
||||
|
||||
/* Shift the tail first */
|
||||
if (tail->iov_len != 0) {
|
||||
p = (char *)tail->iov_base + len;
|
||||
if (tail->iov_len > len) {
|
||||
copy = tail->iov_len - len;
|
||||
memmove(p, tail->iov_base, copy);
|
||||
} else
|
||||
buf->buflen -= len;
|
||||
/* Copy from the inlined pages into the tail */
|
||||
if (tailbuf_len != 0) {
|
||||
unsigned int free_space = tailbuf_len - tail->iov_len;
|
||||
|
||||
if (len < free_space)
|
||||
free_space = len;
|
||||
tail->iov_len += free_space;
|
||||
|
||||
copy = len;
|
||||
if (copy > tail->iov_len)
|
||||
if (tail->iov_len > len) {
|
||||
char *p = (char *)tail->iov_base + len;
|
||||
memmove(p, tail->iov_base, tail->iov_len - len);
|
||||
} else
|
||||
copy = tail->iov_len;
|
||||
/* Copy from the inlined pages into the tail */
|
||||
_copy_from_pages((char *)tail->iov_base,
|
||||
buf->pages, buf->page_base + pglen - len,
|
||||
copy);
|
||||
|
@ -550,6 +572,27 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(xdr_init_decode);
|
||||
|
||||
/**
|
||||
* xdr_inline_peek - Allow read-ahead in the XDR data stream
|
||||
* @xdr: pointer to xdr_stream struct
|
||||
* @nbytes: number of bytes of data to decode
|
||||
*
|
||||
* Check if the input buffer is long enough to enable us to decode
|
||||
* 'nbytes' more bytes of data starting at the current position.
|
||||
* If so return the current pointer without updating the current
|
||||
* pointer position.
|
||||
*/
|
||||
__be32 * xdr_inline_peek(struct xdr_stream *xdr, size_t nbytes)
|
||||
{
|
||||
__be32 *p = xdr->p;
|
||||
__be32 *q = p + XDR_QUADLEN(nbytes);
|
||||
|
||||
if (unlikely(q > xdr->end || q < p))
|
||||
return NULL;
|
||||
return p;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xdr_inline_peek);
|
||||
|
||||
/**
|
||||
* xdr_inline_decode - Retrieve non-page XDR data to decode
|
||||
* @xdr: pointer to xdr_stream struct
|
||||
|
|
Loading…
Reference in New Issue