From 380e04867d10a06d6f063318d0798c8598b20206 Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Fri, 27 Apr 2012 15:20:53 -0400 Subject: [PATCH] test: nfs_ea to create, list, set, and query file EAs nfs_ea create nfs_ea set [value] nfs_ea get [name...] nfs_ea list Note that the test uses NtCreateFile(), so filenames must be specified in NT format: \??\z:\foo Signed-off-by: Casey Bodley --- tests/ea/main.c | 303 +++++++++++++++++++++++++++++++++++++++++++++++ tests/ea/sources | 17 +++ 2 files changed, 320 insertions(+) create mode 100644 tests/ea/main.c create mode 100644 tests/ea/sources diff --git a/tests/ea/main.c b/tests/ea/main.c new file mode 100644 index 0000000..a123d78 --- /dev/null +++ b/tests/ea/main.c @@ -0,0 +1,303 @@ +/* NFSv4.1 client for Windows + * Copyright © 2012 The Regents of the University of Michigan + * + * Olga Kornievskaia + * Casey Bodley + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * without any warranty; without even the implied warranty of merchantability + * or fitness for a particular purpose. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA + */ + +#include +#include +#include + + +#define MAX_LIST_LEN 4096 +#define MAX_EA_VALUE 256 + +#define MAX_GETEA (sizeof(FILE_GET_EA_INFORMATION) + MAX_EA_VALUE) +#define MAX_FULLEA (sizeof(FILE_FULL_EA_INFORMATION) + 2 * MAX_EA_VALUE) + +static NTSTATUS ea_list( + HANDLE FileHandle) +{ + IO_STATUS_BLOCK IoStatusBlock = { 0 }; + CHAR Buffer[MAX_LIST_LEN]; + PFILE_FULL_EA_INFORMATION EaBuffer; + NTSTATUS status; + BOOLEAN RestartScan = TRUE; + +on_overflow: + EaBuffer = (PFILE_FULL_EA_INFORMATION)Buffer; + + status = ZwQueryEaFile(FileHandle, &IoStatusBlock, + EaBuffer, MAX_LIST_LEN, FALSE, NULL, 0, NULL, RestartScan); + switch (status) { + case STATUS_SUCCESS: + case STATUS_BUFFER_OVERFLOW: + break; + case STATUS_NO_EAS_ON_FILE: + printf("No EAs on file.\n", status); + goto out; + default: + fprintf(stderr, "ZwQueryEaFile() failed with %X\n", status); + goto out; + } + + while (EaBuffer) { + printf("%s = %.*s\n", EaBuffer->EaName, EaBuffer->EaValueLength, + EaBuffer->EaName + EaBuffer->EaNameLength + 1); + + if (EaBuffer->NextEntryOffset == 0) + break; + EaBuffer = (PFILE_FULL_EA_INFORMATION) + ((PCHAR)EaBuffer + EaBuffer->NextEntryOffset); + } + + if (status == STATUS_BUFFER_OVERFLOW) { + printf("overflow, querying more\n", status); + RestartScan = FALSE; + goto on_overflow; + } +out: + return status; +} + +static NTSTATUS ea_get( + HANDLE FileHandle, + IN LPCWSTR EaNames[], + IN DWORD Count) +{ + IO_STATUS_BLOCK IoStatusBlock = { 0 }; + CHAR GetBuffer[MAX_LIST_LEN] = { 0 }; + CHAR FullBuffer[MAX_LIST_LEN] = { 0 }; + PFILE_GET_EA_INFORMATION EaList = (PFILE_GET_EA_INFORMATION)GetBuffer, EaQuery; + PFILE_FULL_EA_INFORMATION EaBuffer = (PFILE_FULL_EA_INFORMATION)FullBuffer; + ULONG ActualByteCount, EaListLength; + DWORD i; + NTSTATUS status; + + EaQuery = EaList; + EaListLength = 0; + + for (i = 0; i < Count; i++) { + LPCWSTR EaName = EaNames[i]; + ULONG EaNameLength = (ULONG)((wcslen(EaName)+1) * sizeof(WCHAR)); + + /* convert EaName */ + status = RtlUnicodeToUTF8N(EaQuery->EaName, MAX_EA_VALUE, + &ActualByteCount, EaName, EaNameLength); + if (status) { + fwprintf(stderr, L"RtlUnicodeToUTF8N('%s') failed with %X\n", EaName, status); + goto out; + } + EaQuery->EaNameLength = (UCHAR)ActualByteCount - 1; + EaQuery->NextEntryOffset = FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + EaQuery->EaNameLength + 1; + + if (i == Count - 1) { + EaListLength += EaQuery->NextEntryOffset; + EaQuery->NextEntryOffset = 0; + } else { + EaQuery->NextEntryOffset = 4 + ((EaQuery->NextEntryOffset - 1) & ~3); + EaListLength += EaQuery->NextEntryOffset; + } + EaQuery = (PFILE_GET_EA_INFORMATION)((PCHAR)EaQuery + EaQuery->NextEntryOffset); + } + + status = ZwQueryEaFile(FileHandle, &IoStatusBlock, + EaBuffer, MAX_FULLEA, FALSE, EaList, EaListLength, NULL, TRUE); + switch (status) { + case STATUS_SUCCESS: + break; + case STATUS_NO_EAS_ON_FILE: + printf("No EAs on file.\n", status); + goto out; + default: + fprintf(stderr, "ZwQueryEaFile('%s') failed with %X\n", EaList->EaName, status); + goto out; + } + + while (EaBuffer) { + printf("%s = %.*s\n", EaBuffer->EaName, EaBuffer->EaValueLength, + EaBuffer->EaName + EaBuffer->EaNameLength + 1); + + if (EaBuffer->NextEntryOffset == 0) + break; + EaBuffer = (PFILE_FULL_EA_INFORMATION) + ((PCHAR)EaBuffer + EaBuffer->NextEntryOffset); + } +out: + return status; +} + +static NTSTATUS full_ea_init( + IN LPCWSTR EaName, + IN LPCWSTR EaValue, + OUT PFILE_FULL_EA_INFORMATION EaBuffer, + OUT PULONG EaLength) +{ + ULONG ActualByteCount, EaNameLength; + NTSTATUS status; + + EaBuffer->NextEntryOffset = 0; + EaBuffer->Flags = 0; + + EaNameLength = (ULONG)((wcslen(EaName)+1) * sizeof(WCHAR)); + + /* convert EaName */ + status = RtlUnicodeToUTF8N(EaBuffer->EaName, MAX_FULLEA - + FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName), + &ActualByteCount, EaName, EaNameLength); + if (status) { + fwprintf(stderr, L"RtlUnicodeToUTF8N('%s') failed with %X\n", EaName, status); + goto out; + } + EaBuffer->EaNameLength = (UCHAR)ActualByteCount - 1; + + if (EaValue == NULL) { + EaBuffer->EaValueLength = 0; + } else { + ULONG EaValueLength = (ULONG)((wcslen(EaValue)+1) * sizeof(WCHAR)); + + /* convert EaValue */ + status = RtlUnicodeToUTF8N(EaBuffer->EaName + EaBuffer->EaNameLength + 1, + MAX_FULLEA - FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) - EaBuffer->EaNameLength - 1, + &ActualByteCount, EaValue, EaValueLength); + if (status) { + fwprintf(stderr, L"RtlUnicodeToUTF8N('%s') failed with %X\n", EaName, status); + goto out; + } + EaBuffer->EaValueLength = (UCHAR)ActualByteCount - 1; + } + + *EaLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + + EaBuffer->EaNameLength + 1 + EaBuffer->EaValueLength; +out: + return status; +} + +static NTSTATUS ea_set( + HANDLE FileHandle, + IN PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG EaLength) +{ + IO_STATUS_BLOCK IoStatusBlock = { 0 }; + NTSTATUS status; + + status = ZwSetEaFile(FileHandle, &IoStatusBlock, EaBuffer, EaLength); + switch (status) { + case STATUS_SUCCESS: + printf("%s = %.*s\n", EaBuffer->EaName, EaBuffer->EaValueLength, + EaBuffer->EaName + EaBuffer->EaNameLength + 1); + break; + default: + fprintf(stderr, "ZwSetEaFile() failed with %X\n", status); + break; + } + return status; +} + +int wmain(DWORD argc, LPWSTR argv[]) +{ + UNICODE_STRING FileName; + OBJECT_ATTRIBUTES ObjectAttributes; + ACCESS_MASK DesiredAccess = GENERIC_READ; + ULONG FileAttributes = 0; + ULONG ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + ULONG CreateDisposition = FILE_OPEN_IF; + //ULONG CreateOptions = FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT; + ULONG CreateOptions = 0; + HANDLE FileHandle; + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS status; + + CHAR Buffer[MAX_FULLEA] = { 0 }; + PFILE_FULL_EA_INFORMATION EaBuffer = NULL; + ULONG EaLength = 0; + + if (argc < 3) { + fwprintf(stderr, L"Usage: nfs_ea ...\n"); + status = STATUS_INVALID_PARAMETER; + goto out; + } + if (wcscmp(argv[2], L"create") == 0) { + if (argc < 5) { + fwprintf(stderr, L"Usage: nfs_ea create \n"); + status = STATUS_INVALID_PARAMETER; + goto out; + } + CreateDisposition = FILE_OVERWRITE_IF; + EaBuffer = (PFILE_FULL_EA_INFORMATION)Buffer; + status = full_ea_init(argv[3], argv[4], EaBuffer, &EaLength); + if (status) + goto out; + wprintf(L"Creating file %s.\n", argv[1]); + } else if (wcscmp(argv[2], L"set") == 0) { + if (argc < 4) { + fwprintf(stderr, L"Usage: nfs_ea set [value]\n"); + status = STATUS_INVALID_PARAMETER; + goto out; + } + DesiredAccess |= GENERIC_WRITE; + } else if (wcscmp(argv[2], L"get") == 0) { + if (argc < 4) { + fwprintf(stderr, L"Usage: nfs_ea get [name...]\n"); + status = STATUS_INVALID_PARAMETER; + goto out; + } + } else if (wcscmp(argv[2], L"list") != 0) { + fwprintf(stderr, L"Usage: nfs_ea ...\n"); + status = STATUS_INVALID_PARAMETER; + goto out; + } + + RtlInitUnicodeString(&FileName, argv[1]); + InitializeObjectAttributes(&ObjectAttributes, &FileName, 0, NULL, NULL); + + status = NtCreateFile(&FileHandle, DesiredAccess, &ObjectAttributes, + &IoStatusBlock, NULL, FileAttributes, ShareAccess, + CreateDisposition, CreateOptions, EaBuffer, EaLength); + if (status) { + fwprintf(stderr, L"NtCreateFile(%s) failed with %X\n", FileName.Buffer, status); + goto out; + } + + if (wcscmp(argv[2], L"set") == 0) { + EaBuffer = (PFILE_FULL_EA_INFORMATION)Buffer; + status = full_ea_init(argv[3], argc > 4 ? argv[4] : NULL, + EaBuffer, &EaLength); + if (status) + goto out_close; + + wprintf(L"Setting extended attribute '%s' on file %s:\n", + argv[3], FileName.Buffer); + status = ea_set(FileHandle, EaBuffer, EaLength); + } else if (wcscmp(argv[2], L"get") == 0) { + wprintf(L"Querying extended attribute on file %s:\n", + argv[3], FileName.Buffer); + status = ea_get(FileHandle, argv + 3, argc - 3); + } else if (wcscmp(argv[2], L"list") == 0) { + wprintf(L"Listing extended attributes for %s:\n", FileName.Buffer); + status = ea_list(FileHandle); + } else if (wcscmp(argv[2], L"create") == 0) { + wprintf(L"File '%s' was created with \n", FileName.Buffer); + status = ea_get(FileHandle, argv + 3, 1); + } + +out_close: + NtClose(FileHandle); +out: + return status; +} diff --git a/tests/ea/sources b/tests/ea/sources new file mode 100644 index 0000000..7b3a0bf --- /dev/null +++ b/tests/ea/sources @@ -0,0 +1,17 @@ +TARGETTYPE=PROGRAM +TARGETNAME=nfs_ea +SOURCES=main.c +UMTYPE=console +USE_MSVCRT=1 +TARGETLIBS=$(DDK_LIB_PATH)\ntdll.lib +INCLUDES=$(DDK_INC_PATH) + +UMENTRY=wmain +UNICODE=1 +C_DEFINES=$(C_DEFINES) -DUNICODE -D_UNICODE + +!IF 0 +/W3 is default level +bump to /Wall, but suppress warnings generated by system includes +!ENDIF +MSC_WARNING_LEVEL=/Wall /wd4668 /wd4619 /wd4820 /wd4255 /wd4711