RFC 7862

Network File System (NFS) Version 4 Minor Version 2 Protocol

๐Ÿ“… published November 2016, from ietf

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 (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_secret field of copy_from_auth_priv and the ctap_shared_secret field 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) of rpc_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 in assertions[0].privs.name. the source server unwraps the rpc_gss_svc_privacy RPCSEC_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>. the handle field 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_server list returned by COPY_NOTIFY, and the NFSv4 user id of the user principal. that copy_to_auth_priv instance is placed in rpc_gss3_create_args assertions[0].privs.privilege. the string "copy_to_auth" goes in assertions[0].privs.name. the destination server unwraps the rpc_gss_svc_privacy RPCSEC_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>. the handle field 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 and nfs@<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_mic field is a GSS_GetMIC() of the ctap_shared_secret in copy_to_auth, using the parent handle context. the ccap_username field is the user principal mapped to an NFSv4 user name in the "user"@"domain" form, and it MUST be the same as the ctap_username in copy_to_auth ๐Ÿ’…. the copy_confirm_auth_priv instance goes into rpc_gss3_create_args assertions[0].privs.privilege, and the string "copy_confirm_auth" goes into assertions[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 the cap_shared_secret_mic by calling GSS_VerifyMIC() using the parent context on the cfap_shared_secret from the copy_from_auth privilege it already has, and checks that ccap_username equals cfap_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 NF4REG or NF4NAMEDATTR ๐Ÿ“
  • 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 ๐Ÿ‘€

  1. 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 ๐Ÿฐ
  2. 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

+----------------+--------------------------------------------------+

SEEKNFS4ERR_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_SAMENFS4ERR_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 ๐Ÿฆ‹

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.
    • R means read-only (GETATTR may retrieve, SETATTR may not set).
    • W means write-only (SETATTR may set, GETATTR may not retrieve).
    • R W means 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 the time_metadata attribute. โฐ
  • 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 ๐Ÿ‘‘.

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 implement
  • REC: RECOMMENDED to implement
  • OPT: OPTIONAL to implement
  • MNI: 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 NFS
  • FDELG: File Delegations
  • DDELG: Directory Delegations
  • COPYra: Intra-server Server-Side Copy
  • COPYer: Inter-server Server-Side Copy
  • ADB: 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_SESSION sent with the client ID until every operation in progress is either completed or aborted ๐Ÿ’€.
  • The server holds off replying to later EXCHANGE_ID ops 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_OPS capability is enabled, then a session ID made on one node of the storage cluster MUST be destroyable with DESTROY_SESSION ๐Ÿงš. Plus, DESTROY_CLIENTID and an EXCHANGE_ID carrying 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_time expires. if the cnr_lease_time expires while the destination server is still reading the source file, the destination server is allowed to finish reading the file. if the cnr_lease_time expires before the destination server uses READ or READ_PLUS to begin the transfer, the source server can use NFS4ERR_PARTNER_NO_AUTH to tell the destination server that the cnr_lease_time has 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 by de_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 ๐ŸŽ€

  1. 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. ๐ŸŒธ
  2. 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. ๐Ÿ™ˆ
  3. 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. ๐Ÿงš
  4. 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