WSL/test/linux/unit_tests/flock.c

537 lines
12 KiB
C
Raw Permalink Normal View History

/*++
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;
}