mirror of
https://github.com/microsoft/WSL.git
synced 2025-07-03 15:23:22 +00:00
1294 lines
50 KiB
C
1294 lines
50 KiB
C
![]() |
/*++
|
||
|
|
||
|
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;
|
||
|
}
|