Skip to content

Commit 0e02f48

Browse files
committed
Use share flags for all file operations on Windows
Some file operations provided by the Erlang file module didn't open the target file with all the file share flags. This made some concurrent file operations against the same file fail on Windows, while on other platforms such as GNU/Linux or Mac OS X they succeed. The operations will fail only if they're performed concurrently by different threads (async IO threads or scheduler threads). For example, one Erlang process does a file:delete/1 call while another Erlang process is doing a filelib:file_size/1 call. This made the former process get an eacces error from the file:delete/1 call. On GNU/Linux or Mac OS X the call would succeed. Another example is if one Erlang process attempts to open a file for reading while another one is in the middle of a file:read_file_info/1 call (after it opened the file and before it closed the file). It's easy to verify that if a file is not open with all the share flags, it's impossible for other threads (even if they belong to the same OS process) to open the file while the file is not closed by the first thread. The following test program shows this: #include <windows.h> #include <iostream> // Must be an existing file //#define SHARE_FLAGS (FILE_SHARE_READ) static DWORD WINAPI MyThreadFunction(LPVOID lpParam); static char *lastError(); int main(int argc, char *argv[]) { DWORD threadId; HANDLE threadHandle, hFile; hFile = CreateFile(FILENAME, GENERIC_READ, SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { std::cerr << "File open error from main: " << lastError() << std::endl; return 1; } std::cout << "Main thread opened file successfully" << std::endl; threadHandle = CreateThread(NULL, 0, MyThreadFunction, NULL, 0, &threadId); if (threadHandle == INVALID_HANDLE_VALUE) { std::cerr << "Thread create error from main: " << lastError() << std::endl; return 1; } WaitForSingleObject(threadHandle, INFINITE); CloseHandle(threadHandle); CloseHandle(hFile); return 0; } static DWORD WINAPI MyThreadFunction( LPVOID lpParam ) { HANDLE hFile; hFile = CreateFile(FILENAME, GENERIC_READ, SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { std::cerr << "File open error from second thread: " << lastError() << std::endl; return 1; } std::cout << "Second thread opened file successfully" << std::endl; CloseHandle(hFile); return 0; } static char *lastError() { static char *buf = NULL; DWORD dw = GetLastError(); if (buf != NULL) { LocalFree((LPTSTR) &buf); } FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &buf, 0, NULL); return buf; } Rnning this program with SHARE_FLAGS set to 0 (as efile_fileinfo() does for e.g.), shows that the second thread is unable to open the file: C:\cygwin\home\fdmanana\tmp>touch foo.bar C:\cygwin\home\fdmanana\tmp>threads_fopen_test.exe Main thread opened file successfully File open error from second thread: The process cannot access the file because it is being used by another process. Changing the program's SHARE_FLAGS to FILE_SHARE_READ, shows that both threads are able to open the file: C:\cygwin\home\fdmanana\tmp>touch foo.bar C:\cygwin\home\fdmanana\tmp>threads_test.exe Main thread opened file successfully Second thread opened file successfully Same logic applies to opening files for writing or deleting and renaming files while they're open by some other thread that didn't specify the flags FILE_SHARE_WRITE and FILE_SHARE_DELETE.
1 parent 812f666 commit 0e02f48

File tree

1 file changed

+6
-4
lines changed

1 file changed

+6
-4
lines changed

erts/emulator/drivers/win32/win_efile.c

+6-4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
#define IS_DOT_OR_DOTDOT(s) \
4242
((s)[0] == L'.' && ((s)[1] == L'\0' || ((s)[1] == L'.' && (s)[2] == L'\0')))
4343

44+
#define FILE_SHARE_FLAGS (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
45+
4446
#ifndef INVALID_FILE_ATTRIBUTES
4547
#define INVALID_FILE_ATTRIBUTES ((DWORD) 0xFFFFFFFF)
4648
#endif
@@ -724,7 +726,7 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
724726
crFlags = CREATE_NEW;
725727
}
726728
fd = CreateFileW(wname, access,
727-
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
729+
FILE_SHARE_FLAGS,
728730
NULL, crFlags, FILE_ATTRIBUTE_NORMAL, NULL);
729731

730732
/*
@@ -909,7 +911,7 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
909911
{
910912
HANDLE handle; /* Handle returned by CreateFile() */
911913
BY_HANDLE_FILE_INFORMATION fileInfo; /* from CreateFile() */
912-
if (handle = CreateFileW(name, GENERIC_READ, 0,NULL,
914+
if (handle = CreateFileW(name, GENERIC_READ, FILE_SHARE_FLAGS, NULL,
913915
OPEN_EXISTING, 0, NULL)) {
914916
GetFileInformationByHandle(handle, &fileInfo);
915917
pInfo->links = fileInfo.nNumberOfLinks;
@@ -1021,7 +1023,7 @@ efile_write_info(Efile_error* errInfo,
10211023
}
10221024

10231025
fd = CreateFileW(wname, GENERIC_READ|GENERIC_WRITE,
1024-
FILE_SHARE_READ | FILE_SHARE_WRITE,
1026+
FILE_SHARE_FLAGS,
10251027
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
10261028
if (fd != INVALID_HANDLE_VALUE) {
10271029
BOOL result = SetFileTime(fd, &CreationFileTime, &AccessFileTime, &ModifyFileTime);
@@ -1384,7 +1386,7 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
13841386
DWORD fileAttributes = GetFileAttributesW(wname);
13851387
if ((fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
13861388
BOOLEAN success = 0;
1387-
HANDLE h = CreateFileW(wname, GENERIC_READ, 0,NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1389+
HANDLE h = CreateFileW(wname, GENERIC_READ, FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
13881390
int len;
13891391
if(h != INVALID_HANDLE_VALUE) {
13901392
success = pGetFinalPathNameByHandle(h, wbuffer, size / sizeof(WCHAR),0);

0 commit comments

Comments
 (0)