nfs4 0.1.0

Protocol types for NFSv4.1
Documentation
// copyright 2023 Remi Bernotavicius

use nfs4::{
    Change, Cookie, DeviceData, DirectoryEntry, DirectoryList, EnumMap, EnumSet, FileAttribute,
    FileAttributeId, FileHandle, FileId, FileType, FsId, Mode, StatusResult, Time,
};

/// This canned example was captured from wireshark
#[test]
fn enum_map_serialization_round_trip() {
    let expected_enum_map: EnumMap<FileAttributeId, FileAttribute> = [
        FileAttribute::Type(FileType::Directory),
        FileAttribute::Change(Change(7238422209453649554)),
        FileAttribute::Size(4096),
        FileAttribute::FsId(FsId {
            major: 12333769691692441404,
            minor: 0,
        }),
        FileAttribute::FileId(FileId(250478593)),
        FileAttribute::Mode(Mode(0o0777)),
        FileAttribute::NumLinks(22),
        FileAttribute::Owner("0".into()),
        FileAttribute::OwnerGroup("65534".into()),
        FileAttribute::RawDev(DeviceData { major: 0, minor: 0 }),
        FileAttribute::SpaceUsed(4096),
        FileAttribute::TimeAccess(Time {
            seconds: 1690955739,
            nseconds: 272035443,
        }),
        FileAttribute::TimeMetadata(Time {
            seconds: 1685326502,
            nseconds: 281570962,
        }),
        FileAttribute::TimeModify(Time {
            seconds: 1685326502,
            nseconds: 281570962,
        }),
        FileAttribute::MountedOnFileid(FileId(0x000000000eee0001)),
    ]
    .into_iter()
    .collect();

    let expected = [
        0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x01, 0x1a, 0x00, 0xb0, 0xa2, 0x3a, 0x00, 0x00, 0x00,
        0x84, 0x00, 0x00, 0x00, 0x02, 0x64, 0x74, 0x0a, 0xa6, 0x10, 0xc8, 0x6e, 0x92, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xab, 0x2a, 0x5a, 0x2f, 0xfe, 0xce, 0x9f, 0x3c, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xee, 0x00, 0x01,
        0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x05, 0x36, 0x35, 0x35, 0x33, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x64, 0xc9, 0xef, 0xdb, 0x10, 0x36, 0xee, 0x73, 0x00, 0x00, 0x00, 0x00,
        0x64, 0x74, 0x0a, 0xa6, 0x10, 0xc8, 0x6e, 0x92, 0x00, 0x00, 0x00, 0x00, 0x64, 0x74, 0x0a,
        0xa6, 0x10, 0xc8, 0x6e, 0x92, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xee, 0x00, 0x01,
    ];

    let actual = serde_xdr::to_bytes(&expected_enum_map).unwrap();
    assert!(
        &expected[..] == &actual[..],
        "\nexpected = {expected:x?}\nactual   = {actual:x?}"
    );

    let actual_enum_map: EnumMap<FileAttributeId, FileAttribute> =
        serde_xdr::from_bytes(&expected).unwrap();
    assert_eq!(expected_enum_map, actual_enum_map);
}

/// This canned example was captured from wireshark
#[test]
fn enum_set_serialization_round_trip() {
    let expected_enum_map: EnumSet<FileAttributeId> = [
        FileAttributeId::Type,
        FileAttributeId::Change,
        FileAttributeId::Size,
        FileAttributeId::FsId,
        FileAttributeId::FileId,
        FileAttributeId::Mode,
        FileAttributeId::NumLinks,
        FileAttributeId::Owner,
        FileAttributeId::OwnerGroup,
        FileAttributeId::RawDev,
        FileAttributeId::SpaceUsed,
        FileAttributeId::TimeAccess,
        FileAttributeId::TimeMetadata,
        FileAttributeId::TimeModify,
        FileAttributeId::MountedOnFileid,
    ]
    .into_iter()
    .collect();

    let expected = [
        0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x01, 0x1a, 0x00, 0xb0, 0xa2, 0x3a,
    ];

    let actual = serde_xdr::to_bytes(&expected_enum_map).unwrap();
    assert!(
        &expected[..] == &actual[..],
        "\nexpected = {expected:x?}\nactual   = {actual:x?}"
    );

    let actual_enum_map: EnumSet<FileAttributeId> = serde_xdr::from_bytes(&expected).unwrap();
    assert_eq!(expected_enum_map, actual_enum_map);
}

/// This canned example was captured from wireshark
#[test]
fn directory_list_serialization_round_trip() {
    let expected_list = DirectoryList {
        entries: vec![
            DirectoryEntry {
                cookie: Cookie(7298886750667917989),
                name: "b".into(),
                attrs: [
                    FileAttribute::Type(FileType::Regular),
                    FileAttribute::Change(Change(7263172143267875925)),
                    FileAttribute::Size(0),
                    FileAttribute::FsId(FsId {
                        major: 12333769691692441404,
                        minor: 0,
                    }),
                    FileAttribute::ReadDirAttrError(StatusResult::Ok(())),
                    FileAttribute::FileHandle(FileHandle(vec![
                        0x01, 0x00, 0x07, 0x02, 0x01, 0x00, 0xee, 0x0e, 0x00, 0x00, 0x00, 0x00,
                        0xab, 0x2a, 0x5a, 0x2f, 0xfe, 0xce, 0x9f, 0x3c, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x52, 0x02, 0x34, 0x0f, 0x6a, 0xd7, 0x7a, 0xae,
                        0x50, 0x02, 0x34, 0x0f, 0x68, 0xd7, 0x7a, 0xae,
                    ])),
                    FileAttribute::FileId(FileId(255066706)),
                    FileAttribute::Mode(Mode(0o0664)),
                    FileAttribute::NumLinks(1),
                    FileAttribute::Owner("1001".into()),
                    FileAttribute::OwnerGroup("1001".into()),
                    FileAttribute::RawDev(DeviceData { major: 0, minor: 0 }),
                    FileAttribute::SpaceUsed(0),
                    FileAttribute::TimeAccess(Time {
                        seconds: 1691089045,
                        nseconds: 369003605,
                    }),
                    FileAttribute::TimeMetadata(Time {
                        seconds: 1691089045,
                        nseconds: 369003605,
                    }),
                    FileAttribute::TimeModify(Time {
                        seconds: 1691089045,
                        nseconds: 369003605,
                    }),
                    FileAttribute::MountedOnFileid(FileId(0x000000000f340252)),
                ]
                .into_iter()
                .collect(),
            },
            DirectoryEntry {
                cookie: Cookie(8079073596820193671),
                name: "c".into(),
                attrs: [
                    FileAttribute::Type(FileType::Regular),
                    FileAttribute::Change(Change(7263172143277876118)),
                    FileAttribute::Size(0),
                    FileAttribute::FsId(FsId {
                        major: 12333769691692441404,
                        minor: 0,
                    }),
                    FileAttribute::ReadDirAttrError(StatusResult::Ok(())),
                    FileAttribute::FileHandle(FileHandle(vec![
                        0x01, 0x00, 0x07, 0x02, 0x01, 0x00, 0xee, 0x0e, 0x00, 0x00, 0x00, 0x00,
                        0xab, 0x2a, 0x5a, 0x2f, 0xfe, 0xce, 0x9f, 0x3c, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x53, 0x02, 0x34, 0x0f, 0x6b, 0xd7, 0x7a, 0xae,
                        0x50, 0x02, 0x34, 0x0f, 0x68, 0xd7, 0x7a, 0xae,
                    ])),
                    FileAttribute::FileId(FileId(255066707)),
                    FileAttribute::Mode(Mode(0o0664)),
                    FileAttribute::NumLinks(1),
                    FileAttribute::Owner("1001".into()),
                    FileAttribute::OwnerGroup("1001".into()),
                    FileAttribute::RawDev(DeviceData { major: 0, minor: 0 }),
                    FileAttribute::SpaceUsed(0),
                    FileAttribute::TimeAccess(Time {
                        seconds: 1691089045,
                        nseconds: 379003798,
                    }),
                    FileAttribute::TimeMetadata(Time {
                        seconds: 1691089045,
                        nseconds: 379003798,
                    }),
                    FileAttribute::TimeModify(Time {
                        seconds: 1691089045,
                        nseconds: 379003798,
                    }),
                    FileAttribute::MountedOnFileid(FileId(0x000000000f340253)),
                ]
                .into_iter()
                .collect(),
            },
            DirectoryEntry {
                cookie: Cookie(9223372036854775807),
                name: "a".into(),
                attrs: [
                    FileAttribute::Type(FileType::Regular),
                    FileAttribute::Change(Change(7263172143237875346)),
                    FileAttribute::Size(0),
                    FileAttribute::FsId(FsId {
                        major: 12333769691692441404,
                        minor: 0,
                    }),
                    FileAttribute::ReadDirAttrError(StatusResult::Ok(())),
                    FileAttribute::FileHandle(FileHandle(vec![
                        0x01, 0x00, 0x07, 0x02, 0x01, 0x00, 0xee, 0x0e, 0x00, 0x00, 0x00, 0x00,
                        0xab, 0x2a, 0x5a, 0x2f, 0xfe, 0xce, 0x9f, 0x3c, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x51, 0x02, 0x34, 0x0f, 0x69, 0xd7, 0x7a, 0xae,
                        0x50, 0x02, 0x34, 0x0f, 0x68, 0xd7, 0x7a, 0xae,
                    ])),
                    FileAttribute::FileId(FileId(255066705)),
                    FileAttribute::Mode(Mode(0o0664)),
                    FileAttribute::NumLinks(1),
                    FileAttribute::Owner("1001".into()),
                    FileAttribute::OwnerGroup("1001".into()),
                    FileAttribute::RawDev(DeviceData { major: 0, minor: 0 }),
                    FileAttribute::SpaceUsed(0),
                    FileAttribute::TimeAccess(Time {
                        seconds: 1691089045,
                        nseconds: 339003026,
                    }),
                    FileAttribute::TimeMetadata(Time {
                        seconds: 1691089045,
                        nseconds: 339003026,
                    }),
                    FileAttribute::TimeModify(Time {
                        seconds: 1691089045,
                        nseconds: 339003026,
                    }),
                    FileAttribute::MountedOnFileid(FileId(0x000000000f340251)),
                ]
                .into_iter()
                .collect(),
            },
        ],
        eof: true,
    };

    let expected = [
        0x00, 0x00, 0x00, 0x01, 0x65, 0x4a, 0xda, 0xd4, 0x1d, 0x5d, 0xca, 0xa5, 0x00, 0x00, 0x00,
        0x01, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x18, 0x09, 0x1a, 0x00, 0xb0,
        0xa2, 0x3a, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x01, 0x64, 0xcb, 0xf8, 0x95, 0x15,
        0xfe, 0x8c, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x2a, 0x5a, 0x2f,
        0xfe, 0xce, 0x9f, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x07, 0x02, 0x01, 0x00, 0xee, 0x0e, 0x00, 0x00,
        0x00, 0x00, 0xab, 0x2a, 0x5a, 0x2f, 0xfe, 0xce, 0x9f, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x52, 0x02, 0x34, 0x0f, 0x6a, 0xd7, 0x7a, 0xae, 0x50, 0x02, 0x34, 0x0f,
        0x68, 0xd7, 0x7a, 0xae, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x34, 0x02, 0x52, 0x00, 0x00, 0x01,
        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x31, 0x30, 0x30, 0x31, 0x00, 0x00,
        0x00, 0x04, 0x31, 0x30, 0x30, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xcb, 0xf8, 0x95,
        0x15, 0xfe, 0x8c, 0x55, 0x00, 0x00, 0x00, 0x00, 0x64, 0xcb, 0xf8, 0x95, 0x15, 0xfe, 0x8c,
        0x55, 0x00, 0x00, 0x00, 0x00, 0x64, 0xcb, 0xf8, 0x95, 0x15, 0xfe, 0x8c, 0x55, 0x00, 0x00,
        0x00, 0x00, 0x0f, 0x34, 0x02, 0x52, 0x00, 0x00, 0x00, 0x01, 0x70, 0x1e, 0xa2, 0xa1, 0xac,
        0xb4, 0x81, 0x87, 0x00, 0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
        0x00, 0x18, 0x09, 0x1a, 0x00, 0xb0, 0xa2, 0x3a, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00,
        0x01, 0x64, 0xcb, 0xf8, 0x95, 0x16, 0x97, 0x23, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0xab, 0x2a, 0x5a, 0x2f, 0xfe, 0xce, 0x9f, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x07, 0x02,
        0x01, 0x00, 0xee, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xab, 0x2a, 0x5a, 0x2f, 0xfe, 0xce, 0x9f,
        0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x02, 0x34, 0x0f, 0x6b, 0xd7,
        0x7a, 0xae, 0x50, 0x02, 0x34, 0x0f, 0x68, 0xd7, 0x7a, 0xae, 0x00, 0x00, 0x00, 0x00, 0x0f,
        0x34, 0x02, 0x53, 0x00, 0x00, 0x01, 0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04,
        0x31, 0x30, 0x30, 0x31, 0x00, 0x00, 0x00, 0x04, 0x31, 0x30, 0x30, 0x31, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x64, 0xcb, 0xf8, 0x95, 0x16, 0x97, 0x23, 0x96, 0x00, 0x00, 0x00, 0x00, 0x64,
        0xcb, 0xf8, 0x95, 0x16, 0x97, 0x23, 0x96, 0x00, 0x00, 0x00, 0x00, 0x64, 0xcb, 0xf8, 0x95,
        0x16, 0x97, 0x23, 0x96, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x34, 0x02, 0x53, 0x00, 0x00, 0x00,
        0x01, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x61, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x18, 0x09, 0x1a, 0x00, 0xb0, 0xa2, 0x3a, 0x00,
        0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x01, 0x64, 0xcb, 0xf8, 0x95, 0x14, 0x34, 0xc6, 0x92,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x2a, 0x5a, 0x2f, 0xfe, 0xce, 0x9f,
        0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x2c, 0x01, 0x00, 0x07, 0x02, 0x01, 0x00, 0xee, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xab,
        0x2a, 0x5a, 0x2f, 0xfe, 0xce, 0x9f, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x51, 0x02, 0x34, 0x0f, 0x69, 0xd7, 0x7a, 0xae, 0x50, 0x02, 0x34, 0x0f, 0x68, 0xd7, 0x7a,
        0xae, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x34, 0x02, 0x51, 0x00, 0x00, 0x01, 0xb4, 0x00, 0x00,
        0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x31, 0x30, 0x30, 0x31, 0x00, 0x00, 0x00, 0x04, 0x31,
        0x30, 0x30, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xcb, 0xf8, 0x95, 0x14, 0x34, 0xc6,
        0x92, 0x00, 0x00, 0x00, 0x00, 0x64, 0xcb, 0xf8, 0x95, 0x14, 0x34, 0xc6, 0x92, 0x00, 0x00,
        0x00, 0x00, 0x64, 0xcb, 0xf8, 0x95, 0x14, 0x34, 0xc6, 0x92, 0x00, 0x00, 0x00, 0x00, 0x0f,
        0x34, 0x02, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
    ];

    let actual = serde_xdr::to_bytes(&expected_list).unwrap();
    assert!(
        &expected[..] == &actual[..],
        "\nexpected = {expected:x?}\nactual   = {actual:x?}"
    );

    let actual_list: DirectoryList = serde_xdr::from_bytes(&expected).unwrap();
    assert_eq!(expected_list, actual_list);
}

#[test]
fn session_id_serialization() {
    use nfs4::SessionId;

    let expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
    let id = SessionId(expected.clone());

    let actual = serde_xdr::to_bytes(&id).unwrap();
    assert!(
        &expected[..] == &actual[..],
        "\nexpected = {expected:x?}\nactual   = {actual:x?}"
    );

    let actual_id: SessionId = serde_xdr::from_bytes(&expected[..]).unwrap();
    assert_eq!(actual_id, id);
}