RFC 7862
Network File System (NFS) Version 4 Minor Version 2 Protocol
RFC 7862: Network File System (NFS) Version 4 Minor Version 2 Protocol ๐
Okay bestie, this is the spec for NFSv4.2 ๐ It's from November 2016, written by T. Haynes at Primary Data, and it's a Standards Track doc, so the IESG approved it after public review ๐ It's tagged ISSN 2070-1721. The header doesn't list anything it obsoletes or updates, so it isn't replacing or patching any other RFC by name ๐ง
Abstract ๐ธ
So this doc describes NFS version 4 minor version 2, and it covers the protocol extensions added on top of NFS version 4 minor version 1 โจ The big new features in NFSv4.2 are: Server-Side Copy, Application Input/Output (I/O) Advise, Space Reservations, Sparse Files, Application Data Blocks, and Labeled NFS ๐ฆ
Status of This Memo ๐
This is an Internet Standards Track document ๐
It's a product of the Internet Engineering Task Force (IETF), and it represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). For more on Internet Standards, check Section 2 of RFC 7841 ๐ซถ
For the current status, any errata, and how to send feedback, go to http://www.rfc-editor.org/info/rfc7862 ๐ฅ
Copyright Notice ๐
Copyright (c) 2016 IETF Trust and the persons identified as the document authors. All rights reserved โจ
This doc is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) as they stood on the publication date. Read these docs carefully, because they spell out your rights and restrictions for this document ๐ Code Components pulled out of this document must include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as the Simplified BSD License describes ๐
Table of Contents ๐
1. Introduction ....................................................4
1.1. Requirements Language ......................................4
1.2. Scope of This Document .....................................5
1.3. NFSv4.2 Goals ..............................................5
1.4. Overview of NFSv4.2 Features ...............................6
1.4.1. Server-Side Clone and Copy ..........................6
1.4.2. Application Input/Output (I/O) Advise ...............6
1.4.3. Sparse Files ........................................6
1.4.4. Space Reservation ...................................7
1.4.5. Application Data Block (ADB) Support ................7
1.4.6. Labeled NFS .........................................7
1.4.7. Layout Enhancements .................................7
1.5. Enhancements to Minor Versioning Model .....................7
2. Minor Versioning ................................................8
3. pNFS Considerations for New Operations ..........................9
3.1. Atomicity for ALLOCATE and DEALLOCATE ......................9
3.2. Sharing of Stateids with NFSv4.1 ...........................9
3.3. NFSv4.2 as a Storage Protocol in pNFS: The File
Layout Type ................................................9
3.3.1. Operations Sent to NFSv4.2 Data Servers .............9
4. Server-Side Copy ...............................................10
4.1. Protocol Overview .........................................10
4.1.1. COPY Operations ....................................11
4.1.2. Requirements for Operations ........................11
4.2. Requirements for Inter-Server Copy ........................13
4.3. Implementation Considerations .............................13
4.3.1. Locking the Files ..................................13
4.3.2. Client Caches ......................................14
4.4. Intra-Server Copy .........................................14
4.5. Inter-Server Copy .........................................16
4.6. Server-to-Server Copy Protocol ............................19
4.6.1. Considerations on Selecting a Copy Protocol ........19
4.6.2. Using NFSv4.x as the Copy Protocol .................19
4.6.3. Using an Alternative Copy Protocol .................20
4.7. netloc4 - Network Locations ...............................21
4.8. Copy Offload Stateids .....................................21
4.9. Security Considerations for Server-Side Copy ..............22
4.9.1. Inter-Server Copy Security .........................22
5. Support for Application I/O Hints ..............................30
6. Sparse Files ...................................................30
6.1. Terminology ...............................................31
6.2. New Operations ............................................32
6.2.1. READ_PLUS ..........................................32
6.2.2. DEALLOCATE .........................................32
7. Space Reservation ..............................................32
8. Application Data Block Support .................................34
8.1. Generic Framework .........................................35
8.1.1. Data Block Representation ..........................36
8.2. An Example of Detecting Corruption ........................36
8.3. An Example of READ_PLUS ...................................38
8.4. An Example of Zeroing Space ...............................39
9. Labeled NFS ....................................................39
9.1. Definitions ...............................................40
9.2. MAC Security Attribute ....................................41
9.2.1. Delegations ........................................41
9.2.2. Permission Checking ................................42
9.2.3. Object Creation ....................................42
9.2.4. Existing Objects ...................................42
9.2.5. Label Changes ......................................42
9.3. pNFS Considerations .......................................43
9.4. Discovery of Server Labeled NFS Support ...................43
9.5. MAC Security NFS Modes of Operation .......................43
9.5.1. Full Mode ..........................................44
9.5.2. Limited Server Mode ................................45
9.5.3. Guest Mode .........................................45
9.6. Security Considerations for Labeled NFS ...................46
10. Sharing Change Attribute Implementation Characteristics
with NFSv4 Clients ............................................46
11. Error Values ..................................................47
11.1. Error Definitions ........................................47
11.1.1. General Errors ....................................47
11.1.2. Server-to-Server Copy Errors ......................47
11.1.3. Labeled NFS Errors ................................48
11.2. New Operations and Their Valid Errors ....................49
11.3. New Callback Operations and Their Valid Errors ...........53
12. New File Attributes ...........................................54
12.1. New RECOMMENDED Attributes - List and Definition
References ...............................................54
12.2. Attribute Definitions ....................................54
13. Operations: REQUIRED, RECOMMENDED, or OPTIONAL ................57
14. Modifications to NFSv4.1 Operations ...........................61
14.1. Operation 42: EXCHANGE_ID - Instantiate the client ID ....61
14.2. Operation 48: GETDEVICELIST - Get all device
mappings for a file system ...............................63
15. NFSv4.2 Operations ............................................64
15.1. Operation 59: ALLOCATE - Reserve space in a
region of a file .........................................64
15.2. Operation 60: COPY - Initiate a server-side copy .........65
15.3. Operation 61: COPY_NOTIFY - Notify a source
server of a future copy ..................................70
15.4. Operation 62: DEALLOCATE - Unreserve space in a
region of a file .........................................72
15.5. Operation 63: IO_ADVISE - Send client I/O access
pattern hints to the server ..............................73
15.6. Operation 64: LAYOUTERROR - Provide errors for
the layout ...............................................79
15.7. Operation 65: LAYOUTSTATS - Provide statistics
for the layout ...........................................82
15.8. Operation 66: OFFLOAD_CANCEL - Stop an offloaded
operation ................................................84
15.9. Operation 67: OFFLOAD_STATUS - Poll for the
status of an asynchronous operation ......................85
15.10. Operation 68: READ_PLUS - READ data or holes
from a file .............................................86
15.11. Operation 69: SEEK - Find the next data or hole .........91
15.12. Operation 70: WRITE_SAME - WRITE an ADB multiple
times to a file .........................................92
15.13. Operation 71: CLONE - Clone a range of a file
into another file .......................................96
16. NFSv4.2 Callback Operations ...................................98
16.1. Operation 15: CB_OFFLOAD - Report the results of
an asynchronous operation ................................98
17. Security Considerations .......................................99
18. IANA Considerations ...........................................99
19. References ...................................................100
19.1. Normative References ....................................100
19.2. Informative References ..................................101
Acknowledgments ..................................................103
Author's Address .................................................104
1. Introduction โจ
The NFS version 4 minor version 2 (NFSv4.2) protocol is the third minor version of the NFS version 4 (NFSv4) protocol ๐ The first minor version, NFSv4.0, is described in [RFC7530], and the second, NFSv4.1, is described in [RFC5661] ๐ง
As a minor version, NFSv4.2 keeps up with the overall NFSv4 goals, but it extends the protocol so it can meet those goals even better. That's based on what people learned from using NFSv4.1 ๐ฆ NFSv4.2 also picked up a few new goals of its own, and those goals are behind some of the major extensions in NFSv4.2 ๐ธ
1.1. Requirements Language ๐ซถ
Okay, so the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 [RFC2119] ๐ Those words are the ones that carry real weight, so keep an eye out for them!
Next up: Section 1.2, Scope of This Document ๐๐
1.2. Scope of This Document ๐
okay so this is the part where we figure out what NFSv4.2 actually is ๐ It's a set of extensions sitting on top of the NFSv4.1 spec, and that spec is still current and is the base for everything new here. NFSv4.0's spec is still current too, so no one's getting retired ๐ซถ
It is necessary to implement all the REQUIRED features of NFSv4.1 before adding NFSv4.2 features to the implementation. ๐งฑ Like, you gotta have the foundation before the glitter. And with respect to NFSv4.0 and NFSv4.1, this document does not:
- describe the NFSv4.0 or NFSv4.1 protocols, except where needed to contrast with NFSv4.2
- modify the specification of the NFSv4.0 or NFSv4.1 protocols
- clarify the NFSv4.0 or NFSv4.1 protocols -- that is, any clarifications made here apply only to NFSv4.2 and not to NFSv4.0 or NFSv4.1
NFSv4.2 is a superset of NFSv4.1, with all of the new features being optional โจ so it keeps the same compatibility NFSv4.1 had with NFSv4.0. Any interactions of a new feature with NFSv4.1 semantics is described in the relevant text. The full External Data Representation (XDR) [RFC4506] for NFSv4.2 is presented in [RFC7863]. ๐
1.3. NFSv4.2 Goals ๐ธ
okay the big vision here is taking common local file system features that earlier NFS versions couldn't offer and making them available remotely ๐ฆ These features might:
- already be available on the servers, e.g., sparse files
- be under development as a new standard, e.g., SEEK pulls in both SEEK_HOLE and SEEK_DATA
- be used by clients with the servers via some proprietary means, e.g., Labeled NFS
NFSv4.2 gives clients a way to leverage these features on the server in cases where that just wasn't possible within the confines of the NFS protocol before. It's giving main character energy ๐
1.4. Overview of NFSv4.2 Features โญ
1.4.1. Server-Side Clone and Copy ๐
ngl this one's so relatable. A traditional file copy of a remotely accessed file, whether it's from one server to another or between locations on the same server, puts the data on the network twice ๐ญ -- source to client, then client to destination. New operations are introduced to kill that unnecessary traffic:
- The intra-server CLONE feature allows the client to request a synchronous cloning, perhaps by copy-on-write semantics.
- The intra-server COPY feature allows the client to request the server to perform the copy internally, avoiding unnecessary network traffic.
- The inter-server COPY feature allows the client to authorize the source and destination servers to interact directly.
Copies can be lengthy, so asynchronous support is also provided โณ
1.4.2. Application Input/Output (I/O) Advise ๐ง
Applications and clients want to tell the server what I/O behavior to expect ๐ซถ Using IO_ADVISE (see Section 15.5) to communicate future I/O behavior, like whether a file will be accessed sequentially or randomly and whether it will or won't be touched in the near future, lets servers optimize future I/O requests for a file by, for example, prefetching or evicting data. This operation can be used to support the posix_fadvise() [posix_fadvise] function. It may also be helpful to applications such as databases and video editors ๐ฌ
1.4.3. Sparse Files ๐ซง
Sparse files are files that have unallocated or uninitialized data blocks as holes in the file. Those holes are typically transferred as zeros when read from the file. READ_PLUS (see Section 15.10) lets a server send metadata describing the hole back to the client, and DEALLOCATE (see Section 15.4) lets the client punch holes into a file. SEEK (see Section 15.11) is also provided to scan for the next hole or data from a given location ๐
1.4.4. Space Reservation ๐
When a file is sparse, one concern that applications have is making sure there will always be enough data blocks available for the file during future writes ๐ฐ ALLOCATE (see Section 15.1) lets a client request a guarantee that space will be available. DEALLOCATE (see Section 15.4) also lets the client punch a hole into a file, which releases a space reservation.
1.4.5. Application Data Block (ADB) Support ๐
Some applications treat a file like it's a disk, so they want to initialize (or format) the file image. The WRITE_SAME operation (see Section 15.12) is introduced to send this metadata to the server so it can write the block contents ๐พ
1.4.6. Labeled NFS ๐ท๏ธ
While both clients and servers can use Mandatory Access Control (MAC) security models to enforce data access, there has been no protocol support for interoperability. A new file object attribute, sec_label (see Section 12.2.4), lets the server store MAC labels on files. The client retrieves those labels and uses them to enforce data access (see Section 9.5.3). The format of the sec_label accommodates any MAC security system ๐
1.4.7. Layout Enhancements ๐
In the parallel NFS implementations of NFSv4.1 (see Section 12 of [RFC5661]), the client can't communicate back to the metadata server about any errors or performance characteristics with the storage devices ๐ฅฒ NFSv4.2 adds two new operations to fix that: LAYOUTERROR (see Section 15.6) and LAYOUTSTATS (see Section 15.7), respectively.
1.5. Enhancements to Minor Versioning Model ๐ฆ
In NFSv4.1, the only way to introduce new variants of an operation was to add a whole new operation. For instance, READ would have to be replaced or supplemented by, say, READ2 or READ_PLUS ๐ With discriminated unions as parameters for such functions in NFSv4.2, it's possible to add a new "arm" (i.e., a new entry in the union and a corresponding new field in the structure) in a subsequent minor version. It's also possible to move such an operation from OPTIONAL/RECOMMENDED to REQUIRED. Forcing an implementation to adopt each arm of a discriminated union at that point does not meet the spirit of the minor versioning rules. So new arms of a discriminated union MUST follow the same guidelines for minor versioning as operations in NFSv4.1 -- i.e., they may not be made REQUIRED. ๐ซ
To support this, a new error code, NFS4ERR_UNION_NOTSUPP, lets the server tell the client that the operation is supported but the specific arm of the discriminated union is not ๐
2. Minor Versioning ๐ท
NFSv4.2 is a minor version of NFSv4 and is built upon NFSv4.1 as documented in [RFC5661] and [RFC5662] โจ
NFSv4.2 doesn't modify the rules applicable to the NFSv4 versioning process. It follows the rules set out in [RFC5661] or in Standards Track documents updating that document (e.g., in an RFC based on [NFSv4-Versioning]).
NFSv4.2 only defines extensions to NFSv4.1, and each of them may be supported (or not) independently. It does not:
- introduce infrastructural features
- make existing features MANDATORY to NOT implement
- change the status of existing features (i.e., by changing their status among OPTIONAL, RECOMMENDED, REQUIRED)
The following versioning-related considerations should be noted ๐
- When a new case is added to an existing switch, servers need to report non-support of that new case by returning NFS4ERR_UNION_NOTSUPP.
- As regards the potential cross-minor-version transfer of stateids, Parallel NFS (pNFS) (see Section 12 of [RFC5661]) implementations of the file-mapping type may support the use of an NFSv4.2 metadata server (see Sections 1.7.2.2 and 12.2.2 of [RFC5661]) with NFSv4.1 data servers. In this context, a stateid returned by an NFSv4.2 COMPOUND will be used in an NFSv4.1 COMPOUND directed to the data server (see Sections 3.2 and 3.3). ๐
3. pNFS Considerations for New Operations ๐งฉ
The interactions of the new operations with non-pNFS functionality are straightforward and are covered in the relevant sections. However, the interactions of the new operations with pNFS are more complicated, so this section gives an overview ๐ซ
3.1. Atomicity for ALLOCATE and DEALLOCATE ๐ฅ
Both ALLOCATE (see Section 15.1) and DEALLOCATE (see Section 15.4) are sent to the metadata server, which is responsible for coordinating the changes onto the storage devices. In particular, both operations must either fully succeed or fail; it cannot be the case that one storage device succeeds whilst another fails. No split decisions allowed ๐ โโ๏ธ
3.2. Sharing of Stateids with NFSv4.1 ๐ค
An NFSv4.2 metadata server can hand out a layout to an NFSv4.1 storage device. Section 13.9.1 of [RFC5661] discusses how the client gets a stateid from the metadata server to present to a storage device.
3.3. NFSv4.2 as a Storage Protocol in pNFS: The File Layout Type ๐๏ธ
A file layout provided by an NFSv4.2 server may refer to either (1) a storage device that only implements NFSv4.1 as specified in [RFC5661] or (2) a storage device that implements additions from NFSv4.2, in which case the rules in Section 3.3.1 apply. The file layout type doesn't give a way to tell the client which minor version a particular storage device is providing, so the client has to negotiate this with the storage device via the normal Remote Procedure Call (RPC) semantics of major and minor version discovery. For example, as per Section 16.2.3 of [RFC5661], the client could try a COMPOUND with a minorversion field value of 2; if it gets NFS4ERR_MINOR_VERS_MISMATCH, it would drop back to 1 ๐
3.3.1. Operations Sent to NFSv4.2 Data Servers ๐ค
In addition to the commands listed in [RFC5661], NFSv4.2 data servers MAY accept a COMPOUND containing the following additional operations: IO_ADVISE (see Section 15.5), READ_PLUS (see Section 15.10), WRITE_SAME (see Section 15.12), and SEEK (see Section 15.11), which will be treated like the subset specified as "Operations Sent to NFSv4.1 Data Servers" in Section 13.6 of [RFC5661] ๐
Additional details on the implementation of these operations in a pNFS context are documented in the operation-specific sections.
4. Server-Side Copy ๐ช
The server-side copy features provide mechanisms that let an NFS client copy file data on a server, or between two servers, without the data getting sent back and forth over the network through the NFS client ๐ Without these features, an NFS client would copy data from one location to another by reading it from the source server over the network and then writing it back over the network to the destination server. Ugh, so much wasted bandwidth ๐ค
If the source object and destination object are on different file servers, the file servers will communicate with one another to perform the COPY operation. The server-to-server protocol by which this is accomplished is not defined in this document ๐ค
The copy feature lets the server perform the copying either synchronously or asynchronously. The client can request synchronous copying, but the server may not be able to honor that request. If the server intends to perform asynchronous copying, it supplies the client with a request identifier that the client can use to monitor the progress of the copying and, if appropriate, cancel a request in progress. The request identifier is a stateid representing the internal state held by the server while the copying is performed. Multiple asynchronous copies of all or part of a file may be in progress in parallel on a server, and the stateid request identifier lets monitoring and canceling be applied to the correct request ๐ฏ
4.1. Protocol Overview
okay so the server-side copy offload operations are lowkey the star of the show ๐ They cover both intra-server and inter-server file copies! An intra-server copy is when the source file and the destination file live on the same server ๐ . In an inter-server copy, they live on different servers. And in both cases, the copy can happen synchronously or asynchronously โณ (the options are giving main character energy).
On top of that, the CLONE operation gives you COPY-like vibes in the intra-server case โจ. It's both synchronous and atomic, which means other operations can't catch the target file in some in-between state. It's either the state before the CLONE or the state after it, no cap ๐
.
Throughout the rest of this doc, the NFS server holding the source file is the "source server," and the NFS server the file gets transferred to is the "destination server" ๐ฆ. In an intra-server copy, the source server and destination server are the same box, so both terms refer to the one server doing the work ๐ซถ.
The new operations are built to copy files or regions within them. Other file system objects can still be copied by building on these operations or using other techniques ๐ง. For example, if you wanna copy a directory, the client can synthesize a directory COPY by first creating the destination directory and the individual (empty) files inside it, then copying the contents of each source file into its matching new file ๐ธ.
For inter-server copies, the operations are designed to play nice with the traditional copy authorization approach ๐. The client and user get authorized for reading at the source first, and then for writing at the destination ๐.
4.1.1. COPY Operations
Here's the lineup of copy ops ๐:
CLONE: the client uses this to request a synchronous atomic COPY-like operation. (Section 15.13)COPY_NOTIFY: the client uses this to ask the source server to authorize a future file copy that a given destination server will make on behalf of a given user. (Section 15.3)COPY: the client uses this to request a file copy. (Section 15.2)OFFLOAD_CANCEL: the client uses this to end an asynchronous file copy. (Section 15.8)OFFLOAD_STATUS: the client uses this to poll the status of an asynchronous file copy. (Section 15.9)CB_OFFLOAD: the destination server uses this to report the results of an asynchronous file copy back to the client. (Section 16.1)
4.1.2. Requirements for Operations
Heads up ๐ซง: inter-server copy, intra-server copy, and intra-server clone are each OPTIONAL features in the context of server-side copy. A server can pick any of them independently. A server that implements any of these features might be REQUIRED to implement certain operations. Other operations are OPTIONAL in the context of a particular feature (see Table 5 in Section 13), but they might become REQUIRED depending on server behavior ๐ค. Clients need these operations to successfully copy a file.
For a client doing an intra-server file copy, it needs to use either COPY or CLONE ๐
. If it uses COPY, the client MUST support the CB_OFFLOAD operation ๐จ. If COPY returns a stateid, the client MAY use OFFLOAD_CANCEL and OFFLOAD_STATUS ๐.
For a client doing an inter-server file copy, it needs to use the COPY and COPY_NOTIFY operations and MUST support the CB_OFFLOAD operation ๐ฆ. If COPY returns a stateid, the client MAY use OFFLOAD_CANCEL and OFFLOAD_STATUS โจ.
If a server supports the intra-server COPY feature, then the server MUST support the COPY operation ๐งธ. If the server's COPY operation returns a stateid, the server MUST also support these operations: CB_OFFLOAD, OFFLOAD_CANCEL, and OFFLOAD_STATUS.
If a server supports the CLONE feature, then it MUST support the CLONE operation and the clone_blksize attribute on any file system where CLONE is supported (as either source or destination file) ๐.
If a source server supports the inter-server COPY feature, then it MUST support the COPY_NOTIFY and OFFLOAD_CANCEL operations. If a destination server supports the inter-server COPY feature, then it MUST support the COPY operation. If a destination server's COPY operation returns a stateid, the destination server MUST also support these operations: CB_OFFLOAD, OFFLOAD_CANCEL, COPY_NOTIFY, and OFFLOAD_STATUS ๐.
Each operation runs in the context of the user identified by the Open Network Computing (ONC) RPC credential in the RPC request containing the COMPOUND or CB_COMPOUND request ๐. For example, an OFFLOAD_CANCEL issued by a given user says that a specified COPY started by that same user is meant to be canceled. So an OFFLOAD_CANCEL MUST NOT interfere with a copy of the same file started by another user ๐
โโ๏ธ.
An NFS server MAY allow an administrative user to monitor or cancel COPY operations using an implementation-specific interface ๐ ๏ธ.
4.2. Requirements for Inter-Server Copy
The inter-server copy spec is driven by a few big requirements ๐ก:
- The specification MUST NOT mandate the server-to-server protocol.
- The specification MUST provide guidance for using NFSv4.x as a copy protocol. For source and destination servers willing to use NFSv4.x, there are specific security considerations that the specification MUST address ๐ก๏ธ.
- The specification MUST NOT mandate preconfiguration between the source and destination servers. Requiring that they first have a "copying relationship" ups the admin burden ๐ฉ. However, the specification MUST NOT preclude implementations that require preconfiguration.
- The specification MUST NOT mandate a trust relationship between the source and destination servers. The NFSv4 security model requires mutual authentication between a principal on an NFS client and a principal on an NFS server. This model MUST continue with the introduction of
COPY๐ค.
4.3. Implementation Considerations
4.3.1. Locking the Files
Both the source file and the destination file might need to be locked to protect the content during COPY operations ๐. A client can do this with a combo of OPEN and LOCK operations. Share locks or byte-range locks might both be what you want, depending on the situation.
Note that when the client establishes a lock stateid on the source, the context of that stateid is for the client and not the destination ๐ซฃ. So there might already be an outstanding stateid, issued to the destination as the client of the source, with the same value as the lock stateid. The source MUST interpret the lock stateid as that of the client. In other words, when the destination presents it in the context of an inter-server copy, it's acting on behalf of the client ๐.
4.3.2. Client Caches
In a traditional copy, if the client is in the middle of writing to the file before the copy (and maybe holding a write delegation), it's straightforward to update the destination server โ๏ธ. With an inter-server copy, though, the source has zero insight into changes cached on the client ๐. The client SHOULD write the data back to the source. If it doesn't, the destination could end up with a corrupt copy of the file ๐.
4.4. Intra-Server Copy โจ
okay so, to copy a file on a single server, the client uses a COPY operation ๐
The server can answer the COPY with the final results right away, or it can do the copy asynchronously and deliver the results later with a CB_OFFLOAD callback ๐ฅ If it goes async, the client can poll the status with OFFLOAD_STATUS or cancel with OFFLOAD_CANCEL ๐ซถ
A synchronous intra-server copy is shown in Figure 1 ๐ธ In this example the NFS server decides to do the copy synchronously. The COPY gets fully finished, either successfully or unsuccessfully, before the server replies to the client, so the reply carries the final result. Main character energy, no waiting ๐
Client Server
+ +
| |
|--- OPEN ---------------------------->| Client opens
|<------------------------------------/| the source file
| |
|--- OPEN ---------------------------->| Client opens
|<------------------------------------/| the destination file
| |
|--- COPY ---------------------------->| Client requests
|<------------------------------------/| a file copy
| |
|--- CLOSE --------------------------->| Client closes
|<------------------------------------/| the destination file
| |
|--- CLOSE --------------------------->| Client closes
|<------------------------------------/| the source file
| |
| |
Figure 1: A Synchronous Intra-Server Copy
An asynchronous intra-server copy is shown in Figure 2 ๐ฆ Here the NFS server does the copy asynchronously. The server's reply to the copy request just says the COPY was initiated and the final result will show up later ๐ง The reply also includes a copy stateid. The client can use that stateid to poll for status (as shown) or to cancel the copy with an OFFLOAD_CANCEL. When the server finishes, it does a callback to the client and reports the results ๐
Client Server
+ +
| |
|--- OPEN ---------------------------->| Client opens
|<------------------------------------/| the source file
| |
|--- OPEN ---------------------------->| Client opens
|<------------------------------------/| the destination file
| |
|--- COPY ---------------------------->| Client requests
|<------------------------------------/| a file copy
| |
| |
|--- OFFLOAD_STATUS ------------------>| Client may poll
|<------------------------------------/| for status
| |
| . | Multiple OFFLOAD_STATUS
| . | operations may be sent
| . |
| |
|<-- CB_OFFLOAD -----------------------| Server reports results
|\------------------------------------>|
| |
|--- CLOSE --------------------------->| Client closes
|<------------------------------------/| the destination file
| |
|--- CLOSE --------------------------->| Client closes
|<------------------------------------/| the source file
| |
| |
Figure 2: An Asynchronous Intra-Server Copy
4.5. Inter-Server Copy ๐
a copy can also happen between two servers ๐ The copy protocol is built to handle a bunch of different network topologies. As Figure 3 shows, the client and the servers might be connected by multiple networks. The fun part is that the servers can be linked by a specialized, high-speed network (network 192.0.2.0/24 in the diagram) that doesn't include the client at all. The protocol lets the client set up the copy between the servers (over network 203.0.113.0/24 in the diagram), and then the servers can talk over the high-speed network if they want to ๐
192.0.2.0/24
+-------------------------------------+
| |
| |
| 192.0.2.18 | 192.0.2.56
+-------+------+ +------+------+
| Source | | Destination |
+-------+------+ +------+------+
| 203.0.113.18 | 203.0.113.56
| |
| |
| 203.0.113.0/24 |
+------------------+------------------+
|
|
| 203.0.113.243
+-----+-----+
| Client |
+-----------+
Figure 3: An Example Inter-Server Network Topology
For an inter-server copy, the client tells the source server that a file is going to be copied by the destination server, using a COPY_NOTIFY operation ๐ฃ Then the client kicks things off by sending the COPY to the destination server. The destination server can do the copy synchronously or asynchronously ๐
A synchronous inter-server copy is shown in Figure 4 ๐ In this case the destination server does the copy before it responds to the client's COPY request.
Client Source Destination
+ + +
| | |
|--- OPEN --->| | Returns
|<------------------/| | open state os1
| | |
|--- COPY_NOTIFY --->| |
|<------------------/| |
| | |
|--- OPEN ---------------------------->| Returns
|<------------------------------------/| open state os2
| | |
|--- COPY ---------------------------->|
| | |
| | |
| |<----- READ -----|
| |\--------------->|
| | |
| | . | Multiple READs may
| | . | be necessary
| | . |
| | |
| | |
|<------------------------------------/| Destination replies
| | | to COPY
| | |
|--- CLOSE --------------------------->| Release os2
|<------------------------------------/|
| | |
|--- CLOSE --->| | Release os1
|<------------------/| |
Figure 4: A Synchronous Inter-Server Copy
An asynchronous inter-server copy is shown in Figure 5 ๐งโโ๏ธ This time the destination server answers the client's COPY request immediately and then does the copy asynchronously in the background. Lots of moving parts, but it's giving organized ๐๏ธ
Client Source Destination
+ + +
| | |
|--- OPEN --->| | Returns
|<------------------/| | open state os1
| | |
|--- LOCK --->| | Optional; could be done
|<------------------/| | with a share lock
| | |
|--- COPY_NOTIFY --->| | Need to pass in
|<------------------/| | os1 or lock state
| | |
| | |
| | |
|--- OPEN ---------------------------->| Returns
|<------------------------------------/| open state os2
| | |
|--- LOCK ---------------------------->| Optional ...
|<------------------------------------/|
| | |
|--- COPY ---------------------------->| Need to pass in
|<------------------------------------/| os2 or lock state
| | |
| | |
| |<----- READ -----|
| |\--------------->|
| | |
| | . | Multiple READs may
| | . | be necessary
| | . |
| | |
| | |
|--- OFFLOAD_STATUS ------------------>| Client may poll
|<------------------------------------/| for status
| | |
| | . | Multiple OFFLOAD_STATUS
| | . | operations may be sent
| | . |
| | |
| | |
| | |
|<-- CB_OFFLOAD -----------------------| Destination reports
|\------------------------------------>| results
| | |
|--- LOCKU --------------------------->| Only if LOCK was done
|<------------------------------------/|
| | |
|--- CLOSE --------------------------->| Release os2
|<------------------------------------/|
| | |
|--- LOCKU --->| | Only if LOCK was done
|<------------------/| |
| | |
|--- CLOSE --->| | Release os1
|<------------------/| |
| | |
Figure 5: An Asynchronous Inter-Server Copy
4.6. Server-to-Server Copy Protocol ๐ซง
so who gets to pick the protocol for an inter-server copy? The destination server, ultimately ๐ That said, it has to stay aware that it's acting on the client's behalf ๐ซถ the vibe is "I'm doing this for my bestie, so don't mess it up" ๐
4.6.1. Considerations on Selecting a Copy Protocol ๐
okay so the client can have requirements about two things: how big the transactions are and how error recovery works ๐ . It might want to split the copy into chunks that each get transferred synchronously ๐ซถ. It might also want the copy protocol to copy the bytes in consecutive order, so that if something goes wrong, it can restart at the last known good offset ๐ฆ. If the destination server can't meet these requirements, the client may prefer the traditional copy mechanism so it can still get what it needs ๐ญ.
4.6.2. Using NFSv4.x as the Copy Protocol โจ
The destination server MAY use standard NFSv4.x (where x >= 1) operations to read the data from the source server. bestie, if NFSv4.x is the server-to-server copy protocol, the destination can use the source filehandle and ca_src_stateid from the COPY request with standard NFSv4.x operations to read data from the source server ๐ธ. Note that the ca_src_stateid MUST be the cnr_stateid returned from the source via the COPY_NOTIFY (Section 15.3) ๐.
4.6.3. Using an Alternative Copy Protocol ๐ง
In a homogeneous environment, the source and destination servers might be able to do the file copy extremely efficiently using specialized protocols ๐ฅ. For example, the two servers might be nodes that share a common file system format for the source and destination file systems. So they're in an ideal position to render the source file's image onto the destination by replicating the file system formats at the block level ๐. Another possibility is that the two nodes share a common storage area network, which means there's no need to copy any data at all. Instead, ownership of the file and its contents might simply get reassigned to the destination ๐. To allow for these possibilities, the destination server is allowed to use a server-to-server copy protocol of its choice โญ.
In a heterogeneous environment, using a protocol other than NFSv4.x (e.g., HTTP [RFC7230] or FTP [RFC959]) presents some challenges ๐. The big one is that the destination server has to access the source file when it only has an NFSv4.x filehandle to work with.
One option for protocols that identify source files with pathnames is to use an ASCII hexadecimal representation of the source filehandle as the filename ๐.
Another option for the source server is to use URLs to direct the destination server to a specialized service. For example, the response to COPY_NOTIFY could include the URL <ftp://s1.example.com:9999/_FH/0x12345>, where 0x12345 is the ASCII hexadecimal representation of the source filehandle. When the destination server gets the source server's URL, it would use _FH/0x12345 as the filename to pass to the FTP server listening on port 9999 of s1.example.com. On port 9999 there would be a special instance of the FTP service that knows how to convert NFS filehandles to an open file descriptor. In many operating systems, this would need a new system call, one that is the inverse of the makefh() function that the pre-NFSv4 MOUNT service needs ๐ง .
Authenticating and identifying the destination server to the source server is also a challenge ๐ฅบ. One solution would be to construct unique URLs for each destination server.
4.7. netloc4 - Network Locations ๐
The server-side COPY operations specify network locations using the netloc4 data type shown below (see [RFC7863]):
<CODE BEGINS>
enum netloc_type4 {
NL4_NAME = 1,
NL4_URL = 2,
NL4_NETADDR = 3
};
union netloc4 switch (netloc_type4 nl_type) {
case NL4_NAME: utf8str_cis nl_name;
case NL4_URL: utf8str_cis nl_url;
case NL4_NETADDR: netaddr4 nl_addr;
};
<CODE ENDS>
If the netloc4 is of type NL4_NAME, the nl_name field MUST be specified as a UTF-8 string. The nl_name is expected to get resolved to a network address via DNS, the Lightweight Directory Access Protocol (LDAP), the Network Information Service (NIS), /etc/hosts, or some other means ๐. If the netloc4 is of type NL4_URL, a server URL [RFC3986] that fits the server-to-server COPY operation is specified as a UTF-8 string ๐. If the netloc4 is of type NL4_NETADDR, the nl_addr field MUST contain a valid netaddr4 as defined in Section 3.3.9 of [RFC5661] ๐.
When netloc4 values are used for an inter-server copy as shown in Figure 3, their values may be evaluated on the source server, the destination server, and the client. The network environment these systems operate in should be configured so that the netloc4 values are interpreted as intended on each system โจ.
4.8. Copy Offload Stateids โณ
A server may perform a copy offload operation asynchronously. An asynchronous copy is tracked using a copy offload stateid. Copy offload stateids are included in the COPY, OFFLOAD_CANCEL, OFFLOAD_STATUS, and CB_OFFLOAD operations ๐.
A copy offload stateid will be valid until either (A) the client or server restarts, or (B) the client returns the resource by issuing an OFFLOAD_CANCEL operation or the client replies to a CB_OFFLOAD operation ๐ซง.
A copy offload stateid's seqid MUST NOT be zero. In the context of a copy offload operation, it is inappropriate to indicate "the most recent copy offload operation" using a stateid with a seqid of zero (see Section 8.2.2 of [RFC5661]). It's inappropriate because the stateid refers to internal state in the server, and there may be several asynchronous COPY operations running in parallel on the same file. Therefore, a copy offload stateid with a seqid of zero MUST be considered invalid ๐ซ.
4.9. Security Considerations for Server-Side Copy ๐
this section is seriously important, so pay attention ๐
All security considerations pertaining to NFSv4.1 [RFC5661] apply to this section. As such, the standard security mechanisms used by the protocol can be used to secure the server-to-server operations ๐ก.
NFSv4 clients and servers that support the inter-server COPY operations described in this section are REQUIRED to implement the mechanism described in Section 4.9.1.1 and to support rejecting COPY_NOTIFY requests that do not use the RPC security protocol (RPCSEC_GSS) [RFC7861] with privacy. If the server-to-server copy protocol is based on ONC RPC, the servers are also REQUIRED to implement [RFC7861], including the RPCSEC_GSSv3 "copy_to_auth", "copy_from_auth", and "copy_confirm_auth" structured privileges. This requirement to implement is not a requirement to use. For example, a server may, depending on configuration, also allow COPY_NOTIFY requests that use only AUTH_SYS ๐.
If a server requires the use of an RPCSEC_GSSv3 copy_to_auth, copy_from_auth, or copy_confirm_auth privilege and it is not used, the server will reject the request with NFS4ERR_PARTNER_NO_AUTH ๐.
4.9.1. Inter-Server Copy Security ๐ฆ
4.9.1.1. Inter-Server Copy via ONC RPC with RPCSEC_GSSv3 โจ
okay so when the client sends a COPY_NOTIFY to the source server to say "hey, the destination is about to copy data from you," that copy is happening on behalf of the principal (called the "user principal") who sent the RPC request that wraps the COMPOUND procedure containing the COPY_NOTIFY op. the user principal is identified by the RPC credentials ๐ชช. so we need a mechanism that lets the user principal authorize the destination server to do the copy, lets the source server properly authenticate the destination's copy, and makes sure the destination server can't go beyond that authorization. it's a must-have, not a nice-to-have ๐
an approach that sends delegated credentials of the client's user principal to the destination server is not used, and here's the tea ๐ต. if the client's user delegated its credentials, the destination would authenticate as the user principal. if the destination were using the NFSv4 protocol to do the copy, the source server would authenticate the destination as the user principal, and the file copy would securely go through. BUT this approach would let the destination server copy other files too ๐ฌ. the user principal would have to trust the destination server to not do that. that's counter to the requirements, so it's not considered.
instead, we use a feature of the RPCSEC_GSSv3 protocol [RFC7861] called RPC-application-defined structured privilege assertion ๐ฆ. this lets the destination server authenticate to the source server as acting on behalf of the user principal, and authorizes the destination server to READ the file being copied from the source, on behalf of the user principal. once the copy is complete, the client can destroy the RPCSEC_GSSv3 handles to end the authorization of both the source and destination servers to copy. clean exit, no lingering access โจ
for each structured privilege assertion defined by an RPC application, RPCSEC_GSSv3 requires the application to define a name string and a data structure that gets encoded and passed between client and server as opaque data. for NFSv4, the data structures specified below MUST be serialized using XDR. ๐ง
three RPCSEC_GSSv3 structured privilege assertions work together to authorize the copy, and they're defined here. for each one, the description starts with the name string passed in the rp_name field of the rgss3_privs structure (defined in Section 2.7.1.4 of [RFC7861]), and then specifies the XDR encoding of the associated structured data passed via the rp_privilege field of that structure.
copy_from_auth: a user principal is authorizing a source principal ("nfs@<source>") to allow a destination principal ("nfs@<destination>") to set up the copy_confirm_auth privilege required to copy a file from the source to the destination on behalf of the user principal. this privilege is established on the source server before the user principal sends a COPY_NOTIFY operation to the source server, and the resulting RPCSEC_GSSv3 context is used to secure the COPY_NOTIFY operation. ๐
struct copy_from_auth_priv {
secret4 cfap_shared_secret;
netloc4 cfap_destination;
/* the NFSv4 user name that the user principal maps to */
utf8str_mixed cfap_username;
};
cfap_shared_secret is an automatically generated random number secret value.
copy_to_auth: a user principal is authorizing a destination principal ("nfs@<destination>") to set up a copy_confirm_auth privilege with a source principal ("nfs@<source>") so it can copy a file from the source to the destination on behalf of the user principal. this privilege is established on the destination server before the user principal sends a COPY operation to the destination server, and the resulting RPCSEC_GSSv3 context is used to secure the COPY operation. ๐ฆ
struct copy_to_auth_priv {
/* equal to cfap_shared_secret */
secret4 ctap_shared_secret;
netloc4 ctap_source<>;
/* the NFSv4 user name that the user principal maps to */
utf8str_mixed ctap_username;
};
ctap_shared_secret is the automatically generated secret value used to establish the copy_from_auth privilege with the source principal. see Section 4.9.1.1.1.
copy_confirm_auth: a destination principal ("nfs@<destination>") is confirming with the source principal ("nfs@<source>") that it is authorized to copy data from the source. this privilege is established on the destination server before the file is copied from the source to the destination. the resulting RPCSEC_GSSv3 context is used to secure the READ operations from the source to the destination server. ๐ธ
struct copy_confirm_auth_priv {
/* equal to GSS_GetMIC() of cfap_shared_secret */
opaque ccap_shared_secret_mic<>;
/* the NFSv4 user name that the user principal maps to */
utf8str_mixed ccap_username;
};
4.9.1.1.1. Establishing a Security Context ๐
when the user principal wants to copy a file between two servers, and it hasn't already established copy_from_auth and copy_to_auth privileges on those servers, it sets them up like this:
- as noted in [RFC7861], the client uses an existing RPCSEC_GSSv3 context termed the "parent" handle to establish and protect RPCSEC_GSSv3 structured privilege assertion exchanges. the copy_from_auth privilege uses the context established between the user principal and the source server (the one used to OPEN the source file) as the RPCSEC_GSSv3 parent handle. the copy_to_auth privilege uses the context established between the user principal and the destination server (the one used to OPEN the destination file) as its parent handle.
- a random number is generated to use as a secret to be shared between the two servers. note that the random number SHOULD NOT be reused between establishing different security contexts. the resulting shared secret is placed in the
cfap_shared_secretfield of copy_from_auth_priv and thectap_shared_secretfield of copy_to_auth_priv. because of this shared secret, the RPCSEC_GSS3_CREATE control messages for copy_from_auth and copy_to_auth MUST use a Quality of Protection (QoP) ofrpc_gss_svc_privacy. ๐ - an instance of copy_from_auth_priv is filled in with the shared secret, the destination server, and the NFSv4 user id of the user principal, and it's placed in
rpc_gss3_create_args assertions[0].privs.privilege. the string "copy_from_auth" goes inassertions[0].privs.name. the source server unwraps therpc_gss_svc_privacyRPCSEC_GSS3_CREATE payload and verifies that the NFSv4 user id being asserted matches the source server's mapping of the user principal. if it does, the privilege is established on the source server as <copy_from_auth, user id, destination>. thehandlefield in a successful reply is the RPCSEC_GSSv3 copy_from_auth "child" handle that the client will use in COPY_NOTIFY requests to the source server. โ - an instance of copy_to_auth_priv is filled in with the shared secret, the
cnr_source_serverlist returned by COPY_NOTIFY, and the NFSv4 user id of the user principal. that copy_to_auth_priv instance is placed inrpc_gss3_create_args assertions[0].privs.privilege. the string "copy_to_auth" goes inassertions[0].privs.name. the destination server unwraps therpc_gss_svc_privacyRPCSEC_GSS3_CREATE payload and verifies that the NFSv4 user id being asserted matches the destination server's mapping of the user principal. if it does, the privilege is established on the destination server as <copy_to_auth, user id, source list>. thehandlefield in a successful reply is the RPCSEC_GSSv3 copy_to_auth child handle that the client will use in COPY requests to the destination server involving the source server. ๐งโโ๏ธ
as noted in Section 2.7.1 of [RFC7861] ("New Control Procedure - RPCSEC_GSS_CREATE"), both the client and the source server should associate the RPCSEC_GSSv3 child handle with the parent RPCSEC_GSSv3 handle used to create it. ๐
4.9.1.1.2. Starting a Secure Inter-Server Copy ๐
when the client sends a COPY_NOTIFY request to the source server, it uses the privileged copy_from_auth RPCSEC_GSSv3 handle. cna_destination_server in the COPY_NOTIFY MUST be the same as cfap_destination specified in copy_from_auth_priv. otherwise, the COPY_NOTIFY will fail with NFS4ERR_ACCESS. the source server verifies that the privilege <copy_from_auth, user id, destination> exists, and annotates it with the source filehandle, if the user principal has read access to the source file and if administrative policies give the user principal and the NFS client read access to the source file (i.e., if the ACCESS operation would grant read access). otherwise, the COPY_NOTIFY will fail with NFS4ERR_ACCESS. ๐
when the client sends a COPY request to the destination server, it uses the privileged copy_to_auth RPCSEC_GSSv3 handle. ca_source_server list in the COPY MUST be the same as ctap_source list specified in copy_to_auth_priv. otherwise, the COPY will fail with NFS4ERR_ACCESS. the destination server verifies that the privilege <copy_to_auth, user id, source list> exists, and annotates it with the source and destination filehandles. if the COPY returns a wr_callback_id, then this is an asynchronous copy, and the wr_callback_id must also must be annotated to the copy_to_auth privilege. if the client has failed to establish the copy_to_auth privilege, it will reject the request with NFS4ERR_PARTNER_NO_AUTH. ๐
โโ๏ธ
if either the COPY_NOTIFY operation or the COPY operations fail, the associated copy_from_auth and copy_to_auth RPCSEC_GSSv3 handles MUST be destroyed. no lingering handles, periodt ๐โจ
4.9.1.1.3. Securing ONC RPC Server-to-Server Copy Protocols โจ
okay so once the destination server has its copy_to_auth privilege set up and gets a COPY request, if it already knows it's going to use an ONC RPC protocol to copy the data, it sets up a copy_confirm_auth privilege on the source server before it replies to the COPY operation ๐ซถ. here's how the whole handshake goes:
- before an RPCSEC_GSSv3 context can exist, a parent context has to be in place between
nfs@<destination>as the initiator principal andnfs@<source>as the target principal. if NFS is the copy protocol, that means the destination server has to mount the source server using RPCSEC_GSSv3 ๐ง - a copy_confirm_auth_priv instance gets filled in with info from the copy_to_auth privilege that's already established. the
ccap_shared_secret_micfield is aGSS_GetMIC()of thectap_shared_secretin copy_to_auth, using the parent handle context. theccap_usernamefield is the user principal mapped to an NFSv4 user name in the"user"@"domain"form, and it MUST be the same as thectap_usernamein copy_to_auth ๐ . the copy_confirm_auth_priv instance goes intorpc_gss3_create_args assertions[0].privs.privilege, and the string"copy_confirm_auth"goes intoassertions[0].privs.name - the RPCSEC_GSS3_CREATE copy_from_auth message gets sent to the source server with a QoP of
rpc_gss_svc_privacy. the source server unwraps the RPCSEC_GSS3_CREATE payload, checks thecap_shared_secret_micby callingGSS_VerifyMIC()using the parent context on thecfap_shared_secretfrom the copy_from_auth privilege it already has, and checks thatccap_usernameequalscfap_username๐ - if all the checks pass, the copy_confirm_auth privilege is set up on the source server as
<copy_confirm_auth, shared_secret_mic, user id>. because the shared secret got verified, the resulting copy_confirm_auth RPCSEC_GSSv3 child handle is noted as acting on behalf of the user principal ๐ - if the source server can't verify the copy_from_auth privilege, the COPY_NOTIFY operation gets rejected with
NFS4ERR_PARTNER_NO_AUTH๐ - if the destination server can't verify the copy_to_auth or copy_confirm_auth privilege, the COPY gets rejected with
NFS4ERR_PARTNER_NO_AUTH. that makes the client destroy the associated copy_from_auth and copy_to_auth RPCSEC_GSSv3 structured privilege assertion handles ๐ - every ONC RPC READ request that goes from the destination to copy data from the source to the destination uses the RPCSEC_GSSv3 copy_confirm_auth child handle from then on ๐ฆ
ngl the copy_confirm_auth step is doing a lot of work, so here's what it actually gets you:
- if a protocol like NFS is used with export policies, those export policies can be overridden when the destination server isn't authorized to act as an NFS client ๐
- manual configuration to allow a copy relationship between the source and destination isn't needed ๐
4.9.1.1.4. Maintaining a Secure Inter-Server Copy
if the client decides that either the copy_from_auth or the copy_to_auth handle goes invalid during a copy, then the copy MUST be aborted. the client does that by sending an OFFLOAD_CANCEL to both the source and destination servers and destroying the respective copy-related context handles, as described in Section 4.9.1.1.5 ๐ซ
4.9.1.1.5. Finishing or Stopping a Secure Inter-Server Copy
under normal operation, the client MUST destroy the copy_from_auth and copy_to_auth RPCSEC_GSSv3 handles once the COPY operation returns for a synchronous inter-server copy, or once a CB_OFFLOAD reports the result of an asynchronous copy ๐
the copy_confirm_auth privilege is built from info held by the copy_to_auth privilege, and the destination server MUST destroy it (via an RPCSEC_GSS3_DESTROY call) when the copy_to_auth RPCSEC_GSSv3 handle gets destroyed ๐
the copy_confirm_auth RPCSEC_GSS3 handle is tied to a copy_from_auth RPCSEC_GSS3 handle on the source server through the shared secret. it MUST be destroyed locally when the copy_from_auth RPCSEC_GSSv3 handle is destroyed. there's no RPCSEC_GSS3_DESTROY for this one because the source server isn't the initiator ๐ง
if the client sends an OFFLOAD_CANCEL to the source server to rescind the destination server's synchronous copy privilege, it uses the privileged copy_from_auth RPCSEC_GSSv3 handle. the cra_destination_server in that OFFLOAD_CANCEL MUST be the same as the name of the destination server given in copy_from_auth_priv. the source server then deletes the <copy_from_auth, user id, destination> privilege and fails any later copy requests sent under that privilege from the destination server. the client MUST destroy both the copy_from_auth and copy_to_auth RPCSEC_GSSv3 handles ๐ซง
if the client sends an OFFLOAD_STATUS to the destination server to check on an asynchronous copy, it uses the privileged copy_to_auth RPCSEC_GSSv3 handle. the osa_stateid in that OFFLOAD_STATUS MUST be the same as the wr_callback_id specified in the copy_to_auth privilege stored on the destination server ๐ฅ
if the client sends an OFFLOAD_CANCEL to the destination server to cancel an asynchronous copy, it uses the privileged copy_to_auth RPCSEC_GSSv3 handle. the oaa_stateid in that OFFLOAD_CANCEL MUST be the same as the wr_callback_id specified in the copy_to_auth privilege stored on the destination server. the destination server then deletes the <copy_to_auth, user id, source list> privilege and the associated copy_confirm_auth RPCSEC_GSSv3 handle. the client MUST destroy both the copy_to_auth and copy_from_auth RPCSEC_GSSv3 handles ๐ธ
4.9.1.2. Inter-Server Copy via ONC RPC without RPCSEC_GSS
ONC RPC security flavors other than RPCSEC_GSS MAY be used with the server-side copy offload operations in this section. host-based ONC RPC security flavors like AUTH_NONE and AUTH_SYS MAY be used too ๐งโโ๏ธ. if a host-based security flavor is used, you get a minimal level of protection for the server-to-server copy protocol. it's not a lot, bestie, but it's something ๐ฅน
the biggest issue is that there's no strong security method for the source server and destination server to identify themselves to each other ๐ฌ. it gets worse in a multihomed environment, because the destination server might not contact the source server from the same network address that the client specified in the COPY_NOTIFY. the cnr_stateid returned from the COPY_NOTIFY can be used to uniquely identify the destination server to the source server. using the cnr_stateid gives you initial authentication of the destination server, but it can't defend against man-in-the-middle attacks after that authentication, and it can't defend against an eavesdropper who watches the opaque stateid on the wire. other secure communication techniques (e.g., IPsec) are necessary to block these attacks ๐
Servers SHOULD reject COPY_NOTIFY requests that don't use RPCSEC_GSS with privacy. that way, the cnr_stateid in the COPY_NOTIFY reply stays encrypted ๐
. for the same reason, clients SHOULD send COPY requests to the destination using RPCSEC_GSS with privacy ๐ซถ
5. Support for Application I/O Hints ๐
applications can send client I/O hints via posix_fadvise() [posix_fadvise] to the NFS client ๐ท. that can help the NFS client optimize I/O and caching for a file, but it doesn't let the NFS server and its exported file system do the same. the IO_ADVISE procedure (Section 15.5) is how the client file access patterns get communicated to the NFS server. the NFS server, when it receives an IO_ADVISE operation, MAY choose to change its I/O and caching behavior, but it's under no obligation to do so ๐ง
application-specific NFS clients, like the ones used by hypervisors and databases, can also use application hints to communicate their specialized needs ๐
6. Sparse Files ๐ฆ
a sparse file is a common way to represent a large file without using all the disk space for it. so a sparse file uses less physical space than its size suggests ๐. the file has "holes", which are byte ranges within the file that contain no data. most modern file systems support sparse files, including most UNIX file systems and Microsoft's New Technology File System (NTFS). it should be noted, though, that Apple's Hierarchical File System Plus (HFS+) does not ๐. common examples of sparse files include Virtual Machine (VM) OS/disk images, database files, log files, and even checkpoint recovery files, which are most commonly used by the High-Performance Computing (HPC) community ๐
many modern file systems also support the idea of "unwritten" or "uninitialized" blocks. these have uninitialized space allocated on disk but return zeros until data gets written to them. that functionality is already in the data model of the pNFS block/volume layout (see [RFC5663]). uninitialized blocks can be thought of as holes inside a space reservation window ๐
if an application reads a hole in a sparse file, the file system must return all zeros to the application. for local data access there's barely any penalty, but with NFS those zeros have to be transferred back to the client. if an application uses the NFS client to read data into memory, that wastes time and bandwidth while the application waits for the zeros to arrive ๐ญ
a sparse file is usually created by initializing the file to be all zeros. nothing gets written to the data in the file. instead, the hole is recorded in the file's metadata. so an 8G disk image might start out as a few hundred bits of metadata (on UNIX file systems, the inode) and nothing on disk. if the VM then writes 100M to a file in the middle of the image, there are now two holes recorded in the metadata and 100M in the data ๐ง
no new operation is needed to create a sparsely populated file. when a file is created and a write happens past its current size, the non-allocated region will either be a hole or be filled with zeros. which one it is depends on the underlying file system and is transparent to the application ๐ซง. still, the ability to read sparse files and to punch holes to reinitialize a file's contents are both needed โจ
two new operations are introduced: DEALLOCATE (Section 15.4) and READ_PLUS (Section 15.10) ๐ฅ. DEALLOCATE allows hole punching, where an application might want to reset the allocation and reservation status of a range of the file. READ_PLUS supports all the features of READ but adds an extension for sparse files. READ_PLUS is guaranteed to perform no worse than READ, and it can dramatically improve performance with sparse files ๐ธ. READ_PLUS doesn't depend on pNFS protocol features, but pNFS can use it to support sparse files ๐
6.1. Terminology
- Regular file: an object of file type
NF4REGorNF4NAMEDATTR๐ - Sparse file: a regular file that contains one or more holes ๐ณ๏ธ
- Hole: a byte range within a sparse file that contains all zeros. a hole might or might not have space allocated or reserved to it ๐
6.2. New Operations
6.2.1. READ_PLUS โจ
okay so READ_PLUS is a brand-new glow-up of the NFSv4.1 READ operation [RFC5661] ๐
It supports all the same data semantics as READ, but it also lets the client and server pass along holes super efficiently ๐ฆ The client can't know ahead of time whether a hole is there, so if the client supports READ_PLUS and the server does too, the client SHOULD always reach for READ_PLUS over READ. Wait, scratch that, I'm not allowed to be sneaky with the vibes. Let me say it the way the spec does: if both sides support it, it should always use READ_PLUS in preference to READ ๐
READ_PLUS adds a new response arm for holes, so the server doesn't have to send back data for parts of a file that are just zeros and might not even have a backing store ๐ธ Shipping real data blocks for holes is a total waste of compute and network, and honestly it tanks performance ๐ญ
Here's the tea ๐ต When a client sends a READ, it is not ready to accept a READ_PLUS-style response with that compact hole encoding. So if a READ lands on a sparse file, the server has to expand everything into raw bytes. And if a READ starts in the middle of a hole, the server can only send back bytes starting from that exact offset. Meanwhile, if a READ_PLUS starts in the middle of a hole, the server can send back a range that starts before the offset and goes past the requested length โจ main character energy for sure ๐
6.2.2. DEALLOCATE
the client can use DEALLOCATE on a range of a file as a hole punch ๐ณ๏ธ which means it can skip sending a repeating pattern of zeros across the network ๐ซถ This hole punch works because the unreserved space keeps returning all zeros until something overwrites it ๐
7. Space Reservation ๐
okay so applications really want a few things: they want to reserve space for a file, they want to report how much actual disk space a file is taking up, and they want to free up a file's backing space when it isn't needed anymore ๐ง
One example is the posix_fallocate() operation [posix_fallocate] ๐ It lets applications ask the operating system for space reservations, usually so the file gets a nicer layout and there's less overhead for random or slow-growing file-appending workloads ๐
Another example is space reservation for virtual disks in a hypervisor ๐ป In virtualized setups, virtual disk files often live on NFS-mounted volumes. When a hypervisor creates a virtual disk file, it usually tries to preallocate the space so there are no allocation-related errors later while the VM is running ๐ญ Those errors can stop a VM from continuing to run, which means downtime, and nobody wants that ๐
Right now, to get that guarantee, applications zero out the entire file. The initial zeroing allocates the backing blocks, and then every write after that is an overwrite of blocks that already exist. Honestly this is inefficient in terms of how much I/O it does, and it isn't guaranteed to work on file systems that are log-structured or deduplicated ๐ An efficient way to guarantee space reservation would be a huge win for these apps ๐ฅ
The new ALLOCATE operation (see Section 15.1) lets a client ask for a guarantee that space will be available ๐ฆ If ALLOCATE succeeds for a region, then any future writes to that region will not fail with NFS4ERR_NOSPC. Iconic ๐
Another useful feature is being able to report how many blocks would be freed when a file gets deleted ๐งน Right now NFS reports two size attributes:
size: The logical file size of the file.space_used: The size in bytes that the file occupies on disk.
These attributes are fine for space accounting in traditional file systems, but they fall short in modern file systems that support block-sharing ๐ In those file systems, multiple inodes (the metadata portion of the file system object) can point to a single block, and a block reference count guards against premature freeing โจ Knowing how many blocks would actually be freed if the file were deleted would be super useful for apps that want to migrate files when a volume is running low on space ๐ธ
Since a virtual disk represents a hard drive in a VM, a virtual disk can be viewed as a file system inside a file ๐คฏ Not every block in that file system is in use, so there's a chance to reclaim blocks that are no longer needed ๐ง Deallocating blocks could lead to better space efficiency, since less space might get consumed for backups after block deallocation ๐
The following attribute and operation handle these issues ๐
space_freed: This attribute reports the space that would be freed when a file is deleted, taking block-sharing into consideration.DEALLOCATE: This operation deallocates the blocks backing a region of the file.
Here's the spicy part ๐ถ๏ธ If space_used of a file is read to mean the size in bytes of all disk blocks pointed to by the file's inode, then shared blocks get double-counted, which over-reports the space utilization ๐ญ It also means that deleting a file with shared blocks frees up less than space_used bytes ๐
On the flip side, if space_used is read to mean the size in bytes of only those disk blocks unique to the file's inode, then shared blocks don't get counted in any file. That leads to under-reporting of the space utilization ๐
Let's do an example ๐ Two files, A and B, each have 10 blocks. Six of those blocks are shared between them. So the combined space used by the two files is 14 * BLOCK_SIZE bytes. In the first case, the combined space utilization would be reported as 20 * BLOCK_SIZE. But deleting either file would only free 4 * BLOCK_SIZE. Meanwhile, the second reading would report the space utilization as only 8 * BLOCK_SIZE ๐ฌ
Using the space_freed attribute (see Section 12.2.2) helps solve this ๐ฆ space_freed is the number of blocks allocated to the given file that would be freed on its deletion. In the example, both A and B would report space_freed as 4 * BLOCK_SIZE and space_used as 10 * BLOCK_SIZE. If A gets deleted, B will report space_freed as 10 * BLOCK_SIZE, because deleting B would deallocate all 10 of its blocks ๐ซถ
Using space_freed doesn't fully fix the over-reporting problem. Still, over-reporting is better than under-reporting ๐
8. Application Data Block Support ๐งโโ๏ธ
okay so at the OS level, files live on disk blocks ๐พ But applications are free to impose their own structure on the data inside a file, and that's where an Application Data Block (ADB) comes in โจ From the application's point of view, it only wants to handle ADBs and not raw bytes (see [Strohm11]) ๐ An ADB usually has two sections: a header and data. The header describes the characteristics of the block and can give a way to detect corruption in the data payload. The data section is usually initialized to all zeros ๐ค
The format of the header is application specific, but there are two main components you'll usually run into ๐
- An Application Data Block Number (ADBN), which lets the application figure out which data block is being referenced. This is useful when the client isn't storing the blocks in contiguous memory, meaning it's a logical block number ๐ฐ
- Fields that describe the state of the ADB, plus a way to detect block corruption ๐ก๏ธ For both of these, it's really useful if the allowed values are specially picked so that, if they're passed across the network, corruption from translating between big-endian and little-endian architectures can be detected ๐ง For example, 0xf0dedef0 has the same (32 wide) bit pattern in both architectures, so it's a no-go ๐ โโ๏ธ
Applications already impose structures on files [Strohm11] and detect corruption in data blocks [Ashdown08] ๐ What they can't do yet is efficiently transfer and store ADBs ๐ฉ To initialize a file with ADBs, the client has to send each full ADB to the server, and all of that has to be stored on the server ๐ฆ
This section defines a framework for transferring the ADB from client to server, and it presents one approach to detecting corruption in a given ADB implementation ๐
8.1. Generic Framework
The representation of the ADB needs to be flexible enough to support lots of different applications ๐ The most basic approach is not imposing any block at all, which means working with raw bytes. That would be useful for storing holes, punching holes, and so on ๐ณ๏ธ In more complex deployments, a server might support multiple applications, each with their own definition of the ADB ๐ข One might store the ADBN at the start of the block and then have a guard pattern to detect corruption [McDougall07]. The next might store the ADBN at an offset of 100 bytes within the block and have no guard pattern at all, since existing applications might already have well-defined formats for their data blocks ๐งธ
The guard pattern can represent the state of the block, protect against corruption, or do both ๐ก๏ธ Again, it needs to be able to be placed anywhere within the ADB โจ
Both the starting offset of the block and the size of the block need to be represented ๐ Nothing stops the application from defining different-sized blocks in a file, so it's totally allowed ๐โโ๏ธ
8.1.1. Data Block Representation
<CODE BEGINS>
struct app_data_block4 {
offset4 adb_offset;
length4 adb_block_size;
length4 adb_block_count;
length4 adb_reloff_blocknum;
count4 adb_block_num;
length4 adb_reloff_pattern;
opaque adb_pattern<>;
};
<CODE ENDS>
The app_data_block4 structure captures the abstraction we just talked about for the ADB ๐ The extra fields are there so it can transmit adb_block_count ADBs at one time. adb_block_num is used to convey the ADBN of the first block in the sequence. Each ADB will contain the same adb_pattern string ๐ซถ
Since both adb_block_num and adb_pattern are optional, if either adb_reloff_pattern or adb_reloff_blocknum is set to NFS4_UINT64_MAX, then the corresponding field is not set in any of the ADBs ๐ฅนโจ
8.2. An Example of Detecting Corruption โจ
okay so this section is gonna show us an example ADB format where corruption can be detected ๐ bestie, this is just ONE possible format and means to detect corruption, not the only way, so don't panic if your app does it differently ๐
picture a super basic operating system's disk blocks ๐พ a block is either data, or an indirect block that lets files be bigger than one block ๐ we also want to be able to initialize a block ๐ธ and to quickly unlink a file, a block can be marked invalid ๐ the contents stay intact tho, so the OS app could undelete a file later, which is literally iconic ๐ฆ
the application defines 4K-sized data blocks ๐ there's an 8-byte block counter at offset 0 in the block, and the guard pattern lives at offset 8 inside the block ๐ง the guard pattern can be in one of four states:
0xfeedface - this is the FREE state, and it means the ADB format has been applied โจ
0xcafedead - this is the DATA state, and it means real data has been written to this block ๐
0xe4e5c001 - this is the INDIRECT state, and it means the block holds block counter numbers that are chained off of this block ๐
0xba1ed4a3 - this is the INVALID state, and it means the block has data whose contents are garbage ๐
then there's an 8-byte checksum starting at byte 16 that covers the remaining contents of the block ๐งฎ (see [Baira08] for an example of using checksums to detect data corruption ๐) if the state is FREE, the checksum is trivially zero ๐ซถ so the app doesn't need to transfer the checksum implicitly inside the ADB, meaning it doesn't need to make the transfer layer aware there's a checksum at all ๐ (see [Ashdown08] for an example of checksums used to detect corruption in application data blocks ๐)
so corruption in each ADB can be detected like this:
- if the guard pattern is anything other than one of the allowed values, including all zeros ๐ญ
- if the guard pattern is FREE and any other byte in the rest of the ADB is anything other than zero ๐
- if the guard pattern is anything other than FREE, and the stored checksum doesn't match the computed checksum ๐ง
- if the guard pattern is INDIRECT and one of the stored indirect block numbers is greater than the number of ADBs in the file ๐
- if the guard pattern is INDIRECT and one of the stored indirect block numbers is a duplicate of another stored indirect block number ๐ฏ
ngl the app can catch errors using the combo of guard pattern state AND the checksum, but it can ALSO catch corruption based on the state and the contents of the ADB ๐ฅ
this last point matters a lot for validating the minimum amount of data in the generic framework ๐ the guard pattern is enough for apps to design their own corruption detection, which is very slay ๐
finally, none of these corruption checks happen in the transport layer ๐ซ the server and client components are totally unaware of the file format, so they might report everything as transferred correctly ๐ even when the application detects corruption. main character energy for the app, not the transport ๐
8.3. An Example of READ_PLUS ๐
the hypothetical app from Section 8.2 can be used to show how READ_PLUS returns an array of results ๐ง a file gets created and initialized with 100 4K ADBs in the FREE state using the WRITE_SAME operation (see Section 15.12) ๐:
WRITE_SAME {0, 4K, 100, 0, 0, 8, 0xfeedface}
then say the app writes a single ADB at 16K, changing the guard pattern to 0xcafedead โจ now memory looks like this:
0K -> (4K - 1) : 00 00 00 00 ... fe ed fa ce 00 00 ... 00
4K -> (8K - 1) : 00 00 00 01 ... fe ed fa ce 00 00 ... 00
8K -> (12K - 1) : 00 00 00 02 ... fe ed fa ce 00 00 ... 00
12K -> (16K - 1) : 00 00 00 03 ... fe ed fa ce 00 00 ... 00
16K -> (20K - 1) : 00 00 00 04 ... ca fe de ad 00 00 ... 00
20K -> (24K - 1) : 00 00 00 05 ... fe ed fa ce 00 00 ... 00
24K -> (28K - 1) : 00 00 00 06 ... fe ed fa ce 00 00 ... 00
...
396K -> (400K - 1) : 00 00 00 63 ... fe ed fa ce 00 00 ... 00
and when the client does a READ_PLUS of 64K at the start of the file ๐, it can get back a result of data:
0K -> (4K - 1) : 00 00 00 00 ... fe ed fa ce 00 00 ... 00
4K -> (8K - 1) : 00 00 00 01 ... fe ed fa ce 00 00 ... 00
8K -> (12K - 1) : 00 00 00 02 ... fe ed fa ce 00 00 ... 00
12K -> (16K - 1) : 00 00 00 03 ... fe ed fa ce 00 00 ... 00
16K -> (20K - 1) : 00 00 00 04 ... ca fe de ad 00 00 ... 00
20K -> (24K - 1) : 00 00 00 05 ... fe ed fa ce 00 00 ... 00
24K -> (28K - 1) : 00 00 00 06 ... fe ed fa ce 00 00 ... 00
...
62K -> (64K - 1) : 00 00 00 15 ... fe ed fa ce 00 00 ... 00
8.4. An Example of Zeroing Space ๐งธ
a simpler use case for WRITE_SAME is apps that want to efficiently zero out a file, but don't want to touch space reservations ๐ซง this works by calling WRITE_SAME without ADB block numbers and a pattern, like so:
WRITE_SAME {0, 1K, 10000, 0, 0, 0, 0}
9. Labeled NFS ๐
access control models like UNIX permissions or Access Control Lists (ACLs) are commonly called Discretionary Access Control (DAC) models ๐โโ๏ธ they base access decisions on user identity and resource ownership. Mandatory Access Control (MAC) models, on the other hand, base access control decisions on the label on the subject (usually a process) and the object it wants to access [RFC4949] ๐ these labels may hold user identity info but usually hold extra stuff too. in DAC systems, users are free to set the access rules for resources they own, but MAC models base security decisions on a system-wide policy, set by an admin or org, that users can't override ๐ซ in this section, a MAC model gets added to NFSv4.2 ๐
first, there's a method for transporting and storing security label data on NFSv4 file objects ๐ฆ security labels have several semantics that NFSv4 recommended attributes already meet, like being able to set the label value upon object creation โจ access control on these attributes uses a combo of two mechanisms. like other recommended attributes on file objects, the usual DAC checks (ACLs and permission bits) get performed so proper file ownership is enforced ๐งโโ๏ธ on top of that, a MAC system MAY be used on the client, the server, or both, to enforce extra policy on what subjects can modify security label info ๐
second, there's a method for the client to figure out whether an NFSv4 file object's security label has changed ๐ a client that needs to know if a label on a file (or a set of files) is about to change SHOULD request a delegation on each labeled file ๐ฆ to change a security label, the server has to recall delegations on any file the label change affects, which is how clients get told about the change ๐ฃ
one more useful feature would be changing the RPC layer NFSv4 uses, so RPCs can assert client process subject security labels and enforce Full Mode (see Section 9.5.1) ๐ those changes are outside this document's scope (see [RFC7861]) ๐
9.1. Definitions ๐
Label Format Specifier (LFS): an identifier the client uses to establish the syntactic format of the security label and the semantic meaning of its components. LFSs live in a registry tied to documents that describe the label's format and semantics ๐ท๏ธ
Security Label Format Selection Registry: the IANA registry (see [RFC7569]) holding all registered LFSs, along with references to the documents describing each one's syntactic format and semantics ๐
Policy Identifier (PI): an optional part of an LFS's definition ๐ the PI lets clients and servers identify specific security policies ๐
Object: a passive resource in the system that needs protecting. Objects can be files, directories, pipes, sockets, and lots of other system resources that matter for protecting system state ๐๏ธ
Subject: an active entity, usually a process, that's requesting access to an object ๐โโ๏ธ
MAC-Aware: a server that can transmit and store object labels ๐
MAC-Functional: a client or server that is Labeled NFS enabled ๐ซถ these can interpret labels and apply policies based on the security system โ
Multi-Level Security (MLS): a traditional model where objects get a sensitivity level (Unclassified, Secret, Top Secret, etc.) and a category set (see [LB96], [RFC1108], [RFC2401], and [RFC4949]) ๐ต๏ธโโ๏ธ
(Note: RFC 2401 has been obsoleted by RFC 4301, but we list RFC 2401 here because RFC 4301 doesn't discuss MLS ๐ค)
9.2. MAC Security Attribute ๐ก๏ธ
MAC models base access decisions on security attributes bound to subjects (usually processes) and objects (for NFS, file objects) ๐ง this info can range from a user identity for an identity-based MAC model, to sensitivity levels for MLS, or a type for type enforcement. these models decide based on different criteria, but the semantics of the security attribute stay the same ๐ the semantics the security attribute requires are listed below:
- it MUST provide flexibility with respect to the MAC model ๐ฆ
- it MUST provide the ability to atomically set security information upon object creation โก
- it MUST provide the ability to enforce access control decisions on both the client and the server ๐ค
- it MUST NOT expose an object to either the client or server namespace before its security information has been bound to it ๐ โโ๏ธ
NFSv4 implements the MAC security attribute as a recommended attribute ๐ this attribute has a fixed format and semantics, which clashes with the flexible nature of security attributes in general ๐ฌ to fix that, the MAC security attribute has two components. the first is an LFS, as defined in [RFC7569], so MAC mechanisms can interoperate ๐ค the second is an opaque field, which is the actual security attribute data ๐ to allow for various MAC models, NFSv4 SHOULD be used solely as a transport mechanism for the security attribute ๐ it's the endpoints' job to consume the security attribute and make access decisions based on their own models ๐งโ๐ป creating objects through OPEN and CREATE also lets the security attribute be specified upon creation โจ by giving an atomic create-and-set operation for the security attribute, we can enforce the second and fourth requirements listed above ๐ the recommended attribute FATTR4_SEC_LABEL (see Section 12.2.4) is used to satisfy this requirement ๐ธ
9.2.1. Delegations ๐ค
if a security attribute changes on the server while a client holds a delegation on the file, both the server and the client MUST follow the NFSv4.1 protocol (see Section 10 of [RFC5661]) with respect to attribute changes ๐ the client SHOULD flush all changes back to the server and relinquish the delegation ๐ซถ
9.2.2. Permission Checking โจ
okay so we're jumping right into permission checking ๐ and honestly it's a lowkey messy situation bestie. There are way too many MAC models, and even the protection levels inside each model are a whole thing, so it's not feasible to list them all ๐ญ That means NFSv4 clients and servers can't be expected to make access control decisions straight from the security attribute. Instead, NFSv4 should hand that permission check off to the host system ๐ซถ These checks happen on top of the existing DAC and ACL checks from the NFSv4 protocol, so it's an extra layer, not a replacement. Section 9.5 has a specific example of how the security attribute gets handled under one particular MAC model, so go read that if you want to see it in action ๐ฆ
9.2.3. Object Creation
okay this part is giving "how do we make a file with the right label from the jump" ๐ When you create files in NFSv4, the OPEN and CREATE operations get used. One of the parameters for those is an fattr4 structure, which holds the attributes the file is going to be created with. This is the magic bit that lets NFSv4 set the security attribute of a file atomically at creation time โจ When a client is MAC-Functional, it must always provide the initial security attribute on file creation. If the server is MAC-Functional too, it should decide by policy whether it takes the attribute from the client or makes the call itself ๐ And if the client is not MAC-Functional, then the MAC-Functional server has to pick a default label. There's a deeper dive on all this in Section 9.5 if you're obsessed like me ๐ฅ
9.2.4. Existing Objects
the vibe here is "all objects need labels, no exceptions" ๐ Note that under the MAC model, all objects must have labels. So if an existing server gets upgraded to support Labeled NFS, the security system is the one responsible for defining how existing objects behave ๐ง Not the NFS part, the security system, periodt.
9.2.5. Label Changes
okay this one is a little spicy ๐ถ๏ธ Picture a Guest Mode system (Section 9.5.3) where the clients enforce MAC checks, but the server only has a DAC security system that stores the labels right alongside the file data. In that setup, a user who has the right DAC credentials on a client with poorly configured or disabled MAC labeling enforcement is allowed to access the file label (and the data) on the server, and can change the label ๐ญ Yikes.
So clients that need to know whether a label on a file (or a set of files) has changed SHOULD request a delegation on each labeled file ๐ That way, if another client changes a label, the client finds out through the process described in Section 9.2.1, which must be followed. The delegation gets recalled, and that's what effectively tells the client about the change ๐ฆ
Also, the MAC security policies on a client can be set up so the client can't access the file unless it has a delegation. Plan accordingly, bestie ๐ซถ
9.3. pNFS Considerations
so the FATTR4_SEC_LABEL attribute is metadata, which means the storage device doesn't know what value the metadata server is holding ๐ง Luckily, the NFSv4.1 protocol [RFC5661] already has provisions for access-level checks going from the storage device to the metadata server ๐ To validate the subject label the client presents, the storage device SHOULD use that mechanism.
9.4. Discovery of Server Labeled NFS Support
the server can easily tell a client supports Labeled NFS when the client queries FATTR4_SEC_LABEL for an object ๐ From there, it can figure out which LFS the client understands. Meanwhile, the client might want to find out whether the server supports Labeled NFS and which LFS the server supports, so this is a two-way street ๐ธ
The following COMPOUND MUST NOT be denied by any MAC label check:
PUTROOTFH, GETATTR {FATTR4_SEC_LABEL}
Okay, plot twist ๐ญ the server might have imposed a security flavor on the root that blocks this kind of access. Like, if the server requires Kerberized access and the client sends a COMPOUND using AUTH_SYS, then the server is allowed to return NFS4ERR_WRONGSEC. But if the client presents a correct security flavor, the server MUST return the FATTR4_SEC_LABEL attribute with the supported LFS filled in ๐
9.5. MAC Security NFS Modes of Operation
a system using Labeled NFS can operate in three modes (see Section 4 of [RFC7204]) ๐ The first one gives the most protection and it's called "Full Mode" ๐ In this mode, both the client and the server implement a MAC model, so each end can make its own access control decision.
The second mode is a subset of Full Mode, and it's called "Limited Server Mode" ๐ Here, the server can't enforce the labels, but it can store and transmit them.
The last one is "Guest Mode" ๐ In this mode, one end of the connection isn't implementing a MAC model, which means it offers less protection than Full Mode.
9.5.1. Full Mode
Full Mode environments are made up of MAC-Functional NFSv4 servers and clients, and they might be a mix of MAC models and policies ๐ The system requires that both the client and the server get a chance to do an access control check based on all the relevant information in the network. The file object security attribute is provided using the mechanism described in Section 9.2 โจ
Fully MAC-Functional NFSv4 servers aren't possible without RPCSEC_GSSv3 [RFC7861] support for client process subject label assertion ๐ญ However, servers may make decisions based on the RPC credential information that's available.
9.5.1.1. Initial Labeling and Translation
the ability to create a file is an action a MAC model might want to mediate ๐ฅ The client is in charge of figuring out the initial security attribute that goes on a file. That lets the client decide whether the security attribute is acceptable for creating a file before it sends the request to the server ๐ Once the server gets the creation request, it may choose to evaluate whether the security attribute is acceptable.
Security attributes on the client and server can vary based on MAC model and policy ๐ง To handle that, the security attribute field has an LFS component. This component is how the host identifies the format and meaning of the opaque portion of the security attribute. A Full Mode environment can have hosts running several different LFSs, and in that case, a mechanism for translating the opaque portion of the security attribute is needed ๐ฆ The actual translation function varies based on MAC model and policy, and it's outside the scope of this document. If a translation isn't available for a given LFS, then the request MUST be denied ๐ Another option is to let the host provide a fallback mapping for unknown security attributes.
9.5.1.2. Policy Enforcement
in Full Mode, both the clients and the servers make access control decisions ๐ When a client makes a request, it takes the security attribute from the requesting process and makes an access control decision based on that attribute and the security attribute of the object it's trying to access. If the client denies the access, an RPC to the server is never made ๐ญ If the access is allowed, though, the client makes a call to the NFS server.
When the server gets the request from the client, it uses any credential information conveyed in the RPC request, along with the attributes of the object the client is trying to access, to make an access control decision ๐ If the server's policy allows the access, it fulfills the client's request. Otherwise, it returns NFS4ERR_ACCESS.
Future protocol extensions may also let the server factor in a security label pulled from the RPC request ๐ธ
Implementations MAY validate security attributes supplied over the network to make sure they're within a set of attributes permitted from a specific peer, and if they aren't, reject them ๐ซถ Note that a system may permit a different set of attributes to be accepted from each peer, so it's not one-size-fits-all ๐
9.5.2. Limited Server Mode
a Limited Server mode (see Section 4.2 of [RFC7204]) is a server that's label aware but doesn't enforce policies ๐ Such a server stores and retrieves all the object labels clients present, and it uses the methods described in Section 9.2.5 so clients can detect changing labels. But it may not factor the label into access decisions. Instead, it expects the clients to enforce all of those checks locally ๐ง
9.5.3. Guest Mode
Guest Mode means either the client or the server doesn't handle labels ๐ If the client isn't Labeled NFS aware, it won't offer subject labels to the server. The server is the only entity enforcing policy, and it may selectively provide standard NFS services to clients based on their authentication credentials and/or associated network attributes (e.g., IP address, network interface) ๐ How much trust and access a client gets in this mode is configuration specific. If the server isn't Labeled NFS aware, it won't return object labels to the client. Clients in this environment may be groups implementing different MAC model policies, and the system requires that all clients in the environment be responsible for access control checks ๐
9.6. Security Considerations for Labeled NFS
okay this section is serious, so let's be serious ๐ฅ Depending on the level of protection the MAC system offers, there may be a requirement to tightly bind the security attribute to the data ๐
When only one of the client or server enforces labels, it's important to realize that the other side is not enforcing MAC protections ๐ญ Alternate methods might be in use to handle the lack of MAC support, and care should be taken to identify and mitigate threats from possible tampering outside of those methods ๐ก๏ธ
An example: a server that modifies READDIR or LOOKUP results based on the client's subject label might want to always construct the same subject label for a client that doesn't present one. That way, a non-Labeled NFS client can't end up mixing entries in the directory cache ๐
10. Sharing Change Attribute Implementation Characteristics with NFSv4 Clients โจ
okay the change attribute is the next big thing ๐ฆ Both NFSv4 [RFC7530] and NFSv4.1 [RFC5661] define the change attribute as mandatory to implement, but there's barely any guidance on how to build it ๐ The only mandated constraint is that the value must change whenever the file data or metadata changes.
That gives implementers a ton of freedom, but it leaves the client with no way to tell which change attribute value is the most recent when several RPCs were issued in parallel ๐ญ Like, if two COMPOUNDs, both containing WRITE and GETATTR requests for the same file, go out in parallel, how does the client figure out which of the two change attribute values returned in the GETATTR replies matches the most recent state of the file? Sometimes the only option is to send another COMPOUND with a third GETATTR that's fully serialized with the first two, and that's a lot of extra work ๐
NFSv4.2 fixes this inefficiency by letting the server share details about how the change attribute is expected to evolve. The client can then immediately tell which of the several change attribute values the server returned is the most recent ๐ change_attr_type is defined as a new recommended attribute (see Section 12.2.3), and it's a per-file system attribute ๐
11. Error Values โ
okay bestie, so NFS error numbers get attached to any operation that fails inside a COMPOUND (or CB_COMPOUND) request ๐ง. A COMPOUND request is basically a bundle of NFS operations, and their results get packed into one COMPOUND reply in the same order ๐ฆ. When an operation works, its result starts with NFS4_OK and then the encoded results follow โจ. When one fails, the error status goes into the reply and the whole COMPOUND request gets terminated right there ๐ no cap, it's an instant stop.
11.1. Error Definitions ๐
here's the full list of protocol errors, with a pointer to where each one gets the tea ๐
+-------------------------+--------+------------------+
| Error | Number | Description |
+-------------------------+--------+------------------+
| NFS4ERR_BADLABEL | 10093 | Section 11.1.3.1 |
| NFS4ERR_OFFLOAD_DENIED | 10091 | Section 11.1.2.1 |
| NFS4ERR_OFFLOAD_NO_REQS | 10094 | Section 11.1.2.2 |
| NFS4ERR_PARTNER_NO_AUTH | 10089 | Section 11.1.2.3 |
| NFS4ERR_PARTNER_NOTSUPP | 10088 | Section 11.1.2.4 |
| NFS4ERR_UNION_NOTSUPP | 10090 | Section 11.1.1.1 |
| NFS4ERR_WRONG_LFS | 10092 | Section 11.1.3.2 |
+-------------------------+--------+------------------+
Table 1: Protocol Error Definitions
11.1.1. General Errors ๐ธ
this bucket is for the errors that show up across lots of different situations ๐
11.1.1.1. NFS4ERR_UNION_NOTSUPP (Error Code 10090) ๐งฉ
so one of the operation's arguments is a discriminated union ๐ซถ. The server knows the operation, โ , but it doesn't support the particular arm of the union that the client picked ๐ญ. That's the whole vibe: the operation is supported, the specific choice just isn't.
11.1.2. Server-to-Server Copy Errors ๐ฆ
these errors are all about how server-to-server copies talk to each other ๐ฅ
11.1.2.1. NFS4ERR_OFFLOAD_DENIED (Error Code 10091) ๐
the COPY offload operation is supported by both the source and the destination, but the destination is saying "not for this file" ๐ โโ๏ธ. If the client runs into this, it should fall back to the normal copy semantics ๐ .
11.1.2.2. NFS4ERR_OFFLOAD_NO_REQS (Error Code 10094) ๐
both servers support the COPY offload operation, but the destination can't meet what the client needs for either consecutive byte copy or synchronous copy ๐ฅบ. If the client gets this, it should either relax those requirements (if there are any) or fall back to the normal copy semantics โจ.
11.1.2.3. NFS4ERR_PARTNER_NO_AUTH (Error Code 10089) ๐
the source server isn't authorizing a server-to-server COPY offload operation ๐ซ. This may be because the client never sent the COPY_NOTIFY operation to the source server, because the source server got the server-to-server copy offload request after the copy lease time had already expired, or because of some other permission problem ๐.
the destination server isn't authorizing it either ๐. This may be because it's an inter-server COPY request where the destination wants RPCSEC_GSSv3 and it's not being used, or it's some other permissions problem ๐.
11.1.2.4. NFS4ERR_PARTNER_NOTSUPP (Error Code 10088) ๐
the remote server just doesn't support the server-to-server COPY offload protocol ๐ค.
11.1.3. Labeled NFS Errors ๐ท๏ธ
these errors are for Labeled NFS, so if you're into labels, this is your section ๐
11.1.3.1. NFS4ERR_BADLABEL (Error Code 10093) ๐จ
the label that got specified is invalid in some way ๐.
11.1.3.2. NFS4ERR_WRONG_LFS (Error Code 10092) โ ๏ธ
the LFS in the subject label isn't compatible with the LFS in the object label ๐ฌ. Those two just don't match, bestie.
11.2. New Operations and Their Valid Errors ๐
this section has a table listing the valid error returns for each new NFSv4.2 protocol operation ๐. The error code NFS4_OK (meaning no error) isn't listed, but just know it can be returned by all the new operations ๐. The error values for every other operation are defined in Section 15.2 of [RFC5661] ๐ง.
Okay bestie, this is the big grid of which errors each operation is allowed to return ๐โจ It's basically a cheat sheet, so every single cell matters ๐ซถ
+----------------+--------------------------------------------------+
| Operation | Errors |
+----------------+--------------------------------------------------+
| ALLOCATE | NFS4ERR_ACCESS, NFS4ERR_ADMIN_REVOKED, |
| | NFS4ERR_BADXDR, NFS4ERR_BAD_STATEID, |
| | NFS4ERR_DEADSESSION, NFS4ERR_DELAY, |
| | NFS4ERR_DELEG_REVOKED, NFS4ERR_DQUOT, |
| | NFS4ERR_EXPIRED, NFS4ERR_FBIG, |
| | NFS4ERR_FHEXPIRED, NFS4ERR_GRACE, NFS4ERR_INVAL, |
| | NFS4ERR_IO, NFS4ERR_ISDIR, NFS4ERR_MOVED, |
| | NFS4ERR_NOFILEHANDLE, NFS4ERR_NOSPC, |
| | NFS4ERR_NOTSUPP, NFS4ERR_OLD_STATEID, |
| | NFS4ERR_OPENMODE, NFS4ERR_OP_NOT_IN_SESSION, |
| | NFS4ERR_REP_TOO_BIG, |
| | NFS4ERR_REP_TOO_BIG_TO_CACHE, |
| | NFS4ERR_REQ_TOO_BIG, NFS4ERR_RETRY_UNCACHED_REP, |
| | NFS4ERR_ROFS, NFS4ERR_SERVERFAULT, |
| | NFS4ERR_STALE, NFS4ERR_SYMLINK, |
| | NFS4ERR_TOO_MANY_OPS, NFS4ERR_WRONG_TYPE |
+----------------+--------------------------------------------------+
| CLONE | NFS4ERR_ACCESS, NFS4ERR_ADMIN_REVOKED, |
| | NFS4ERR_BADXDR, NFS4ERR_BAD_STATEID, |
| | NFS4ERR_DEADSESSION, NFS4ERR_DELAY, |
| | NFS4ERR_DELEG_REVOKED, NFS4ERR_DQUOT, |
| | NFS4ERR_EXPIRED, NFS4ERR_FBIG, |
| | NFS4ERR_FHEXPIRED, NFS4ERR_GRACE, NFS4ERR_INVAL, |
| | NFS4ERR_IO, NFS4ERR_ISDIR, NFS4ERR_MOVED, |
| | NFS4ERR_NOFILEHANDLE, NFS4ERR_NOSPC, |
| | NFS4ERR_NOTSUPP, NFS4ERR_OLD_STATEID, |
| | NFS4ERR_OPENMODE, NFS4ERR_OP_NOT_IN_SESSION, |
| | NFS4ERR_REP_TOO_BIG, |
| | NFS4ERR_REP_TOO_BIG_TO_CACHE, |
| | NFS4ERR_REQ_TOO_BIG, NFS4ERR_RETRY_UNCACHED_REP, |
| | NFS4ERR_ROFS, NFS4ERR_SERVERFAULT, |
| | NFS4ERR_STALE, NFS4ERR_SYMLINK, |
| | NFS4ERR_TOO_MANY_OPS, NFS4ERR_WRONG_TYPE, |
| | NFS4ERR_XDEV |
+----------------+--------------------------------------------------+
| COPY | NFS4ERR_ACCESS, NFS4ERR_ADMIN_REVOKED, |
| | NFS4ERR_BADXDR, NFS4ERR_BAD_STATEID, |
| | NFS4ERR_DEADSESSION, NFS4ERR_DELAY, |
| | NFS4ERR_DELEG_REVOKED, NFS4ERR_DQUOT, |
| | NFS4ERR_EXPIRED, NFS4ERR_FBIG, |
| | NFS4ERR_FHEXPIRED, NFS4ERR_GRACE, NFS4ERR_INVAL, |
| | NFS4ERR_IO, NFS4ERR_ISDIR, NFS4ERR_LOCKED, |
| | NFS4ERR_MOVED, NFS4ERR_NOFILEHANDLE, |
| | NFS4ERR_NOSPC, NFS4ERR_OFFLOAD_DENIED, |
| | NFS4ERR_OLD_STATEID, NFS4ERR_OPENMODE, |
| | NFS4ERR_OP_NOT_IN_SESSION, |
| | NFS4ERR_PARTNER_NO_AUTH, |
| | NFS4ERR_PARTNER_NOTSUPP, NFS4ERR_PNFS_IO_HOLE, |
| | NFS4ERR_PNFS_NO_LAYOUT, NFS4ERR_REP_TOO_BIG, |
| | NFS4ERR_REP_TOO_BIG_TO_CACHE, |
| | NFS4ERR_REQ_TOO_BIG, NFS4ERR_RETRY_UNCACHED_REP, |
| | NFS4ERR_ROFS, NFS4ERR_SERVERFAULT, |
| | NFS4ERR_STALE, NFS4ERR_SYMLINK, |
| | NFS4ERR_TOO_MANY_OPS, NFS4ERR_WRONG_TYPE |
+----------------+--------------------------------------------------+
| COPY_NOTIFY | NFS4ERR_ACCESS, NFS4ERR_ADMIN_REVOKED, |
| | NFS4ERR_BADXDR, NFS4ERR_BAD_STATEID, |
| | NFS4ERR_DEADSESSION, NFS4ERR_DELAY, |
| | NFS4ERR_DELEG_REVOKED, NFS4ERR_EXPIRED, |
| | NFS4ERR_FHEXPIRED, NFS4ERR_GRACE, NFS4ERR_INVAL, |
| | NFS4ERR_IO, NFS4ERR_ISDIR, NFS4ERR_LOCKED, |
| | NFS4ERR_MOVED, NFS4ERR_NOFILEHANDLE, |
| | NFS4ERR_OLD_STATEID, NFS4ERR_OPENMODE, |
| | NFS4ERR_OP_NOT_IN_SESSION, NFS4ERR_PNFS_IO_HOLE, |
| | NFS4ERR_PNFS_NO_LAYOUT, NFS4ERR_REP_TOO_BIG, |
| | NFS4ERR_REP_TOO_BIG_TO_CACHE, |
| | NFS4ERR_REQ_TOO_BIG, NFS4ERR_RETRY_UNCACHED_REP, |
| | NFS4ERR_SERVERFAULT, NFS4ERR_STALE, |
| | NFS4ERR_SYMLINK, NFS4ERR_TOO_MANY_OPS, |
| | NFS4ERR_WRONG_TYPE |
+----------------+--------------------------------------------------+
| DEALLOCATE | NFS4ERR_ACCESS, NFS4ERR_ADMIN_REVOKED, |
| | NFS4ERR_BADXDR, NFS4ERR_BAD_STATEID, |
| | NFS4ERR_DEADSESSION, NFS4ERR_DELAY, |
| | NFS4ERR_DELEG_REVOKED, NFS4ERR_EXPIRED, |
| | NFS4ERR_FBIG, NFS4ERR_FHEXPIRED, NFS4ERR_GRACE, |
| | NFS4ERR_INVAL, NFS4ERR_IO, NFS4ERR_ISDIR, |
| | NFS4ERR_MOVED, NFS4ERR_NOFILEHANDLE, |
| | NFS4ERR_NOTSUPP, NFS4ERR_OLD_STATEID, |
| | NFS4ERR_OPENMODE, NFS4ERR_OP_NOT_IN_SESSION, |
| | NFS4ERR_REP_TOO_BIG, |
| | NFS4ERR_REP_TOO_BIG_TO_CACHE, |
| | NFS4ERR_REQ_TOO_BIG, NFS4ERR_RETRY_UNCACHED_REP, |
| | NFS4ERR_ROFS, NFS4ERR_SERVERFAULT, |
| | NFS4ERR_STALE, NFS4ERR_SYMLINK, |
| | NFS4ERR_TOO_MANY_OPS, NFS4ERR_WRONG_TYPE |
+----------------+--------------------------------------------------+
| GETDEVICELIST | NFS4ERR_NOTSUPP |
+----------------+--------------------------------------------------+
| IO_ADVISE | NFS4ERR_ACCESS, NFS4ERR_ADMIN_REVOKED, |
| | NFS4ERR_BADXDR, NFS4ERR_BAD_STATEID, |
| | NFS4ERR_DEADSESSION, NFS4ERR_DELAY, |
| | NFS4ERR_DELEG_REVOKED, NFS4ERR_EXPIRED, |
| | NFS4ERR_FBIG, NFS4ERR_FHEXPIRED, NFS4ERR_GRACE, |
| | NFS4ERR_INVAL, NFS4ERR_IO, NFS4ERR_ISDIR, |
| | NFS4ERR_MOVED, NFS4ERR_NOFILEHANDLE, |
| | NFS4ERR_NOTSUPP, NFS4ERR_OLD_STATEID, |
| | NFS4ERR_OP_NOT_IN_SESSION, |
| | NFS4ERR_RETRY_UNCACHED_REP, NFS4ERR_SERVERFAULT, |
| | NFS4ERR_STALE, NFS4ERR_SYMLINK, |
| | NFS4ERR_TOO_MANY_OPS, NFS4ERR_WRONG_TYPE |
+----------------+--------------------------------------------------+
| LAYOUTERROR | NFS4ERR_ADMIN_REVOKED, NFS4ERR_BADXDR, |
| | NFS4ERR_BAD_STATEID, NFS4ERR_DEADSESSION, |
| | NFS4ERR_DELAY, NFS4ERR_DELEG_REVOKED, |
| | NFS4ERR_EXPIRED, NFS4ERR_FHEXPIRED, |
| | NFS4ERR_GRACE, NFS4ERR_INVAL, NFS4ERR_ISDIR, |
| | NFS4ERR_MOVED, NFS4ERR_NOFILEHANDLE, |
| | NFS4ERR_NOTSUPP, NFS4ERR_NO_GRACE, |
| | NFS4ERR_OLD_STATEID, NFS4ERR_OP_NOT_IN_SESSION, |
| | NFS4ERR_REP_TOO_BIG, |
| | NFS4ERR_REP_TOO_BIG_TO_CACHE, |
| | NFS4ERR_REQ_TOO_BIG, NFS4ERR_RETRY_UNCACHED_REP, |
| | NFS4ERR_SERVERFAULT, NFS4ERR_STALE, |
| | NFS4ERR_TOO_MANY_OPS, |
| | NFS4ERR_UNKNOWN_LAYOUTTYPE, NFS4ERR_WRONG_CRED, |
| | NFS4ERR_WRONG_TYPE |
+----------------+--------------------------------------------------+
| LAYOUTSTATS | NFS4ERR_ADMIN_REVOKED, NFS4ERR_BADXDR, |
| | NFS4ERR_BAD_STATEID, NFS4ERR_DEADSESSION, |
| | NFS4ERR_DELAY, NFS4ERR_DELEG_REVOKED, |
| | NFS4ERR_EXPIRED, NFS4ERR_FHEXPIRED, |
| | NFS4ERR_GRACE, NFS4ERR_INVAL, NFS4ERR_ISDIR, |
| | NFS4ERR_MOVED, NFS4ERR_NOFILEHANDLE, |
| | NFS4ERR_NOTSUPP, NFS4ERR_NO_GRACE, |
| | NFS4ERR_OLD_STATEID, NFS4ERR_OP_NOT_IN_SESSION, |
| | NFS4ERR_REP_TOO_BIG, |
| | NFS4ERR_REP_TOO_BIG_TO_CACHE, |
| | NFS4ERR_REQ_TOO_BIG, NFS4ERR_RETRY_UNCACHED_REP, |
| | NFS4ERR_SERVERFAULT, NFS4ERR_STALE, |
| | NFS4ERR_TOO_MANY_OPS, |
| | NFS4ERR_UNKNOWN_LAYOUTTYPE, NFS4ERR_WRONG_CRED, |
| | NFS4ERR_WRONG_TYPE |
+----------------+--------------------------------------------------+
| OFFLOAD_CANCEL | NFS4ERR_ADMIN_REVOKED, NFS4ERR_BADXDR, |
| | NFS4ERR_BAD_STATEID, NFS4ERR_COMPLETE_ALREADY, |
| | NFS4ERR_DEADSESSION, NFS4ERR_DELAY, |
| | NFS4ERR_EXPIRED, NFS4ERR_GRACE, NFS4ERR_NOTSUPP, |
| | NFS4ERR_OLD_STATEID, NFS4ERR_OP_NOT_IN_SESSION, |
| | NFS4ERR_SERVERFAULT, NFS4ERR_TOO_MANY_OPS |
+----------------+--------------------------------------------------+
| OFFLOAD_STATUS | NFS4ERR_ADMIN_REVOKED, NFS4ERR_BADXDR, |
| | NFS4ERR_BAD_STATEID, NFS4ERR_COMPLETE_ALREADY, |
| | NFS4ERR_DEADSESSION, NFS4ERR_DELAY, |
| | NFS4ERR_EXPIRED, NFS4ERR_GRACE, NFS4ERR_NOTSUPP, |
| | NFS4ERR_OLD_STATEID, NFS4ERR_OP_NOT_IN_SESSION, |
| | NFS4ERR_SERVERFAULT, NFS4ERR_TOO_MANY_OPS |
+----------------+--------------------------------------------------+
| READ_PLUS | NFS4ERR_ACCESS, NFS4ERR_ADMIN_REVOKED, |
| | NFS4ERR_BADXDR, NFS4ERR_BAD_STATEID, |
| | NFS4ERR_DEADSESSION, NFS4ERR_DELAY, |
| | NFS4ERR_DELEG_REVOKED, NFS4ERR_EXPIRED, |
| | NFS4ERR_FHEXPIRED, NFS4ERR_GRACE, NFS4ERR_INVAL, |
| | NFS4ERR_IO, NFS4ERR_ISDIR, NFS4ERR_LOCKED, |
| | NFS4ERR_MOVED, NFS4ERR_NOFILEHANDLE, |
| | NFS4ERR_NOTSUPP, NFS4ERR_OLD_STATEID, |
| | NFS4ERR_OPENMODE, NFS4ERR_OP_NOT_IN_SESSION, |
| | NFS4ERR_PARTNER_NO_AUTH, NFS4ERR_PNFS_IO_HOLE, |
| | NFS4ERR_PNFS_NO_LAYOUT, NFS4ERR_REP_TOO_BIG, |
| | NFS4ERR_REP_TOO_BIG_TO_CACHE, |
| | NFS4ERR_REQ_TOO_BIG, NFS4ERR_RETRY_UNCACHED_REP, |
| NFS4ERR_SERVERFAULT, NFS4ERR_STALE, | |
|---|---|
| NFS4ERR_SYMLINK, NFS4ERR_TOO_MANY_OPS, | |
| NFS4ERR_WRONG_TYPE |
+----------------+--------------------------------------------------+
| SEEK | NFS4ERR_ACCESS, NFS4ERR_ADMIN_REVOKED, |
|---|---|
| NFS4ERR_BADXDR, NFS4ERR_BAD_STATEID, | |
| NFS4ERR_DEADSESSION, NFS4ERR_DELAY, | |
| NFS4ERR_DELEG_REVOKED, NFS4ERR_EXPIRED, | |
| NFS4ERR_FHEXPIRED, NFS4ERR_GRACE, NFS4ERR_INVAL, | |
| NFS4ERR_IO, NFS4ERR_ISDIR, NFS4ERR_LOCKED, | |
| NFS4ERR_MOVED, NFS4ERR_NOFILEHANDLE, | |
| NFS4ERR_NOTSUPP, NFS4ERR_OLD_STATEID, | |
| NFS4ERR_OPENMODE, NFS4ERR_OP_NOT_IN_SESSION, | |
| NFS4ERR_PNFS_IO_HOLE, NFS4ERR_PNFS_NO_LAYOUT, | |
| NFS4ERR_REP_TOO_BIG, | |
| NFS4ERR_REP_TOO_BIG_TO_CACHE, | |
| NFS4ERR_REQ_TOO_BIG, NFS4ERR_RETRY_UNCACHED_REP, | |
| NFS4ERR_SERVERFAULT, NFS4ERR_STALE, | |
| NFS4ERR_SYMLINK, NFS4ERR_TOO_MANY_OPS, | |
| NFS4ERR_UNION_NOTSUPP, NFS4ERR_WRONG_TYPE |
+----------------+--------------------------------------------------+
| WRITE_SAME | NFS4ERR_ACCESS, NFS4ERR_ADMIN_REVOKED, |
|---|---|
| NFS4ERR_BADXDR, NFS4ERR_BAD_STATEID, | |
| NFS4ERR_DEADSESSION, NFS4ERR_DELAY, | |
| NFS4ERR_DELEG_REVOKED, NFS4ERR_DQUOT, | |
| NFS4ERR_EXPIRED, NFS4ERR_FBIG, | |
| NFS4ERR_FHEXPIRED, NFS4ERR_GRACE, NFS4ERR_INVAL, | |
| NFS4ERR_IO, NFS4ERR_ISDIR, NFS4ERR_LOCKED, | |
| NFS4ERR_MOVED, NFS4ERR_NOFILEHANDLE, | |
| NFS4ERR_NOSPC, NFS4ERR_NOTSUPP, | |
| NFS4ERR_OLD_STATEID, NFS4ERR_OPENMODE, | |
| NFS4ERR_OP_NOT_IN_SESSION, NFS4ERR_PNFS_IO_HOLE, | |
| NFS4ERR_PNFS_NO_LAYOUT, NFS4ERR_REP_TOO_BIG, | |
| NFS4ERR_REP_TOO_BIG_TO_CACHE, | |
| NFS4ERR_REQ_TOO_BIG, NFS4ERR_RETRY_UNCACHED_REP, | |
| NFS4ERR_ROFS, NFS4ERR_SERVERFAULT, | |
| NFS4ERR_STALE, NFS4ERR_SYMLINK, | |
| NFS4ERR_TOO_MANY_OPS, NFS4ERR_WRONG_TYPE |
+----------------+--------------------------------------------------+
Table 2: Valid Error Returns for Each New Protocol Operation ๐ธ
11.3. New Callback Operations and Their Valid Errors ๐
Okay so this section has a table listing the valid error returns for each new NFSv4.2 callback operation ๐. NFS4_OK (no error) isn't listed in it, but it should be understood to be returnable by all new callback operations โจ. The error values for every other callback operation live in Section 15.3 of [RFC5661] ๐.
+------------+------------------------------------------------------+
| Callback | Errors |
| Operation | |
+------------+------------------------------------------------------+
| CB_OFFLOAD | NFS4ERR_BADHANDLE, NFS4ERR_BADXDR, |
| | NFS4ERR_BAD_STATEID, NFS4ERR_DELAY, |
| | NFS4ERR_OP_NOT_IN_SESSION, NFS4ERR_REP_TOO_BIG, |
| | NFS4ERR_REP_TOO_BIG_TO_CACHE, NFS4ERR_REQ_TOO_BIG, |
| | NFS4ERR_RETRY_UNCACHED_REP, NFS4ERR_SERVERFAULT, |
| | NFS4ERR_TOO_MANY_OPS |
+------------+------------------------------------------------------+
Table 3: Valid Error Returns for Each New Protocol Callback Operation ๐ซถ
12. New File Attributes ๐ฆ
12.1. New RECOMMENDED Attributes - List and Definition References ๐
Here's the list of the new RECOMMENDED attributes, all tucked into Table 4 ๐. Quick decoder for the columns:
- Name: The name of the attribute. ๐ท๏ธ
- Id: The number assigned to the attribute. In the event of conflicts between the assigned number and [RFC7863], the latter is authoritative, but in such an event, it should be resolved with errata to this document and/or [RFC7863]. See [IESG08] for the errata process. ๐
- Data Type: The XDR data type of the attribute. ๐งฉ
- Acc: Access allowed to the attribute.
Rmeans read-only (GETATTR may retrieve, SETATTR may not set).Wmeans write-only (SETATTR may set, GETATTR may not retrieve).R Wmeans read/write (GETATTR may retrieve, SETATTR may set).
- Defined in: The section of this specification that describes the attribute. ๐
+------------------+----+-------------------+-----+----------------+
| Name | Id | Data Type | Acc | Defined in |
+------------------+----+-------------------+-----+----------------+
| clone_blksize | 77 | uint32_t | R | Section 12.2.1 |
| space_freed | 78 | length4 | R | Section 12.2.2 |
| change_attr_type | 79 | change_attr_type4 | R | Section 12.2.3 |
| sec_label | 80 | sec_label4 | R W | Section 12.2.4 |
+------------------+----+-------------------+-----+----------------+
Table 4: New RECOMMENDED Attributes โจ
12.2. Attribute Definitions ๐
12.2.1. Attribute 77: clone_blksize ๐ง
clone_blksize tells you the granularity of a CLONE operation. That's it, that's the whole vibe ๐.
12.2.2. Attribute 78: space_freed ๐
space_freed gives the number of bytes freed if the file is deleted. It's read-only, it's of type length4, and it's a per-file attribute. Very clean ๐ซง.
12.2.3. Attribute 79: change_attr_type ๐ฆ
enum change_attr_type4 {
NFS4_CHANGE_TYPE_IS_MONOTONIC_INCR = 0,
NFS4_CHANGE_TYPE_IS_VERSION_COUNTER = 1,
NFS4_CHANGE_TYPE_IS_VERSION_COUNTER_NOPNFS = 2,
NFS4_CHANGE_TYPE_IS_TIME_METADATA = 3,
NFS4_CHANGE_TYPE_IS_UNDEFINED = 4
};
change_attr_type is a per-file system attribute that lets the NFSv4.2 server spill the tea on how it expects the change attribute value to evolve after file data or metadata has changed ๐. Section 5.4 of [RFC5661] covers per-file system attributes in general, and the expectation here is that the value of change_attr_type won't depend on the value of "homogeneous" and will only change in the event of a migration ๐.
Here are the five possible values:
NFS4_CHANGE_TYPE_IS_MONOTONIC_INCR: The change attribute value MUST monotonically increase for every atomic change to the file attributes, data, or directory contents. ๐NFS4_CHANGE_TYPE_IS_VERSION_COUNTER: The change attribute value MUST be incremented by one unit for every atomic change to the file attributes, data, or directory contents. This property is preserved when writing to pNFS data servers. ๐ขNFS4_CHANGE_TYPE_IS_VERSION_COUNTER_NOPNFS: The change attribute value MUST be incremented by one unit for every atomic change to the file attributes, data, or directory contents. In the case where the client is writing to pNFS data servers, the number of increments is not guaranteed to exactly match the number of WRITEs. ๐ซฃNFS4_CHANGE_TYPE_IS_TIME_METADATA: The change attribute is implemented as suggested in [RFC7530] in terms of thetime_metadataattribute. โฐNFS4_CHANGE_TYPE_IS_UNDEFINED: The change attribute does not take values that fit into any of these categories. ๐
If either NFS4_CHANGE_TYPE_IS_MONOTONIC_INCR, NFS4_CHANGE_TYPE_IS_VERSION_COUNTER, or NFS4_CHANGE_TYPE_IS_TIME_METADATA is set, then the client knows at the very least that the change attribute is monotonically increasing, which is enough to settle which value is the most recent ๐.
If the client sees the value NFS4_CHANGE_TYPE_IS_TIME_METADATA, then by inspecting the value of the time_delta attribute it additionally has the option of detecting rogue server implementations that use time_metadata in violation of the specification ๐ต๏ธโโ๏ธ.
If the client sees NFS4_CHANGE_TYPE_IS_VERSION_COUNTER, it can predict what the resulting change attribute value should be after a COMPOUND containing a SETATTR, WRITE, or CREATE. This again lets it detect changes made in parallel by another client ๐ฏ. The value NFS4_CHANGE_TYPE_IS_VERSION_COUNTER_NOPNFS permits the same, but only if the client is not doing pNFS WRITEs ๐
โโ๏ธ.
Finally, if the server does not support change_attr_type or if NFS4_CHANGE_TYPE_IS_UNDEFINED is set, then the server SHOULD make an effort to implement the change attribute in terms of the time_metadata attribute ๐ ๏ธ.
12.2.4. Attribute 80: sec_label ๐
typedef uint32_t policy4;
struct labelformat_spec4 {
policy4 lfs_lfs;
policy4 lfs_pi;
};
struct sec_label4 {
labelformat_spec4 slai_lfs;
opaque slai_data<>;
};
The FATTR4_SEC_LABEL holds an array of two components, and the first one is an LFS ๐ท๏ธ. That's what gives the receiving end what it needs to translate the security attribute into something it can actually use ๐. Label Formats assigned an LFS may optionally choose to include a Policy Identifier field, which allows for complex policy deployments ๐ง . The LFS and the Security Label Format Selection Registry are described in detail in [RFC7569]. The translation used to interpret the security attribute is not specified as part of the protocol, since it may depend on various factors ๐คทโโ๏ธ. The second component is an opaque section holding the data of the attribute, and it's dependent on the MAC model to interpret and enforce ๐ก๏ธ.
The LFS specification is responsible for defining a maximum size for the opaque section, slai_data<>. When creating or modifying a label for an object, the client needs to be guaranteed that the server will accept a label that is sized correctly. Since both client and server are part of a specific MAC model, the client will be aware of the size ๐.
13. Operations: REQUIRED, RECOMMENDED, or OPTIONAL โจ
okay so Tables 5 and 6 are basically the cheat sheet for every NFSv4.2 operation ๐ They tell you whether each one is REQUIRED, RECOMMENDED, or OPTIONAL to implement, or MUST NOT implement ๐ซ The MUST NOT implement label is only for operations that were defined in NFSv4.0 or NFSv4.1 and MUST NOT be implemented in NFSv4.2. no cap, that one's a hard no.
For the most part, the REQUIRED, RECOMMENDED, or OPTIONAL label on client-sent operations is really about what the server has to implement ๐ฅ๏ธ The client is generally required to implement the operations its own operating environment needs. For example, a read-only NFSv4.2 client has zero reason to implement WRITE, so it is not required to do so. the vibe is "only build what you actually use" ๐ธ
Callback operations are a different story ๐ The REQUIRED or OPTIONAL label for callback operations sent by the server applies to both the client and the server. Usually the client gets to choose whether to create the backchannel, and the operations it sends on the forechannel are what set up the server to send callbacks. The one partial exception is CB_RECALL_SLOT. The only way a client can skip supporting it is by not creating a backchannel at all ๐ฆ
This is only a summary, so it skips some subtleties ๐ If you have a question about what you actually have to implement, go read the operation descriptions themselves, plus the other explanatory text in this specification or in the NFSv4.1 specification [RFC5661]. the details live there, bestie.
The abbreviations used in the second and third columns of Tables 5 and 6 mean the following:
REQ: REQUIRED to implementREC: RECOMMENDED to implementOPT: OPTIONAL to implementMNI: MUST NOT implement
Here's the optional-features deal โจ For NFSv4.2 features that are OPTIONAL, the operations that support them are OPTIONAL too. If the server doesn't implement one of those operations, the server MUST return NFS4ERR_NOTSUPP when the client tries to use it. If an OPTIONAL feature is supported, a bunch of related operations can suddenly become REQUIRED to implement ๐ง The third column of the tables shows the feature(s) and whether the operation is REQUIRED or OPTIONAL when that feature is supported.
The OPTIONAL features and their abbreviations are:
pNFS: Parallel NFSFDELG: File DelegationsDDELG: Directory DelegationsCOPYra: Intra-server Server-Side CopyCOPYer: Inter-server Server-Side CopyADB: Application Data Blocks
+----------------------+--------------------+-----------------------+
| Operation | REQ, REC, OPT, or | Feature (REQ, REC, or |
| | MNI | OPT) |
+----------------------+--------------------+-----------------------+
| ACCESS | REQ | |
| ALLOCATE | OPT | |
| BACKCHANNEL_CTL | REQ | |
| BIND_CONN_TO_SESSION | REQ | |
| CLONE | OPT | |
| CLOSE | REQ | |
| COMMIT | REQ | |
| COPY | OPT | COPYer (REQ), COPYra |
| | | (REQ) |
| COPY_NOTIFY | OPT | COPYer (REQ) |
| CREATE | REQ | |
| CREATE_SESSION | REQ | |
| DEALLOCATE | OPT | |
| DELEGPURGE | OPT | FDELG (REQ) |
| DELEGRETURN | OPT | FDELG, DDELG, pNFS |
| | | (REQ) |
| DESTROY_CLIENTID | REQ | |
| DESTROY_SESSION | REQ | |
| EXCHANGE_ID | REQ | |
| FREE_STATEID | REQ | |
| GETATTR | REQ | |
| GETDEVICEINFO | OPT | pNFS (REQ) |
| GETDEVICELIST | MNI | pNFS (MNI) |
| GETFH | REQ | |
| GET_DIR_DELEGATION | OPT | DDELG (REQ) |
| ILLEGAL | REQ | |
| IO_ADVISE | OPT | |
| LAYOUTCOMMIT | OPT | pNFS (REQ) |
| LAYOUTERROR | OPT | pNFS (OPT) |
| LAYOUTGET | OPT | pNFS (REQ) |
| LAYOUTRETURN | OPT | pNFS (REQ) |
| LAYOUTSTATS | OPT | pNFS (OPT) |
| LINK | OPT | |
| LOCK | REQ | |
| LOCKT | REQ | |
| LOCKU | REQ | |
| LOOKUP | REQ | |
| LOOKUPP | REQ | |
| NVERIFY | REQ | |
| OFFLOAD_CANCEL | OPT | COPYer (OPT), COPYra |
| | | (OPT) |
| OFFLOAD_STATUS | OPT | COPYer (OPT), COPYra |
| | | (OPT) |
| OPEN | REQ | |
| OPENATTR | OPT | |
| OPEN_CONFIRM | MNI | |
| OPEN_DOWNGRADE | REQ | |
| PUTFH | REQ | |
| PUTPUBFH | REQ | |
| PUTROOTFH | REQ | |
| READ | REQ | |
| READDIR | REQ | |
| READLINK | OPT | |
| READ_PLUS | OPT | |
| RECLAIM_COMPLETE | REQ | |
| RELEASE_LOCKOWNER | MNI | |
| REMOVE | REQ | |
| RENAME | REQ | |
| RENEW | MNI | |
| RESTOREFH | REQ | |
| SAVEFH | REQ | |
| SECINFO | REQ | |
| SECINFO_NO_NAME | REC | pNFS file layout |
| | | (REQ) |
| SEEK | OPT | |
| SEQUENCE | REQ | |
| SETATTR | REQ | |
| SETCLIENTID | MNI | |
| SETCLIENTID_CONFIRM | MNI | |
| SET_SSV | REQ | |
| TEST_STATEID | REQ | |
| VERIFY | REQ | |
| WANT_DELEGATION | OPT | FDELG (OPT) |
| WRITE | REQ | |
| WRITE_SAME | OPT | ADB (REQ) |
+----------------------+--------------------+-----------------------+
Table 5: Operations
+-------------------------+------------------+----------------------+
| Operation | REQ, REC, OPT, | Feature (REQ, REC, |
| | or MNI | or OPT) |
+-------------------------+------------------+----------------------+
| CB_GETATTR | OPT | FDELG (REQ) |
| CB_ILLEGAL | REQ | |
| CB_LAYOUTRECALL | OPT | pNFS (REQ) |
| CB_NOTIFY | OPT | DDELG (REQ) |
| CB_NOTIFY_DEVICEID | OPT | pNFS (OPT) |
| CB_NOTIFY_LOCK | OPT | |
| CB_OFFLOAD | OPT | COPYer (REQ), COPYra |
| | | (REQ) |
| CB_PUSH_DELEG | OPT | FDELG (OPT) |
| CB_RECALL | OPT | FDELG, DDELG, pNFS |
| | | (REQ) |
| CB_RECALL_ANY | OPT | FDELG, DDELG, pNFS |
| | | (REQ) |
| CB_RECALL_SLOT | REQ | |
| CB_RECALLABLE_OBJ_AVAIL | OPT | DDELG, pNFS (REQ) |
| CB_SEQUENCE | OPT | FDELG, DDELG, pNFS |
| | | (REQ) |
| CB_WANTS_CANCELLED | OPT | FDELG, DDELG, pNFS |
| | | (REQ) |
+-------------------------+------------------+----------------------+
Table 6: Callback Operations
14. Modifications to NFSv4.1 Operations ๐
14.1. Operation 42: EXCHANGE_ID - Instantiate the client ID ๐
14.1.1. ARGUMENT
<CODE BEGINS>
/* new */
const EXCHGID4_FLAG_SUPP_FENCE_OPS = 0x00000004;
<CODE ENDS>
14.1.2. RESULT
Unchanged โ
14.1.3. MOTIVATION
Enterprise apps need a guarantee that an operation has either aborted or completed, and NFSv4.1 gives you that as long as the session is alive ๐ฅ You just send a SEQUENCE operation on the same slot with a new sequence number, and if SEQUENCE returns successfully, the previous operation is done. The problem is when the session is lost. Then there's no way to know whether operations in progress aborted or completed ๐ญ Looking back, the NFSv4.1 specification should have required DESTROY_SESSION to either abort or complete all outstanding operations. hindsight is 20/20, bestie ๐
14.1.4. DESCRIPTION ๐
okay so, a client SHOULD ask for the EXCHGID4_FLAG_SUPP_FENCE_OPS capability whenever it sends an EXCHANGE_ID op ๐. The server SHOULD set that capability in its EXCHANGE_ID reply even if the client didn't ask for it ๐ซถ. The server's reply is what decides whether the capability is actually in effect ๐. When it IS in effect, these things happen:
- The server will not reply to any
DESTROY_SESSIONsent with the client ID until every operation in progress is either completed or aborted ๐. - The server holds off replying to later
EXCHANGE_IDops from the same client owner that come with a new verifier until everything in progress on the client ID's session is completed or aborted ๐. - If your NFS server runs as a cluster, it supports client ID trunking, and the
EXCHGID4_FLAG_SUPP_FENCE_OPScapability is enabled, then a session ID made on one node of the storage cluster MUST be destroyable withDESTROY_SESSION๐ง. Plus,DESTROY_CLIENTIDand anEXCHANGE_IDcarrying a new verifier hit every session, no matter which node they were created on ๐.
14.2. Operation 48: GETDEVICELIST - Get all device mappings for a file system
14.2.1. ARGUMENT
struct GETDEVICELIST4args {
/* CURRENT_FH: object belonging to the file system */
layouttype4 gdla_layout_type;
/* number of device IDs to return */
count4 gdla_maxdevices;
nfs_cookie4 gdla_cookie;
verifier4 gdla_cookieverf;
};
14.2.2. RESULT
struct GETDEVICELIST4resok {
nfs_cookie4 gdlr_cookie;
verifier4 gdlr_cookieverf;
deviceid4 gdlr_deviceid_list<>;
bool gdlr_eof;
};
union GETDEVICELIST4res switch (nfsstat4 gdlr_status) {
case NFS4_OK:
GETDEVICELIST4resok gdlr_resok4;
default:
void;
};
14.2.3. MOTIVATION
okay, story time ๐. GETDEVICELIST came along in [RFC5661] to grab a list of devices at file system mount time, mainly for block layout type servers ๐งฑ. BUT using it creates a race condition against the change notifications for pNFS device IDs that CB_NOTIFY_DEVICEID provides ๐ฌ. Implementers working with block layout servers found there's no real need for GETDEVICELIST at all ๐
โโ๏ธ. Clients need to be able to ask for new devices with GETDEVICEINFO whenever they want, either after seeing a new deviceid in LAYOUTGET results or when the CB_NOTIFY_DEVICEID callback op arrives ๐ฆ.
14.2.4. DESCRIPTION
the tea is simple: clients and servers MUST NOT implement the GETDEVICELIST operation ๐ซ๐
.
15. NFSv4.2 Operations โจ
15.1. Operation 59: ALLOCATE - Reserve space in a region of a file
15.1.1. ARGUMENT
struct ALLOCATE4args {
/* CURRENT_FH: file */
stateid4 aa_stateid;
offset4 aa_offset;
length4 aa_length;
};
15.1.2. RESULT
struct ALLOCATE4res {
nfsstat4 ar_status;
};
15.1.3. DESCRIPTION
whenever a client wants to reserve space for a region in a file ๐ธ, it calls the ALLOCATE operation. The current filehandle gets set to the filehandle of that file, and the start offset and length in bytes go into aa_offset and aa_length, respectively ๐ฆ.
CURRENT_FH must be a regular file. If CURRENT_FH isn't a regular file, the operation MUST fail and return NFS4ERR_WRONG_TYPE ๐
โโ๏ธ.
The aa_stateid MUST refer to a stateid that is valid for a WRITE operation and follows the rules for stateids in Sections 8.2.5 and 18.32.3 of [RFC5661] ๐.
The server makes sure backing blocks are reserved for the region given by aa_offset and aa_length, so no future writes into that region will return NFS4ERR_NOSPC โจ. If the region lies partially or fully outside the current file size, the file size gets set to aa_offset + aa_length implicitly ๐ง. If the server can't guarantee this, it must return NFS4ERR_NOSPC ๐.
ALLOCATE can also extend a file's size if the region from aa_offset and aa_length goes past the current file size ๐ฆ. In that case, any data outside the previous file size reads back as zeros until something is actually written there ๐ค.
It is not required that the server allocate the space to the file before it returns success ๐ซ . The allocation can be deferred, but it must be guaranteed that it won't fail for lack of space. The deferral doesn't turn into an asynchronous reply ๐.
The ALLOCATE operation results in the space_used and space_freed attributes going up by the number of bytes reserved, unless those bytes were previously reserved or written and not shared ๐.
15.2. Operation 60: COPY - Initiate a server-side copy
15.2.1. ARGUMENT
struct COPY4args {
/* SAVED_FH: source file */
/* CURRENT_FH: destination file */
stateid4 ca_src_stateid;
stateid4 ca_dst_stateid;
offset4 ca_src_offset;
offset4 ca_dst_offset;
length4 ca_count;
bool ca_consecutive;
bool ca_synchronous;
netloc4 ca_source_server<>;
};
15.2.2. RESULT
struct write_response4 {
stateid4 wr_callback_id<1>;
length4 wr_count;
stable_how4 wr_committed;
verifier4 wr_writeverf;
};
struct copy_requirements4 {
bool cr_consecutive;
bool cr_synchronous;
};
struct COPY4resok {
write_response4 cr_response;
copy_requirements4 cr_requirements;
};
union COPY4res switch (nfsstat4 cr_status) {
case NFS4_OK:
COPY4resok cr_resok4;
case NFS4ERR_OFFLOAD_NO_REQS:
copy_requirements4 cr_requirements;
default:
void;
};
15.2.3. DESCRIPTION โจ
okay so COPY is the op for BOTH intra-server and inter-server copies ๐ and in both cases the client always sends it to the destination server of the file copy ๐
. it asks for a range in the file named by SAVED_FH to get copied into a range in the file named by CURRENT_FH ๐ฆ
Both SAVED_FH and CURRENT_FH MUST be regular files. if either one isn't a regular file, the operation MUST fail and return NFS4ERR_WRONG_TYPE ๐ญ
SAVED_FH and CURRENT_FH must be different files. if they refer to the same file, the operation MUST fail with NFS4ERR_INVAL ๐
if the request is for an inter-server copy, the source-fh is a filehandle from the source server, and the COMPOUND procedure is running on the destination server ๐ธ. so the source-fh is a foreign filehandle on the server that's receiving the COPY request. if either PUTFH or SAVEFH checked the validity of the filehandle, the operation would likely fail and return NFS4ERR_STALE ๐
if a server supports the inter-server copy feature, a PUTFH followed by a SAVEFH MUST NOT return NFS4ERR_STALE for either operation โจ. these restrictions aren't a big deal for servers tbh ๐ซถ. CURRENT_FH and SAVED_FH can be validated in the context of the operation that references them, and an NFS4ERR_STALE error gets returned for an invalid filehandle at that point ๐
The ca_dst_stateid MUST refer to a stateid that is valid for a WRITE operation and follows the rules for stateids in Sections 8.2.5 and 18.32.3 of [RFC5661]. for an inter-server copy, the ca_src_stateid MUST be the cnr_stateid returned from the earlier COPY_NOTIFY operation ๐ฆ. for an intra-server copy, ca_src_stateid MUST refer to a stateid that is valid for a READ operation and follows the rules for stateids in Sections 8.2.5 and 18.22.3 of [RFC5661] ๐ฅ. if either stateid is invalid, then the operation MUST fail ๐ญ
the ca_src_offset is the offset within the source file where the data gets read from, the ca_dst_offset is the offset within the destination file where the data gets written to, and the ca_count is how many bytes will be copied ๐ง. an offset of 0 (zero) means the start of the file. a count of 0 (zero) asks for all bytes from ca_src_offset through EOF to be copied to the destination ๐. if concurrent modifications to the source file overlap with the source file region being copied, the copied data may include all, some, or none of those modifications ๐. if the client is worried about this, it can use standard NFS operations (e.g., OPEN with OPEN4_SHARE_DENY_WRITE or mandatory byte-range locks) to guard against concurrent modifications ๐ก๏ธ. if the source file's EOF is being modified in parallel with a COPY that specifies a count of 0 (zero) bytes, the amount of data copied is implementation dependent (clients may guard against this case by specifying a non-zero count value or by preventing modification of the source file as mentioned above) ๐
if the source offset, or the source offset plus count, is greater than the size of the source file, the operation MUST fail with NFS4ERR_INVAL ๐ซ. the destination offset, or the destination offset plus count, may be greater than the size of the destination file ๐. this lets the client issue parallel copies to implement operations such as:
% cat file1 file2 file3 file4 > dest
if the ca_source_server list is specified, then this is an inter-server COPY operation and the source file lives on a remote server ๐. the client is expected to have previously sent a successful COPY_NOTIFY request to the remote source server ๐ฎ. the ca_source_server list MUST be the same as the COPY_NOTIFY response's cnr_source_server list ๐
. if the client includes the entries from the COPY_NOTIFY response's cnr_source_server list in the ca_source_server list, the source server can indicate a specific copy protocol for the destination server to use by returning a URL that specifies both a protocol service and a server name ๐ฆ. server-to-server copy protocol considerations are described in Sections 4.6 and 4.9.1 ๐
if ca_consecutive is set, then the client has specified that the copy protocol selected MUST copy bytes in consecutive order from ca_src_offset to ca_count ๐งต. if the destination server can't meet this requirement, then it MUST return an error of NFS4ERR_OFFLOAD_NO_REQS and set cr_consecutive to be FALSE ๐ค. likewise, if ca_synchronous is set, then the client has required that the copy protocol selected MUST perform a synchronous copy ๐. if the destination server can't meet this requirement, then it MUST return an error of NFS4ERR_OFFLOAD_NO_REQS and set cr_synchronous to be FALSE ๐
if both are set by the client, then the destination SHOULD try to figure out whether it can respond to both requirements at the same time ๐ง . if it can't make that determination, it must set to TRUE the one it can and set to FALSE the other. the client, upon getting an NFS4ERR_OFFLOAD_NO_REQS error, has to check both cr_consecutive and cr_synchronous against the respective values of ca_consecutive and ca_synchronous to figure out which possible requirement wasn't met ๐. it MUST be prepared for the destination server not being able to determine both requirements at the same time ๐ซ
once the client gets NFS4ERR_OFFLOAD_NO_REQS, it has to decide whether it wants to re-request the copy with a relaxed set of requirements or go back to copying the data manually ๐ค. if it decides to copy the data manually and this is a remote copy, then the client is responsible for telling the source that the earlier COPY_NOTIFY is no longer valid by sending it an OFFLOAD_CANCEL ๐ฃ
if the operation doesn't result in an immediate failure, the server will return NFS4_OK ๐ฅณ
if a wr_callback_id is returned, that means an asynchronous COPY operation was started and a CB_OFFLOAD callback will deliver the final results of the operation ๐ฌ. the wr_callback_id stateid is called a "copy stateid" in this context. the server gets to choose whether to return the results in a callback, since the data might take a pretty long time to copy โณ
if no wr_callback_id is returned, the operation completed synchronously and the server won't issue any callback ๐
. the completion status of the operation is shown in cr_status โ
if the copy completes successfully, either synchronously or asynchronously, the data copied from the source file to the destination file MUST appear identical to the NFS client ๐ช. however, the NFS server's on-disk representation of the data in the source file and destination file MAY differ ๐. for example, the NFS server might encrypt, compress, deduplicate, or otherwise represent the on-disk data in the source and destination files differently ๐๏ธ
if a failure does happen for a synchronous copy, wr_count gets set to the number of bytes copied to the destination file before the error occurred ๐ฌ. if cr_consecutive is TRUE, then the bytes were copied in order. if the failure happened for an asynchronous copy, then the client will have been notified of the consecutive copy order when it got the copy stateid ๐. it can work out the bytes copied from the coa_bytes_copied in the CB_OFFLOAD argument ๐งฎ
either way, if cr_consecutive wasn't TRUE, there's no assurance as to exactly which bytes in the range were copied ๐. the client MUST assume that there's a mixture of the original contents of the range and the new bytes ๐ซฃ. if the COPY wrote past the end of the file on the destination, then the last byte written determines the new file size ๐. the contents of any block that wasn't written to and is past the original size of the file will be as if a normal WRITE extended the file ๐
15.3. Operation 61: COPY_NOTIFY - Notify a source server of a future copy โจ
15.3.1. ARGUMENT
struct COPY_NOTIFY4args {
/* CURRENT_FH: source file */
stateid4 cna_src_stateid;
netloc4 cna_destination_server;
};
15.3.2. RESULT
struct COPY_NOTIFY4resok {
nfstime4 cnr_lease_time;
stateid4 cnr_stateid;
netloc4 cnr_source_server<>;
};
union COPY_NOTIFY4res switch (nfsstat4 cnr_status) {
case NFS4_OK:
COPY_NOTIFY4resok resok4;
default:
void;
};
15.3.3. DESCRIPTION
this op is used for an inter-server copy ๐. a client sends it in a COMPOUND request to the source server to authorize a destination server, identified by cna_destination_server, to read the file specified by CURRENT_FH on behalf of the given user ๐
The cna_src_stateid MUST refer to either open or locking states provided earlier by the server. if it's invalid, then the operation MUST fail ๐จ
The cna_destination_server MUST be specified using the netloc4 network location format. the server isn't required to resolve the cna_destination_server address before completing this operation ๐
if this operation succeeds, the source server will let the cna_destination_server copy the specified file on behalf of the given user, as long as both of these conditions are met ๐ซ:
- the destination server begins reading the source file before the
cnr_lease_timeexpires. if thecnr_lease_timeexpires while the destination server is still reading the source file, the destination server is allowed to finish reading the file. if thecnr_lease_timeexpires before the destination server uses READ or READ_PLUS to begin the transfer, the source server can useNFS4ERR_PARTNER_NO_AUTHto tell the destination server that thecnr_lease_timehas expired โฐ - the client has not issued an OFFLOAD_CANCEL for the same combination of user, filehandle, and destination server ๐ซ
the cnr_lease_time is chosen by the source server. a cnr_lease_time of 0 (zero) means an infinite lease โพ๏ธ. to avoid needing synchronized clocks, copy lease times are granted by the server as a time delta โ. to renew the copy lease time, the client should resend the same copy notification request to the source server ๐
The cnr_stateid is a copy stateid that uniquely describes the state needed on the source server to track the proposed COPY. as defined in Section 8.2 of [RFC5661], a stateid is tied to the current filehandle, and if the same stateid is presented by two different clients, it may refer to different states ๐ชช. since the source doesn't know which netloc4 network location the destination might use to establish the COPY operation, it can use the cnr_stateid to identify that the destination is operating on behalf of the client ๐ต๏ธ. so the source server MUST construct copy stateids such that they are distinct from all other stateids handed out to clients ๐งฉ. these copy stateids MUST denote the same set of locks as each of the earlier delegation, locking, and open states for the client on the given file (see Section 4.3.1) ๐
a successful response will also contain a list of netloc4 network location formats called cnr_source_server, which the source is willing to accept connections from the destination on ๐. these might not be reachable from the client, and they might be on networks the client has no connection to ๐
this operation is unnecessary for an intra-server copy ๐
15.4. Operation 62: DEALLOCATE - Unreserve space in a region of a file ๐ฆ
15.4.1. ARGUMENT
okay so the DEALLOCATE args are giving simple and clean ๐
struct DEALLOCATE4args {
/* CURRENT_FH: file */
stateid4 da_stateid;
offset4 da_offset;
length4 da_length;
};
15.4.2. RESULT
the result is just a status code, bestie ๐
struct DEALLOCATE4res {
nfsstat4 dr_status;
};
15.4.3. DESCRIPTION
okay so whenever a client wants to unreserve space for a region in a file โจ, it calls DEALLOCATE. The current filehandle gets set to the filehandle of that file, and the start offset and length in bytes go into da_offset and da_length, respectively. If no space was allocated or reserved for all or parts of that region, DEALLOCATE has no effect on the part that's already unreserved ๐ฆ. All further READs from the region passed to DEALLOCATE MUST return zeros until it's overwritten.
CURRENT_FH must be a regular file. If CURRENT_FH isn't a regular file, the operation MUST fail and return NFS4ERR_WRONG_TYPE. no cap, that's a hard rule ๐
The da_stateid MUST refer to a stateid that is valid for a WRITE operation and follows the rules for stateids in Sections 8.2.5 and 18.32.3 of [RFC5661].
Sometimes da_offset and/or da_offset + da_length won't line up with a boundary where the server does allocations or deallocations. For most file systems, that boundary is the block size of the file system. In that case, the server can deallocate as many bytes as it can in the region. The blocks that can't be deallocated MUST be zeroed ๐ง
DEALLOCATE makes the space_used attribute go down by the number of bytes that were deallocated. The space_freed attribute may or may not decrease, depending on the support and on whether the blocks backing the range were shared or not. The size attribute stays exactly the same ๐
15.5. Operation 63: IO_ADVISE - Send client I/O access pattern hints to the server
15.5.1. ARGUMENT
here are the enum and args for IO_ADVISE ๐
enum IO_ADVISE_type4 {
IO_ADVISE4_NORMAL = 0,
IO_ADVISE4_SEQUENTIAL = 1,
IO_ADVISE4_SEQUENTIAL_BACKWARDS = 2,
IO_ADVISE4_RANDOM = 3,
IO_ADVISE4_WILLNEED = 4,
IO_ADVISE4_WILLNEED_OPPORTUNISTIC = 5,
IO_ADVISE4_DONTNEED = 6,
IO_ADVISE4_NOREUSE = 7,
IO_ADVISE4_READ = 8,
IO_ADVISE4_WRITE = 9,
IO_ADVISE4_INIT_PROXIMITY = 10
};
struct IO_ADVISE4args {
/* CURRENT_FH: file */
stateid4 iaa_stateid;
offset4 iaa_offset;
length4 iaa_count;
bitmap4 iaa_hints;
};
15.5.2. RESULT
struct IO_ADVISE4resok {
bitmap4 ior_hints;
};
union IO_ADVISE4res switch (nfsstat4 ior_status) {
case NFS4_OK:
IO_ADVISE4resok resok4;
default:
void;
};
15.5.3. DESCRIPTION
okay so IO_ADVISE sends an I/O access pattern hint to the server for the owner of the stateid, covering the byte range given by iar_offset and iar_count โจ. The byte range given by iaa_offset and iaa_count doesn't need to exist in the file yet, but the iaa_hints will apply to that byte range once it does. If iaa_count is 0, then all data following iaa_offset is specified. The server MAY ignore the advice ๐ซถ
these are the hints a stateid holder is allowed to send:
IO_ADVISE4_NORMAL: There's no advice to give. This is the default behavior.IO_ADVISE4_SEQUENTIAL: Expects to access the specified data sequentially, from lower offsets to higher offsets.IO_ADVISE4_SEQUENTIAL_BACKWARDS: Expects to access the specified data sequentially, from higher offsets to lower offsets.IO_ADVISE4_RANDOM: Expects to access the specified data in a random order.IO_ADVISE4_WILLNEED: Expects to access the specified data in the near future.IO_ADVISE4_WILLNEED_OPPORTUNISTIC: Expects to possibly access the data in the near future. This is a speculative hint, so the server should prefetch data or indirect blocks only if it can be done at a marginal cost.IO_ADVISE_DONTNEED: Expects that it won't access the specified data in the near future.IO_ADVISE_NOREUSE: Expects to access the specified data once and then not reuse it afterwards.IO_ADVISE4_READ: Expects to read the specified data in the near future.IO_ADVISE4_WRITE: Expects to write the specified data in the near future.IO_ADVISE4_INIT_PROXIMITY: Tells the server that the data in the byte range is still important to the client.
since IO_ADVISE is only a hint, a server SHOULD NOT return an error and invalidate an entire COMPOUND request if one of the hints in iar_hints isn't supported by the server ๐ธ. Also, the server MUST NOT return an error if the client sends contradictory hints, e.g., IO_ADVISE4_SEQUENTIAL and IO_ADVISE4_RANDOM in a single IO_ADVISE operation. In those cases, the server MUST return success and an ior_hints value that shows the hint it intends to implement. That might just mean returning IO_ADVISE4_NORMAL.
the ior_hints the server sends back is mainly for debugging, since the server isn't obligated to actually carry out the hints it describes in ior_hints. Plus, even if the server meant to implement those hints, it may need to change how it handles the file. For example, that could happen because of memory pressure, extra IO_ADVISE hints from other clients, or file access patterns it detected on its own ๐ฅ
The server MAY return different advice than what the client asked for. Some examples: another client advising a different I/O access pattern, another client actually using a different I/O access pattern, or the server being unable to support the requested pattern ๐
each new IO_ADVISE overrides all the earlier ones for a given byte range. This basically follows a "last hint wins" strategy for a given stateid and byte range ๐
clients should assume that hints sent in an IO_ADVISE operation will be forgotten once the file is closed ๐ฆ
15.5.4. IMPLEMENTATION
the NFS client may choose to send an IO_ADVISE operation to the server in several different situations ๐ง
the most obvious one is when an application calls posix_fadvise(). In that case, IO_ADVISE4_WRITE and IO_ADVISE4_READ may be set, depending on the type of file access specified when the file was opened โจ
15.5.5. IO_ADVISE4_INIT_PROXIMITY
the IO_ADVISE4_INIT_PROXIMITY hint is non-POSIX in origin ๐. It can be used to say that the client has recently accessed the byte range in its own cache. In other words, it hasn't accessed it on the server, but it has accessed it locally. When the server runs out of resources, knowing which data matters more helps the server make better choices about what to purge from a cache or move to secondary storage ๐. It also tells the server which delegations are more important. If delegations are working correctly, then once a delegation goes to a client and the client has read the content for that byte range, the server might never get another READ request for that byte range ๐
the IO_ADVISE4_INIT_PROXIMITY hint can also be used in a pNFS setting, so the client can tell the metadata server about the I/O statistics between the client and the storage devices. The metadata server is then free to use that information about client I/O to optimize where the data is stored ๐
this hint is also useful for NFS clients that network-boot from a server. If the first client to boot sends this hint, it keeps the cache warm for the remaining clients ๐ซถ
15.5.6. pNFS File Layout Data Type Considerations
the IO_ADVISE considerations for pNFS are very similar to the COMMIT considerations for pNFS (see Section 13.7 of [RFC5661]) ๐ . Just like with COMMIT, some NFS server implementations prefer that IO_ADVISE be done on the storage device, and some prefer it be done on the metadata server.
for a file's layout type, NFSv4.2 includes an extra hint, NFL42_CARE_IO_ADVISE_THRU_MDS, which is valid only on metadata servers running NFSv4.2 or higher. ("NFL" stands for "NFS File Layout".) Any file's layout obtained from an NFSv4.1 metadata server MUST NOT have NFL42_UFLG_IO_ADVISE_THRU_MDS set. Any file's layout obtained with an NFSv4.2 metadata server MAY have NFL42_UFLG_IO_ADVISE_THRU_MDS set. However, if the layout uses NFSv4.1 storage devices, the IO_ADVISE operation can't be sent to them ๐ฅ
If NFL42_UFLG_IO_ADVISE_THRU_MDS is set, the client MUST send the IO_ADVISE operation to the metadata server for the storage device to honor it ๐. Once the metadata server receives the IO_ADVISE operation, it communicates the advice to each storage device.
If NFL42_UFLG_IO_ADVISE_THRU_MDS is not set, then the client SHOULD send an IO_ADVISE operation to the appropriate storage device for the specified byte range. The client MAY always send IO_ADVISE to the metadata server, but if the server hasn't set NFL42_UFLG_IO_ADVISE_THRU_MDS, the client should expect that such an IO_ADVISE is futile ๐. Note that a client SHOULD use the same set of arguments on each IO_ADVISE it sends to a storage device for the same open file reference.
the server isn't required to support different advice for different storage devices with the same open file reference ๐ธ
15.5.6.1. Dense and Sparse Packing Considerations ๐งฉ
okay so the IO_ADVISE operation MUST use the iar_offset and byte range depending on whether NFL4_UFLG_DENSE is present or absent (see Section 13.4.4 of [RFC5661]) ๐
Let's walk through the dense case first โจ If NFL4_UFLG_DENSE is present, then (1) a READ or WRITE to the storage device for iaa_offset 0 really means iaa_offset 10000 in the logical file, and (2) an IO_ADVISE for iaa_offset 0 means iaa_offset 10000 in the logical file. Iconic ๐
Now the no-dense case ๐ธ If NFL4_UFLG_DENSE is absent, then (1) a READ or WRITE to the storage device for iaa_offset 0 really means iaa_offset 0 in the logical file, and (2) an IO_ADVISE for iaa_offset 0 means iaa_offset 0 in the logical file. Simple and clean, no notes ๐ซถ
Here's a dense example with a stripe unit of 1000 bytes and a stripe count of 10, where the dense storage device file is serving iar_offset 0. A READ or WRITE to the storage device for iaa_offsets 0, 1000, 2000, and 3000 really means iaa_offsets 10000, 20000, 30000, and 40000 (which implies a stripe count of 10 and a stripe unit of 1000). Then an IO_ADVISE sent to the same storage device with an iaa_offset of 500 and an iaa_count of 3000 means the IO_ADVISE applies to these byte ranges of the dense storage device file ๐ง
- 500 to 999
- 1000 to 1999
- 2000 to 2999
- 3000 to 3499
That is, the contiguous range 500 to 3499, as specified in IO_ADVISE. ๐
It also applies to these byte ranges of the logical file ๐ฆ
- 10500 to 10999 (500 bytes)
- 20000 to 20999 (1000 bytes)
- 30000 to 30999 (1000 bytes)
- 40000 to 40499 (500 bytes)
(total 3000 bytes)
Next up is a sparse example ๐ If NFL4_UFLG_DENSE is absent, the stripe unit is 250 bytes, the stripe count is 4, and the sparse storage device file is serving iaa_offset 0. Then, a READ or WRITE to the storage device for iaa_offsets 0, 1000, 2000, and 3000 really means iaa_offsets 0, 1000, 2000, and 3000 in the logical file. Keep in mind that in the storage device file, byte ranges 250 to 999, 1250 to 1999, 2250 to 2999, and 3250 to 3999 are not accessible ๐
Then an IO_ADVISE sent to the same storage device with an iaa_offset of 500 and an iaa_count of 3000 means the IO_ADVISE applies to these byte ranges of the logical file and the sparse storage device file. Here's the breakdown ๐
- 500 to 999 (500 bytes) - no effect
- 1000 to 1249 (250 bytes) - effective
- 1250 to 1999 (750 bytes) - no effect
- 2000 to 2249 (250 bytes) - effective
- 2250 to 2999 (750 bytes) - no effect
- 3000 to 3249 (250 bytes) - effective
- 3250 to 3499 (250 bytes) - no effect
(subtotal 2250 bytes) - no effect
(subtotal 750 bytes) - effective
(grand total 3000 bytes) - no effect + effective
Now for the important part, the no-flag situation ๐จ If neither the NFL42_UFLG_IO_ADVISE_THRU_MDS flag nor the NFL4_UFLG_DENSE flag is set in the layout, then any IO_ADVISE request sent to the data server with a byte range that overlaps stripe units the data server does not serve MUST NOT result in the status NFS4ERR_PNFS_IO_HOLE. Instead, the response SHOULD be successful. And if the server applies IO_ADVISE hints on any stripe units that overlap with the specified range, those hints SHOULD be indicated in the response ๐ซถ๐ฅ
15.6. Operation 64: LAYOUTERROR - Provide errors for the layout ๐จ
15.6.1. ARGUMENT ๐
struct device_error4 {
deviceid4 de_deviceid;
nfsstat4 de_status;
nfs_opnum4 de_opnum;
};
struct LAYOUTERROR4args {
/* CURRENT_FH: file */
offset4 lea_offset;
length4 lea_length;
stateid4 lea_stateid;
device_error4 lea_errors<>;
};
15.6.2. RESULT ๐
struct LAYOUTERROR4res {
nfsstat4 ler_status;
};
15.6.3. DESCRIPTION โจ
okay so the client can use LAYOUTERROR to tell the metadata server about errors in its interaction with the layout (see Section 12 of [RFC5661]). The layout is represented by the current filehandle, the client ID (derived from the session ID in the preceding SEQUENCE operation), the byte range (lea_offset + lea_length), and lea_stateid ๐ง
Each individual device_error4 describes a single error associated with a storage device, which is identified via de_deviceid. If the layout type (see Section 12.2.7 of [RFC5661]) supports NFSv4 operations, then the operation that returned the error is identified via de_opnum. If the layout type does not support NFSv4 operations, then either (1) it MAY choose to map the operation onto one of the allowed operations that can be sent to a storage device with the file layout type (see Section 3.3), or (2) it can signal no support for operations by marking de_opnum with the ILLEGAL operation. Finally, the NFS error value (nfsstat4) encountered is provided via de_status, and it may consist of the following error codes ๐ฆ
NFS4ERR_NXIO: The client was unable to establish any communication with the storage device.NFS4ERR_*: The client was able to establish communication with the storage device and is returning one of the allowed error codes for the operation denoted byde_opnum.
Note that while the metadata server may return an error associated with the layout stateid or the open file, it MUST NOT return an error in the processing of the errors. If LAYOUTERROR is in a COMPOUND before LAYOUTRETURN, it MUST NOT introduce an error other than what LAYOUTRETURN would already encounter ๐
15.6.4. IMPLEMENTATION ๐
There are two broad classes of errors: transient and persistent. The client SHOULD strive to only use this new mechanism to report persistent errors. It MUST be able to deal with transient issues by itself. Also, while the client might consider an issue to be persistent, it MUST be prepared for the metadata server to consider such issues to be transient ๐
A prime example of this is if the metadata server fences off a client from either a stateid or a filehandle. The client will get an error from the storage device and might relay either NFS4ERR_ACCESS or NFS4ERR_BAD_STATEID back to the metadata server, with the belief that this is a hard error. If the metadata server is informed by the client that there is an error, it can safely ignore that. For the metadata server, the mission is accomplished in that the client has returned a layout that the metadata server had most likely recalled ๐
The client might also need to inform the metadata server that it cannot reach one or more of the storage devices. While the metadata server can detect the connectivity of both of these paths:
- metadata server to storage device
- metadata server to client
it cannot determine if the client and storage device path is working. As with the case of the storage device passing errors to the client, it must be prepared for the metadata server to consider such outages as being transitory ๐
Clients are expected to tolerate transient storage device errors, and hence clients SHOULD NOT use the LAYOUTERROR error handling for device access problems that may be transient. The methods by which a client decides whether a device access problem is transient or persistent are implementation specific, but they may include retrying I/Os to a data server under appropriate conditions ๐งฉ
When an I/O to a storage device fails, the client SHOULD retry the failed I/O via the metadata server. In this situation, before retrying the I/O, the client SHOULD return the layout, or the affected portion thereof, and SHOULD indicate which storage device or devices was problematic. The client needs to do this when the storage device is being unresponsive, in order to fence off any failed write attempts and ensure that they do not end up overwriting any later data being written through the metadata server. If the client does not do this, the metadata server MAY issue a layout recall callback in order to perform the retried I/O ๐ซถ
The client needs to be cognizant that since this error handling is optional in the metadata server, the metadata server may silently ignore this functionality. Also, as the metadata server may consider some issues the client reports to be expected, the client might find it difficult to detect a metadata server that has not implemented error handling via LAYOUTERROR ๐
If a metadata server is aware that a storage device is proving problematic to a client, the metadata server SHOULD NOT include that storage device in any pNFS layouts sent to that client. If the metadata server is aware that a storage device is affecting many clients, then the metadata server SHOULD NOT include that storage device in any pNFS layouts sent out. If a client asks for a new layout for the file from the metadata server, it MUST be prepared for the metadata server to return that storage device in the layout. The metadata server might not have any choice in using the storage device, i.e., there might only be one possible layout for the system ๐
Also, in the case of existing files, the metadata server might have no choice regarding which storage devices to hand out to clients ๐ฆ
The metadata server is not required to indefinitely retain per-client storage device error information. The metadata server is also not required to automatically reinstate the use of a previously problematic storage device; administrative intervention may be required instead ๐
15.7. Operation 65: LAYOUTSTATS - Provide statistics for the layout ๐
15.7.1. ARGUMENT ๐ธ
struct layoutupdate4 {
layouttype4 lou_type;
opaque lou_body<>;
};
struct io_info4 {
uint64_t ii_count;
uint64_t ii_bytes;
};
struct LAYOUTSTATS4args {
/* CURRENT_FH: file */
offset4 lsa_offset;
length4 lsa_length;
stateid4 lsa_stateid;
io_info4 lsa_read;
io_info4 lsa_write;
deviceid4 lsa_deviceid;
layoutupdate4 lsa_layoutupdate;
};
15.7.2. RESULT ๐ง
struct LAYOUTSTATS4res {
nfsstat4 lsr_status;
};
15.7.3. DESCRIPTION ๐โจ
okay bestie, so LAYOUTSTATS is how the client tells the metadata server how it's been vibing with the layout ๐
(see Section 12 of [RFC5661]). It's tied to the current filehandle, the client ID (derived from the session ID in the preceding SEQUENCE operation), a byte range (lsa_offset and lsa_length), and the lsa_stateid. lsa_read and lsa_write let you report the stats that aren't specific to any one layout type ๐ and lsa_deviceid tells the server which storage device the stats are about ๐ง Everything else the client shares is layout-type-specific and goes in lsa_layoutupdate. Each layout type MUST define the contents of lsa_layoutupdate in its own specification ๐
LAYOUTSTATS can be combined with IO_ADVISE (see Section 15.5) to level up how the metadata server handles a file ๐ฅ IO_ADVISE tells the server that a byte range has a certain characteristic, but not how intense that characteristic is. That's where the stats come in ๐
The statistics are cumulative, so multiple LAYOUTSTATS updates can be in flight at the same time ๐ The metadata server can check the packet's timestamp to put them in order โฐ The first LAYOUTSTATS sent by the client SHOULD be from the opening of the file ๐ How often the client updates the metadata server is totally up to the client ๐ฆ
Note that while the metadata server may return an error tied to the layout stateid or the open file, it MUST NOT return an error in the processing of the statistics ๐
15.8. Operation 66: OFFLOAD_CANCEL - Stop an offloaded operation ๐
15.8.1. ARGUMENT
struct OFFLOAD_CANCEL4args {
/* CURRENT_FH: file to cancel */
stateid4 oca_stateid;
};
15.8.2. RESULT
struct OFFLOAD_CANCEL4res {
nfsstat4 ocr_status;
};
15.8.3. DESCRIPTION
OFFLOAD_CANCEL is how the client shuts down an asynchronous operation ๐
โโ๏ธ The operation is identified by both CURRENT_FH and the oca_stateid. There can be multiple OFFLOAD_CANCEL operations acting on the same file, and the stateid tells the server exactly which one to stop ๐ฏ Right now, only two operations can decide to be asynchronous: COPY and WRITE_SAME ๐
In the context of server-to-server copy, the client can send OFFLOAD_CANCEL to either the source or the destination server, just with a different stateid ๐ The client uses OFFLOAD_CANCEL to tell the destination to stop the active transfer, and it uses the stateid it got back from the COPY operation. The client uses OFFLOAD_CANCEL along with the stateid it used in the COPY_NOTIFY to tell the source not to allow any more copying from the destination ๐ซ
OFFLOAD_CANCEL is also useful when the source server granted a very long or infinite lease on the destination server's ability to read the source file, and all COPY operations on the source file have been completed ๐งน
15.9. Operation 67: OFFLOAD_STATUS - Poll for the status of an asynchronous operation ๐
15.9.1. ARGUMENT
struct OFFLOAD_STATUS4args {
/* CURRENT_FH: destination file */
stateid4 osa_stateid;
};
15.9.2. RESULT
struct OFFLOAD_STATUS4resok {
length4 osr_count;
nfsstat4 osr_complete<1>;
};
union OFFLOAD_STATUS4res switch (nfsstat4 osr_status) {
case NFS4_OK:
OFFLOAD_STATUS4resok osr_resok4;
default:
void;
};
15.9.3. DESCRIPTION
OFFLOAD_STATUS is how the client checks on the progress of an asynchronous operation โณ That operation is identified by both CURRENT_FH and the osa_stateid. If the call succeeds, the number of bytes processed comes back in the osr_count field ๐
If the optional osr_complete field is present, the asynchronous operation has completed ๐ In that case, the status value tells you the result of the asynchronous operation. In all cases, the server will also deliver the final results of the asynchronous operation in a CB_OFFLOAD operation ๐ฌ
The failure of this operation does not indicate the result of the asynchronous operation in any way ๐คทโโ๏ธ
15.10. Operation 68: READ_PLUS - READ data or holes from a file ๐งฉ
15.10.1. ARGUMENT
struct READ_PLUS4args {
/* CURRENT_FH: file */
stateid4 rpa_stateid;
offset4 rpa_offset;
count4 rpa_count;
};
15.10.2. RESULT
enum data_content4 {
NFS4_CONTENT_DATA = 0,
NFS4_CONTENT_HOLE = 1
};
struct data_info4 {
offset4 di_offset;
length4 di_length;
};
struct data4 {
offset4 d_offset;
opaque d_data<>;
};
union read_plus_content switch (data_content4 rpc_content) {
case NFS4_CONTENT_DATA:
data4 rpc_data;
case NFS4_CONTENT_HOLE:
data_info4 rpc_hole;
default:
void;
};
/*
* Allow a return of an array of contents.
*/
struct read_plus_res4 {
bool rpr_eof;
read_plus_content rpr_contents<>;
};
union READ_PLUS4res switch (nfsstat4 rp_status) {
case NFS4_OK:
read_plus_res4 rp_resok4;
default:
void;
};
15.10.3. DESCRIPTION ๐ฆ
READ_PLUS is based on the NFSv4.1 READ operation (see Section 18.22 of [RFC5661]), so it reads data from the regular file identified by the current filehandle in a similar way ๐
The client gives an rpa_offset for where the READ_PLUS should start, and an rpa_count for how many bytes to read ๐ฏ An rpa_offset of zero means the data is read from the very beginning of the file. If rpa_offset is greater than or equal to the size of the file, the status NFS4_OK comes back with di_length (the data length) set to zero and eof set to TRUE ๐
The READ_PLUS result is made up of an array of rpr_contents, and each one describes a data_content4 type of data. For NFSv4.2, the allowed values are data and hole ๐ A server MUST support both the data type and the hole if it uses READ_PLUS. If it doesn't want to support holes, it MUST use READ ๐
โโ๏ธ The array contents MUST be contiguous in the file ๐งต
Holes SHOULD be returned in their entirety ๐ณ๏ธ Clients have to be ready to get more info than they asked for, because both the start and the end of the hole may go past what was requested. If the data to be returned is made up entirely of zeros, the server SHOULD return that data as a hole instead ๐
The server may elect to return adjacent elements of the same type. For example, if the server has a range of data that's entirely zeros followed by a hole, it might want to send two adjacent holes to the client ๐งธ
If the client specifies an rpa_count of zero, READ_PLUS succeeds and returns zero bytes of data ๐ซง In all situations, the server may choose to return fewer bytes than the client specified. The client needs to check for this and handle it appropriately ๐
If the client specifies data that's entirely contained within a hole of the file (meaning both rpa_offset and rpa_offset + rpa_count are within the hole), then the di_offset and di_length returned MAY be for the entire hole ๐ณ๏ธโจ If the owner has a locked byte range covering rpa_offset and rpa_count entirely, the di_offset and di_length MUST NOT be extended outside the locked byte range ๐ This result is valid until the file is changed (detected via the change attribute). The server MUST provide the same semantics for the hole as if the client had read the region and received zeros. The implied hole's contents lifetime MUST be exactly the same as any other read data โณ
If the client specifies data by an rpa_offset that begins in a non-hole part of the file but extends into a hole (rpa_offset + rpa_count lands in the hole), the server should return an array made of both data and a hole. The client MUST be prepared for the server to return a short read describing just the data. The client will then issue another READ_PLUS for the remaining bytes, and the server will respond with info about the hole in the file ๐งฉ
Except when special stateids are used, the stateid value for a READ_PLUS request represents a value returned from a previous byte-range lock or share reservation request, or the stateid associated with a delegation ๐ชช The stateid identifies the associated owners, if any, and the server uses it to verify that the associated locks are still valid (for example, that they haven't been revoked) ๐
If the read ended at the end of the file (formally, in a correctly formed READ_PLUS operation, rpa_offset + rpa_count is equal to the size of the file), or the READ_PLUS operation goes beyond the size of the file (rpa_offset + rpa_count is greater than the size of the file), eof is returned as TRUE. Otherwise, it's FALSE. A successful READ_PLUS of an empty file will always return eof as TRUE ๐
If the current filehandle is not an ordinary file, an error comes back to the client ๐จ If the current filehandle represents an object of type NF4DIR, NFS4ERR_ISDIR is returned. If the current filehandle designates a symbolic link, NFS4ERR_SYMLINK is returned. In all other cases, NFS4ERR_WRONG_TYPE is returned ๐
For a READ_PLUS with a stateid value of all bits equal to zero, the server MAY allow the READ_PLUS to be serviced subject to mandatory byte-range locks or the current share deny modes for the file ๐ For a READ_PLUS with a stateid value of all bits equal to one, the server MAY allow READ_PLUS operations to bypass locking checks at the server ๐โโ๏ธ
On success, the current filehandle keeps its value ๐
15.10.3.1. Note on Client Support of Arms of the Union ๐ฆ
It was decided not to add a way for the client to tell the server which arms of READ_PLUS it supports ๐ In a later minor version, it may become necessary to introduce a new operation that lets the client tell the server whether it supports the new arms of the union of data types available in READ_PLUS ๐ฎ
15.10.4. IMPLEMENTATION ๐ ๏ธ
In general, the IMPLEMENTATION notes for READ in Section 18.22.4 of [RFC5661] also apply to READ_PLUS ๐ซถ
15.10.4.1. Additional pNFS Implementation Information ๐
With pNFS, READ_PLUS works the same way it always does ๐ Any data server MAY return a hole result for a READ_PLUS request it receives. When a data server chooses to return one, it has the option of returning information for the data stored on that data server (as defined by the data layout), but it MUST NOT return results for a byte range that includes data managed by another data server ๐ง
If mandatory locking is enforced, the data server must also make sure that only information within the owner's locked byte range is returned ๐โจ
15.10.5. READ_PLUS with Sparse Files: Example โจ
okay so this bit is a worked example with a sparse file ๐ฆ The table lays out a file where each byte range is either non-zero data or a hole. The server in this example only bothers creating a hole if it's greater than 32K, so tiny gaps don't count ๐ซถ
+-------------+----------+
| Byte Range | Contents |
+-------------+----------+
| 0-15999 | Hole |
| 16K-31999 | Non-Zero |
| 32K-255999 | Hole |
| 256K-287999 | Non-Zero |
| 288K-353999 | Hole |
| 354K-417999 | Non-Zero |
+-------------+----------+
Table 7: Sparse File
Under these circumstances, if a client reads the file with a maximum read size of 64K, these are the results for the READ_PLUS calls ๐ This assumes the client has already opened the file, acquired a valid stateid ("s" in the example), and just needs to issue READ_PLUS requests ๐
- READ_PLUS(s, 0, 64K) --> NFS_OK, eof = FALSE, <data[0,32K], hole[32K,224K]>. Since the first hole is less than the server's minimum hole size, the first 32K of the file is returned as data and the remaining 32K is returned as a hole that actually extends to 256K. ๐ธ
- READ_PLUS(s, 32K, 64K) --> NFS_OK, eof = FALSE, <hole[32K,224K]>. The requested range was all zeros, and the current hole begins at offset 32K and is 224K in length. Note that the client should not have followed up the previous READ_PLUS request with this one, as the hole information from the previous call extended past what the client was requesting. ๐
- READ_PLUS(s, 256K, 64K) --> NFS_OK, eof = FALSE, <data[256K, 288K], hole[288K, 354K]>. Returns an array of the 32K data and the hole, which extends to 354K. ๐ง
- READ_PLUS(s, 354K, 64K) --> NFS_OK, eof = TRUE, <data[354K, 418K]>. Returns the final 64K of data and informs the client that there is no more data in the file. ๐
15.11. Operation 69: SEEK - Find the next data or hole ๐
15.11.1. ARGUMENT ๐
enum data_content4 {
NFS4_CONTENT_DATA = 0,
NFS4_CONTENT_HOLE = 1
};
struct SEEK4args {
/* CURRENT_FH: file */
stateid4 sa_stateid;
offset4 sa_offset;
data_content4 sa_what;
};
15.11.2. RESULT ๐
struct seek_res4 {
bool sr_eof;
offset4 sr_offset;
};
union SEEK4res switch (nfsstat4 sa_status) {
case NFS4_OK:
seek_res4 resok4;
default:
void;
};
15.11.3. DESCRIPTION ๐ฆ
SEEK is an operation that lets a client find the location of the next data_content4 in a file โจ It's the emerging extension to the lseek(2) function, so clients can find the next hole while they're in data, or the next data while they're in a hole ๐
From the given sa_offset, the server finds the next data_content4 of type sa_what in the file. If it can't find a matching sa_what, the status is still NFS4_OK, but sr_eof would be TRUE. If it does find the sa_what, sr_offset is the start of that content. If sa_offset is beyond the end of the file, then SEEK MUST return NFS4ERR_NXIO ๐
All files MUST have a virtual hole at the end of the file. That is, if a file system does not support sparse files, then a COMPOUND with {SEEK 0 NFS4_CONTENT_HOLE;} would return a result of {SEEK 1 X;}, where "X" was the size of the file. ๐ซถ
SEEK must follow the same rules for stateids as READ_PLUS (Section 15.10.3). ๐ฅ
15.12. Operation 70: WRITE_SAME - WRITE an ADB multiple times to a file ๐
15.12.1. ARGUMENT ๐
enum stable_how4 {
UNSTABLE4 = 0,
DATA_SYNC4 = 1,
FILE_SYNC4 = 2
};
struct app_data_block4 {
offset4 adb_offset;
length4 adb_block_size;
length4 adb_block_count;
length4 adb_reloff_blocknum;
count4 adb_block_num;
length4 adb_reloff_pattern;
opaque adb_pattern<>;
};
struct WRITE_SAME4args {
/* CURRENT_FH: file */
stateid4 wsa_stateid;
stable_how4 wsa_stable;
app_data_block4 wsa_adb;
};
15.12.2. RESULT ๐
struct write_response4 {
stateid4 wr_callback_id<1>;
length4 wr_count;
stable_how4 wr_committed;
verifier4 wr_writeverf;
};
union WRITE_SAME4res switch (nfsstat4 wsr_status) {
case NFS4_OK:
write_response4 resok4;
default:
void;
};
15.12.3. DESCRIPTION ๐ฆ
WRITE_SAME writes an application data block to the regular file identified by the current filehandle (see WRITE SAME (10) in [T10-SBC2]) โจ The target file is specified by the current filehandle, and the data to be written is described by an app_data_block4 structure (Section 8.1.1) ๐ The client uses the wsa_stable parameter to say how the server should process the data. It's treated like the stable parameter in the NFSv4.1 WRITE operation (see Section 18.32.3 of [RFC5661]) ๐ซถ
A successful WRITE_SAME constructs a reply for wr_count, wr_committed, and wr_writeverf the same way the NFSv4.1 WRITE operation results do. If wr_callback_id is set, that means it's an asynchronous reply (see Section 15.12.3.1) ๐ธ
As it is an OPTIONAL operation, WRITE_SAME has to support NFS4ERR_NOTSUPP. As it's an extension of WRITE, it has to support all of the errors WRITE returns. If the client supports WRITE_SAME, it MUST support CB_OFFLOAD ๐ฅ
If the server supports ADBs, then it MUST support the WRITE_SAME operation ๐ The server has no concept of the structure the application imposes. Order only gets imposed when the application writes to a section of the file. To catch corruption before the application even uses the file, the application will want to initialize a range of ADBs using WRITE_SAME ๐ง
When the client invokes WRITE_SAME, it wants to record the block structure described by the app_data_block4 into the file ๐
When the server receives WRITE_SAME, it MUST populate adb_block_count ADBs in the file, starting at adb_offset. The block size is given by adb_block_size. The ADBN (if provided) starts at adb_reloff_blocknum, and each block is monotonically numbered, starting from adb_block_num in the first block. The pattern (if provided) is at adb_reloff_pattern of each block and is provided in adb_pattern ๐
The server SHOULD return an asynchronous result if it can determine that the operation will be long-running (see Section 15.12.3.1). Once either WRITE_SAME finishes synchronously or the server uses CB_OFFLOAD to tell the client about the asynchronous completion, the server MUST return the ADBs to clients as data ๐
15.12.3.1. Asynchronous Transactions โณ
ADB initialization may cause a server to decide to service the operation asynchronously. If it does, it sets the stateid in wr_callback_id to be that of the wsa_stateid. If it does not set wr_callback_id, then the result is synchronous ๐
When the client determines that the reply will be given asynchronously, it should not assume anything about the contents of what it wrote until the server says the operation is complete. It can use OFFLOAD_STATUS (Section 15.9) to monitor the operation, and OFFLOAD_CANCEL (Section 15.8) to cancel it ๐ Here's an example of an asynchronous WRITE_SAME in Figure 6. Note that, as with the COPY operation, WRITE_SAME must provide a stateid for tracking the asynchronous operation ๐
Client Server
+ +
| |
|--- OPEN ---------------------------->| Client opens
|<------------------------------------/| the file
| |
|--- WRITE_SAME ---------------------->| Client initializes
|<------------------------------------/| an ADB
| |
| |
|--- OFFLOAD_STATUS ------------------>| Client may poll
|<------------------------------------/| for status
| |
| . | Multiple OFFLOAD_STATUS
| . | operations may be sent.
| . |
| |
|<-- CB_OFFLOAD -----------------------| Server reports results
|\------------------------------------>|
| |
|--- CLOSE --------------------------->| Client closes
|<------------------------------------/| the file
| |
| |
Figure 6: An Asynchronous WRITE_SAME
When CB_OFFLOAD tells the client about a successful WRITE_SAME, the write_response4 embedded in the operation gives the info a synchronous WRITE_SAME would have provided โจ
Whether the operation is asynchronous or synchronous, it MUST still support the COMMIT operation semantics outlined in Section 18.3 of [RFC5661] ๐ That is, COMMIT works on one or more WRITE operations, and WRITE_SAME can appear to the server as several WRITE operations ๐ซถ The client can use locking operations to control how the server behaves with respect to long-running asynchronous WRITE_SAME operations ๐ฅ
15.12.3.2. Error Handling of a Partially Complete WRITE_SAME ๐จ
WRITE_SAME clones adb_block_count copies of the given ADB in consecutive order in the file, starting at adb_offset. An error can happen after the Nth ADB is written to the file. WRITE_SAME MUST appear to populate the range of the file as if the client had used WRITE to transfer the instantiated ADBs. That way, the contents of the range are easy for the client to figure out in the case of a partially complete WRITE_SAME ๐
15.13. Operation 71: CLONE - Clone a range of a file into another file ๐งฌ
15.13.1. ARGUMENT ๐
struct CLONE4args {
/* SAVED_FH: source file */
/* CURRENT_FH: destination file */
stateid4 cl_src_stateid;
stateid4 cl_dst_stateid;
offset4 cl_src_offset;
offset4 cl_dst_offset;
length4 cl_count;
};
15.13.2. RESULT ๐
struct CLONE4res {
nfsstat4 cl_status;
};
15.13.3. DESCRIPTION โจ
okay bestie, CLONE is giving copy-without-copying ๐ฆ It takes file content from the source file named by SAVED_FH and drops it into the destination file named by CURRENT_FH, and the data never actually gets duplicated. Think copy-on-write vibes, e.g., ๐
Both SAVED_FH and CURRENT_FH must be regular files. If either one isn't, the operation MUST fail and return NFS4ERR_WRONG_TYPE ๐
โโ๏ธ
The ca_dst_stateid MUST refer to a stateid that's valid for a WRITE operation and follows the stateid rules in Sections 8.2.5 and 18.32.3 of [RFC5661]. The ca_src_stateid MUST refer to a stateid that's valid for a READ operation and follows the stateid rules in Sections 8.2.5 and 18.22.3 of [RFC5661]. If either stateid is invalid, then the operation MUST fail ๐
Now the offsets ๐ cl_src_offset is where the data to be cloned starts in the source file, and cl_dst_offset is where the cloned data lands in the target region. An offset of 0 (zero) means the start of the respective file. The number of bytes comes from cl_count, except that a cl_count of 0 (zero) means "everything from cl_src_offset up to the EOF of the source file." Both cl_src_offset and cl_dst_offset must be aligned to the clone block size (Section 12.2.1). The number of bytes to be cloned must be a multiple of the clone block size, except when cl_src_offset plus the number of bytes to be cloned is equal to the source file size. The math has to mathematically slay ๐งฎ
โ ๏ธ If the source offset, or the source offset plus count, is greater than the size of the source file, the operation MUST fail with NFS4ERR_INVAL. The destination offset or destination offset plus count may be greater than the size of the destination file, and that part is totally fine ๐ธ
If SAVED_FH and CURRENT_FH refer to the same file and the source and target ranges overlap, the operation MUST fail with NFS4ERR_INVAL. No cloning yourself into yourself, periodt ๐
โโ๏ธ๐
If the target area of the CLONE operation ends beyond the end of the destination file, the offset at the end of the target area decides the new size of the destination file. The contents of any block that isn't part of the target area will be the same as if the file size were extended by a WRITE ๐ง
If the area to be cloned isn't a multiple of the clone block size and the size of the destination file is past the end of the target area, then the area between the end of the target area and the next multiple of the clone block size will be zeroed ๐งฝ
The CLONE operation is atomic, which means other operations may not see any intermediate states between the two files before and after the operation. This is the main character moment ๐ READs of the destination file will never see some blocks of the target area cloned without all of them being cloned. WRITEs of the source area will either have no effect on the data of the target file or be fully reflected in the target area of the destination file. Nothing half-baked, ever ๐
The completion status of the operation is indicated by cr_status. ๐ฌ
16. NFSv4.2 Callback Operations ๐
16.1. Operation 15: CB_OFFLOAD - Report the results of an asynchronous operation ๐
16.1.1. ARGUMENT
struct write_response4 {
stateid4 wr_callback_id<1>;
length4 wr_count;
stable_how4 wr_committed;
verifier4 wr_writeverf;
};
union offload_info4 switch (nfsstat4 coa_status) {
case NFS4_OK:
write_response4 coa_resok4;
default:
length4 coa_bytes_copied;
};
struct CB_OFFLOAD4args {
nfs_fh4 coa_fh;
stateid4 coa_stateid;
offload_info4 coa_offload_info;
};
16.1.2. RESULT
struct CB_OFFLOAD4res {
nfsstat4 cor_status;
};
16.1.3. DESCRIPTION
CB_OFFLOAD is the server's way of reporting back to the client on an asynchronous operation, e.g., server-side COPY or WRITE_SAME ๐ฎ The coa_fh and coa_stateid identify the transaction, and the coa_status tells you whether it worked or flopped. The coa_resok4.wr_callback_id MUST NOT be set ๐
โโ๏ธ If the transaction failed, then the coa_bytes_copied holds the number of bytes copied before the failure happened. Just know that coa_bytes_copied tells you how many bytes were copied, not which specific bytes got copied ๐คทโโ๏ธ
If the client supports any of the following operations:
- COPY: for both intra-server and inter-server asynchronous copies
- WRITE_SAME: for ADB initialization
then the client is REQUIRED to support the CB_OFFLOAD operation ๐ซถ
There's a potential race between the reply to the original transaction on the forechannel and the CB_OFFLOAD callback on the backchannel. Section 2.10.6.3 of [RFC5661] describes how to handle this type of issue, so go read that if you're getting the vibe that things might arrive out of order ๐ญ
Upon success, the coa_resok4.wr_count presents, for each operation:
- COPY: the total number of bytes copied
- WRITE_SAME: the same information that a synchronous WRITE_SAME would provide
17. Security Considerations ๐
NFSv4.2 has all of the security concerns present in NFSv4.1 (see Section 21 of [RFC5661]), as well as those present in the server-side copy (see Section 4.9) and in Labeled NFS (see Section 9.6). Lots of baggage to carry here, so pack your security bag carefully ๐๐ญ
18. IANA Considerations ๐๏ธ
The IANA considerations for Labeled NFS are addressed in [RFC7569]. ๐ฆ
19. References ๐
19.1. Normative References
[posix_fadvise]
The Open Group, "Section 'posix_fadvise()' of System
Interfaces of The Open Group Base Specifications Issue 7",
IEEE Std 1003.1, 2016 Edition (HTML Version),
ISBN 1937218812, September 2016,
<http://www.opengroup.org/>.
[posix_fallocate]
The Open Group, "Section 'posix_fallocate()' of System
Interfaces of The Open Group Base Specifications Issue 7",
IEEE Std 1003.1, 2016 Edition (HTML Version),
ISBN 1937218812, September 2016,
<http://www.opengroup.org/>.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<http://www.rfc-editor.org/info/rfc2119>.
[RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
Resource Identifier (URI): Generic Syntax", STD 66,
RFC 3986, DOI 10.17487/RFC3986, January 2005,
<http://www.rfc-editor.org/info/rfc3986>.
[RFC5661] Shepler, S., Ed., Eisler, M., Ed., and D. Noveck, Ed.,
"Network File System (NFS) Version 4 Minor Version 1
Protocol", RFC 5661, DOI 10.17487/RFC5661, January 2010,
<http://www.rfc-editor.org/info/rfc5661>.
[RFC5662] Shepler, S., Ed., Eisler, M., Ed., and D. Noveck, Ed.,
"Network File System (NFS) Version 4 Minor Version 1
External Data Representation Standard (XDR) Description",
RFC 5662, DOI 10.17487/RFC5662, January 2010,
<http://www.rfc-editor.org/info/rfc5662>.
[RFC7569] Quigley, D., Lu, J., and T. Haynes, "Registry
Specification for Mandatory Access Control (MAC) Security
Label Formats", RFC 7569, DOI 10.17487/RFC7569, July 2015,
<http://www.rfc-editor.org/info/rfc7569>.
[RFC7861] Adamson, A. and N. Williams, "Remote Procedure Call (RPC)
Security Version 3", RFC 7861, DOI 10.17487/RFC7861,
November 2016, <http://www.rfc-editor.org/info/rfc7861>.
[RFC7863] Haynes, T., "Network File System (NFS) Version 4 Minor
Version 2 External Data Representation Standard (XDR)
Description", RFC 7863, DOI 10.17487/RFC7863,
November 2016, <http://www.rfc-editor.org/info/rfc7863>.
19.2. Informative References ๐
Okay, bestie, here's the reading list for the "nice to know, but it'll make you smarter" stuff ๐ค. These are the Oracle docs, USENIX papers, and older RFCs the spec leans on.
[Ashdown08]
Ashdown, L., "Chapter 15: Validating Database Files and
Backups", Oracle Database Backup and Recovery User's
Guide 11g Release 1 (11.1), August 2008,
<http://download.oracle.com/docs/cd/B28359_01/backup.111/
b28270/rcmvalid.htm>.
[Baira08] Bairavasundaram, L., Goodson, G., Schroeder, B.,
Arpaci-Dusseau, A., and R. Arpaci-Dusseau, "An Analysis of
Data Corruption in the Storage Stack", Proceedings of the
6th USENIX Symposium on File and Storage Technologies
(FAST '08), 2008,
<http://www.usenix.org/events/fast08/tech/full_papers/
bairavasundaram/bairavasundaram.pdf>.
[IESG08] IESG, "IESG Processing of RFC Errata for the IETF Stream",
July 2008, <https://www.ietf.org/iesg/statement/
errata-processing.html>.
[LB96] LaPadula, L. and D. Bell, "MITRE Technical Report 2547,
Volume II", Journal of Computer Security, Volume 4,
Issue 2-3, 239-263, IOS Press, Amsterdam, The Netherlands,
January 1996.
[McDougall07]
McDougall, R. and J. Mauro, "Section 11.4.3: Detecting
Memory Corruption", Solaris Internals: Solaris 10 and
OpenSolaris Kernel Architecture, 2nd Edition, 2007.
[NFSv4-Versioning]
Noveck, D., "Rules for NFSv4 Extensions and Minor
Versions", Work in Progress,
draft-ietf-nfsv4-versioning-07, October 2016.
[RFC959] Postel, J. and J. Reynolds, "File Transfer Protocol",
STD 9, RFC 959, DOI 10.17487/RFC0959, October 1985,
<http://www.rfc-editor.org/info/rfc959>.
[RFC1108] Kent, S., "U.S. Department of Defense Security Options for
the Internet Protocol", RFC 1108, DOI 10.17487/RFC1108,
November 1991, <http://www.rfc-editor.org/info/rfc1108>.
[RFC2401] Kent, S. and R. Atkinson, "Security Architecture for the
Internet Protocol", RFC 2401, DOI 10.17487/RFC2401,
November 1998, <http://www.rfc-editor.org/info/rfc2401>.
[RFC4506] Eisler, M., Ed., "XDR: External Data Representation
Standard", STD 67, RFC 4506, DOI 10.17487/RFC4506,
May 2006, <http://www.rfc-editor.org/info/rfc4506>.
[RFC4949] Shirey, R., "Internet Security Glossary, Version 2",
FYI 36, RFC 4949, DOI 10.17487/RFC4949, August 2007,
<http://www.rfc-editor.org/info/rfc4949>.
[RFC5663] Black, D., Fridella, S., and J. Glasgow, "Parallel NFS
(pNFS) Block/Volume Layout", RFC 5663,
DOI 10.17487/RFC5663, January 2010,
<http://www.rfc-editor.org/info/rfc5663>.
[RFC7204] Haynes, T., "Requirements for Labeled NFS", RFC 7204,
DOI 10.17487/RFC7204, April 2014,
<http://www.rfc-editor.org/info/rfc7204>.
[RFC7230] Fielding, R., Ed., and J. Reschke, Ed., "Hypertext
Transfer Protocol (HTTP/1.1): Message Syntax and Routing",
RFC 7230, DOI 10.17487/RFC7230, June 2014,
<http://www.rfc-editor.org/info/rfc7230>.
[RFC7530] Haynes, T., Ed., and D. Noveck, Ed., "Network File System
(NFS) Version 4 Protocol", RFC 7530, DOI 10.17487/RFC7530,
March 2015, <http://www.rfc-editor.org/info/rfc7530>.
[Strohm11] Strohm, R., "Chapter 2: Data Blocks, Extents, and
Segments", Oracle Database Concepts 11g Release 1 (11.1),
January 2011,
<http://download.oracle.com/docs/cd/B28359_01/server.111/
b28318/logical.htm>.
[T10-SBC2] Elliott, R., Ed., "ANSI INCITS 405-2005, Information
Technology - SCSI Block Commands - 2 (SBC-2)",
November 2004,
<ftp://www.t10.org/t10/document.05/05-344r0.pdf>.
Acknowledgments ๐ซถ
Tom Haynes would like to thank NetApp, Inc. for its funding of his time on this project. Big thanks to him, honestly ๐
For the topic "sharing change attribute implementation characteristics with NFSv4 clients", the original document was by Trond Myklebust. Legend โจ
For the NFS server-side copy, the original document was by James Lentini, Mike Eisler, Deepak Kenchammana, Anshul Madan, and Rahul Iyer. Tom Talpey co-authored an unpublished version of that document. It was also reviewed by a whole crew of people: Pranoop Erasani, Tom Haynes, Arthur Lent, Trond Myklebust, Dave Noveck, Theresa Lingutla-Raj, Manjunath Shankararao, Satyam Vaghani, and Nico Williams. Anna Schumaker's early prototyping helped the team dodge some traps ๐ชค. Olga Kornievskaia and Andy Adamson brought implementation experience to the use of copy stateids in the inter-server copy, and Jorge Mora made the handling of errors for the result of COPY way better ๐ฅ.
For the NFS space reservation operations, the original document was by Mike Eisler, James Lentini, Manjunath Shankararao, and Rahul Iyer.
For the sparse file support, the original document was by Dean Hildebrand and Marc Eshel. Valuable input and advice came from Sorin Faibish, Bruce Fields, Benny Halevy, Trond Myklebust, and Richard Scheffenegger ๐ธ.
For the application I/O hints, the original document was by Dean Hildebrand, Mike Eisler, Trond Myklebust, and Sam Falkner. Some early reviewers included Benny Halevy and Pranoop Erasani.
For Labeled NFS, the original document was by David Quigley, James Morris, Jarrett Lu, and Tom Haynes. Peter Staubach, Trond Myklebust, Stephen Smalley, Sorin Faibish, Nico Williams, and David Black also showed up for the final push to get it accepted ๐.
Christoph Hellwig was super helpful in getting the WRITE_SAME semantics to model more of what T10 was doing for WRITE SAME (10) [T10-SBC2]. He also led the push to get space reservations to more closely model the posix_fallocate() operation ๐.
Andy Adamson picked up the RPCSEC_GSSv3 work, which enabled both Labeled NFS and server-side copy to offer more secure options ๐.
Christoph Hellwig also provided the update to GETDEVICELIST.
Jorge Mora gave a very detailed review and caught some important issues with the tables ๐.
During the review process, Talia Reyes-Ortiz kept the sessions running smoothly. Lots of people contributed here and there, but the core reviewers were Andy Adamson, Pranoop Erasani, Bruce Fields, Chuck Lever, Trond Myklebust, David Noveck, Peter Staubach, and Mike Kupfer. Iconic squad ๐ .
Elwyn Davies was the General Area Reviewer for this document, and his insights into how this document relates to both [RFC5661] and [RFC7530] were very much appreciated! Thank you, Elwyn! ๐
Author's Address ๐ฆ
Thomas Haynes
Primary Data, Inc.
4300 El Camino Real Ste 100
Los Altos, CA 94022
United States of America
Phone: +1 408 215 1519
Email: thomas.haynes@primarydata.com
๐ฆ where this page comes from
- ietf: rfc7862 2016-11
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms