mirror of
https://github.com/microsoft/WSL.git
synced 2025-07-04 07:43:21 +00:00
537 lines
12 KiB
C
537 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;
|
||
|
}
|