WSL/test/linux/unit_tests/xattr.c

1294 lines
50 KiB
C
Raw Permalink Normal View History

/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
Xattr.c
Abstract:
This file is a xattr test.
--*/
#include "lxtcommon.h"
#include "unittests.h"
#include "lxtutil.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/uio.h>
#include <sched.h>
#include <signal.h>
#include <time.h>
#include <limits.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/user.h>
#include <stddef.h>
#include <fcntl.h>
#include "lxtfs.h"
#include <sys/xattr.h>
#include <libmount/libmount.h>
#include "lxtmount.h"
#if !defined(__amd64__) && !defined(__aarch64__)
#include <sys/capability.h>
#else
#include <sys/cdefs.h>
#include <linux/capability.h>
#define _LINUX_CAPABILITY_VERSION_3 0x20080522
#endif
#define LXT_NAME "xattr"
#define LXT_NAME_DRVFS "xattr_drvfs"
#define LXT_XATTR_MODE 0777
#define LXT_XATTR_UID 1004
#define LXT_XATTR_GID 1004
#define LXT_XATTR_TEST_PARENT "/data/xattrtest"
#define LXT_XATTR_ACCESS_FILE_PATH LXT_XATTR_TEST_PARENT "/xattrAccessFile"
#define LXT_XATTR_FILE_PATH LXT_XATTR_TEST_PARENT "/xattrFile"
#define LXT_XATTR_DIR_PATH LXT_XATTR_TEST_PARENT "/xattrDir"
#define LXT_XATTR_LINK_PATH LXT_XATTR_TEST_PARENT "/xattrLink"
#define LXT_XATTR_FIFO_PATH LXT_XATTR_TEST_PARENT "/xattrFifo"
#define LXT_XATTR_SIZE_MAX (4040)
#define LXT_XATTR_CASE_SENSITIVE_LENGTH (sizeof(LXT_XATTR_CASE_SENSITIVE) - 1)
#define LXT_XATTR_TEST_VALUE "test"
#define LXT_XATTR_TEST_LENGTH (sizeof(LXT_XATTR_TEST_VALUE) - 1)
#define LXT_XATTR_PATH_COUNT (LXT_COUNT_OF(g_XattrPaths))
int XattrTestCreatePaths(int* Fds);
void XattrTestDeletePaths(int* Fds);
LXT_VARIATION_HANDLER XattrAccessTest;
LXT_VARIATION_HANDLER XattrListTest;
LXT_VARIATION_HANDLER XattrGetTest;
LXT_VARIATION_HANDLER XattrRemoveTest;
LXT_VARIATION_HANDLER XattrSetTest;
//
// Global constants
//
static const LXT_VARIATION g_LxtVariations[] = {
{"xattr list", XattrListTest}, {"xattr get", XattrGetTest}, {"xattr set", XattrSetTest}, {"xattr remove", XattrRemoveTest}, {"xattr access", XattrAccessTest}};
static const char* g_XattrPaths[] = {
LXT_XATTR_FILE_PATH, LXT_XATTR_DIR_PATH, LXT_XATTR_LINK_PATH, LXT_XATTR_FIFO_PATH, "/dev/null", "/proc/cpuinfo"};
ssize_t GetXattr(const char* Path, const char* Name, void* Value, size_t Size)
{
size_t Result = LxtGetxattr(Path, Name, Value, Size);
if (Result < 0)
{
int SavedErrno = errno;
ssize_t SavedResult = Result;
Result = LxtGetxattr(Path, Name, NULL, 0);
LxtLogInfo("getxattr(%s, %s, NULL, 0) = %Iu", Path, Name, Result);
errno = SavedErrno;
Result = SavedResult;
}
return Result;
}
int XattrTestEntry(int Argc, char* Argv[])
/*++
--*/
{
LXT_ARGS Args;
int Index;
const char* Name;
int Result;
bool UseDrvFs;
Name = LXT_NAME;
UseDrvFs = false;
for (Index = 1; Index < Argc; Index += 1)
{
if (strcmp(Argv[Index], "drvfs") == 0)
{
UseDrvFs = true;
Name = LXT_NAME_DRVFS;
break;
}
}
LxtCheckResult(LxtInitialize(Argc, Argv, &Args, Name));
LxtCheckResult(LxtFsTestSetup(&Args, LXT_XATTR_TEST_PARENT, "/xattrtest", UseDrvFs));
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
ErrorExit:
LxtFsTestCleanup(LXT_XATTR_TEST_PARENT, "/xattrtest", UseDrvFs);
LxtUninitialize();
return !LXT_SUCCESS(Result);
}
int XattrTestCreatePaths(int* Fds)
/*++
--*/
{
int Fd;
int Index;
int Result;
memset(Fds, -1, sizeof(*Fds) * LXT_XATTR_PATH_COUNT);
XattrTestDeletePaths(NULL);
LxtCheckErrno(Fd = creat(LXT_XATTR_FILE_PATH, LXT_XATTR_MODE));
LxtClose(Fd);
LxtCheckErrno(mkdir(LXT_XATTR_DIR_PATH, LXT_XATTR_MODE));
LxtCheckErrno(symlink(LXT_XATTR_FILE_PATH, LXT_XATTR_LINK_PATH));
LxtCheckErrno(mkfifo(LXT_XATTR_FIFO_PATH, LXT_XATTR_MODE));
for (Index = 0; Index < LXT_XATTR_PATH_COUNT; ++Index)
{
Fds[Index] = open(g_XattrPaths[Index], O_RDONLY | O_NONBLOCK);
}
ErrorExit:
return Result;
}
void XattrTestDeletePaths(int* Fds)
{
int Index;
if (Fds != NULL)
{
for (Index = 0; Index < LXT_XATTR_PATH_COUNT; ++Index)
{
if (Fds[Index] != -1)
{
LxtClose(Fds[Index]);
Fds[Index] = -1;
}
}
}
unlink(LXT_XATTR_FILE_PATH);
rmdir(LXT_XATTR_DIR_PATH);
unlink(LXT_XATTR_LINK_PATH);
unlink(LXT_XATTR_FIFO_PATH);
return;
}
int XattrListTest(PLXT_ARGS Args)
/*++
--*/
{
char Buffer[1024];
ssize_t ExpectedSize;
int Fds[LXT_XATTR_PATH_COUNT];
int Index;
ssize_t Result;
ssize_t Size;
LxtCheckErrno(XattrTestCreatePaths(Fds));
for (Index = 0; Index < (LXT_XATTR_PATH_COUNT - 1); ++Index)
{
LxtLogInfo("%s", g_XattrPaths[Index]);
//
// Check that the xattr syscalls return 0 for all entries to indicate no
// attributes are present.
//
// N.B. DrvFs (not in WslFs mode) will return the case sensitivity
// attribute for directories only.
//
if ((Index == 1) && ((g_LxtFsInfo.FsType == LxtFsTypeDrvFs) || (g_LxtFsInfo.FsType == LxtFsTypeVirtioFs)))
{
ExpectedSize = LXT_XATTR_CASE_SENSITIVE_LENGTH + 1;
}
else
{
ExpectedSize = 0;
}
LxtCheckErrno(Size = LxtListxattr(g_XattrPaths[Index], Buffer, sizeof(Buffer)));
LxtCheckEqual(Size, ExpectedSize, "%lld");
LxtCheckErrno(Size = LxtLlistxattr(g_XattrPaths[Index], Buffer, sizeof(Buffer)));
LxtCheckEqual(Size, ExpectedSize, "%lld");
LxtCheckErrno(Size = LxtFlistxattr(Fds[Index], Buffer, sizeof(Buffer)));
LxtCheckEqual(Size, ExpectedSize, "%lld");
//
// Check that the buffer and size are not validated if there are no
// attributes.
//
if (ExpectedSize == 0)
{
LxtCheckErrnoZeroSuccess(LxtListxattr(g_XattrPaths[Index], 0x1, sizeof(Buffer)));
LxtCheckErrnoZeroSuccess(LxtLlistxattr(g_XattrPaths[Index], 0x1, sizeof(Buffer)));
LxtCheckErrnoZeroSuccess(LxtFlistxattr(Fds[Index], 0x1, sizeof(Buffer)));
LxtCheckErrnoZeroSuccess(LxtListxattr(g_XattrPaths[Index], Buffer, -1));
LxtCheckErrnoZeroSuccess(LxtLlistxattr(g_XattrPaths[Index], Buffer, -1));
LxtCheckErrnoZeroSuccess(LxtFlistxattr(Fds[Index], Buffer, -1));
}
}
//
// Check for invalid parameters.
//
LxtCheckErrnoFailure(LxtListxattr(0x1, Buffer, sizeof(Buffer)), EFAULT);
LxtCheckErrnoFailure(LxtLlistxattr(0x1, Buffer, sizeof(Buffer)), EFAULT);
LxtCheckErrnoFailure(LxtFlistxattr(-1, Buffer, sizeof(Buffer)), EBADF);
Result = LXT_RESULT_SUCCESS;
ErrorExit:
XattrTestDeletePaths(Fds);
return (int)Result;
}
int XattrGetTest(PLXT_ARGS Args)
/*++
--*/
{
char Buffer[1024];
int Failures[] = {ENODATA, ENODATA, ENODATA, ENODATA, ENODATA, ENOTSUP, ENOTSUP};
int Fds[LXT_XATTR_PATH_COUNT];
int Index;
char* Name = "security.capability";
ssize_t Result;
LxtCheckErrno(XattrTestCreatePaths(Fds));
for (Index = 0; Index < LXT_XATTR_PATH_COUNT; ++Index)
{
//
// Check that the xattr syscalls return 0 for all entries to indicate no
// attributes are present.
//
LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[Index], Name, Buffer, sizeof(Buffer)), Failures[Index]);
LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[Index], Name, Buffer, sizeof(Buffer)), Failures[Index]);
LxtCheckErrnoFailure(LxtFgetxattr(Fds[Index], Name, Buffer, sizeof(Buffer)), Failures[Index]);
//
// Check that the buffer and size are not validated if there are no
// attributes.
//
LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[Index], Name, 0x1, sizeof(Buffer)), Failures[Index]);
LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[Index], Name, 0x1, sizeof(Buffer)), Failures[Index]);
LxtCheckErrnoFailure(LxtFgetxattr(Fds[Index], Name, 0x1, sizeof(Buffer)), Failures[Index]);
LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[Index], Name, Buffer, -1), Failures[Index]);
LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[Index], Name, Buffer, -1), Failures[Index]);
LxtCheckErrnoFailure(LxtFgetxattr(Fds[Index], Name, Buffer, -1), Failures[Index]);
}
//
// Check for invalid parameters.
//
LxtCheckErrnoFailure(LxtGetxattr(0x1, Name, Buffer, sizeof(Buffer)), EFAULT);
LxtCheckErrnoFailure(LxtLgetxattr(0x1, Name, Buffer, sizeof(Buffer)), EFAULT);
LxtCheckErrnoFailure(LxtFgetxattr(-1, Name, Buffer, sizeof(Buffer)), EBADF);
LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], 0x1, Buffer, sizeof(Buffer)), EFAULT);
LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[0], 0x1, Buffer, sizeof(Buffer)), EFAULT);
LxtCheckErrnoFailure(LxtFgetxattr(Fds[0], 0x1, Buffer, sizeof(Buffer)), EFAULT);
if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)
{
LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer)), ENOTSUP);
LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer)), ENOTSUP);
LxtCheckErrnoFailure(LxtFgetxattr(Fds[0], "invalid.name", Buffer, sizeof(Buffer)), ENOTSUP);
}
else
{
// The virtioFs implementation does not restrict the allowed attribute names, but these values will not be present.
LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer)), ENODATA);
LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer)), ENODATA);
LxtCheckErrnoFailure(LxtFgetxattr(Fds[0], "invalid.name", Buffer, sizeof(Buffer)), ENODATA);
}
Result = LXT_RESULT_SUCCESS;
ErrorExit:
XattrTestDeletePaths(Fds);
return (int)Result;
}
int XattrRemoveTest(PLXT_ARGS Args)
/*++
--*/
{
char Buffer[1024];
int Fds[LXT_XATTR_PATH_COUNT];
int Index;
char* Name = "security.capability";
int Result;
char TestData[] = "test";
LxtCheckErrno(XattrTestCreatePaths(Fds));
for (Index = 0; Index < LXT_XATTR_PATH_COUNT - 1; ++Index)
{
LxtLogInfo("%s %s", g_XattrPaths[Index], Name);
//
// Check that the xattr syscalls return the correct error code for all
// entries to indicate no attributes are present.
//
LxtCheckErrnoFailure(LxtRemovexattr(g_XattrPaths[Index], Name), ENODATA);
LxtCheckErrnoFailure(LxtLremovexattr(g_XattrPaths[Index], Name), ENODATA);
LxtCheckErrnoFailure(LxtFremovexattr(Fds[Index], Name), ENODATA);
}
//
// Create three ea's and delete them in various orders.
//
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.1", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.2", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.3", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.1"));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.2"));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.3"));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.1", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.2", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.3", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.1"));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.3"));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.2"));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.1", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.2", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.3", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.2"));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.1"));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.3"));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.1", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.2", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.3", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.2"));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.3"));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.1"));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.1", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.2", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.3", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.3"));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.1"));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.2"));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.1", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.2", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.3", TestData, sizeof(TestData), 0));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.3"));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.2"));
LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.1"));
//
// Check for invalid parameters.
//
LxtCheckErrnoFailure(LxtRemovexattr(0x1, Name), EFAULT);
LxtCheckErrnoFailure(LxtLremovexattr(0x1, Name), EFAULT);
LxtCheckErrnoFailure(LxtFremovexattr(-1, Name), EBADF);
LxtCheckErrnoFailure(LxtRemovexattr(g_XattrPaths[0], 0x1), EFAULT);
LxtCheckErrnoFailure(LxtLremovexattr(g_XattrPaths[0], 0x1), EFAULT);
LxtCheckErrnoFailure(LxtFremovexattr(Fds[0], 0x1), EFAULT);
if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)
{
LxtCheckErrnoFailure(LxtRemovexattr(g_XattrPaths[0], "invalid.name"), ENOTSUP);
LxtCheckErrnoFailure(LxtLremovexattr(g_XattrPaths[0], "invalid.name"), ENOTSUP);
LxtCheckErrnoFailure(LxtFremovexattr(Fds[0], "invalid.name"), ENOTSUP);
}
else
{
// The virtioFs implementation does not restrict the allowed attribute names, but these values will not be present.
LxtCheckErrnoFailure(LxtRemovexattr(g_XattrPaths[0], "invalid.name"), ENODATA);
LxtCheckErrnoFailure(LxtLremovexattr(g_XattrPaths[0], "invalid.name"), ENODATA);
LxtCheckErrnoFailure(LxtFremovexattr(Fds[0], "invalid.name"), ENODATA);
}
Result = LXT_RESULT_SUCCESS;
ErrorExit:
XattrTestDeletePaths(Fds);
return Result;
}
int XattrSetTest(PLXT_ARGS Args)
/*++
--*/
{
char* AttrName;
char Buffer[1024];
int Count;
char* DynamicBuffer;
int DynamicBufferSize;
int Fds[LXT_XATTR_PATH_COUNT];
int Index;
char* Name = "security.foo";
int NameIndex;
ssize_t Result;
int Size;
struct
{
char Name[256];
bool Found;
} TestXattrs[103];
int TotalCount;
int TotalSize;
DynamicBuffer = NULL;
LxtCheckErrno(XattrTestCreatePaths(Fds));
//
// Check for invalid parameters.
//
LxtCheckErrnoFailure(LxtSetxattr(0x1, Name, Buffer, sizeof(Buffer), 0), EFAULT);
LxtCheckErrnoFailure(LxtLsetxattr(0x1, Name, Buffer, sizeof(Buffer), 0), EFAULT);
LxtCheckErrnoFailure(LxtFsetxattr(-1, Name, Buffer, sizeof(Buffer), 0), EBADF);
LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], 0x1, Buffer, sizeof(Buffer), 0), EFAULT);
LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], 0x1, Buffer, sizeof(Buffer), 0), EFAULT);
LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], 0x1, Buffer, sizeof(Buffer), 0), EFAULT);
if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)
{
LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer), 0), ENOTSUP);
LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer), 0), ENOTSUP);
LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], "invalid.name", Buffer, sizeof(Buffer), 0), ENOTSUP);
LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "security.", Buffer, sizeof(Buffer), 0), EINVAL);
LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], "security.", Buffer, sizeof(Buffer), 0), EINVAL);
LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], "security.", Buffer, sizeof(Buffer), 0), EINVAL);
LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "trusted.", Buffer, sizeof(Buffer), 0), EINVAL);
LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], "trusted.", Buffer, sizeof(Buffer), 0), EINVAL);
LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], "trusted.", Buffer, sizeof(Buffer), 0), EINVAL);
LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "user.", Buffer, sizeof(Buffer), 0), EINVAL);
LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], "user.", Buffer, sizeof(Buffer), 0), EINVAL);
LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], "user.", Buffer, sizeof(Buffer), 0), EINVAL);
}
else
{
// The virtioFs implementation does not restrict the allowed attribute names.
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer), 0));
LxtCheckErrno(LxtLsetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer), 0));
LxtCheckErrno(LxtFsetxattr(Fds[0], "invalid.name", Buffer, sizeof(Buffer), 0));
}
LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "system.", Buffer, sizeof(Buffer), 0), ENOTSUP);
LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], "system.", Buffer, sizeof(Buffer), 0), ENOTSUP);
LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], "system.", Buffer, sizeof(Buffer), 0), ENOTSUP);
AttrName =
"user."
"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
"ooooooooooo";
LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], AttrName, Buffer, sizeof(Buffer), 0), ERANGE);
LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], AttrName, Buffer, sizeof(Buffer), 0), ERANGE);
LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], AttrName, Buffer, sizeof(Buffer), 0), ERANGE);
LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], Name, Buffer, sizeof(Buffer), 10), EINVAL);
//
// Create an attribute and read it back, using various buffer sizes.
//
LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.test", "1234", 4, 0));
LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.test", NULL, 0));
LxtCheckEqual(Size, 4, "%d");
LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.test", Buffer, sizeof(Buffer)));
LxtCheckEqual(Size, 4, "%d");
Buffer[4] = '\0';
LxtCheckStringEqual(Buffer, "1234");
LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.test", Buffer, 4));
LxtCheckEqual(Size, 4, "%d");
Buffer[4] = '\0';
LxtCheckStringEqual(Buffer, "1234");
LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], "user.test", Buffer, 3), ERANGE);
LxtCheckErrnoZeroSuccess(LxtRemovexattr(g_XattrPaths[0], "user.test"));
//
// Create a max length attribute, and ensure that it exists afterwards.
//
// N.B. Max length is based on ext4 limits; LxFs and DrvFs allow bigger
// attributes.
//
DynamicBuffer = malloc(LXT_XATTR_SIZE_MAX);
for (Index = 0; Index < LXT_XATTR_SIZE_MAX; Index += 1)
{
DynamicBuffer[Index] = (char)Index;
}
LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.0", DynamicBuffer, LXT_XATTR_SIZE_MAX, 0));
LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.0", NULL, 0));
LxtCheckEqual(Size, LXT_XATTR_SIZE_MAX, "%d");
LxtCheckErrnoZeroSuccess(LxtRemovexattr(g_XattrPaths[0], "user.0"));
Count = 0;
memset(TestXattrs, 0, sizeof(TestXattrs));
//
// Create a zero length attribute.
//
LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.zero", NULL, 0, 0));
LxtCheckErrnoZeroSuccess(LxtGetxattr(g_XattrPaths[0], "user.zero", NULL, 0));
strncpy(TestXattrs[Count].Name, "user.zero", sizeof(TestXattrs[Count].Name) - 1);
Count += 1;
//
// Create an attribute with the maximum name length.
//
if (g_LxtFsInfo.Flags.DrvFsBehavior != 0)
{
AttrName =
"user."
"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
"oooooooooooooo";
}
else
{
AttrName =
"user."
"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
"oooooooooooooooooo";
}
LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], AttrName, NULL, 0, 0));
LxtCheckErrnoZeroSuccess(LxtGetxattr(g_XattrPaths[0], AttrName, NULL, 0));
strncpy(TestXattrs[Count].Name, AttrName, sizeof(TestXattrs[Count].Name) - 1);
Count += 1;
//
// A bunch of attributes to exercise listing.
//
for (int Index = 0; Index < 100; Index += 1)
{
snprintf(TestXattrs[Count].Name, sizeof(TestXattrs[Count].Name), "user.test%d", Index);
LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], TestXattrs[Count].Name, DynamicBuffer, 10, 0));
Count += 1;
}
//
// Check the behavior of XATTR_CREATE.
//
LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.test0", NULL, 0));
LxtCheckEqual(Size, 10, "%d");
LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "user.test0", DynamicBuffer, 15, XATTR_CREATE), EEXIST);
LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.test0", NULL, 0));
LxtCheckEqual(Size, 10, "%d");
LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.new", DynamicBuffer, 15, XATTR_CREATE));
LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.new", NULL, 0));
LxtCheckEqual(Size, 15, "%d");
strncpy(TestXattrs[Count].Name, "user.new", sizeof(TestXattrs[Count].Name) - 1);
Count += 1;
//
// Check the behavior of XATTR_REPLACE.
//
LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.test0", NULL, 0));
LxtCheckEqual(Size, 10, "%d");
LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "user.new2", DynamicBuffer, 15, XATTR_REPLACE), ENODATA);
LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], "user.new2", NULL, 0), ENODATA);
LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.new", DynamicBuffer, 10, XATTR_REPLACE));
LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.new", NULL, 0));
LxtCheckEqual(Size, 10, "%d");
//
// Set a zero-length extended attribute with XATTR_REPLACE.
//
// N.B. Plan 9 does not support this, as it treats this operation like a
// remove.
//
if (g_LxtFsInfo.FsType != LxtFsTypePlan9)
{
LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.new", NULL, 0, XATTR_REPLACE));
LxtCheckErrnoZeroSuccess(LxtGetxattr(g_XattrPaths[0], "user.new", NULL, 0));
LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.new", "", 0, XATTR_REPLACE));
LxtCheckErrnoZeroSuccess(LxtGetxattr(g_XattrPaths[0], "user.new", NULL, 0));
}
//
// List the attributes.
//
TotalCount = Count;
for (Index = 0; Index < 10; Index += 1)
{
LxtCheckErrno(DynamicBufferSize = LxtListxattr(g_XattrPaths[0], 0, NULL));
LxtLogInfo("listxattr returned %d", DynamicBufferSize);
DynamicBuffer = realloc(DynamicBuffer, DynamicBufferSize);
if (DynamicBuffer == NULL)
{
Result = LXT_RESULT_FAILURE;
LxtLogError("realloc(%p %d) failed", DynamicBuffer, DynamicBufferSize);
goto ErrorExit;
}
//
// Ensure that the number of extended attributes returned matches the number created.
//
DynamicBufferSize = LxtListxattr(g_XattrPaths[0], DynamicBuffer, DynamicBufferSize);
if (DynamicBufferSize > 0)
{
break;
}
else if (errno != ERANGE)
{
LxtLogError("listxattr returned %d", errno);
goto ErrorExit;
}
//
// Sleep before retrying.
//
sleep(1);
}
if (DynamicBufferSize < 0)
{
LxtLogError("listxattr returned %d", errno);
Result = LXT_RESULT_FAILURE;
goto ErrorExit;
}
for (Index = 0; Index < DynamicBufferSize; Index += strlen(&DynamicBuffer[Index]) + 1)
{
for (NameIndex = 0; NameIndex < TotalCount; NameIndex += 1)
{
if (strcmp(TestXattrs[NameIndex].Name, &DynamicBuffer[Index]) == 0)
{
if (TestXattrs[NameIndex].Found != false)
{
LxtLogError("Duplicate attribute: %s", &DynamicBuffer[Index]);
Result = LXT_RESULT_FAILURE;
goto ErrorExit;
}
TestXattrs[NameIndex].Found = true;
break;
}
}
if (NameIndex == TotalCount)
{
LxtLogError("Unknown attribute in listing: %s", &DynamicBuffer[Index]);
Result = LXT_RESULT_FAILURE;
goto ErrorExit;
}
LxtRemovexattr(g_XattrPaths[0], &DynamicBuffer[Index]);
Count -= 1;
}
for (NameIndex = 0; NameIndex < TotalCount; NameIndex += 1)
{
if (TestXattrs[NameIndex].Found == false)
{
LxtLogError("Attribute missing from listing: %s", TestXattrs[NameIndex].Name);
Result = LXT_RESULT_FAILURE;
goto ErrorExit;
}
}
LxtCheckEqual(Count, 0, "%d");
LxtCheckEqual(LxtListxattr(g_XattrPaths[0], DynamicBuffer, DynamicBufferSize), 0, "%d");
//
// Ensure that two extended attributes with the same name but different
// cases can be created.
//
// N.B. DrvFs, WslFs and Plan 9 do not support this.
//
if (g_LxtFsInfo.Flags.DrvFsBehavior == 0)
{
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.foo", NULL, 0, 0));
LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.FOO", NULL, 0, 0));
LxtCheckErrno(Result = LxtListxattr(g_XattrPaths[0], Buffer, sizeof(Buffer)));
Count = 0;
for (Index = 0; Index < Result; Index += strlen(&Buffer[Index]) + 1)
{
LxtLogInfo("%s", &Buffer[Index]);
Count += 1;
}
LxtCheckEqual(Count, 2, "%d");
}
Result = LXT_RESULT_SUCCESS;
ErrorExit:
XattrTestDeletePaths(Fds);
if (DynamicBuffer != NULL)
{
free(DynamicBuffer);
}
return (int)Result;
}
int XattrAccessTest(PLXT_ARGS Args)
/*++
--*/
{
// Example system.posix_acl_access structure created by "setfacl -m u:root:r"
char AclAccess[] = {2, 0, 0, 0, 1, 0, 6, 0, 0xff, 0xff, 0xff, 0xff, 2, 0, 4, 0, 0, 0, 0, 0, 4, 0,
4, 0, 0xff, 0xff, 0xff, 0xff, 16, 0, 4, 0, 0xff, 0xff, 0xff, 0xff, 32, 0, 4, 0, -1, -1, -1, -1};
// Example security.capability structure created by "setcap cap_net_raw+ep"
char Capability[] = {1, 0, 0, 2, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char Buffer[1024];
struct __user_cap_data_struct CapData[2];
struct __user_cap_header_struct CapHeader;
int ChildPid;
int Fd;
ssize_t Result;
ChildPid = -1;
Fd = -1;
LxtCheckErrno(Fd = creat(LXT_XATTR_ACCESS_FILE_PATH, 0777));
/*
//
// TODO: Enable this test variation once extended attributes in the system
// namespace are supported.
//
//
// Set the system.posix_acl_access EA and validate access to get, set, and list.
//
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", AclAccess, sizeof(AclAccess), 0));
LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", Buffer, sizeof(Buffer)));
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000));
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0) {
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
LxtCheckErrno(setgid(LXT_XATTR_GID));
LxtCheckErrno(setuid(LXT_XATTR_UID));
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
CapData[CAP_TO_INDEX(CAP_FOWNER)].permitted |= CAP_TO_MASK(CAP_FOWNER);
CapData[0].effective = CapData[0].permitted;
CapData[1].effective = CapData[1].permitted;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("system.posix_acl_access"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", Buffer, sizeof(Buffer)));
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", AclAccess, sizeof(AclAccess), 0));
//
// Drop the CAP_FOWNER capability and attempt again.
//
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("system.posix_acl_access"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", Buffer, sizeof(Buffer)));
LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", AclAccess, sizeof(AclAccess), 0),
EPERM); Result = LXT_RESULT_SUCCESS; goto ErrorExit;
}
//
// Wait for the child to exit.
//
LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000));
//
// Set the system.posix_acl_access EA and ensure it is able to be queried.
//
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", AclAccess, sizeof(AclAccess), 0));
LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", Buffer, sizeof(Buffer)));
LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, LXT_XATTR_UID, LXT_XATTR_GID));
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0) {
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
LxtCheckErrno(setgid(LXT_XATTR_GID));
LxtCheckErrno(setuid(LXT_XATTR_UID));
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);
CapData[0].effective = CapData[0].permitted;
CapData[1].effective = CapData[1].permitted;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("system.posix_acl_access"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", Buffer, sizeof(Buffer)));
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", AclAccess, sizeof(AclAccess), 0));
//
// Drop the CAP_DAC_OVERRIDE capability and attempt again.
//
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("system.posix_acl_access"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", Buffer, sizeof(Buffer)));
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", AclAccess, sizeof(AclAccess), 0));
Result = LXT_RESULT_SUCCESS;
goto ErrorExit;
}
//
// Wait for the child to exit.
//
LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
LxtCheckErrno(LxtRemovexattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access"));
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0777));
*/
//
// Set the security.capability EA and validate access to get, set, and list.
//
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Capability, sizeof(Capability), 0));
LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer)));
LxtCheckEqual(Result, sizeof(Capability), "%Iu");
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000));
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
LxtCheckErrno(setgid(LXT_XATTR_GID));
LxtCheckErrno(setuid(LXT_XATTR_UID));
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
CapData[CAP_TO_INDEX(CAP_SETFCAP)].permitted |= CAP_TO_MASK(CAP_SETFCAP);
CapData[0].effective = CapData[0].permitted;
CapData[1].effective = CapData[1].permitted;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("security.capability"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer)));
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Capability, sizeof(Capability), 0));
//
// Drop the CAP_SETFCAP capability and attempt again.
//
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("security.capability"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer)));
LxtCheckEqual(Result, sizeof(Capability), "%Iu");
LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Capability, sizeof(Capability), 0), EPERM);
Result = LXT_RESULT_SUCCESS;
goto ErrorExit;
}
//
// Wait for the child to exit.
//
LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
//
// Validate that the security.capability EA is removed when the file changes owners.
//
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Capability, sizeof(Capability), 0));
LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer)));
LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, 0, 0));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, 0, "%Iu");
LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer)), ENODATA);
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Capability, sizeof(Capability), 0));
LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer)));
LxtCheckEqual(Result, sizeof(Capability), "%Iu");
LxtCheckErrno(LxtRemovexattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability"));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, 0, "%Iu");
LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer)), ENODATA);
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0777));
//
// Set the security.foo EA and validate access to get, set, and list.
//
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Capability, sizeof(Capability), 0));
LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer)));
LxtCheckEqual(Result, sizeof(Capability), "%Iu");
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000));
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
LxtCheckErrno(setgid(LXT_XATTR_GID));
LxtCheckErrno(setuid(LXT_XATTR_UID));
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
CapData[CAP_TO_INDEX(CAP_SYS_ADMIN)].permitted |= CAP_TO_MASK(CAP_SYS_ADMIN);
CapData[0].effective = CapData[0].permitted;
CapData[1].effective = CapData[1].permitted;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("security.foo"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer)));
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Capability, sizeof(Capability), 0));
//
// Drop the CAP_SYS_ADMIN capability and attempt again.
//
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("security.foo"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer)));
LxtCheckEqual(Result, sizeof(Capability), "%Iu");
LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Capability, sizeof(Capability), 0), EPERM);
Result = LXT_RESULT_SUCCESS;
goto ErrorExit;
}
//
// Wait for the child to exit.
//
LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
//
// Validate that the security.foo EA is not removed when the file changes owners.
//
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Capability, sizeof(Capability), 0));
LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer)));
LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, 0, 0));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("security.foo"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer)));
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Capability, sizeof(Capability), 0));
LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer)));
LxtCheckEqual(Result, sizeof(Capability), "%Iu");
LxtCheckErrno(LxtRemovexattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo"));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, 0, "%Iu");
LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer)), ENODATA);
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0777));
//
// Set the user.foo EA and ensure it is able to be queried.
//
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0));
LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0));
LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, LXT_XATTR_UID, LXT_XATTR_GID));
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0700));
//
// Fork and change the child to a UID that does not own the file.
//
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
LxtCheckErrno(setuid(LXT_XATTR_UID));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("user.foo"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0));
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0));
goto ErrorExit;
}
//
// Wait for the child to exit.
//
LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0070));
//
// Fork and change the child to a UID that does not own the file.
//
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
//
// First try with the CAP_DAC_OVERRIDE capability.
//
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
LxtCheckErrno(setuid(LXT_XATTR_UID + 1));
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);
CapData[0].effective = CapData[0].permitted;
CapData[1].effective = CapData[1].permitted;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("user.foo"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0));
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0));
//
// Drop the CAP_DAC_OVERRIDE capability and attempt again.
//
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("user.foo"), "%Iu");
LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0), EACCES);
LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0), EACCES);
goto ErrorExit;
}
//
// Wait for the child to exit.
//
LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0007));
//
// Fork and change the child to a UID that does not own the file with the
// other bits set.
//
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
LxtCheckErrno(setuid(LXT_XATTR_UID + 1));
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("user.foo"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0));
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0));
goto ErrorExit;
}
//
// Wait for the child to exit.
//
LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0002));
//
// Fork and change the child to a UID that does not own the file with the
// other bits set.
//
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
LxtCheckErrno(setuid(LXT_XATTR_UID + 1));
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("user.foo"), "%Iu");
LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0), EACCES);
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0));
goto ErrorExit;
}
//
// Wait for the child to exit.
//
LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0004));
//
// Fork and change the child to a UID that does not own the file with the
// other bits set.
//
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
LxtCheckErrno(setuid(LXT_XATTR_UID + 1));
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("user.foo"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0));
LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0), EACCES);
goto ErrorExit;
}
//
// Wait for the child to exit.
//
LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
LxtCheckErrno(LxtRemovexattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo"));
LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, 0, 0));
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0777));
//
// Test the trusted namespace, the caller requires the CAP_SYS_ADMIN
// capability to read to write EA's in the trusted namespace.
//
Buffer[0] = 'y';
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "trusted.overlay.opaque", Buffer, 1, 0));
LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "trusted.overlay.opaque", Buffer, sizeof(Buffer)));
LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000));
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
CapData[CAP_TO_INDEX(CAP_SYS_ADMIN)].permitted |= CAP_TO_MASK(CAP_SYS_ADMIN);
CapData[0].effective = CapData[0].permitted;
CapData[1].effective = CapData[1].permitted;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
LxtCheckEqual(Result, sizeof("trusted.overlay.opaque"), "%Iu");
LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "trusted.overlay.opaque", NULL, 0));
LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "trusted.overlay.opaque", Buffer, 1, 0));
//
// Drop the CAP_SYS_ADMIN capability and attempt again.
//
// N.B. Unlike other namespaces, names in the trusted namespace will
// not be returned if the caller does not have the correct permission.
// This is file system specific, and Plan 9 does not do this.
//
memset(&CapData, 0, sizeof(CapData));
memset(&CapHeader, 0, sizeof(CapHeader));
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));
if ((g_LxtFsInfo.FsType == LxtFsTypePlan9) || (g_LxtFsInfo.FsType == LxtFsTypeVirtioFs))
{
LxtCheckEqual(Result, sizeof("trusted.overlay.opaque"), "%Iu");
}
else
{
LxtCheckEqual(Result, 0, "%Iu");
}
LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, "trusted.overlay.opaque", NULL, 0), ENODATA);
LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "trusted.overlay.opaque", Buffer, 1, 0), EPERM);
goto ErrorExit;
}
//
// Wait for the child to exit.
//
LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
Result = LXT_RESULT_SUCCESS;
ErrorExit:
if (Fd != 0)
{
LxtClose(Fd);
}
if (ChildPid == 0)
{
_exit(Result);
}
unlink(LXT_XATTR_ACCESS_FILE_PATH);
return (int)Result;
}