304 lines
10 KiB
C
304 lines
10 KiB
C
|
|
/* NFSv4.1 client for Windows
|
|||
|
|
* Copyright <EFBFBD> 2012 The Regents of the University of Michigan
|
|||
|
|
*
|
|||
|
|
* Olga Kornievskaia <aglo@umich.edu>
|
|||
|
|
* Casey Bodley <cbodley@umich.edu>
|
|||
|
|
*
|
|||
|
|
* 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 <ntifs.h>
|
|||
|
|
#include <strsafe.h>
|
|||
|
|
#include <stdio.h>
|
|||
|
|
|
|||
|
|
|
|||
|
|
#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 <filename> <create|set|get|list> ...\n");
|
|||
|
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
|
goto out;
|
|||
|
|
}
|
|||
|
|
if (wcscmp(argv[2], L"create") == 0) {
|
|||
|
|
if (argc < 5) {
|
|||
|
|
fwprintf(stderr, L"Usage: nfs_ea <filename> create <name> <value>\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 <filename> set <name> [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 <filename> get <name> [name...]\n");
|
|||
|
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
|
goto out;
|
|||
|
|
}
|
|||
|
|
} else if (wcscmp(argv[2], L"list") != 0) {
|
|||
|
|
fwprintf(stderr, L"Usage: nfs_ea <filename> <create|set|get|list> ...\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;
|
|||
|
|
}
|