Skip to content

ls.exe -n does not work on NFS filesystems #86

@aureliencouderc

Description

@aureliencouderc

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;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions