summaryrefslogtreecommitdiff
path: root/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/tests/palsuite/filemapping_memmgt/UnlockFile')
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/CMakeLists.txt7
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/UnlockFile.h112
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/helper.c92
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/test1.c154
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/test2.c154
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/helper.c103
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/test3.c142
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/test4.c187
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/testinfo.dat16
16 files changed, 1127 insertions, 0 deletions
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/CMakeLists.txt
new file mode 100644
index 0000000000..a3847f8ca9
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/CMakeLists.txt
@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+add_subdirectory(test4)
+
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/UnlockFile.h b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/UnlockFile.h
new file mode 100644
index 0000000000..8fce2695a7
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/UnlockFile.h
@@ -0,0 +1,112 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================
+**
+** Source: UnLockFile.h
+**
+** Purpose: This header file has a RunHelper method which will be used to
+** start a child proccess in many LockFile testcases. The CreateAndLockFile
+** method Creates a file and calls LockFile upon it. And the two Signal
+** methods are used for IPC.
+**
+**
+**============================================================*/
+
+#include <palsuite.h>
+
+HANDLE CreateAndLockFile(HANDLE TheFile, char* FileName, char* WriteBuffer,
+ DWORD LockStart, DWORD LockLength)
+{
+ DWORD BytesWritten;
+
+ TheFile = CreateFile(FileName,
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (TheFile == INVALID_HANDLE_VALUE)
+ {
+ Fail("ERROR: Could not open file '%s' with CreateFile. "
+ "GetLastError() returned %d.\n",FileName,GetLastError());
+ }
+
+ if(WriteFile(TheFile, WriteBuffer,
+ strlen(WriteBuffer),&BytesWritten, NULL) == 0)
+ {
+ Fail("ERROR: WriteFile has failed. It returned 0 when we "
+ "attempted to write to the file '%s'. GetLastError() "
+ "returned %d.\n",FileName,GetLastError());
+ }
+
+ if(FlushFileBuffers(TheFile) == 0)
+ {
+ Fail("ERROR: FlushFileBuffers returned failure. GetLastError() "
+ "returned %d.\n",GetLastError());
+ }
+
+ if(LockFile(TheFile, LockStart, 0, LockLength, 0) == 0)
+ {
+ Fail("ERROR: LockFile failed. GetLastError returns %d.\n",
+ GetLastError());
+ }
+
+ return TheFile;
+}
+
+void SignalAndBusyWait(HANDLE TheFile)
+{
+ int size;
+ DWORD BytesWritten;
+
+ size = GetFileSize(TheFile,NULL)+1;
+
+ if(SetFilePointer(TheFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
+ {
+ Fail("ERROR: SetFilePointer was unable to set the pointer to the "
+ "end of the file. GetLastError() returned %d.\n",GetLastError());
+ }
+
+ if(WriteFile(TheFile, "x", 1,&BytesWritten, NULL) == 0)
+ {
+ Fail("ERROR: WriteFile was unable to write to the WaitFile. "
+ "GetLastError() returned %d.\n",GetLastError());
+ }
+
+ if(FlushFileBuffers(TheFile) == 0)
+ {
+ Fail("ERROR: FlushFileBuffers failed when flushing the WaitFile. "
+ "GetLastError() returned %d.\n");
+ }
+
+ while(GetFileSize(TheFile,NULL) == size) {}
+}
+
+void SignalFinish(HANDLE TheFile)
+{
+ DWORD BytesWritten;
+
+ if(SetFilePointer(TheFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
+ {
+ Fail("ERROR: SetFilePointer was unable to set the pointer to the "
+ "end of the WaitFile. GetLastError() returned %d.\n",
+ GetLastError());
+ }
+
+ if(WriteFile(TheFile, "x", 1,&BytesWritten, NULL) == 0)
+ {
+ Fail("ERROR: WriteFile was unable to write to the WaitFile. "
+ "GetLastError returned %d.\n",GetLastError());
+ }
+
+ if(FlushFileBuffers(TheFile) == 0)
+ {
+ Fail("ERROR: FlushFileBuffers failed when flushing the WaitFile. "
+ "GetLastError() returned %d.\n");
+ }
+
+}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/CMakeLists.txt
new file mode 100644
index 0000000000..0e6d6fef4c
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ test1.c
+)
+
+add_executable(paltest_unlockfile_test1
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_unlockfile_test1 coreclrpal)
+
+target_link_libraries(paltest_unlockfile_test1
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ helper.c
+)
+
+add_executable(paltest_unlockfile_test1_helper
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_unlockfile_test1_helper coreclrpal)
+
+target_link_libraries(paltest_unlockfile_test1_helper
+ pthread
+ m
+ coreclrpal
+) \ No newline at end of file
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/helper.c b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/helper.c
new file mode 100644
index 0000000000..c2ef5a6736
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/helper.c
@@ -0,0 +1,92 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================
+**
+** Source: helper.c
+**
+** Purpose: A child process which will attempt to read from the
+** locked file to ensure it is locked. After it has been unlocked, it
+** will then read again to check that Unlock worked.
+**
+**
+**============================================================*/
+
+#include <palsuite.h>
+#include "../UnlockFile.h"
+
+#define FILENAME "testfile.txt"
+#define WAITFILENAME "waitfile"
+#define BUF_SIZE 128
+
+int __cdecl main(int argc, char *argv[])
+{
+ HANDLE TheFile, WaitFile;
+ int result = 0;
+ char DataBuffer[BUF_SIZE];
+ DWORD BytesRead;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /* Open the same file that the parent has opened and locked */
+ TheFile = CreateFile(FILENAME,
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (TheFile == INVALID_HANDLE_VALUE)
+ {
+ Trace("ERROR: Could not open file '%s' with CreateFile.\n",FILENAME);
+ result = 1;
+ }
+
+ /* Open up the WaitFile that we're using for IPC */
+ WaitFile = CreateFile(WAITFILENAME,
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (WaitFile == INVALID_HANDLE_VALUE)
+ {
+ Trace("ERROR: Could not open file '%s' with CreateFile. "
+ "GetLastError() returned %d.\n",WAITFILENAME,GetLastError());
+ result = 1;
+ }
+
+
+ /* Check to ensure the parent lock is respected */
+ if(ReadFile(TheFile, DataBuffer, 10, &BytesRead, NULL) != 0)
+ {
+ Trace("ERROR: ReadFile returned success when it should "
+ "have failed. Attempted to read the first 10 bytes "
+ "of a file which was locked by the parent process.\n");
+ result = 1;
+ }
+
+ // Sleep for a bit to give the parent a chance to block before we do.
+ Sleep(1000);
+
+ /* Switch back to the parent, so it can unlock the file */
+ SignalAndBusyWait(WaitFile);
+
+ if(ReadFile(TheFile, DataBuffer, 10, &BytesRead, NULL) == 0)
+ {
+ Trace("ERROR: ReadFile was unable to read from the file after it "
+ "had been unlocked. Attempted to read 10 bytes and ReadFile "
+ "returned 0. GetLastError() returned %d.\n",GetLastError());
+ result = 1;
+ }
+
+ PAL_TerminateEx(result);
+ return result;
+}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/test1.c b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/test1.c
new file mode 100644
index 0000000000..14634c7f7a
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/test1.c
@@ -0,0 +1,154 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================
+**
+** Source: test1.c
+**
+** Purpose:
+** Have the parent Lock a file, then have the child check the lock, then
+** have the parent unlock the file, and the child check again.
+** This requires some IPC, which is done here with a crude busy wait on a
+** file (waiting for the file size to change) to avoid too many more
+** dependencies.
+**
+**
+**============================================================*/
+
+#include <palsuite.h>
+#include "../UnlockFile.h"
+
+#define HELPER "helper"
+#define FILENAME "testfile.txt"
+#define WAITFILENAME "waitfile"
+#define BUF_SIZE 128
+
+int RunTest(char* Helper, HANDLE TheFile, HANDLE WaitFile)
+{
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ DWORD ChildRetCode = 0;
+ DWORD ParentRetCode = 0;
+ DWORD FileEnd;
+
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ ZeroMemory( &pi, sizeof(pi) );
+
+ /* Load up the helper Process, and then Wait until it signals that it
+ is finished locking.
+ */
+ if(!CreateProcess( NULL, Helper, NULL,
+ NULL, FALSE, 0,
+ NULL, NULL, &si, &pi))
+ {
+ Fail("ERROR: CreateProcess failed to load executable '%s'.\n",Helper);
+ }
+
+ SignalAndBusyWait(WaitFile);
+
+ /* When the child proccess is finished verifying the lock, find the end
+ of the file and unlock the file.
+ */
+
+ FileEnd = SetFilePointer(TheFile, 0, NULL, FILE_END);
+
+ if(FileEnd == INVALID_SET_FILE_POINTER)
+ {
+ Trace("ERROR: SetFilePointer failed to set the file pointer to the "
+ "end of the file. GetLastError() returned %d.\n",
+ GetLastError());
+ ParentRetCode = 1;
+ }
+
+ if(UnlockFile(TheFile, 0, 0, FileEnd, 0) == 0)
+ {
+ Trace("ERROR: The call to UnlockFile returned 0 when attempting to "
+ "unlock the file within the parent. This should have "
+ "succeeded. GetLastError returned %d.\n",GetLastError());
+ ParentRetCode = 1;
+ }
+
+ /* Switch back to the child so that it can ensure the unlock worked
+ properly.
+ */
+
+ SignalFinish(WaitFile);
+ WaitForSingleObject(pi.hProcess,INFINITE);
+
+ /* Get the return value from the helper process */
+ if (GetExitCodeProcess(pi.hProcess, &ChildRetCode) == 0)
+ {
+ Fail("ERROR: GetExitCodeProccess failed when attempting to retrieve "
+ "the exit code of the child process.\n");
+ }
+
+ if(CloseHandle( pi.hProcess ) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the process.\n");
+ }
+
+ if(CloseHandle( pi.hThread ) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the thread.\n");
+ }
+
+ return (ChildRetCode || ParentRetCode);
+}
+
+int __cdecl main(int argc, char *argv[])
+{
+ HANDLE TheFile = NULL;
+ HANDLE WaitFile = NULL;
+ char* WriteBuffer = "12345678901234567890123456";
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /* Open up the file we'll be using for some crude IPC */
+ WaitFile = CreateFile(WAITFILENAME,
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (WaitFile == INVALID_HANDLE_VALUE)
+ {
+ Fail("ERROR: Could not open file '%s' with CreateFile. "
+ "GetLastError() returned %d.\n",WAITFILENAME,GetLastError());
+ }
+
+ /* Call the helper function to Create a file, write 'WriteBuffer' to
+ the file, and lock the file from start to end.
+ */
+ TheFile = CreateAndLockFile(TheFile, FILENAME, WriteBuffer,
+ 0, strlen(WriteBuffer));
+
+ /* Run the test. Better errors are displayed by Trace throughout. */
+ if(RunTest(HELPER, TheFile, WaitFile))
+ {
+ Fail("ERROR: Checking to ensure that Unlock successfully unlocked "
+ "a file failed.\n");
+ }
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file used for "
+ "testing the locks. GetLastError() returns %d.\n",
+ GetLastError());
+ }
+
+ if(CloseHandle(WaitFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the wait file. "
+ "GetLastError() returns %d.\n",GetLastError());
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/testinfo.dat
new file mode 100644
index 0000000000..4d0ad6afc9
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/testinfo.dat
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Filemapping_memmgt
+Function = UnlockFile
+Name = Positive test for UnlockFile API
+TYPE = DEFAULT
+EXE1 = test1
+EXE2 = helper
+Description
+= Have the parent Lock a file, then have the child check the lock, then
+= have the parent unlock the file, and the child check again.
+= This requires some IPC, which is done here with a crude busy wait on a
+= file (waiting for the file size to change) to avoid too many more
+= dependencies.
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/CMakeLists.txt
new file mode 100644
index 0000000000..142b2763cb
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_unlockfile_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_unlockfile_test2 coreclrpal)
+
+target_link_libraries(paltest_unlockfile_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/test2.c b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/test2.c
new file mode 100644
index 0000000000..22c2cce2fb
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/test2.c
@@ -0,0 +1,154 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================
+**
+** Source: test2.c
+**
+** Purpose: Open a file, and call Unlock on the file, even though it has yet
+** to be locked. Then lock a portion of the file, and attempt to call unlock
+** on a larger portion of the file. Also, try to unlock a smaller portion
+** than was locked.
+**
+**
+**============================================================*/
+
+#include <palsuite.h>
+#include "../UnlockFile.h"
+
+int __cdecl main(int argc, char *argv[])
+{
+ HANDLE TheFile = NULL;
+ const char lpBuffer[] = "This is a test file.";
+ DWORD bytesWritten;
+ BOOL bRc = TRUE;
+ char fileName[] = "testfile.tmp";
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /* Open a file which is in the directory */
+ TheFile = CreateFile(fileName,
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (TheFile == INVALID_HANDLE_VALUE)
+ {
+ Fail("ERROR: Could not open file '%s' with CreateFile. "
+ "GetLastError() returned %d.\n",fileName,GetLastError());
+ }
+
+ bRc = WriteFile(
+ TheFile, // handle to file
+ lpBuffer, // data buffer
+ (DWORD)sizeof(lpBuffer), // number of bytes to write
+ &bytesWritten, // number of bytes written
+ NULL // overlapped buffer
+ );
+
+ if(!bRc)
+ {
+ Trace("ERROR: Could not write to file '%s' with WriteFile.",fileName);
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+
+ }
+ else if(bytesWritten != (DWORD)sizeof(lpBuffer))
+ {
+ Trace("ERROR: Could not write the correct number of bytes to the "
+ "file '%s' with WriteFile.",fileName);
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+ }
+
+ /* Call unlock file on an unlocked file, this should return 0 */
+ if(UnlockFile(TheFile, 0, 0, 5, 0) != 0)
+ {
+ Trace("ERROR: Attempted to unlock a file which was not locked and "
+ "the UnlockFile call was successful.\n");
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+ }
+
+ /* Lock the file */
+ if(LockFile(TheFile, 0, 0, 5, 0) == 0)
+ {
+ Trace("ERROR: Failed to call LockFile on a valid file handle. "
+ "GetLastError returned %d.\n",GetLastError());
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+ }
+
+ /* Try to unlock more of the file than was locked by LockFile */
+ if(UnlockFile(TheFile, 0, 0, 10, 0) != 0)
+ {
+ Trace("ERROR: Attempted to unlock bytes 0 to 9, but only bytes "
+ "0 to 4 are locked. But, UnlockFile was successful, when it "
+ "should have failed.\n");
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+ }
+
+ /* Try to unlock less of the file than was locked by LockFile */
+ if(UnlockFile(TheFile, 0, 0, 3, 0) != 0)
+ {
+ Trace("ERROR: Attempted to unlock bytes 0 to 2, but the bytes 0 to "
+ "4 were locked by LockFile. Unlockfile should have failed "
+ "when attempting this operation.\n");
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+ }
+
+ /* Properly unlock the file */
+ if(UnlockFile(TheFile, 0, 0, 5, 0) == 0)
+ {
+ Trace("ERROR: UnlockFile failed to unlock bytes 0 to 4 of the file. "
+ "GetLastError returned %d.\n",GetLastError());
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+ }
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.\n");
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/testinfo.dat
new file mode 100644
index 0000000000..932a4a2b8a
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/testinfo.dat
@@ -0,0 +1,16 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Filemapping_memmgt
+Function = UnlockFile
+Name = Positive test for UnlockFile API
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Open a file, and call Unlock on the file, even though it has yet
+= to be locked. Then lock a portion of the file, and attempt to call unlock
+= on a larger portion of the file. Also, try to unlock a smaller portion
+= than was locked.
+
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/CMakeLists.txt
new file mode 100644
index 0000000000..b4ec37c88c
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ test3.c
+)
+
+add_executable(paltest_unlockfile_test3
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_unlockfile_test3 coreclrpal)
+
+target_link_libraries(paltest_unlockfile_test3
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ helper.c
+)
+
+add_executable(paltest_unlockfile_test3_helper
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_unlockfile_test3_helper coreclrpal)
+
+target_link_libraries(paltest_unlockfile_test3_helper
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/helper.c b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/helper.c
new file mode 100644
index 0000000000..650abf49ad
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/helper.c
@@ -0,0 +1,103 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================
+**
+** Source: helper.c
+**
+** Purpose: A child process which will lock a portion of the file,
+** then try to unlock a portion of the file which was locked by the parent.
+**
+**
+**============================================================*/
+
+#include <palsuite.h>
+#include "../UnlockFile.h"
+
+#define FILENAME "testfile.txt"
+#define WAITFILENAME "waitfile"
+#define BUF_SIZE 128
+
+int __cdecl main(int argc, char *argv[])
+{
+ HANDLE TheFile, WaitFile;
+ int result = 0;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /* Open the same file that the parent has opened and locked */
+ TheFile = CreateFile(FILENAME,
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (TheFile == INVALID_HANDLE_VALUE)
+ {
+ Trace("ERROR: Could not open file '%s' with CreateFile.\n",FILENAME);
+ result = 1;
+ }
+
+ /* Open up the WaitFile that we're using for IPC */
+ WaitFile = CreateFile(WAITFILENAME,
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (WaitFile == INVALID_HANDLE_VALUE)
+ {
+ Trace("ERROR: Could not open file '%s' with CreateFile. "
+ "GetLastError() returned %d.\n",WAITFILENAME,GetLastError());
+ result = 1;
+ }
+
+ /* Lock a section of the file different from which was locked in the
+ parent proccess
+ */
+ if(LockFile(TheFile, 10, 0, 10, 0) == 0)
+ {
+ Trace("ERROR: The LockFile call within the child failed to lock "
+ "the file. GetLastError() returned %d.\n",GetLastError());
+ result = 1;
+ }
+
+ /* Attempt to unlock the portion of the file which was locked within the
+ parent process.
+ */
+ if(UnlockFile(TheFile, 0, 0, 10, 0) != 0)
+ {
+ Trace("ERROR: The UnlockFile call within the child succeeded in "
+ "calling UnlockFile on the portion of the file which was "
+ "locked by the parent.\n");
+ result = 1;
+ }
+
+ // Sleep for a bit to give the parent a chance to block before we do.
+ Sleep(1000);
+
+ /* Switch back to the parent, so it can check the child lock */
+ SignalAndBusyWait(WaitFile);
+
+ /* Finally, clean up the lock which was done within this proccess and
+ exit.
+ */
+ if(UnlockFile(TheFile, 10, 0, 10, 0) == 0)
+ {
+ Trace("ERROR: The UnlockFile call within the child failed to unlock "
+ "the portion of the file which was locked by the child. "
+ "GetLastError() returned %d.\n", GetLastError());
+ result = 1;
+ }
+
+ PAL_TerminateEx(result);
+ return result;
+}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/test3.c b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/test3.c
new file mode 100644
index 0000000000..cf27aba0a3
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/test3.c
@@ -0,0 +1,142 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================
+**
+** Source: test3.c
+**
+** Purpose:
+** Lock a portion of the file with the parent. Then have the child lock
+** another portion. Have the child attempt to call Unlock on the parent's
+** locked data, and the parent do the same to the child. Ensure that the
+** locks are respected.
+**
+**
+**============================================================*/
+
+#include <palsuite.h>
+#include "../UnlockFile.h"
+
+#define HELPER "helper"
+#define FILENAME "testfile.txt"
+#define WAITFILENAME "waitfile"
+#define BUF_SIZE 128
+
+int RunTest(char* Helper, HANDLE TheFile, HANDLE WaitFile)
+{
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ DWORD ChildRetCode = 0;
+ DWORD ParentRetCode = 0;
+
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ ZeroMemory( &pi, sizeof(pi) );
+
+ /* Load up the helper Process, and then Wait until it signals that it
+ is finished locking.
+ */
+ if(!CreateProcess( NULL, Helper, NULL,
+ NULL, FALSE, 0,
+ NULL, NULL, &si, &pi))
+ {
+ Fail("ERROR: CreateProcess failed to load executable '%s'.\n",Helper);
+ }
+
+ SignalAndBusyWait(WaitFile);
+
+ /* When the child proccess is finished setting its lock and testing the
+ parent lock, then the parent can test the child's lock.
+ */
+
+ if(UnlockFile(TheFile, 10, 0, 10, 0) != 0)
+ {
+ Trace("ERROR: The parent proccess called Unlock on the child "
+ "proccesses lock, and the function returned non-zero, when "
+ "it should have failed.\n");
+ ParentRetCode = 1;
+ }
+
+ /* Switch back to the child so that it can unlock its portion and
+ cleanup.
+ */
+
+ SignalFinish(WaitFile);
+ WaitForSingleObject(pi.hProcess,INFINITE);
+
+ /* Get the return value from the helper process */
+ if (GetExitCodeProcess(pi.hProcess, &ChildRetCode) == 0)
+ {
+ Fail("ERROR: GetExitCodeProccess failed when attempting to retrieve "
+ "the exit code of the child process.\n");
+ }
+
+ if(CloseHandle( pi.hProcess ) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the process.\n");
+ }
+
+ if(CloseHandle( pi.hThread ) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the thread.\n");
+ }
+
+ return (ChildRetCode || ParentRetCode);
+}
+
+int __cdecl main(int argc, char *argv[])
+{
+ HANDLE TheFile = NULL;
+ HANDLE WaitFile = NULL;
+ char* WriteBuffer = "12345678901234567890123456";
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /* Open up the file we'll be using for some crude IPC */
+ WaitFile = CreateFile(WAITFILENAME,
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (WaitFile == INVALID_HANDLE_VALUE)
+ {
+ Fail("ERROR: Could not open file '%s' with CreateFile. "
+ "GetLastError() returned %d.\n",WAITFILENAME,GetLastError());
+ }
+
+ /* Call the helper function to Create a file, write 'WriteBuffer' to
+ the file, and lock the file from bytes 0-9.
+ */
+ TheFile = CreateAndLockFile(TheFile, FILENAME, WriteBuffer,
+ 0, 10);
+
+ /* Run the test. Better errors are displayed by Trace throughout. */
+ if(RunTest(HELPER, TheFile, WaitFile))
+ {
+ Fail("ERROR: The test to check that the Unlock will not work on "
+ "on locks set by other proccesses failed.\n");
+ }
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file used for "
+ "testing the locks. GetLastError() returns %d.\n",
+ GetLastError());
+ }
+
+ if(CloseHandle(WaitFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the wait file. "
+ "GetLastError() returns %d.\n",GetLastError());
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/testinfo.dat
new file mode 100644
index 0000000000..bf7ec5f809
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/testinfo.dat
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Filemapping_memmgt
+Function = UnlockFile
+Name = Positive test for UnlockFile API
+TYPE = DEFAULT
+EXE1 = test3
+EXE2 = helper
+Description
+= Lock a portion of the file with the parent. Then have the child lock
+= another portion. Have the child attempt to call Unlock on the parent's
+= locked data, and the parent do the same to the child. Ensure that the
+= locks are respected.
+
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/CMakeLists.txt
new file mode 100644
index 0000000000..d6bee307c2
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test4.c
+)
+
+add_executable(paltest_unlockfile_test4
+ ${SOURCES}
+)
+
+add_dependencies(paltest_unlockfile_test4 coreclrpal)
+
+target_link_libraries(paltest_unlockfile_test4
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/test4.c b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/test4.c
new file mode 100644
index 0000000000..55abcd24bc
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/test4.c
@@ -0,0 +1,187 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================
+**
+** Source: test4.c
+**
+** Purpose: Pass an invalid handle to UnlockFile. Pass a null handle to
+** UnlockFile. Create a file and lock two consecuative regions and call
+** UnlockFile on the whole region (this should fail, see msdn)
+**
+**
+**============================================================*/
+
+#include <palsuite.h>
+#include "../UnlockFile.h"
+
+int __cdecl main(int argc, char *argv[])
+{
+ HANDLE TheFile = NULL;
+ const char lpBuffer[] = "This is a test file.";
+ DWORD bytesWritten;
+ BOOL bRc = TRUE;
+ char fileName[] = "testfile.tmp";
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /* Open a file which is in the directory */
+ TheFile = CreateFile(fileName,
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (TheFile == INVALID_HANDLE_VALUE)
+ {
+ Fail("ERROR: Could not open file '%s' with CreateFile. "
+ "GetLastError() returned %d.\n",fileName,GetLastError());
+ }
+
+ bRc = WriteFile(
+ TheFile, // handle to file
+ lpBuffer, // data buffer
+ (DWORD)sizeof(lpBuffer), // number of bytes to write
+ &bytesWritten, // number of bytes written
+ NULL // overlapped buffer
+ );
+
+ if(!bRc)
+ {
+ Trace("ERROR: Could not write to file '%s' with WriteFile.",fileName);
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+
+ }
+ else if(bytesWritten != (DWORD)sizeof(lpBuffer))
+ {
+ Trace("ERROR: Could not write the correct number of bytes to the "
+ "file '%s' with WriteFile.",fileName);
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+ }
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.\n");
+ }
+
+
+ /* Test an invalid handle and a NULL handle */
+ if(UnlockFile(TheFile, 0, 0, 0, 0) != 0)
+ {
+ Fail("ERROR: Called UnlockFile on an invalid HANDLE and it "
+ "returned a success value.\n");
+ }
+
+ if(UnlockFile(NULL, 0, 0, 0, 0) != 0)
+ {
+ Fail("ERROR: Called UnlockFile with NULL passed for the HANDLE and "
+ "it returned a success value.\n");
+ }
+
+ /* Re-open the file */
+ TheFile = CreateFile(fileName,
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (TheFile == INVALID_HANDLE_VALUE)
+ {
+ Fail("ERROR: Could not open file '%s' with CreateFile. "
+ "GetLastError() returned %d.\n",fileName,GetLastError());
+ }
+
+ /* Lock two consecuative regions of this file */
+ if(LockFile(TheFile, 0, 0, 5, 0) == 0)
+ {
+ Trace("ERROR: LockFile failed attempting to lock bytes 0-4. "
+ "GetLastError() returned %d.\n",GetLastError());
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+ }
+
+ if(LockFile(TheFile, 5, 0, 5, 0) == 0)
+ {
+ Fail("ERROR: LockFile failed attempting to lock bytes 5-9. "
+ "GetLastError() returned %d.\n",GetLastError());
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+ }
+
+ /* Attempt to unlock the entire region which was locked with one
+ call to UnlockFile. This should fail.
+ */
+ if(UnlockFile(TheFile, 0, 0, 10, 0) != 0)
+ {
+ Fail("ERROR: Called UnlockFile on bytes 0-9 which were locked with "
+ "two seperate LockFile calls. This should have failed. "
+ "UnlockFile will not unlock consecuative locked regions.\n");
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+ }
+
+
+ /* Now, unlock the regions one at a time. */
+ if(UnlockFile(TheFile, 0, 0, 5, 0) == 0)
+ {
+ Fail("ERROR: UnlockFile failed when attempting to unlock bytes "
+ "0-4 of the file. GetLastError() returned %d.\n",GetLastError());
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+ }
+
+ if(UnlockFile(TheFile, 5, 0, 5, 0) == 0)
+ {
+ Fail("ERROR: UnlockFile failed when attempting to unlock bytes "
+ "5-9 of the file. GetLastError() returned %d.\n",GetLastError());
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.");
+ }
+ Fail("");
+ }
+
+ if(CloseHandle(TheFile) == 0)
+ {
+ Fail("ERROR: CloseHandle failed to close the file.\n");
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/testinfo.dat
new file mode 100644
index 0000000000..4f3885b978
--- /dev/null
+++ b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/testinfo.dat
@@ -0,0 +1,16 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Filemapping_memmgt
+Function = UnlockFile
+Name = Positive test for UnlockFile API
+TYPE = DEFAULT
+EXE1 = test4
+Description
+= Pass an invalid handle to UnlockFile. Pass a null handle to
+= UnlockFile. Create a file and lock two consecuative regions and call
+= UnlockFile on the whole region (this should fail, see msdn)
+
+