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.
536 lines
12 KiB
C
536 lines
12 KiB
C
/*++
|
|
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
flock.c
|
|
|
|
Abstract:
|
|
|
|
This file is a flock test.
|
|
|
|
--*/
|
|
|
|
#include "lxtcommon.h"
|
|
#include "unittests.h"
|
|
#include <sys/file.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#define LXT_NAME "Flock"
|
|
|
|
int FnctlLockingVariation0(PLXT_ARGS Args);
|
|
|
|
int FlockVariation0(PLXT_ARGS Args);
|
|
|
|
//
|
|
// Global constants
|
|
//
|
|
|
|
static const LXT_VARIATION g_LxtVariations[] = {{"Flock", FlockVariation0}, {"Fcntl Locking", FnctlLockingVariation0}};
|
|
|
|
int FlockTestEntry(int Argc, char* Argv[])
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
LXT_ARGS Args;
|
|
int Result;
|
|
|
|
LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));
|
|
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
|
|
|
|
ErrorExit:
|
|
LxtUninitialize();
|
|
return !LXT_SUCCESS(Result);
|
|
}
|
|
|
|
int FnctlLockingVariation0(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tests file locking through the FCNTL system call.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies a pointer to the test arguments.
|
|
|
|
Return Value:
|
|
|
|
0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[10];
|
|
char ByteAlignedBuffer[sizeof(struct flock) + sizeof(struct flock64)];
|
|
int ChildPid;
|
|
int FileDescriptor;
|
|
struct flock* LockDescriptor;
|
|
struct flock64* LockDescriptor64;
|
|
int Result;
|
|
|
|
FileDescriptor = -1;
|
|
ChildPid = -1;
|
|
|
|
//
|
|
// Initialize the file for this test.
|
|
//
|
|
|
|
LxtCheckErrno(FileDescriptor = open("/data/test/fcntl_lock_test.bin", O_RDWR | O_CREAT, S_IRWXU));
|
|
|
|
LxtCheckErrno(write(FileDescriptor, Buffer, 10));
|
|
|
|
//
|
|
// Set the lock descriptor.
|
|
//
|
|
|
|
memset(ByteAlignedBuffer, 0, sizeof(ByteAlignedBuffer));
|
|
LockDescriptor = (struct flock*)&ByteAlignedBuffer[1];
|
|
LockDescriptor->l_type = F_WRLCK;
|
|
LockDescriptor->l_whence = SEEK_SET;
|
|
LockDescriptor->l_start = 0;
|
|
LockDescriptor->l_len = 10;
|
|
|
|
//
|
|
// Get the current lock state.
|
|
//
|
|
|
|
LxtLogInfo("Fnctl locking - Checking that lock can be set.");
|
|
LxtCheckErrno(fcntl(FileDescriptor, F_GETLK, LockDescriptor));
|
|
LxtCheckEqual(LockDescriptor->l_type, F_UNLCK, "%x");
|
|
|
|
//
|
|
// Set the lock.
|
|
//
|
|
|
|
LxtLogInfo("Fcntl locking - Setting the read lock by the parent process");
|
|
memset(ByteAlignedBuffer, 0, sizeof(ByteAlignedBuffer));
|
|
LockDescriptor->l_type = F_RDLCK;
|
|
LxtCheckErrno(fcntl(FileDescriptor, F_SETLK, LockDescriptor));
|
|
|
|
//
|
|
// Now change the lock to be a write lock.
|
|
//
|
|
|
|
memset(ByteAlignedBuffer, 0, sizeof(ByteAlignedBuffer));
|
|
LockDescriptor64 = (struct flock64*)&ByteAlignedBuffer[1];
|
|
LockDescriptor64->l_type = F_WRLCK;
|
|
LockDescriptor64->l_whence = SEEK_SET;
|
|
LockDescriptor64->l_start = 0;
|
|
LockDescriptor64->l_len = 10;
|
|
|
|
//
|
|
// Set the lock.
|
|
//
|
|
|
|
LxtLogInfo("Fcntl locking - Setting the write lock with 64 bit set lock");
|
|
LxtCheckErrno(fcntl(FileDescriptor, F_SETLK64, LockDescriptor64));
|
|
|
|
//
|
|
// Fork the process.
|
|
//
|
|
|
|
LxtLogInfo("Creating child process to test the lock.");
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
|
|
//
|
|
// Ensure that the lock was correctly set before. The lock descriptor is
|
|
// correctly set from before. The test expects to see the lock type be
|
|
// changed to exlusive, WRLCK, though.
|
|
//
|
|
|
|
LxtLogInfo("Fcntl child locking - Reading lock type");
|
|
LxtCheckErrno(fcntl(FileDescriptor, F_GETLK, LockDescriptor));
|
|
LxtCheckEqual(LockDescriptor->l_type, F_WRLCK, "%X");
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Result = LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS);
|
|
|
|
ErrorExit:
|
|
if (FileDescriptor != 0)
|
|
{
|
|
close(FileDescriptor);
|
|
}
|
|
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(Result);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int FlockVariation0(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
int Result;
|
|
int FileDescriptor1;
|
|
int FileDescriptor2;
|
|
int FileDescriptor3;
|
|
int DupedDescriptor;
|
|
int ChildPid;
|
|
int Index;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
FileDescriptor1 = -1;
|
|
FileDescriptor2 = -1;
|
|
FileDescriptor3 = -1;
|
|
DupedDescriptor = -1;
|
|
ChildPid = -1;
|
|
|
|
//
|
|
// Open a file that will be locked.
|
|
//
|
|
|
|
FileDescriptor1 = open("/data/test/flock_test.bin", O_RDWR | O_CREAT, S_IRWXU);
|
|
|
|
if (FileDescriptor1 == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Could not create test file! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
FileDescriptor2 = open("/data/test/flock_test.bin", O_RDWR | O_CREAT, S_IRWXU);
|
|
|
|
if (FileDescriptor2 == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Could not create test file! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
Result = flock(FileDescriptor1, LOCK_EX);
|
|
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Lock failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
FileDescriptor3 = open("/data/test/flock_test.bin", O_RDWR | O_CREAT, S_IRWXU);
|
|
|
|
if (FileDescriptor3 == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Could not create test file! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Lock the file from another descriptor (non-blocking) and it should fail
|
|
// accordingly.
|
|
//
|
|
|
|
Result = flock(FileDescriptor2, LOCK_EX | LOCK_NB);
|
|
|
|
if (Result == 0)
|
|
{
|
|
Result = -1;
|
|
LxtLogError("Lock succeeded but should have failed!");
|
|
goto cleanup;
|
|
}
|
|
|
|
Result = errno;
|
|
if (Result != EWOULDBLOCK)
|
|
{
|
|
LxtLogError("Lock failed but with wrong error! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Dupe the owner descriptor and lock the file shared. This should convert
|
|
// the file to shared.
|
|
//
|
|
|
|
DupedDescriptor = dup(FileDescriptor1);
|
|
|
|
if (DupedDescriptor < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Could not dup the descriptor! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
for (Index = 0; Index < 2; Index += 1)
|
|
{
|
|
|
|
Result = flock(DupedDescriptor, LOCK_EX);
|
|
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Lock exclusive conversion failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
Result = flock(DupedDescriptor, LOCK_SH);
|
|
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Lock shared conversion failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The lock is now owned shared by descriptor1 (via duped descriptor) so now
|
|
// descriptor 2 should be able to acquire it shared.
|
|
//
|
|
|
|
Result = flock(FileDescriptor2, LOCK_SH | LOCK_NB);
|
|
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Lock shared failed for descriptor2! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Unlock via descriptor1. That leaves just descriptor2 shared.
|
|
//
|
|
|
|
Result = flock(FileDescriptor1, LOCK_UN);
|
|
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Unlock failed for descriptor1! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Fork to create another thread to wait for lock.
|
|
//
|
|
|
|
for (Index = 0; Index < 2; Index += 1)
|
|
{
|
|
|
|
ChildPid = fork();
|
|
|
|
if (ChildPid == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Fork failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (ChildPid == 0)
|
|
{
|
|
int FileDescriptor;
|
|
|
|
if (Index == 0)
|
|
{
|
|
FileDescriptor = FileDescriptor1;
|
|
}
|
|
else
|
|
{
|
|
FileDescriptor = FileDescriptor3;
|
|
}
|
|
|
|
//
|
|
// Wait for exclusive lock.
|
|
//
|
|
|
|
close(FileDescriptor2);
|
|
FileDescriptor2 = -1;
|
|
|
|
LxtLogInfo("C%u: Waiting for lock on FileDescriptor...", Index);
|
|
|
|
Result = flock(FileDescriptor, LOCK_EX);
|
|
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Lock acquire failed for descriptor1! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("C%u: Lock acquired on FileDescriptor...", Index);
|
|
|
|
Result = flock(FileDescriptor, LOCK_UN);
|
|
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Unlock failed for descriptor1! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("C%u: Sleeping 3 secs...", Index);
|
|
|
|
usleep(3 * 1000 * 1000);
|
|
|
|
LxtLogInfo("C%u: Waiting for lock shared on FileDescriptor...", Index);
|
|
|
|
Result = flock(FileDescriptor, LOCK_SH);
|
|
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Lock acquire failed for descriptor1! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("C%u: Lock acquired on FileDescriptor...", Index);
|
|
|
|
Result = flock(FileDescriptor, LOCK_UN);
|
|
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Unlock failed for descriptor1! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (Index == 0)
|
|
{
|
|
Result = LXT_RESULT_SUCCESS;
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("C%u: Sleeping 3 secs...", Index);
|
|
|
|
usleep(3 * 1000 * 1000);
|
|
|
|
LxtLogInfo("C%u: Waiting for lock exclusive to be terminated...", Index);
|
|
|
|
Result = flock(FileDescriptor, LOCK_EX);
|
|
|
|
if (Result == 0)
|
|
{
|
|
Result = -1;
|
|
LxtLogError("Lock acquisition succeeded but EINTR expected!");
|
|
goto cleanup;
|
|
}
|
|
|
|
Result = errno;
|
|
|
|
if (Result != EINTR)
|
|
{
|
|
LxtLogError("Lock acquisition failed but not with EINTR! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
goto cleanup;
|
|
}
|
|
}
|
|
LxtLogInfo("P: Waiting 3 seconds before releasing lock shared...");
|
|
|
|
usleep(3 * 1000 * 1000);
|
|
|
|
Result = flock(FileDescriptor2, LOCK_UN);
|
|
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Unlock failed for descriptor2! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
usleep(1 * 1000 * 1000);
|
|
|
|
LxtLogInfo("P: Waiting to acquire lock exclusive...");
|
|
|
|
Result = flock(FileDescriptor2, LOCK_EX);
|
|
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Lock exclusive failed for descriptor2! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("P: Acquired lock exclusive.");
|
|
|
|
LxtLogInfo("P: Sleeping 5 secs...");
|
|
|
|
usleep(5 * 1000 * 1000);
|
|
|
|
LxtLogInfo("P: Releasing lock exclusive...");
|
|
Result = flock(FileDescriptor2, LOCK_UN);
|
|
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Unlock failed for descriptor2! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("P: Waiting to acquire lock shared...");
|
|
|
|
Result = flock(FileDescriptor2, LOCK_SH);
|
|
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Lock shared failed for descriptor2! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("P: Sleeping 5 secs...");
|
|
|
|
usleep(5 * 1000 * 1000);
|
|
|
|
//
|
|
// Force the child's file lock wait to be interrupted by a signal.
|
|
//
|
|
|
|
kill(ChildPid, SIGKILL);
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
if (FileDescriptor1 != -1)
|
|
{
|
|
close(FileDescriptor1);
|
|
}
|
|
|
|
if (FileDescriptor2 != -1)
|
|
{
|
|
close(FileDescriptor2);
|
|
}
|
|
|
|
if (DupedDescriptor != -1)
|
|
{
|
|
close(DupedDescriptor);
|
|
}
|
|
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(0);
|
|
}
|
|
|
|
return Result;
|
|
}
|