mirror of
https://github.com/microsoft/WSL.git
synced 2025-07-03 07:23:20 +00:00

Many Microsoft employees have contributed to the Windows Subsystem for Linux, this commit is the result of their work since 2016. The entire history of the Windows Subsystem for Linux can't be shared here, but here's an overview of WSL's history after it moved to it own repository in 2021: Number of commits on the main branch: 2930 Number of contributors: 31 Head over https://github.com/microsoft/WSL/releases for a more detailed history of the features added to WSL since 2021.
469 lines
12 KiB
C
469 lines
12 KiB
C
/*++
|
|
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
utimensat.c
|
|
|
|
Abstract:
|
|
|
|
This file contains test routines for the utimensat and utimes system
|
|
calls.
|
|
|
|
--*/
|
|
|
|
#include "lxtcommon.h"
|
|
#include "unittests.h"
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
|
|
#if !defined(__amd64__) && !defined(__aarch64__)
|
|
#include <sys/capability.h>
|
|
#endif
|
|
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <libmount/libmount.h>
|
|
#include "lxtfs.h"
|
|
#include "lxtmount.h"
|
|
|
|
#define LXT_NAME "Utimensat"
|
|
|
|
LXT_VARIATION_HANDLER TestBasicFunctions;
|
|
LXT_VARIATION_HANDLER TestUtimes;
|
|
LXT_VARIATION_HANDLER TestInvalid;
|
|
LXT_VARIATION_HANDLER TestPermissions;
|
|
|
|
//
|
|
// Global constants
|
|
//
|
|
|
|
const char ChildFileName[] = "testfile";
|
|
const char ChildFileFullPath[] = "/data/test_utimensat/testfile";
|
|
const char DirPath[] = "/data/test_utimensat";
|
|
const char LinkFileName[] = "testlink";
|
|
const char LinkFullPath[] = "/data/test_utimensat/testlink";
|
|
|
|
static const LXT_VARIATION g_LxtVariations[] = {
|
|
{"Test basic functionality", TestBasicFunctions},
|
|
{"Test utimes", TestUtimes},
|
|
{"Test invalid parameters", TestInvalid},
|
|
{"Test permissions", TestPermissions},
|
|
};
|
|
|
|
typedef struct timespec TIMESPEC, *PTIMESPEC;
|
|
|
|
enum
|
|
{
|
|
PermissionsRoot,
|
|
PermissionsOmit,
|
|
PermissionsNow,
|
|
PermissionsSet,
|
|
PermissionsNowOmit,
|
|
PermissionsOmitNow,
|
|
PermissionsBeyondMax
|
|
};
|
|
|
|
typedef struct _PERMISSIONS_TEST_CASE
|
|
{
|
|
TIMESPEC SetTime[2];
|
|
} PERMISSIONS_TEST_CASE, *PPERMISSIONS_TEST_CASE;
|
|
|
|
PERMISSIONS_TEST_CASE PermissionsTest[] = {
|
|
{{{1234567, 98765432}, {4444444, 5555555}}},
|
|
{{{1234567, UTIME_OMIT}, {4444444, UTIME_OMIT}}},
|
|
{{{1234567, UTIME_NOW}, {4444444, UTIME_NOW}}},
|
|
{{{1234567, 11111111}, {4444444, 22222222}}},
|
|
{{{1234567, UTIME_NOW}, {4444444, UTIME_OMIT}}},
|
|
{{{1234567, UTIME_OMIT}, {4444444, UTIME_NOW}}}};
|
|
|
|
typedef struct _PERMISSIONS_FILE
|
|
{
|
|
const char* Filename;
|
|
uid_t Owner;
|
|
mode_t Mode;
|
|
int Changeable[PermissionsBeyondMax];
|
|
} PERMISSIONS_FILE, *PPERMISSIONS_FILE;
|
|
|
|
enum
|
|
{
|
|
OwnerMatchingThread = 1000,
|
|
OwnerNotMatchingThread
|
|
};
|
|
|
|
PERMISSIONS_FILE PermissionsFiles[] = {
|
|
{"OwnedAndWritable", OwnerMatchingThread, 0666, {0, 0, 0, 0, 0, 0}},
|
|
{"OwnedReadonly", OwnerMatchingThread, 0444, {0, 0, 0, 0, 0, 0}},
|
|
{"OwnedWriteonly", OwnerMatchingThread, 0222, {0, 0, 0, 0, 0, 0}},
|
|
{"UnownedAndWritable", OwnerNotMatchingThread, 0666, {0, 0, 0, EPERM, EPERM, EPERM}},
|
|
{"UnownedAndReadonly", OwnerNotMatchingThread, 0444, {0, 0, EACCES, EPERM, EPERM, EPERM}},
|
|
{"UnownedAndWriteonly", OwnerNotMatchingThread, 0222, {0, 0, 0, EPERM, EPERM, EPERM}}};
|
|
|
|
int TestBasicFunctions(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine executes basic test functions, including setting timestamps
|
|
to a range of values including UTIME_NOW or UTIME_OMIT, on a range of
|
|
different ways to specify the target file, including relative, absolute,
|
|
descriptor, via symbolic link, on a symbolic link, and validating that
|
|
the expected outcome occurs.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies a pointer to test arguments.
|
|
|
|
Return Value:
|
|
|
|
0 if all variations complete successfully, -1 if they do not.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Flags;
|
|
int Result;
|
|
|
|
//
|
|
// If running on wslfs, timestamps use NT precision.
|
|
//
|
|
|
|
Flags = 0;
|
|
if (g_LxtFsInfo.Flags.DrvFsBehavior != 0)
|
|
{
|
|
LxtLogInfo("Using NT precision timestamps.");
|
|
Flags = FS_UTIME_NT_PRECISION;
|
|
}
|
|
|
|
Result = LxtFsUtimeBasicCommon(DirPath, Flags);
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int TestUtimes(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine executes test functions specific to the utimes syscall.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies a pointer to test arguments.
|
|
|
|
Return Value:
|
|
|
|
0 if all variations complete successfully, -1 if they do not.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Fd;
|
|
int Result;
|
|
struct timeval SetTimeVal[2];
|
|
|
|
LxtCheckErrno(Fd = creat(ChildFileName, 0777));
|
|
memset(SetTimeVal, 0, sizeof(SetTimeVal));
|
|
LxtCheckResult(utimes(ChildFileName, SetTimeVal));
|
|
LxtCheckResult(utimes(ChildFileName, NULL));
|
|
|
|
//
|
|
// Invalid parameter variations.
|
|
//
|
|
|
|
memset(SetTimeVal, 0, sizeof(SetTimeVal));
|
|
SetTimeVal[1].tv_usec = 999999 + 1;
|
|
LxtCheckErrnoFailure(utimes(ChildFileName, SetTimeVal), EINVAL);
|
|
memset(SetTimeVal, 0, sizeof(SetTimeVal));
|
|
SetTimeVal[1].tv_usec = 999999 + 1;
|
|
LxtCheckErrnoFailure(utimes(ChildFileName, SetTimeVal), EINVAL);
|
|
|
|
ErrorExit:
|
|
if (Fd != -1)
|
|
{
|
|
LxtClose(Fd);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int TestInvalid(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine executes test functions which are expected to fail, and
|
|
validates that they fail with the correct error. This includes invalid
|
|
descriptors, flags, nonexistent files, or invalid timestamps.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies a pointer to test arguments.
|
|
|
|
Return Value:
|
|
|
|
0 if all variations complete successfully, -1 if they do not.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Fd;
|
|
int Result;
|
|
TIMESPEC SetTime[2];
|
|
struct timeval SetTimeVal[2];
|
|
|
|
Fd = -1;
|
|
memset(SetTime, 0, sizeof(SetTime));
|
|
memset(SetTimeVal, 0, sizeof(SetTimeVal));
|
|
|
|
LxtCheckErrnoFailure(utimensat(12345, ChildFileName, SetTime, 0), EBADF);
|
|
|
|
LxtCheckErrnoFailure(utimensat(0, ChildFileFullPath, SetTime, 0x70000000), EINVAL);
|
|
|
|
LxtCheckErrnoFailure(utimensat(AT_FDCWD, "bogus", SetTime, 0), ENOENT);
|
|
|
|
LxtCheckResult(Fd = open(ChildFileFullPath, O_RDWR));
|
|
|
|
LxtCheckErrnoFailure(utimensat(Fd, "bogus", SetTime, 0), ENOTDIR);
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wnonnull"
|
|
|
|
LxtCheckErrnoFailure(utimensat(Fd, NULL, SetTime, AT_SYMLINK_NOFOLLOW), EINVAL);
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
|
|
SetTime[0].tv_nsec = 1000000000;
|
|
LxtCheckErrnoFailure(utimensat(0, ChildFileFullPath, SetTime, 0), EINVAL);
|
|
|
|
SetTimeVal[0].tv_usec = 1000000;
|
|
LxtCheckErrnoFailure(utimes(ChildFileFullPath, SetTimeVal), EINVAL);
|
|
|
|
SetTimeVal[0].tv_usec = UTIME_NOW;
|
|
LxtCheckErrnoFailure(utimes(ChildFileFullPath, SetTimeVal), EINVAL);
|
|
|
|
SetTimeVal[0].tv_usec = UTIME_OMIT;
|
|
LxtCheckErrnoFailure(utimes(ChildFileFullPath, SetTimeVal), EINVAL);
|
|
|
|
SetTimeVal[0].tv_usec = 0;
|
|
LxtCheckErrnoFailure(utimes("bogus", SetTimeVal), ENOENT);
|
|
Result = 0;
|
|
|
|
ErrorExit:
|
|
if (Fd != -1)
|
|
{
|
|
LxtClose(Fd);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int TestPermissions(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine executes test functions that should succeed or fail
|
|
depending on inputs and user states, and validates that they fail with
|
|
the expected code. In particular, validates that callers without
|
|
privilege or file ownership cannot set file timestamps, and those
|
|
without privilege, file ownership or write access cannot set timestamps
|
|
to current.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies a pointer to test arguments.
|
|
|
|
Return Value:
|
|
|
|
0 if all variations complete successfully, -1 if they do not.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
pid_t ChildPid;
|
|
int Fd;
|
|
unsigned int FileNumber;
|
|
int Result;
|
|
unsigned int TestCase;
|
|
|
|
Result = chdir(DirPath);
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("Could not change directory: %d", Result);
|
|
Result = -1;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
for (FileNumber = 0; FileNumber < LXT_COUNT_OF(PermissionsFiles); FileNumber += 1)
|
|
{
|
|
Fd = open(PermissionsFiles[FileNumber].Filename, O_CREAT | O_RDWR, PermissionsFiles[FileNumber].Mode);
|
|
if ((Fd < 0) && (errno != EEXIST))
|
|
{
|
|
LxtLogError("Could not create file: %d", Result);
|
|
Result = -1;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Result = fchown(Fd, PermissionsFiles[FileNumber].Owner, PermissionsFiles[FileNumber].Owner);
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("Could not change owner: %d", Result);
|
|
Result = -1;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Result = fchmod(Fd, PermissionsFiles[FileNumber].Mode);
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("Could not change mode: %d", Result);
|
|
Result = -1;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
close(Fd);
|
|
Fd = 0;
|
|
}
|
|
|
|
//
|
|
// While still root, check that we can perform all of the things it should
|
|
// be able to do.
|
|
//
|
|
|
|
TestCase = PermissionsRoot;
|
|
|
|
for (FileNumber = 0; FileNumber < LXT_COUNT_OF(PermissionsFiles); FileNumber += 1)
|
|
{
|
|
Result = utimensat(AT_FDCWD, PermissionsFiles[FileNumber].Filename, PermissionsTest[TestCase].SetTime, 0);
|
|
if (PermissionsFiles[FileNumber].Changeable[TestCase] == 0)
|
|
{
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("Could not change time as expected, file %s case %i, Result %d", PermissionsFiles[FileNumber].Filename, TestCase, Result);
|
|
Result = -1;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((Result == 0) || (errno != PermissionsFiles[FileNumber].Changeable[TestCase]))
|
|
{
|
|
|
|
LxtLogError(
|
|
"Could change time, or failed with the wrong code, file %s case %i, Result %d, errno %i, expected %i",
|
|
PermissionsFiles[FileNumber].Filename,
|
|
TestCase,
|
|
Result,
|
|
errno,
|
|
PermissionsFiles[FileNumber].Changeable[TestCase]);
|
|
Result = -1;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set to user that should owns some of the files, and lacks privilege
|
|
// to access others
|
|
//
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
Result = setuid(OwnerMatchingThread);
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("Could not setuid: %d", Result);
|
|
Result = -1;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Advance to the next test (the first one was performed above), and continue
|
|
//
|
|
|
|
for (TestCase += 1; TestCase < PermissionsBeyondMax; TestCase += 1)
|
|
{
|
|
for (FileNumber = 0; FileNumber < LXT_COUNT_OF(PermissionsFiles); FileNumber += 1)
|
|
{
|
|
Result = utimensat(AT_FDCWD, PermissionsFiles[FileNumber].Filename, PermissionsTest[TestCase].SetTime, 0);
|
|
if (PermissionsFiles[FileNumber].Changeable[TestCase] == 0)
|
|
{
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("Could not change time as expected, file %s case %i, Result %d", PermissionsFiles[FileNumber].Filename, TestCase, Result);
|
|
Result = -1;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((Result == 0) || (errno != PermissionsFiles[FileNumber].Changeable[TestCase]))
|
|
{
|
|
|
|
LxtLogError(
|
|
"Could change time, or failed with the wrong code, file %s case %i, Result %d, errno %i, expected %i",
|
|
PermissionsFiles[FileNumber].Filename,
|
|
TestCase,
|
|
Result,
|
|
errno,
|
|
PermissionsFiles[FileNumber].Changeable[TestCase]);
|
|
Result = -1;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
exit(0);
|
|
}
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));
|
|
Result = 0;
|
|
|
|
ErrorExit:
|
|
for (FileNumber = 0; FileNumber < LXT_COUNT_OF(PermissionsFiles); FileNumber += 1)
|
|
{
|
|
unlink(PermissionsFiles[FileNumber].Filename);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int UtimensatTestEntry(int Argc, char* Argv[])
|
|
{
|
|
LXT_ARGS Args;
|
|
int Fd;
|
|
int Result;
|
|
|
|
Fd = -1;
|
|
LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));
|
|
LxtCheckResult(LxtFsUtimeCreateTestFiles(DirPath, 0));
|
|
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
|
|
|
|
ErrorExit:
|
|
if (Fd >= 0)
|
|
{
|
|
close(Fd);
|
|
}
|
|
|
|
LxtFsUtimeCleanupTestFiles(DirPath);
|
|
LxtUninitialize();
|
|
return !LXT_SUCCESS(Result);
|
|
}
|