forked from Minki/linux
NFS Client Updates for Linux 5.18
- New Features: - Add support for 'dacl' and 'sacl' attributes - Bugfixes and Cleanups: - Fixes for reporting mapping errors - Fixes for memory allocation errors - Improve warning message when locks are lost - Update documentation for the nfs4_unique_id parameter - Add an explanation of NFSv4 client identifiers - Ensure the i_size attribute is written to the fscache storage - Fix freeing uninitialized nfs4_labels - Better handling when xprtrdma bc_serv is NULL - Marke qualified async operations as MOVEABLE tasks -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEnZ5MQTpR7cLU7KEp18tUv7ClQOsFAmKWhFQACgkQ18tUv7Cl QOszjRAAllmtKLbzOkwQwcT3e5ljh9NEJ8NL+Nv1SXjozpFY+1fuXc0ivT4rniU6 68ZHz+faK2UtLytwOO94M0jo2RCAlYS5rfnts89CpdfP3bqmGPAj0Ytw/c/vg+Qf 4eQbAzz++T35DgU7cdeKKZKg9Wtwbq7g0kYv1W8QCiCbxakSjnc/V9Ll5XhS/CAC 1WqKD90TRKUkX0Y1NNsNdXB1dJn/6QAq9B6JTjan+2Rhn7/NCTU8p98mEZGcVD7r cPHyXTqkPF4IH7lgjEMIRf6eXEzDDZNIs98QLdHJ2Gk0LxW7p7IL7VW8TKzYunvl coA1bZfYhUZBUJ+eDrrKZ5hHMSn/+eNR5iiIcfqtyU8o3J0NXAXGlLh/iJSGsxIH PjyjWSfpCgoZVPc4dG3lxR9Iu7UZeAuuB2ZoiNakUkd+UNKK5U5PpaPnYT6adaIp TegivZclCmgyLQiAdPRifDzhaL5J2pp6kVb5iMY6oX+ObyclW/UcqzKMqIKSt3R8 6+JAmZ6633ojS4r3xFsw/dlEUWuuVq7kYwXK209LqiBn5vvjWNa/WgH4MaSfnJe9 rlw+fs8Aky0w59IhzRJMMVCJ/Q2EYDKmtQLQgYVw80RBFiFgBpMW0wDqMGiddTcu 1IZ2c5+t1GxfASpyu8miexQjRJW6A2MTp0gfHGiHarxdCpaAycA= =0ccI -----END PGP SIGNATURE----- Merge tag 'nfs-for-5.19-1' of git://git.linux-nfs.org/projects/anna/linux-nfs Pull NFS client updates from Anna Schumaker: "New Features: - Add support for 'dacl' and 'sacl' attributes Bugfixes and Cleanups: - Fixes for reporting mapping errors - Fixes for memory allocation errors - Improve warning message when locks are lost - Update documentation for the nfs4_unique_id parameter - Add an explanation of NFSv4 client identifiers - Ensure the i_size attribute is written to the fscache storage - Fix freeing uninitialized nfs4_labels - Better handling when xprtrdma bc_serv is NULL - Mark qualified async operations as MOVEABLE tasks" * tag 'nfs-for-5.19-1' of git://git.linux-nfs.org/projects/anna/linux-nfs: NFSv4.1 mark qualified async operations as MOVEABLE tasks xprtrdma: treat all calls not a bcall when bc_serv is NULL NFSv4: Fix free of uninitialized nfs4_label on referral lookup. NFS: Pass i_size to fscache_unuse_cookie() when a file is released Documentation: Add an explanation of NFSv4 client identifiers NFS: update documentation for the nfs4_unique_id parameter NFS: Improve warning message when locks are lost. NFSv4.1: Enable access to the NFSv4.1 'dacl' and 'sacl' attributes NFSv4: Add encoders/decoders for the NFSv4.1 dacl and sacl attributes NFSv4: Specify the type of ACL to cache NFSv4: Don't hold the layoutget locks across multiple RPC calls pNFS/files: Fall back to I/O through the MDS on non-fatal layout errors NFS: Further fixes to the writeback error handling NFSv4/pNFS: Do not fail I/O when we fail to allocate the pNFS layout NFS: Memory allocation failures are not server fatal errors NFS: Don't report errors from nfs_pageio_complete() more than once NFS: Do not report flush errors in nfs_write_end() NFS: Don't report ENOSPC write errors twice NFS: fsync() should report filesystem errors over EINTR/ERESTARTSYS NFS: Do not report EINTR/ERESTARTSYS as mapping errors
This commit is contained in:
commit
700170bf6b
@ -36,10 +36,9 @@ administrative requirements that require particular behavior that does not
|
||||
work well as part of an nfs_client_id4 string.
|
||||
|
||||
The nfs.nfs4_unique_id boot parameter specifies a unique string that can be
|
||||
used instead of a system's node name when an NFS client identifies itself to
|
||||
a server. Thus, if the system's node name is not unique, or it changes, its
|
||||
nfs.nfs4_unique_id stays the same, preventing collision with other clients
|
||||
or loss of state during NFS reboot recovery or transparent state migration.
|
||||
used together with a system's node name when an NFS client identifies itself to
|
||||
a server. Thus, if the system's node name is not unique, its
|
||||
nfs.nfs4_unique_id can help prevent collisions with other clients.
|
||||
|
||||
The nfs.nfs4_unique_id string is typically a UUID, though it can contain
|
||||
anything that is believed to be unique across all NFS clients. An
|
||||
@ -53,8 +52,12 @@ outstanding NFSv4 state has expired, to prevent loss of NFSv4 state.
|
||||
|
||||
This string can be stored in an NFS client's grub.conf, or it can be provided
|
||||
via a net boot facility such as PXE. It may also be specified as an nfs.ko
|
||||
module parameter. Specifying a uniquifier string is not support for NFS
|
||||
clients running in containers.
|
||||
module parameter.
|
||||
|
||||
This uniquifier string will be the same for all NFS clients running in
|
||||
containers unless it is overridden by a value written to
|
||||
/sys/fs/nfs/net/nfs_client/identifier which will be local to the network
|
||||
namespace of the process which writes.
|
||||
|
||||
|
||||
The DNS resolver
|
||||
|
216
Documentation/filesystems/nfs/client-identifier.rst
Normal file
216
Documentation/filesystems/nfs/client-identifier.rst
Normal file
@ -0,0 +1,216 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=======================
|
||||
NFSv4 client identifier
|
||||
=======================
|
||||
|
||||
This document explains how the NFSv4 protocol identifies client
|
||||
instances in order to maintain file open and lock state during
|
||||
system restarts. A special identifier and principal are maintained
|
||||
on each client. These can be set by administrators, scripts
|
||||
provided by site administrators, or tools provided by Linux
|
||||
distributors.
|
||||
|
||||
There are risks if a client's NFSv4 identifier and its principal
|
||||
are not chosen carefully.
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The NFSv4 protocol uses "lease-based file locking". Leases help
|
||||
NFSv4 servers provide file lock guarantees and manage their
|
||||
resources.
|
||||
|
||||
Simply put, an NFSv4 server creates a lease for each NFSv4 client.
|
||||
The server collects each client's file open and lock state under
|
||||
the lease for that client.
|
||||
|
||||
The client is responsible for periodically renewing its leases.
|
||||
While a lease remains valid, the server holding that lease
|
||||
guarantees the file locks the client has created remain in place.
|
||||
|
||||
If a client stops renewing its lease (for example, if it crashes),
|
||||
the NFSv4 protocol allows the server to remove the client's open
|
||||
and lock state after a certain period of time. When a client
|
||||
restarts, it indicates to servers that open and lock state
|
||||
associated with its previous leases is no longer valid and can be
|
||||
destroyed immediately.
|
||||
|
||||
In addition, each NFSv4 server manages a persistent list of client
|
||||
leases. When the server restarts and clients attempt to recover
|
||||
their state, the server uses this list to distinguish amongst
|
||||
clients that held state before the server restarted and clients
|
||||
sending fresh OPEN and LOCK requests. This enables file locks to
|
||||
persist safely across server restarts.
|
||||
|
||||
NFSv4 client identifiers
|
||||
------------------------
|
||||
|
||||
Each NFSv4 client presents an identifier to NFSv4 servers so that
|
||||
they can associate the client with its lease. Each client's
|
||||
identifier consists of two elements:
|
||||
|
||||
- co_ownerid: An arbitrary but fixed string.
|
||||
|
||||
- boot verifier: A 64-bit incarnation verifier that enables a
|
||||
server to distinguish successive boot epochs of the same client.
|
||||
|
||||
The NFSv4.0 specification refers to these two items as an
|
||||
"nfs_client_id4". The NFSv4.1 specification refers to these two
|
||||
items as a "client_owner4".
|
||||
|
||||
NFSv4 servers tie this identifier to the principal and security
|
||||
flavor that the client used when presenting it. Servers use this
|
||||
principal to authorize subsequent lease modification operations
|
||||
sent by the client. Effectively this principal is a third element of
|
||||
the identifier.
|
||||
|
||||
As part of the identity presented to servers, a good
|
||||
"co_ownerid" string has several important properties:
|
||||
|
||||
- The "co_ownerid" string identifies the client during reboot
|
||||
recovery, therefore the string is persistent across client
|
||||
reboots.
|
||||
- The "co_ownerid" string helps servers distinguish the client
|
||||
from others, therefore the string is globally unique. Note
|
||||
that there is no central authority that assigns "co_ownerid"
|
||||
strings.
|
||||
- Because it often appears on the network in the clear, the
|
||||
"co_ownerid" string does not reveal private information about
|
||||
the client itself.
|
||||
- The content of the "co_ownerid" string is set and unchanging
|
||||
before the client attempts NFSv4 mounts after a restart.
|
||||
- The NFSv4 protocol places a 1024-byte limit on the size of the
|
||||
"co_ownerid" string.
|
||||
|
||||
Protecting NFSv4 lease state
|
||||
----------------------------
|
||||
|
||||
NFSv4 servers utilize the "client_owner4" as described above to
|
||||
assign a unique lease to each client. Under this scheme, there are
|
||||
circumstances where clients can interfere with each other. This is
|
||||
referred to as "lease stealing".
|
||||
|
||||
If distinct clients present the same "co_ownerid" string and use
|
||||
the same principal (for example, AUTH_SYS and UID 0), a server is
|
||||
unable to tell that the clients are not the same. Each distinct
|
||||
client presents a different boot verifier, so it appears to the
|
||||
server as if there is one client that is rebooting frequently.
|
||||
Neither client can maintain open or lock state in this scenario.
|
||||
|
||||
If distinct clients present the same "co_ownerid" string and use
|
||||
distinct principals, the server is likely to allow the first client
|
||||
to operate normally but reject subsequent clients with the same
|
||||
"co_ownerid" string.
|
||||
|
||||
If a client's "co_ownerid" string or principal are not stable,
|
||||
state recovery after a server or client reboot is not guaranteed.
|
||||
If a client unexpectedly restarts but presents a different
|
||||
"co_ownerid" string or principal to the server, the server orphans
|
||||
the client's previous open and lock state. This blocks access to
|
||||
locked files until the server removes the orphaned state.
|
||||
|
||||
If the server restarts and a client presents a changed "co_ownerid"
|
||||
string or principal to the server, the server will not allow the
|
||||
client to reclaim its open and lock state, and may give those locks
|
||||
to other clients in the meantime. This is referred to as "lock
|
||||
stealing".
|
||||
|
||||
Lease stealing and lock stealing increase the potential for denial
|
||||
of service and in rare cases even data corruption.
|
||||
|
||||
Selecting an appropriate client identifier
|
||||
------------------------------------------
|
||||
|
||||
By default, the Linux NFSv4 client implementation constructs its
|
||||
"co_ownerid" string starting with the words "Linux NFS" followed by
|
||||
the client's UTS node name (the same node name, incidentally, that
|
||||
is used as the "machine name" in an AUTH_SYS credential). In small
|
||||
deployments, this construction is usually adequate. Often, however,
|
||||
the node name by itself is not adequately unique, and can change
|
||||
unexpectedly. Problematic situations include:
|
||||
|
||||
- NFS-root (diskless) clients, where the local DCHP server (or
|
||||
equivalent) does not provide a unique host name.
|
||||
|
||||
- "Containers" within a single Linux host. If each container has
|
||||
a separate network namespace, but does not use the UTS namespace
|
||||
to provide a unique host name, then there can be multiple NFS
|
||||
client instances with the same host name.
|
||||
|
||||
- Clients across multiple administrative domains that access a
|
||||
common NFS server. If hostnames are not assigned centrally
|
||||
then uniqueness cannot be guaranteed unless a domain name is
|
||||
included in the hostname.
|
||||
|
||||
Linux provides two mechanisms to add uniqueness to its "co_ownerid"
|
||||
string:
|
||||
|
||||
nfs.nfs4_unique_id
|
||||
This module parameter can set an arbitrary uniquifier string
|
||||
via the kernel command line, or when the "nfs" module is
|
||||
loaded.
|
||||
|
||||
/sys/fs/nfs/client/net/identifier
|
||||
This virtual file, available since Linux 5.3, is local to the
|
||||
network namespace in which it is accessed and so can provide
|
||||
distinction between network namespaces (containers) when the
|
||||
hostname remains uniform.
|
||||
|
||||
Note that this file is empty on name-space creation. If the
|
||||
container system has access to some sort of per-container identity
|
||||
then that uniquifier can be used. For example, a uniquifier might
|
||||
be formed at boot using the container's internal identifier:
|
||||
|
||||
sha256sum /etc/machine-id | awk '{print $1}' \\
|
||||
> /sys/fs/nfs/client/net/identifier
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
|
||||
The use of cryptographic security for lease management operations
|
||||
is strongly encouraged.
|
||||
|
||||
If NFS with Kerberos is not configured, a Linux NFSv4 client uses
|
||||
AUTH_SYS and UID 0 as the principal part of its client identity.
|
||||
This configuration is not only insecure, it increases the risk of
|
||||
lease and lock stealing. However, it might be the only choice for
|
||||
client configurations that have no local persistent storage.
|
||||
"co_ownerid" string uniqueness and persistence is critical in this
|
||||
case.
|
||||
|
||||
When a Kerberos keytab is present on a Linux NFS client, the client
|
||||
attempts to use one of the principals in that keytab when
|
||||
identifying itself to servers. The "sec=" mount option does not
|
||||
control this behavior. Alternately, a single-user client with a
|
||||
Kerberos principal can use that principal in place of the client's
|
||||
host principal.
|
||||
|
||||
Using Kerberos for this purpose enables the client and server to
|
||||
use the same lease for operations covered by all "sec=" settings.
|
||||
Additionally, the Linux NFS client uses the RPCSEC_GSS security
|
||||
flavor with Kerberos and the integrity QOS to prevent in-transit
|
||||
modification of lease modification requests.
|
||||
|
||||
Additional notes
|
||||
----------------
|
||||
The Linux NFSv4 client establishes a single lease on each NFSv4
|
||||
server it accesses. NFSv4 mounts from a Linux NFSv4 client of a
|
||||
particular server then share that lease.
|
||||
|
||||
Once a client establishes open and lock state, the NFSv4 protocol
|
||||
enables lease state to transition to other servers, following data
|
||||
that has been migrated. This hides data migration completely from
|
||||
running applications. The Linux NFSv4 client facilitates state
|
||||
migration by presenting the same "client_owner4" to all servers it
|
||||
encounters.
|
||||
|
||||
========
|
||||
See Also
|
||||
========
|
||||
|
||||
- nfs(5)
|
||||
- kerberos(7)
|
||||
- RFC 7530 for the NFSv4.0 specification
|
||||
- RFC 8881 for the NFSv4.1 specification.
|
@ -6,6 +6,8 @@ NFS
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
client-identifier
|
||||
exporting
|
||||
pnfs
|
||||
rpc-cache
|
||||
rpc-server-gss
|
||||
|
@ -206,15 +206,16 @@ static int
|
||||
nfs_file_fsync_commit(struct file *file, int datasync)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
int ret;
|
||||
int ret, ret2;
|
||||
|
||||
dprintk("NFS: fsync file(%pD2) datasync %d\n", file, datasync);
|
||||
|
||||
nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
|
||||
ret = nfs_commit_inode(inode, FLUSH_SYNC);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return file_check_and_advance_wb_err(file);
|
||||
ret2 = file_check_and_advance_wb_err(file);
|
||||
if (ret2 < 0)
|
||||
return ret2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
@ -387,11 +388,8 @@ static int nfs_write_end(struct file *file, struct address_space *mapping,
|
||||
return status;
|
||||
NFS_I(mapping->host)->write_io += copied;
|
||||
|
||||
if (nfs_ctx_key_to_expire(ctx, mapping->host)) {
|
||||
status = nfs_wb_all(mapping->host);
|
||||
if (status < 0)
|
||||
return status;
|
||||
}
|
||||
if (nfs_ctx_key_to_expire(ctx, mapping->host))
|
||||
nfs_wb_all(mapping->host);
|
||||
|
||||
return copied;
|
||||
}
|
||||
@ -606,18 +604,6 @@ static const struct vm_operations_struct nfs_file_vm_ops = {
|
||||
.page_mkwrite = nfs_vm_page_mkwrite,
|
||||
};
|
||||
|
||||
static int nfs_need_check_write(struct file *filp, struct inode *inode,
|
||||
int error)
|
||||
{
|
||||
struct nfs_open_context *ctx;
|
||||
|
||||
ctx = nfs_file_open_context(filp);
|
||||
if (nfs_error_is_fatal_on_server(error) ||
|
||||
nfs_ctx_key_to_expire(ctx, inode))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
struct file *file = iocb->ki_filp;
|
||||
@ -645,7 +631,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
|
||||
if (iocb->ki_flags & IOCB_APPEND || iocb->ki_pos > i_size_read(inode)) {
|
||||
result = nfs_revalidate_file_size(inode, file);
|
||||
if (result)
|
||||
goto out;
|
||||
return result;
|
||||
}
|
||||
|
||||
nfs_clear_invalid_mapping(file->f_mapping);
|
||||
@ -664,6 +650,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
|
||||
|
||||
written = result;
|
||||
iocb->ki_pos += written;
|
||||
nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written);
|
||||
|
||||
if (mntflags & NFS_MOUNT_WRITE_EAGER) {
|
||||
result = filemap_fdatawrite_range(file->f_mapping,
|
||||
@ -681,17 +668,22 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
|
||||
}
|
||||
result = generic_write_sync(iocb, written);
|
||||
if (result < 0)
|
||||
goto out;
|
||||
return result;
|
||||
|
||||
out:
|
||||
/* Return error values */
|
||||
error = filemap_check_wb_err(file->f_mapping, since);
|
||||
if (nfs_need_check_write(file, inode, error)) {
|
||||
int err = nfs_wb_all(inode);
|
||||
if (err < 0)
|
||||
result = err;
|
||||
switch (error) {
|
||||
default:
|
||||
break;
|
||||
case -EDQUOT:
|
||||
case -EFBIG:
|
||||
case -ENOSPC:
|
||||
nfs_wb_all(inode);
|
||||
error = file_check_and_advance_wb_err(file);
|
||||
if (error < 0)
|
||||
result = error;
|
||||
}
|
||||
nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written);
|
||||
out:
|
||||
return result;
|
||||
|
||||
out_swapfile:
|
||||
|
@ -839,7 +839,12 @@ fl_pnfs_update_layout(struct inode *ino,
|
||||
|
||||
lseg = pnfs_update_layout(ino, ctx, pos, count, iomode, strict_iomode,
|
||||
gfp_flags);
|
||||
if (IS_ERR_OR_NULL(lseg))
|
||||
if (IS_ERR(lseg)) {
|
||||
/* Fall back to MDS on recoverable errors */
|
||||
if (!nfs_error_is_fatal_on_server(PTR_ERR(lseg)))
|
||||
lseg = NULL;
|
||||
goto out;
|
||||
} else if (!lseg)
|
||||
goto out;
|
||||
|
||||
lo = NFS_I(ino)->layout;
|
||||
|
@ -231,11 +231,10 @@ void nfs_fscache_release_file(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct nfs_fscache_inode_auxdata auxdata;
|
||||
struct fscache_cookie *cookie = nfs_i_fscache(inode);
|
||||
loff_t i_size = i_size_read(inode);
|
||||
|
||||
if (fscache_cookie_valid(cookie)) {
|
||||
nfs_fscache_update_auxdata(&auxdata, inode);
|
||||
fscache_unuse_cookie(cookie, &auxdata, NULL);
|
||||
}
|
||||
nfs_fscache_update_auxdata(&auxdata, inode);
|
||||
fscache_unuse_cookie(cookie, &auxdata, &i_size);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -841,6 +841,7 @@ static inline bool nfs_error_is_fatal_on_server(int err)
|
||||
case 0:
|
||||
case -ERESTARTSYS:
|
||||
case -EINTR:
|
||||
case -ENOMEM:
|
||||
return false;
|
||||
}
|
||||
return nfs_error_is_fatal(err);
|
||||
|
@ -417,6 +417,9 @@ static int nfs_do_refmount(struct fs_context *fc, struct rpc_clnt *client)
|
||||
fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
|
||||
if (!fs_locations)
|
||||
goto out_free;
|
||||
fs_locations->fattr = nfs_alloc_fattr();
|
||||
if (!fs_locations->fattr)
|
||||
goto out_free_2;
|
||||
|
||||
/* Get locations */
|
||||
dentry = ctx->clone_data.dentry;
|
||||
@ -427,14 +430,16 @@ static int nfs_do_refmount(struct fs_context *fc, struct rpc_clnt *client)
|
||||
err = nfs4_proc_fs_locations(client, d_inode(parent), &dentry->d_name, fs_locations, page);
|
||||
dput(parent);
|
||||
if (err != 0)
|
||||
goto out_free_2;
|
||||
goto out_free_3;
|
||||
|
||||
err = -ENOENT;
|
||||
if (fs_locations->nlocations <= 0 ||
|
||||
fs_locations->fs_path.ncomponents <= 0)
|
||||
goto out_free_2;
|
||||
goto out_free_3;
|
||||
|
||||
err = nfs_follow_referral(fc, fs_locations);
|
||||
out_free_3:
|
||||
kfree(fs_locations->fattr);
|
||||
out_free_2:
|
||||
kfree(fs_locations);
|
||||
out_free:
|
||||
|
@ -1162,7 +1162,7 @@ static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,
|
||||
{
|
||||
unsigned short task_flags = 0;
|
||||
|
||||
if (server->nfs_client->cl_minorversion)
|
||||
if (server->caps & NFS_CAP_MOVEABLE)
|
||||
task_flags = RPC_TASK_MOVEABLE;
|
||||
return nfs4_do_call_sync(clnt, server, msg, args, res, task_flags);
|
||||
}
|
||||
@ -2568,7 +2568,7 @@ static int nfs4_run_open_task(struct nfs4_opendata *data,
|
||||
};
|
||||
int status;
|
||||
|
||||
if (server->nfs_client->cl_minorversion)
|
||||
if (nfs_server_capable(dir, NFS_CAP_MOVEABLE))
|
||||
task_setup_data.flags |= RPC_TASK_MOVEABLE;
|
||||
|
||||
kref_get(&data->kref);
|
||||
@ -3098,6 +3098,10 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
|
||||
}
|
||||
|
||||
out:
|
||||
if (opendata->lgp) {
|
||||
nfs4_lgopen_release(opendata->lgp);
|
||||
opendata->lgp = NULL;
|
||||
}
|
||||
if (!opendata->cancelled)
|
||||
nfs4_sequence_free_slot(&opendata->o_res.seq_res);
|
||||
return ret;
|
||||
@ -3733,7 +3737,7 @@ int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait)
|
||||
};
|
||||
int status = -ENOMEM;
|
||||
|
||||
if (server->nfs_client->cl_minorversion)
|
||||
if (nfs_server_capable(state->inode, NFS_CAP_MOVEABLE))
|
||||
task_setup_data.flags |= RPC_TASK_MOVEABLE;
|
||||
|
||||
nfs4_state_protect(server->nfs_client, NFS_SP4_MACH_CRED_CLEANUP,
|
||||
@ -4243,6 +4247,8 @@ static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir,
|
||||
if (locations == NULL)
|
||||
goto out;
|
||||
|
||||
locations->fattr = fattr;
|
||||
|
||||
status = nfs4_proc_fs_locations(client, dir, name, locations, page);
|
||||
if (status != 0)
|
||||
goto out;
|
||||
@ -4252,17 +4258,14 @@ static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir,
|
||||
* referral. Cause us to drop into the exception handler, which
|
||||
* will kick off migration recovery.
|
||||
*/
|
||||
if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) {
|
||||
if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &fattr->fsid)) {
|
||||
dprintk("%s: server did not return a different fsid for"
|
||||
" a referral at %s\n", __func__, name->name);
|
||||
status = -NFS4ERR_MOVED;
|
||||
goto out;
|
||||
}
|
||||
/* Fixup attributes for the nfs_lookup() call to nfs_fhget() */
|
||||
nfs_fixup_referral_attributes(&locations->fattr);
|
||||
|
||||
/* replace the lookup nfs_fattr with the locations nfs_fattr */
|
||||
memcpy(fattr, &locations->fattr, sizeof(struct nfs_fattr));
|
||||
nfs_fixup_referral_attributes(fattr);
|
||||
memset(fhandle, 0, sizeof(struct nfs_fh));
|
||||
out:
|
||||
if (page)
|
||||
@ -4404,7 +4407,7 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
|
||||
};
|
||||
unsigned short task_flags = 0;
|
||||
|
||||
if (server->nfs_client->cl_minorversion)
|
||||
if (nfs_server_capable(dir, NFS_CAP_MOVEABLE))
|
||||
task_flags = RPC_TASK_MOVEABLE;
|
||||
|
||||
/* Is this is an attribute revalidation, subject to softreval? */
|
||||
@ -5768,9 +5771,17 @@ static int nfs4_proc_renew(struct nfs_client *clp, const struct cred *cred)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int nfs4_server_supports_acls(struct nfs_server *server)
|
||||
static bool nfs4_server_supports_acls(const struct nfs_server *server,
|
||||
enum nfs4_acl_type type)
|
||||
{
|
||||
return server->caps & NFS_CAP_ACLS;
|
||||
switch (type) {
|
||||
default:
|
||||
return server->attr_bitmask[0] & FATTR4_WORD0_ACL;
|
||||
case NFS4ACL_DACL:
|
||||
return server->attr_bitmask[1] & FATTR4_WORD1_DACL;
|
||||
case NFS4ACL_SACL:
|
||||
return server->attr_bitmask[1] & FATTR4_WORD1_SACL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that
|
||||
@ -5809,6 +5820,7 @@ unwind:
|
||||
}
|
||||
|
||||
struct nfs4_cached_acl {
|
||||
enum nfs4_acl_type type;
|
||||
int cached;
|
||||
size_t len;
|
||||
char data[];
|
||||
@ -5829,7 +5841,8 @@ static void nfs4_zap_acl_attr(struct inode *inode)
|
||||
nfs4_set_cached_acl(inode, NULL);
|
||||
}
|
||||
|
||||
static inline ssize_t nfs4_read_cached_acl(struct inode *inode, char *buf, size_t buflen)
|
||||
static ssize_t nfs4_read_cached_acl(struct inode *inode, char *buf,
|
||||
size_t buflen, enum nfs4_acl_type type)
|
||||
{
|
||||
struct nfs_inode *nfsi = NFS_I(inode);
|
||||
struct nfs4_cached_acl *acl;
|
||||
@ -5839,6 +5852,8 @@ static inline ssize_t nfs4_read_cached_acl(struct inode *inode, char *buf, size_
|
||||
acl = nfsi->nfs4_acl;
|
||||
if (acl == NULL)
|
||||
goto out;
|
||||
if (acl->type != type)
|
||||
goto out;
|
||||
if (buf == NULL) /* user is just asking for length */
|
||||
goto out_len;
|
||||
if (acl->cached == 0)
|
||||
@ -5854,7 +5869,9 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size_t pgbase, size_t acl_len)
|
||||
static void nfs4_write_cached_acl(struct inode *inode, struct page **pages,
|
||||
size_t pgbase, size_t acl_len,
|
||||
enum nfs4_acl_type type)
|
||||
{
|
||||
struct nfs4_cached_acl *acl;
|
||||
size_t buflen = sizeof(*acl) + acl_len;
|
||||
@ -5871,6 +5888,7 @@ static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size
|
||||
goto out;
|
||||
acl->cached = 0;
|
||||
}
|
||||
acl->type = type;
|
||||
acl->len = acl_len;
|
||||
out:
|
||||
nfs4_set_cached_acl(inode, acl);
|
||||
@ -5886,14 +5904,17 @@ out:
|
||||
* length. The next getxattr call will then produce another round trip to
|
||||
* the server, this time with the input buf of the required size.
|
||||
*/
|
||||
static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
|
||||
static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf,
|
||||
size_t buflen, enum nfs4_acl_type type)
|
||||
{
|
||||
struct page **pages;
|
||||
struct nfs_getaclargs args = {
|
||||
.fh = NFS_FH(inode),
|
||||
.acl_type = type,
|
||||
.acl_len = buflen,
|
||||
};
|
||||
struct nfs_getaclres res = {
|
||||
.acl_type = type,
|
||||
.acl_len = buflen,
|
||||
};
|
||||
struct rpc_message msg = {
|
||||
@ -5943,7 +5964,8 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
|
||||
ret = -ERANGE;
|
||||
goto out_free;
|
||||
}
|
||||
nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len);
|
||||
nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len,
|
||||
type);
|
||||
if (buf) {
|
||||
if (res.acl_len > buflen) {
|
||||
ret = -ERANGE;
|
||||
@ -5963,14 +5985,15 @@ out_free:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
|
||||
static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf,
|
||||
size_t buflen, enum nfs4_acl_type type)
|
||||
{
|
||||
struct nfs4_exception exception = {
|
||||
.interruptible = true,
|
||||
};
|
||||
ssize_t ret;
|
||||
do {
|
||||
ret = __nfs4_get_acl_uncached(inode, buf, buflen);
|
||||
ret = __nfs4_get_acl_uncached(inode, buf, buflen, type);
|
||||
trace_nfs4_get_acl(inode, ret);
|
||||
if (ret >= 0)
|
||||
break;
|
||||
@ -5979,34 +6002,37 @@ static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bufl
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
|
||||
static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen,
|
||||
enum nfs4_acl_type type)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(inode);
|
||||
int ret;
|
||||
|
||||
if (!nfs4_server_supports_acls(server))
|
||||
if (!nfs4_server_supports_acls(server, type))
|
||||
return -EOPNOTSUPP;
|
||||
ret = nfs_revalidate_inode(inode, NFS_INO_INVALID_CHANGE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
|
||||
nfs_zap_acl_cache(inode);
|
||||
ret = nfs4_read_cached_acl(inode, buf, buflen);
|
||||
ret = nfs4_read_cached_acl(inode, buf, buflen, type);
|
||||
if (ret != -ENOENT)
|
||||
/* -ENOENT is returned if there is no ACL or if there is an ACL
|
||||
* but no cached acl data, just the acl length */
|
||||
return ret;
|
||||
return nfs4_get_acl_uncached(inode, buf, buflen);
|
||||
return nfs4_get_acl_uncached(inode, buf, buflen, type);
|
||||
}
|
||||
|
||||
static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
|
||||
static int __nfs4_proc_set_acl(struct inode *inode, const void *buf,
|
||||
size_t buflen, enum nfs4_acl_type type)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(inode);
|
||||
struct page *pages[NFS4ACL_MAXPAGES];
|
||||
struct nfs_setaclargs arg = {
|
||||
.fh = NFS_FH(inode),
|
||||
.acl_pages = pages,
|
||||
.acl_len = buflen,
|
||||
.fh = NFS_FH(inode),
|
||||
.acl_type = type,
|
||||
.acl_len = buflen,
|
||||
.acl_pages = pages,
|
||||
};
|
||||
struct nfs_setaclres res;
|
||||
struct rpc_message msg = {
|
||||
@ -6020,7 +6046,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
|
||||
/* You can't remove system.nfs4_acl: */
|
||||
if (buflen == 0)
|
||||
return -EINVAL;
|
||||
if (!nfs4_server_supports_acls(server))
|
||||
if (!nfs4_server_supports_acls(server, type))
|
||||
return -EOPNOTSUPP;
|
||||
if (npages > ARRAY_SIZE(pages))
|
||||
return -ERANGE;
|
||||
@ -6051,12 +6077,13 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
|
||||
static int nfs4_proc_set_acl(struct inode *inode, const void *buf,
|
||||
size_t buflen, enum nfs4_acl_type type)
|
||||
{
|
||||
struct nfs4_exception exception = { };
|
||||
int err;
|
||||
do {
|
||||
err = __nfs4_proc_set_acl(inode, buf, buflen);
|
||||
err = __nfs4_proc_set_acl(inode, buf, buflen, type);
|
||||
trace_nfs4_set_acl(inode, err);
|
||||
if (err == -NFS4ERR_BADOWNER || err == -NFS4ERR_BADNAME) {
|
||||
/*
|
||||
@ -6612,10 +6639,13 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred,
|
||||
.rpc_client = server->client,
|
||||
.rpc_message = &msg,
|
||||
.callback_ops = &nfs4_delegreturn_ops,
|
||||
.flags = RPC_TASK_ASYNC | RPC_TASK_TIMEOUT | RPC_TASK_MOVEABLE,
|
||||
.flags = RPC_TASK_ASYNC | RPC_TASK_TIMEOUT,
|
||||
};
|
||||
int status = 0;
|
||||
|
||||
if (nfs_server_capable(inode, NFS_CAP_MOVEABLE))
|
||||
task_setup_data.flags |= RPC_TASK_MOVEABLE;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
@ -6929,10 +6959,8 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
|
||||
.workqueue = nfsiod_workqueue,
|
||||
.flags = RPC_TASK_ASYNC,
|
||||
};
|
||||
struct nfs_client *client =
|
||||
NFS_SERVER(lsp->ls_state->inode)->nfs_client;
|
||||
|
||||
if (client->cl_minorversion)
|
||||
if (nfs_server_capable(lsp->ls_state->inode, NFS_CAP_MOVEABLE))
|
||||
task_setup_data.flags |= RPC_TASK_MOVEABLE;
|
||||
|
||||
nfs4_state_protect(NFS_SERVER(lsp->ls_state->inode)->nfs_client,
|
||||
@ -7203,9 +7231,8 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
|
||||
.flags = RPC_TASK_ASYNC | RPC_TASK_CRED_NOREF,
|
||||
};
|
||||
int ret;
|
||||
struct nfs_client *client = NFS_SERVER(state->inode)->nfs_client;
|
||||
|
||||
if (client->cl_minorversion)
|
||||
if (nfs_server_capable(state->inode, NFS_CAP_MOVEABLE))
|
||||
task_setup_data.flags |= RPC_TASK_MOVEABLE;
|
||||
|
||||
data = nfs4_alloc_lockdata(fl, nfs_file_open_context(fl->fl_file),
|
||||
@ -7655,21 +7682,70 @@ static int nfs4_xattr_set_nfs4_acl(const struct xattr_handler *handler,
|
||||
const char *key, const void *buf,
|
||||
size_t buflen, int flags)
|
||||
{
|
||||
return nfs4_proc_set_acl(inode, buf, buflen);
|
||||
return nfs4_proc_set_acl(inode, buf, buflen, NFS4ACL_ACL);
|
||||
}
|
||||
|
||||
static int nfs4_xattr_get_nfs4_acl(const struct xattr_handler *handler,
|
||||
struct dentry *unused, struct inode *inode,
|
||||
const char *key, void *buf, size_t buflen)
|
||||
{
|
||||
return nfs4_proc_get_acl(inode, buf, buflen);
|
||||
return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_ACL);
|
||||
}
|
||||
|
||||
static bool nfs4_xattr_list_nfs4_acl(struct dentry *dentry)
|
||||
{
|
||||
return nfs4_server_supports_acls(NFS_SERVER(d_inode(dentry)));
|
||||
return nfs4_server_supports_acls(NFS_SB(dentry->d_sb), NFS4ACL_ACL);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
#define XATTR_NAME_NFSV4_DACL "system.nfs4_dacl"
|
||||
|
||||
static int nfs4_xattr_set_nfs4_dacl(const struct xattr_handler *handler,
|
||||
struct user_namespace *mnt_userns,
|
||||
struct dentry *unused, struct inode *inode,
|
||||
const char *key, const void *buf,
|
||||
size_t buflen, int flags)
|
||||
{
|
||||
return nfs4_proc_set_acl(inode, buf, buflen, NFS4ACL_DACL);
|
||||
}
|
||||
|
||||
static int nfs4_xattr_get_nfs4_dacl(const struct xattr_handler *handler,
|
||||
struct dentry *unused, struct inode *inode,
|
||||
const char *key, void *buf, size_t buflen)
|
||||
{
|
||||
return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_DACL);
|
||||
}
|
||||
|
||||
static bool nfs4_xattr_list_nfs4_dacl(struct dentry *dentry)
|
||||
{
|
||||
return nfs4_server_supports_acls(NFS_SB(dentry->d_sb), NFS4ACL_DACL);
|
||||
}
|
||||
|
||||
#define XATTR_NAME_NFSV4_SACL "system.nfs4_sacl"
|
||||
|
||||
static int nfs4_xattr_set_nfs4_sacl(const struct xattr_handler *handler,
|
||||
struct user_namespace *mnt_userns,
|
||||
struct dentry *unused, struct inode *inode,
|
||||
const char *key, const void *buf,
|
||||
size_t buflen, int flags)
|
||||
{
|
||||
return nfs4_proc_set_acl(inode, buf, buflen, NFS4ACL_SACL);
|
||||
}
|
||||
|
||||
static int nfs4_xattr_get_nfs4_sacl(const struct xattr_handler *handler,
|
||||
struct dentry *unused, struct inode *inode,
|
||||
const char *key, void *buf, size_t buflen)
|
||||
{
|
||||
return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_SACL);
|
||||
}
|
||||
|
||||
static bool nfs4_xattr_list_nfs4_sacl(struct dentry *dentry)
|
||||
{
|
||||
return nfs4_server_supports_acls(NFS_SB(dentry->d_sb), NFS4ACL_SACL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
|
||||
|
||||
static int nfs4_xattr_set_nfs4_label(const struct xattr_handler *handler,
|
||||
@ -7902,7 +7978,7 @@ static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,
|
||||
else
|
||||
bitmask[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
|
||||
|
||||
nfs_fattr_init(&fs_locations->fattr);
|
||||
nfs_fattr_init(fs_locations->fattr);
|
||||
fs_locations->server = server;
|
||||
fs_locations->nlocations = 0;
|
||||
status = nfs4_call_sync(client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
||||
@ -7967,7 +8043,7 @@ static int _nfs40_proc_get_locations(struct nfs_server *server,
|
||||
unsigned long now = jiffies;
|
||||
int status;
|
||||
|
||||
nfs_fattr_init(&locations->fattr);
|
||||
nfs_fattr_init(locations->fattr);
|
||||
locations->server = server;
|
||||
locations->nlocations = 0;
|
||||
|
||||
@ -8032,7 +8108,7 @@ static int _nfs41_proc_get_locations(struct nfs_server *server,
|
||||
};
|
||||
int status;
|
||||
|
||||
nfs_fattr_init(&locations->fattr);
|
||||
nfs_fattr_init(locations->fattr);
|
||||
locations->server = server;
|
||||
locations->nlocations = 0;
|
||||
|
||||
@ -10391,7 +10467,8 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
|
||||
| NFS_CAP_POSIX_LOCK
|
||||
| NFS_CAP_STATEID_NFSV41
|
||||
| NFS_CAP_ATOMIC_OPEN_V1
|
||||
| NFS_CAP_LGOPEN,
|
||||
| NFS_CAP_LGOPEN
|
||||
| NFS_CAP_MOVEABLE,
|
||||
.init_client = nfs41_init_client,
|
||||
.shutdown_client = nfs41_shutdown_client,
|
||||
.match_stateid = nfs41_match_stateid,
|
||||
@ -10426,7 +10503,8 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
|
||||
| NFS_CAP_LAYOUTSTATS
|
||||
| NFS_CAP_CLONE
|
||||
| NFS_CAP_LAYOUTERROR
|
||||
| NFS_CAP_READ_PLUS,
|
||||
| NFS_CAP_READ_PLUS
|
||||
| NFS_CAP_MOVEABLE,
|
||||
.init_client = nfs41_init_client,
|
||||
.shutdown_client = nfs41_shutdown_client,
|
||||
.match_stateid = nfs41_match_stateid,
|
||||
@ -10587,6 +10665,22 @@ static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
|
||||
.set = nfs4_xattr_set_nfs4_acl,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
static const struct xattr_handler nfs4_xattr_nfs4_dacl_handler = {
|
||||
.name = XATTR_NAME_NFSV4_DACL,
|
||||
.list = nfs4_xattr_list_nfs4_dacl,
|
||||
.get = nfs4_xattr_get_nfs4_dacl,
|
||||
.set = nfs4_xattr_set_nfs4_dacl,
|
||||
};
|
||||
|
||||
static const struct xattr_handler nfs4_xattr_nfs4_sacl_handler = {
|
||||
.name = XATTR_NAME_NFSV4_SACL,
|
||||
.list = nfs4_xattr_list_nfs4_sacl,
|
||||
.get = nfs4_xattr_get_nfs4_sacl,
|
||||
.set = nfs4_xattr_set_nfs4_sacl,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NFS_V4_2
|
||||
static const struct xattr_handler nfs4_xattr_nfs4_user_handler = {
|
||||
.prefix = XATTR_USER_PREFIX,
|
||||
@ -10597,6 +10691,10 @@ static const struct xattr_handler nfs4_xattr_nfs4_user_handler = {
|
||||
|
||||
const struct xattr_handler *nfs4_xattr_handlers[] = {
|
||||
&nfs4_xattr_nfs4_acl_handler,
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
&nfs4_xattr_nfs4_dacl_handler,
|
||||
&nfs4_xattr_nfs4_sacl_handler,
|
||||
#endif
|
||||
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
|
||||
&nfs4_xattr_nfs4_label_handler,
|
||||
#endif
|
||||
|
@ -1602,7 +1602,8 @@ static inline void nfs42_complete_copies(struct nfs4_state_owner *sp,
|
||||
#endif /* CONFIG_NFS_V4_2 */
|
||||
|
||||
static int __nfs4_reclaim_open_state(struct nfs4_state_owner *sp, struct nfs4_state *state,
|
||||
const struct nfs4_state_recovery_ops *ops)
|
||||
const struct nfs4_state_recovery_ops *ops,
|
||||
int *lost_locks)
|
||||
{
|
||||
struct nfs4_lock_state *lock;
|
||||
int status;
|
||||
@ -1620,7 +1621,7 @@ static int __nfs4_reclaim_open_state(struct nfs4_state_owner *sp, struct nfs4_st
|
||||
list_for_each_entry(lock, &state->lock_states, ls_locks) {
|
||||
trace_nfs4_state_lock_reclaim(state, lock);
|
||||
if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags))
|
||||
pr_warn_ratelimited("NFS: %s: Lock reclaim failed!\n", __func__);
|
||||
*lost_locks += 1;
|
||||
}
|
||||
spin_unlock(&state->state_lock);
|
||||
}
|
||||
@ -1630,7 +1631,9 @@ static int __nfs4_reclaim_open_state(struct nfs4_state_owner *sp, struct nfs4_st
|
||||
return status;
|
||||
}
|
||||
|
||||
static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs4_state_recovery_ops *ops)
|
||||
static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp,
|
||||
const struct nfs4_state_recovery_ops *ops,
|
||||
int *lost_locks)
|
||||
{
|
||||
struct nfs4_state *state;
|
||||
unsigned int loop = 0;
|
||||
@ -1666,7 +1669,7 @@ restart:
|
||||
#endif /* CONFIG_NFS_V4_2 */
|
||||
refcount_inc(&state->count);
|
||||
spin_unlock(&sp->so_lock);
|
||||
status = __nfs4_reclaim_open_state(sp, state, ops);
|
||||
status = __nfs4_reclaim_open_state(sp, state, ops, lost_locks);
|
||||
|
||||
switch (status) {
|
||||
default:
|
||||
@ -1909,6 +1912,7 @@ static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recov
|
||||
struct rb_node *pos;
|
||||
LIST_HEAD(freeme);
|
||||
int status = 0;
|
||||
int lost_locks = 0;
|
||||
|
||||
restart:
|
||||
rcu_read_lock();
|
||||
@ -1928,8 +1932,11 @@ restart:
|
||||
spin_unlock(&clp->cl_lock);
|
||||
rcu_read_unlock();
|
||||
|
||||
status = nfs4_reclaim_open_state(sp, ops);
|
||||
status = nfs4_reclaim_open_state(sp, ops, &lost_locks);
|
||||
if (status < 0) {
|
||||
if (lost_locks)
|
||||
pr_warn("NFS: %s: lost %d locks\n",
|
||||
clp->cl_hostname, lost_locks);
|
||||
set_bit(ops->owner_flag_bit, &sp->so_flags);
|
||||
nfs4_put_state_owner(sp);
|
||||
status = nfs4_recovery_handle_error(clp, status);
|
||||
@ -1943,6 +1950,9 @@ restart:
|
||||
}
|
||||
rcu_read_unlock();
|
||||
nfs4_free_state_owners(&freeme);
|
||||
if (lost_locks)
|
||||
pr_warn("NFS: %s: lost %d locks\n",
|
||||
clp->cl_hostname, lost_locks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2106,6 +2116,11 @@ static int nfs4_try_migration(struct nfs_server *server, const struct cred *cred
|
||||
dprintk("<-- %s: no memory\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
locations->fattr = nfs_alloc_fattr();
|
||||
if (locations->fattr == NULL) {
|
||||
dprintk("<-- %s: no memory\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
inode = d_inode(server->super->s_root);
|
||||
result = nfs4_proc_get_locations(server, NFS_FH(inode), locations,
|
||||
@ -2120,7 +2135,7 @@ static int nfs4_try_migration(struct nfs_server *server, const struct cred *cred
|
||||
if (!locations->nlocations)
|
||||
goto out;
|
||||
|
||||
if (!(locations->fattr.valid & NFS_ATTR_FATTR_V4_LOCATIONS)) {
|
||||
if (!(locations->fattr->valid & NFS_ATTR_FATTR_V4_LOCATIONS)) {
|
||||
dprintk("<-- %s: No fs_locations data, migration skipped\n",
|
||||
__func__);
|
||||
goto out;
|
||||
@ -2145,6 +2160,8 @@ static int nfs4_try_migration(struct nfs_server *server, const struct cred *cred
|
||||
out:
|
||||
if (page != NULL)
|
||||
__free_page(page);
|
||||
if (locations != NULL)
|
||||
kfree(locations->fattr);
|
||||
kfree(locations);
|
||||
if (result) {
|
||||
pr_err("NFS: migration recovery failed (server %s)\n",
|
||||
|
@ -1680,19 +1680,35 @@ encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
|
||||
encode_op_hdr(xdr, OP_RESTOREFH, decode_restorefh_maxsz, hdr);
|
||||
}
|
||||
|
||||
static void
|
||||
encode_setacl(struct xdr_stream *xdr, const struct nfs_setaclargs *arg,
|
||||
struct compound_hdr *hdr)
|
||||
static void nfs4_acltype_to_bitmap(enum nfs4_acl_type type, __u32 bitmap[2])
|
||||
{
|
||||
__be32 *p;
|
||||
switch (type) {
|
||||
default:
|
||||
bitmap[0] = FATTR4_WORD0_ACL;
|
||||
bitmap[1] = 0;
|
||||
break;
|
||||
case NFS4ACL_DACL:
|
||||
bitmap[0] = 0;
|
||||
bitmap[1] = FATTR4_WORD1_DACL;
|
||||
break;
|
||||
case NFS4ACL_SACL:
|
||||
bitmap[0] = 0;
|
||||
bitmap[1] = FATTR4_WORD1_SACL;
|
||||
}
|
||||
}
|
||||
|
||||
static void encode_setacl(struct xdr_stream *xdr,
|
||||
const struct nfs_setaclargs *arg,
|
||||
struct compound_hdr *hdr)
|
||||
{
|
||||
__u32 bitmap[2];
|
||||
|
||||
nfs4_acltype_to_bitmap(arg->acl_type, bitmap);
|
||||
|
||||
encode_op_hdr(xdr, OP_SETATTR, decode_setacl_maxsz, hdr);
|
||||
encode_nfs4_stateid(xdr, &zero_stateid);
|
||||
p = reserve_space(xdr, 2*4);
|
||||
*p++ = cpu_to_be32(1);
|
||||
*p = cpu_to_be32(FATTR4_WORD0_ACL);
|
||||
p = reserve_space(xdr, 4);
|
||||
*p = cpu_to_be32(arg->acl_len);
|
||||
xdr_encode_bitmap4(xdr, bitmap, ARRAY_SIZE(bitmap));
|
||||
encode_uint32(xdr, arg->acl_len);
|
||||
xdr_write_pages(xdr, arg->acl_pages, 0, arg->acl_len);
|
||||
}
|
||||
|
||||
@ -2587,11 +2603,11 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
|
||||
struct compound_hdr hdr = {
|
||||
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
|
||||
};
|
||||
const __u32 nfs4_acl_bitmap[1] = {
|
||||
[0] = FATTR4_WORD0_ACL,
|
||||
};
|
||||
__u32 nfs4_acl_bitmap[2];
|
||||
uint32_t replen;
|
||||
|
||||
nfs4_acltype_to_bitmap(args->acl_type, nfs4_acl_bitmap);
|
||||
|
||||
encode_compound_hdr(xdr, req, &hdr);
|
||||
encode_sequence(xdr, &args->seq_args, &hdr);
|
||||
encode_putfh(xdr, args->fh, &hdr);
|
||||
@ -5386,7 +5402,7 @@ decode_restorefh(struct xdr_stream *xdr)
|
||||
}
|
||||
|
||||
static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
|
||||
struct nfs_getaclres *res)
|
||||
struct nfs_getaclres *res, enum nfs4_acl_type type)
|
||||
{
|
||||
unsigned int savep;
|
||||
uint32_t attrlen,
|
||||
@ -5404,26 +5420,39 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
|
||||
if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
|
||||
goto out;
|
||||
|
||||
if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U)))
|
||||
return -EIO;
|
||||
if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
|
||||
switch (type) {
|
||||
default:
|
||||
if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U)))
|
||||
return -EIO;
|
||||
if (!(bitmap[0] & FATTR4_WORD0_ACL))
|
||||
return -EOPNOTSUPP;
|
||||
break;
|
||||
case NFS4ACL_DACL:
|
||||
if (unlikely(bitmap[0] || bitmap[1] & (FATTR4_WORD1_DACL - 1U)))
|
||||
return -EIO;
|
||||
if (!(bitmap[1] & FATTR4_WORD1_DACL))
|
||||
return -EOPNOTSUPP;
|
||||
break;
|
||||
case NFS4ACL_SACL:
|
||||
if (unlikely(bitmap[0] || bitmap[1] & (FATTR4_WORD1_SACL - 1U)))
|
||||
return -EIO;
|
||||
if (!(bitmap[1] & FATTR4_WORD1_SACL))
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* The bitmap (xdr len + bitmaps) and the attr xdr len words
|
||||
* are stored with the acl data to handle the problem of
|
||||
* variable length bitmaps.*/
|
||||
res->acl_data_offset = xdr_page_pos(xdr);
|
||||
res->acl_len = attrlen;
|
||||
|
||||
/* Check for receive buffer overflow */
|
||||
if (res->acl_len > xdr_stream_remaining(xdr) ||
|
||||
res->acl_len + res->acl_data_offset > xdr->buf->page_len) {
|
||||
res->acl_flags |= NFS4_ACL_TRUNC;
|
||||
dprintk("NFS: acl reply: attrlen %u > page_len %zu\n",
|
||||
attrlen, xdr_stream_remaining(xdr));
|
||||
}
|
||||
} else
|
||||
status = -EOPNOTSUPP;
|
||||
/* The bitmap (xdr len + bitmaps) and the attr xdr len words
|
||||
* are stored with the acl data to handle the problem of
|
||||
* variable length bitmaps.*/
|
||||
res->acl_data_offset = xdr_page_pos(xdr);
|
||||
res->acl_len = attrlen;
|
||||
|
||||
/* Check for receive buffer overflow */
|
||||
if (res->acl_len > xdr_stream_remaining(xdr) ||
|
||||
res->acl_len + res->acl_data_offset > xdr->buf->page_len) {
|
||||
res->acl_flags |= NFS4_ACL_TRUNC;
|
||||
dprintk("NFS: acl reply: attrlen %u > page_len %zu\n",
|
||||
attrlen, xdr_stream_remaining(xdr));
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
@ -6486,7 +6515,7 @@ nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
|
||||
status = decode_putfh(xdr);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_getacl(xdr, rqstp, res);
|
||||
status = decode_getacl(xdr, rqstp, res, res->acl_type);
|
||||
|
||||
out:
|
||||
return status;
|
||||
@ -7051,7 +7080,7 @@ static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req,
|
||||
if (res->migration) {
|
||||
xdr_enter_page(xdr, PAGE_SIZE);
|
||||
status = decode_getfattr_generic(xdr,
|
||||
&res->fs_locations->fattr,
|
||||
res->fs_locations->fattr,
|
||||
NULL, res->fs_locations,
|
||||
res->fs_locations->server);
|
||||
if (status)
|
||||
@ -7064,7 +7093,7 @@ static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req,
|
||||
goto out;
|
||||
xdr_enter_page(xdr, PAGE_SIZE);
|
||||
status = decode_getfattr_generic(xdr,
|
||||
&res->fs_locations->fattr,
|
||||
res->fs_locations->fattr,
|
||||
NULL, res->fs_locations,
|
||||
res->fs_locations->server);
|
||||
}
|
||||
|
@ -767,6 +767,9 @@ int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr,
|
||||
.flags = RPC_TASK_ASYNC | flags,
|
||||
};
|
||||
|
||||
if (nfs_server_capable(hdr->inode, NFS_CAP_MOVEABLE))
|
||||
task_setup_data.flags |= RPC_TASK_MOVEABLE;
|
||||
|
||||
hdr->rw_ops->rw_initiate(hdr, &msg, rpc_ops, &task_setup_data, how);
|
||||
|
||||
dprintk("NFS: initiated pgio call "
|
||||
|
@ -2000,6 +2000,7 @@ lookup_again:
|
||||
lo = pnfs_find_alloc_layout(ino, ctx, gfp_flags);
|
||||
if (lo == NULL) {
|
||||
spin_unlock(&ino->i_lock);
|
||||
lseg = ERR_PTR(-ENOMEM);
|
||||
trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg,
|
||||
PNFS_UPDATE_LAYOUT_NOMEM);
|
||||
goto out;
|
||||
@ -2128,6 +2129,7 @@ lookup_again:
|
||||
|
||||
lgp = pnfs_alloc_init_layoutget_args(ino, ctx, &stateid, &arg, gfp_flags);
|
||||
if (!lgp) {
|
||||
lseg = ERR_PTR(-ENOMEM);
|
||||
trace_pnfs_update_layout(ino, pos, count, iomode, lo, NULL,
|
||||
PNFS_UPDATE_LAYOUT_NOMEM);
|
||||
nfs_layoutget_end(lo);
|
||||
|
@ -102,6 +102,10 @@ static void nfs_do_call_unlink(struct inode *inode, struct nfs_unlinkdata *data)
|
||||
};
|
||||
struct rpc_task *task;
|
||||
struct inode *dir = d_inode(data->dentry->d_parent);
|
||||
|
||||
if (nfs_server_capable(inode, NFS_CAP_MOVEABLE))
|
||||
task_setup_data.flags |= RPC_TASK_MOVEABLE;
|
||||
|
||||
nfs_sb_active(dir->i_sb);
|
||||
data->args.fh = NFS_FH(dir);
|
||||
nfs_fattr_init(data->res.dir_attr);
|
||||
@ -344,6 +348,10 @@ nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
|
||||
.flags = RPC_TASK_ASYNC | RPC_TASK_CRED_NOREF,
|
||||
};
|
||||
|
||||
if (nfs_server_capable(old_dir, NFS_CAP_MOVEABLE) &&
|
||||
nfs_server_capable(new_dir, NFS_CAP_MOVEABLE))
|
||||
task_setup_data.flags |= RPC_TASK_MOVEABLE;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
@ -603,8 +603,9 @@ static void nfs_write_error(struct nfs_page *req, int error)
|
||||
* Find an associated nfs write request, and prepare to flush it out
|
||||
* May return an error if the user signalled nfs_wait_on_request().
|
||||
*/
|
||||
static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
|
||||
struct page *page)
|
||||
static int nfs_page_async_flush(struct page *page,
|
||||
struct writeback_control *wbc,
|
||||
struct nfs_pageio_descriptor *pgio)
|
||||
{
|
||||
struct nfs_page *req;
|
||||
int ret = 0;
|
||||
@ -630,11 +631,11 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
|
||||
/*
|
||||
* Remove the problematic req upon fatal errors on the server
|
||||
*/
|
||||
if (nfs_error_is_fatal(ret)) {
|
||||
if (nfs_error_is_fatal_on_server(ret))
|
||||
goto out_launder;
|
||||
} else
|
||||
ret = -EAGAIN;
|
||||
if (nfs_error_is_fatal_on_server(ret))
|
||||
goto out_launder;
|
||||
if (wbc->sync_mode == WB_SYNC_NONE)
|
||||
ret = AOP_WRITEPAGE_ACTIVATE;
|
||||
redirty_page_for_writepage(wbc, page);
|
||||
nfs_redirty_request(req);
|
||||
pgio->pg_error = 0;
|
||||
} else
|
||||
@ -650,15 +651,8 @@ out_launder:
|
||||
static int nfs_do_writepage(struct page *page, struct writeback_control *wbc,
|
||||
struct nfs_pageio_descriptor *pgio)
|
||||
{
|
||||
int ret;
|
||||
|
||||
nfs_pageio_cond_complete(pgio, page_index(page));
|
||||
ret = nfs_page_async_flush(pgio, page);
|
||||
if (ret == -EAGAIN) {
|
||||
redirty_page_for_writepage(wbc, page);
|
||||
ret = AOP_WRITEPAGE_ACTIVATE;
|
||||
}
|
||||
return ret;
|
||||
return nfs_page_async_flush(page, wbc, pgio);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -681,11 +675,7 @@ static int nfs_writepage_locked(struct page *page,
|
||||
err = nfs_do_writepage(page, wbc, &pgio);
|
||||
pgio.pg_error = 0;
|
||||
nfs_pageio_complete(&pgio);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (nfs_error_is_fatal(pgio.pg_error))
|
||||
return pgio.pg_error;
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
int nfs_writepage(struct page *page, struct writeback_control *wbc)
|
||||
@ -737,19 +727,19 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
|
||||
priority = wb_priority(wbc);
|
||||
}
|
||||
|
||||
nfs_pageio_init_write(&pgio, inode, priority, false,
|
||||
&nfs_async_write_completion_ops);
|
||||
pgio.pg_io_completion = ioc;
|
||||
err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio);
|
||||
pgio.pg_error = 0;
|
||||
nfs_pageio_complete(&pgio);
|
||||
do {
|
||||
nfs_pageio_init_write(&pgio, inode, priority, false,
|
||||
&nfs_async_write_completion_ops);
|
||||
pgio.pg_io_completion = ioc;
|
||||
err = write_cache_pages(mapping, wbc, nfs_writepages_callback,
|
||||
&pgio);
|
||||
pgio.pg_error = 0;
|
||||
nfs_pageio_complete(&pgio);
|
||||
} while (err < 0 && !nfs_error_is_fatal(err));
|
||||
nfs_io_completion_put(ioc);
|
||||
|
||||
if (err < 0)
|
||||
goto out_err;
|
||||
err = pgio.pg_error;
|
||||
if (nfs_error_is_fatal(err))
|
||||
goto out_err;
|
||||
return 0;
|
||||
out_err:
|
||||
return err;
|
||||
@ -1444,7 +1434,7 @@ static void nfs_async_write_error(struct list_head *head, int error)
|
||||
while (!list_empty(head)) {
|
||||
req = nfs_list_entry(head->next);
|
||||
nfs_list_remove_request(req);
|
||||
if (nfs_error_is_fatal(error))
|
||||
if (nfs_error_is_fatal_on_server(error))
|
||||
nfs_write_error(req, error);
|
||||
else
|
||||
nfs_redirty_request(req);
|
||||
@ -1719,6 +1709,10 @@ int nfs_initiate_commit(struct rpc_clnt *clnt, struct nfs_commit_data *data,
|
||||
.flags = RPC_TASK_ASYNC | flags,
|
||||
.priority = priority,
|
||||
};
|
||||
|
||||
if (nfs_server_capable(data->inode, NFS_CAP_MOVEABLE))
|
||||
task_setup_data.flags |= RPC_TASK_MOVEABLE;
|
||||
|
||||
/* Set up the initial task struct. */
|
||||
nfs_ops->commit_setup(data, &msg, &task_setup_data.rpc_client);
|
||||
trace_nfs_initiate_commit(data);
|
||||
|
@ -451,6 +451,8 @@ enum lock_type4 {
|
||||
#define FATTR4_WORD1_TIME_MODIFY (1UL << 21)
|
||||
#define FATTR4_WORD1_TIME_MODIFY_SET (1UL << 22)
|
||||
#define FATTR4_WORD1_MOUNTED_ON_FILEID (1UL << 23)
|
||||
#define FATTR4_WORD1_DACL (1UL << 26)
|
||||
#define FATTR4_WORD1_SACL (1UL << 27)
|
||||
#define FATTR4_WORD1_FS_LAYOUT_TYPES (1UL << 30)
|
||||
#define FATTR4_WORD2_LAYOUT_TYPES (1UL << 0)
|
||||
#define FATTR4_WORD2_LAYOUT_BLKSIZE (1UL << 1)
|
||||
|
@ -287,4 +287,5 @@ struct nfs_server {
|
||||
#define NFS_CAP_XATTR (1U << 28)
|
||||
#define NFS_CAP_READ_PLUS (1U << 29)
|
||||
#define NFS_CAP_FS_LOCATIONS (1U << 30)
|
||||
#define NFS_CAP_MOVEABLE (1U << 31)
|
||||
#endif
|
||||
|
@ -800,9 +800,17 @@ struct nfs_setattrargs {
|
||||
const struct nfs4_label *label;
|
||||
};
|
||||
|
||||
enum nfs4_acl_type {
|
||||
NFS4ACL_NONE = 0,
|
||||
NFS4ACL_ACL,
|
||||
NFS4ACL_DACL,
|
||||
NFS4ACL_SACL,
|
||||
};
|
||||
|
||||
struct nfs_setaclargs {
|
||||
struct nfs4_sequence_args seq_args;
|
||||
struct nfs_fh * fh;
|
||||
enum nfs4_acl_type acl_type;
|
||||
size_t acl_len;
|
||||
struct page ** acl_pages;
|
||||
};
|
||||
@ -814,6 +822,7 @@ struct nfs_setaclres {
|
||||
struct nfs_getaclargs {
|
||||
struct nfs4_sequence_args seq_args;
|
||||
struct nfs_fh * fh;
|
||||
enum nfs4_acl_type acl_type;
|
||||
size_t acl_len;
|
||||
struct page ** acl_pages;
|
||||
};
|
||||
@ -822,6 +831,7 @@ struct nfs_getaclargs {
|
||||
#define NFS4_ACL_TRUNC 0x0001 /* ACL was truncated */
|
||||
struct nfs_getaclres {
|
||||
struct nfs4_sequence_res seq_res;
|
||||
enum nfs4_acl_type acl_type;
|
||||
size_t acl_len;
|
||||
size_t acl_data_offset;
|
||||
int acl_flags;
|
||||
@ -1212,7 +1222,7 @@ struct nfs4_fs_location {
|
||||
|
||||
#define NFS4_FS_LOCATIONS_MAXENTRIES 10
|
||||
struct nfs4_fs_locations {
|
||||
struct nfs_fattr fattr;
|
||||
struct nfs_fattr *fattr;
|
||||
const struct nfs_server *server;
|
||||
struct nfs4_pathname fs_path;
|
||||
int nlocations;
|
||||
|
@ -1121,6 +1121,7 @@ static bool
|
||||
rpcrdma_is_bcall(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep)
|
||||
#if defined(CONFIG_SUNRPC_BACKCHANNEL)
|
||||
{
|
||||
struct rpc_xprt *xprt = &r_xprt->rx_xprt;
|
||||
struct xdr_stream *xdr = &rep->rr_stream;
|
||||
__be32 *p;
|
||||
|
||||
@ -1144,6 +1145,10 @@ rpcrdma_is_bcall(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep)
|
||||
if (*p != cpu_to_be32(RPC_CALL))
|
||||
return false;
|
||||
|
||||
/* No bc service. */
|
||||
if (xprt->bc_serv == NULL)
|
||||
return false;
|
||||
|
||||
/* Now that we are sure this is a backchannel call,
|
||||
* advance to the RPC header.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user