ls.exe -n does not work on NFS filesystems

Open
#86 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
45/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Quiet
Tech stack
rust

Research direction

Start at the Rust implementation of ls.exe's metadata handling and trace how the -n option obtains file attributes on Windows. Compare that path with the NfsV3Attributes EA and ZwQueryEaFile example in the issue, then test against one of the listed Windows NFS drivers. Done means ls.exe -n reports the expected numeric metadata on NFS filesystems.

Written by the indexing model from the issue text.

Description

C-bug

ls.exe -n does not work on NFS filesystems, e.g. Windows NFSv3 driver, OpenText/Exceed NFSv4 driver, ms-nfs41-client driver.

All these drivers implement the ServicesForUNIX API to obtain stat()-alike information.

The API is simply to get the EA "NfsV3Attributes" for for a file or directory, which contains a nfs3_attrs struct:

#define EA_NFSV3ATTRIBUTES "NfsV3Attributes"
#define EA_NFSV3ATTRIBUTES_LEN (15)

/*
 * "NfsV3Attributes" uses |nfs3_attrs| as content
 */
/*
 * Note that we cannot use <stdint.h> in the Windows kernel, so we
 * use Windows Types here
 */
typedef struct _nfs3_attrs_timestruc_t {
    INT32   tv_sec;
    UINT32  tv_nsec;
} nfs3_attrs_timestruc_t;

typedef struct _nfs3_attrs {
    UINT32 type, mode, nlink, uid, gid, filler1;
    UINT64 size, used;
    struct {
        UINT32 specdata1;
        UINT32 specdata2;
    } rdev;
    UINT64 fsid, fileid;
    nfs3_attrs_timestruc_t atime, mtime, ctime;
} nfs3_attrs;

enum ftype3 {
    NF3REG = 1,
    NF3DIR,
    NF3BLK,
    NF3CHR,
    NF3LNK,
    NF3SOCK,
    NF3FIFO
};

Example function, based on https://github.com/kofemann/ms-nfs41-client/blob/master/include/nfs_ea.h and https://github.com/kofemann/ms-nfs41-client/blob/master/tests/winfsinfo1/winfsinfo.c

int get_nfs3attr(const char *progname, const char *filename)
{
    int res = EXIT_FAILURE;

    HANDLE fileHandle = CreateFileA(filename,
        GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS, NULL);
    if (fileHandle == INVALID_HANDLE_VALUE) {
        (void)fprintf(stderr,
            "%s: Error opening file '%s'. Last error was %d.\n",
            progname,
            filename,
            (int)GetLastError());
        return EXIT_FAILURE;
    }

    struct {
        FILE_FULL_EA_INFORMATION ffeai;
        char buf[sizeof(EA_NFSV3ATTRIBUTES) + sizeof(nfs3_attrs)];
    } ffeai_buf;
    struct {
        FILE_GET_EA_INFORMATION fgeai;
        char buf[sizeof(EA_NFSV3ATTRIBUTES)];
    } fgeai_buf;

    NTSTATUS status;
    IO_STATUS_BLOCK io;

    fgeai_buf.fgeai.NextEntryOffset = 0;
    fgeai_buf.fgeai.EaNameLength = 15;
    (void)strcpy(fgeai_buf.fgeai.EaName, EA_NFSV3ATTRIBUTES);

    status = ZwQueryEaFile(fileHandle, &io,
        &ffeai_buf.ffeai, sizeof(ffeai_buf), TRUE,
        &fgeai_buf.fgeai, sizeof(fgeai_buf), NULL, TRUE);

    switch (status) {
        case STATUS_SUCCESS:
            break;
        case STATUS_NO_EAS_ON_FILE:
            (void)fprintf(stderr, "No EAs on file, status=0x%lx.\n", (long)status);
            res = EXIT_FAILURE;
            goto done;
        default:
            (void)fprintf(stderr, "ZwQueryEaFile() failed with 0x%lx\n", (long)status);
            res = EXIT_FAILURE;
            goto done;
    }

    if (ffeai_buf.ffeai.EaValueLength < sizeof(nfs3_attrs)) {
            (void)fprintf(stderr,
                "EA '%s' size too small (%ld bytes), "
                "expected at least %ld bytes for nfs3_attrs\n",
                EA_NFSV3ATTRIBUTES,
                (long)ffeai_buf.ffeai.EaValueLength,
                (long)sizeof(nfs3_attrs));
            res = EXIT_FAILURE;
            goto done;
    }

    nfs3_attrs *n3a = (nfs3_attrs *)(ffeai_buf.ffeai.EaName
        + ffeai_buf.ffeai.EaNameLength + 1);

    (void)printf("(\n");

    (void)printf("\tfilename='%s'\n"
        "\ttype=%d\n"
        "\tmode=0%o\n"
        "\tnlink=%d\n"
        "\tuid=%d\n\tgid=%d\n"
        "\tsize=%lld\n\tused=%lld\n"
        "\trdev=( specdata1=0x%x specdata2=0x%x )\n"
        "\tfsid=0x%llx\n\tfileid=0x%llx\n"
        "\tatime=( tv_sec=%ld tv_nsec=%lu )\n"
        "\tmtime=( tv_sec=%ld tv_nsec=%lu )\n"
        "\tctime=( tv_sec=%ld tv_nsec=%lu )\n"
        ")\n",
        filename,
        (int)n3a->type,
        (int)n3a->mode,
        (int)n3a->nlink,
        (int)n3a->uid,
        (int)n3a->gid,
        (long long)n3a->size,
        (long long)n3a->used,
        (int)n3a->rdev.specdata1,
        (int)n3a->rdev.specdata2,
        (unsigned long long)n3a->fsid,
        (unsigned long long)n3a->fileid,
        (long)n3a->atime.tv_sec, (unsigned long)n3a->atime.tv_nsec,
        (long)n3a->mtime.tv_sec, (unsigned long)n3a->mtime.tv_nsec,
        (long)n3a->ctime.tv_sec, (unsigned long)n3a->ctime.tv_nsec);
    res = EXIT_SUCCESS;

done:
    (void)CloseHandle(fileHandle);
    return res;
}
Dominant language
Rust
Stars
5.2k
Forks
107
Avg merge
2d 19h
Merged PRs (30d)
4

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from microsoft/coreutils

All issues in microsoft/coreutils

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.