Skip to content

lanl/nfs-utility-suite

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

96 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NFS and RPC Utilities

This repo houses a collection of utilities for XDR / RPC programs.

LANL software release number: O4912. See the LICENSE file for copyright and license.

Organization

  • xdr_codegen/ -- this directory houses the library used to compile an XDR language specification into a Rust library that implements the [de]serialization of the defined data types.
  • test_xdr/ -- this directory holds tests for the xdr_codegen library.
  • rpc_protocol/ -- this directory holds a library that implements the RPC protocol.
  • rpcbind/ -- binaries that implement the rpcbind protocol, both client and server side. These are effectively (currently incomplete) clones of the standard rpcbind and rpcinfo binaries.
  • nfs3/ -- programs and libraries related to the NFS v3 protocol.

nfs3

See nfs3/README.md for information on the NFS v3 programs.

xdr_codegen

The xdr_codegen library works by taking in a specification in the XDR language (defined in RFC 4506) and the RPC language extension (defined in RFC 5531) and outputs Rust code that implements serialization and deserialization routines for the data types defined in the XDR spec.

For example, given the following XDR code:

struct File {
    unsigned int size;
    string name<>;
};

xdr_codegen will generate a Rust module that defines the following struct and methods:

#[derive(Debug, PartialEq, Clone)]
pub struct File {
    pub size: u32,
    pub name: std::ffi::OsString,
}
impl File {
    pub fn serialize_alloc(&self) -> Vec<u8>;
    pub fn deserialize(&mut self, input: &mut &[u8]) -> Result<(), xdr_lib::DeserializeError>;
}

How to use xdr_codegen

You can run xdr_codegen via the command line:

$ echo "struct foo { int bar; };" | cargo run --bin xdr_codegen
#[allow(non_camel_case_types, non_snake_case)]
pub mod XdrInterface {
    #[derive(Debug, PartialEq, Clone)]
    pub struct foo {
        pub bar: i32,
    }

    ...
}

or in build.rs:

fn main() {
    xdr_codegen::Compiler::new()
        .file("protocol_spec.x")
        .run()
        .expect("Generating code failed");
}

XDR Data Types

Primitive Types

The primitive types translate from XDR to Rust in the predictable way, e.g., int => i32 unsigned hyper => u64, etc.

Arrays

XDR Arrays are encoded as Rust arrays (for fixed-length arrays) or Vectors (for variable length arrays, both limited and unlimited). Opaque arrays are encoded as u8s.

XDR Strings are represented as ffi::OsStrings.

XDR Rust
struct Arrays {
        /* Strings: */
        string lim<10>;
        string unlim<>;

        /* Byte arrays: */
        opaque fixed[4];
        opaque byte_lim<5>;
        opaque byte_unlim<>;
};
pub struct Arrays {
    pub lim: std::ffi::OsString /* max length: 10 */,
    pub unlim: std::ffi::OsString,
    pub fixed: [u8; 4],
    pub byte_lim: Vec<u8> /* max length: 5 */,
    pub byte_unlim: Vec<u8>,
}

Optionals

XDR uses "optional" types to implement a "linked list"-like structure. XDR's "pointer" syntax suggests that this type of structure be implemented as a literal linked list. This might be natural in C, but it certainly is not in Rust. Therefore, this library represents optionals using a Rust Vector.

The implementation requires that the self-referential structure has a container type, and that container type--NOT the self-referential type itself--is the one that contains the vector. It is also required that the optional field be the last field of the self-referential struct. For example:

XDR Rust
struct Node {
        string name<>;
        Node *next; /* must be the last field ! */
};

struct NodeList {
        Node *list;
};
pub struct Node {
    pub name: std::ffi::OsString,
}
pub struct NodeList {
    pub list: Vec<Node>,
}

If the self-referential type does not have a container type, then this library will not serialize it properly! In practice, most/all of the real protocol definitions I have looked at do use container types for lists. For example, in the following types from the NFSv3 spec:

struct entry3 {
   fileid3      fileid;
   filename3    name;
   cookie3      cookie;
   entry3       *nextentry;
};

struct dirlist3 {
   entry3       *entries;
   bool         eof;
};

dirlist3 is the needed container type.

Non-recursive optionals

Although non-recursive optionals seem to be rare or non-existent in practice, they are possible. A non-recursive optional is simply encoded as a Rust Option:

XDR Rust
struct MyOption {
    int *maybe;
};
pub struct MyOption {
    pub maybe: Option<i32>,
}

Enums

XDR enums are simply represented as Rust enums:

XDR Rust
enum Cases {
    one = 1,
    two = 2,
    three = 3
};
pub enum Cases {
    one,
    two,
    three,
}

Unions

XDR unions come in multiple flavors depending on the type of the discriminant. Bool-discriminated unions are the simplest, represented in Rust as Options:

XDR Rust
union MyOption switch (bool yes) {
case TRUE:
    int data;
case FALSE:
    void;
};
pub struct MyOption {
    pub inner: Option<i32>,
}

When the discriminant is a bool, this library requires the False arm to be void.

Enum-discriminated unions are represented as Rust enums:

XDR Rust
enum Cases {
    One = 1,
    Two = 2
};

union Things switch (Cases blah) {
case One:
    int a;
case Two:
    bool b;
default:
    void;
};
pub enum Cases {
    One,
    Two,
}

pub enum Things {
    One(i32),
    Two(bool),
    Default,
}

Note that when the union has a default arm, that is represented by Default enum variant in Rust, which should be distinguished from the default() method on the enum!

The XDR spec allows for int-discriminated enums, which are effectively equivalent to enum-discriminated unions in terms of their encoding. The only difference is whether the arms are given meaningful names or not. Int-discriminated unions do not appear to be used in practice, so this library does not support them.

Naming Conventions

XDR normally uses snake_case for type names, while Rust uses CamelCase. This library makes no attempt to rename types to conform to the Rust style. I recommend using CamelCase names in the XDR specs that you write, so that the generated Rust code will be idiomatic.

Protocol Definitions

Here are some relevant RFCs:

RPC Related:

NFS Related:

About

A collection of XDR/RPC/NFS tools.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors