I stumbled onto this while using freopen() for debugging purposes. Basically, freopen() fails if the FILE has been created with an invalid handle.
So, this MR contains: - basic tests for freopen (no issue there, just for coverage purposes) - tests for freopen on FILE with invalid handle - fix for freopen
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/msvcrt/tests/file.c | 68 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+)
diff --git a/dlls/msvcrt/tests/file.c b/dlls/msvcrt/tests/file.c index 3f57b3f871f..32f4bddc208 100644 --- a/dlls/msvcrt/tests/file.c +++ b/dlls/msvcrt/tests/file.c @@ -1167,6 +1167,73 @@ static void test_fputwc(void) _unlink(tempfile); }
+static void test_freopen( void ) +{ + char filename1[8] = "AXXXXXX"; + char filename2[8] = "BXXXXXX"; + FILE *file; + int ret; + int fd; + char ch; + long pos; + + mktemp(filename1); + mktemp(filename2); + + file = fopen(filename1, "wt"); + ok(file != NULL, "fopen(filename1) returned NULL\n"); + ret = fwrite("1", 1, 1, file); + ok(ret == 1, "fwrite() returned %d (%d)\n", ret, errno); + ret = fclose(file); + ok(ret == 0, "fclose() returned %d\n", ret); + + file = fopen(filename2, "wt"); + ok(file != NULL, "fopen(filename1) returned NULL\n"); + ret = fwrite("2", 1, 1, file); + ok(ret == 1, "fwrite() returned %d (%d)\n", ret, errno); + ret = fclose(file); + ok(ret == 0, "fclose() returned %d\n", ret); + + file = fopen(filename1, "rt"); + ok(file != NULL, "fopen(filename1) returned NULL\n"); + file = freopen(filename2, "rt", file); + ok(file != NULL, "fopen(filename2) returned NULL\n"); + ch = '#'; + ret = fread(&ch, 1, 1, file); + ok(ret == 1, "fread() returned %d\n", ret); + ok(ch == '2', "fread() read %c\n", ch); + ret = fclose(file); + ok(ret == 0, "fclose() returned %d\n", ret); + + file = fopen(filename1, "at"); + ok(file != NULL, "fopen(filename1) returned NULL\n"); + file = freopen(filename1, "rt", file); + ok(file != NULL, "fopen(filename1) returned NULL\n"); + pos = ftell(file); + ok(pos == 0, "ftell() returned %ld\n", pos); + ch = '#'; + ret = fread(&ch, 1, 1, file); + ok(ret == 1, "fread() returned %d\n", ret); + ok(ch == '1', "fread() read %c\n", ch); + ret = fclose(file); + ok(ret == 0, "fclose() returned %d\n", ret); + + file = fopen(filename1, "rt"); + ok(file != NULL, "fopen(filename1) returned NULL\n"); + fd = fileno(file); + ok(fd > 0, "fileno() returned %d\n", fd); + file = freopen("_:", "rt", file); + ok(file == NULL, "fopen(_:) returned non NULL\n"); + errno = 0xdeadbeef; + ch = '#'; + ret = read(fd, &ch, 1); + ok(ret == -1, "read() returned %d\n", ret); + ok(errno == EBADF, "errno is %d\n", errno); + + DeleteFileA(filename1); + DeleteFileA(filename2); +} + static void test_ctrlz( void ) { char* tempf; @@ -2977,6 +3044,7 @@ START_TEST(file) test_fgetwc_locale("AB\x83\xa9", "C", 0); test_fgetwc_unicode(); test_fputwc(); + test_freopen(); test_ctrlz(); test_file_put_get(); test_tmpnam();
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/msvcrt/tests/file.c | 95 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-)
diff --git a/dlls/msvcrt/tests/file.c b/dlls/msvcrt/tests/file.c index 32f4bddc208..c7c9597860a 100644 --- a/dlls/msvcrt/tests/file.c +++ b/dlls/msvcrt/tests/file.c @@ -1823,6 +1823,7 @@ static void test_invalid_stdin_child( void ) ioinfo *info; int ret; char c; + int fd;
errno = 0xdeadbeef; handle = (HANDLE)_get_osfhandle(STDIN_FILENO); @@ -1835,6 +1836,9 @@ static void test_invalid_stdin_child( void )
ok(stdin->_file == -2, "stdin->_file = %d\n", stdin->_file);
+ fd = fileno(stdin); + ok(fd == -2, "fileno(stdin) returned %d\n", fd); + errno = 0xdeadbeef; ret = fread(&c, 1, 1, stdin); ok(!ret, "fread(stdin) returned %d\n", ret); @@ -1886,6 +1890,81 @@ static void test_invalid_stdin_child( void ) ok((ret==-1 && errno==EBADF) || (!ret && errno==0xdeadbeef), "errno = %d\n", errno); }
+static void test_invalid_stdin_child_subcmd( const char *subcmd ) +{ + if (!strcmp(subcmd, "/freopen")) + { + char name[12+1] = "abcdefXXXXXX"; + FILE *new; + HANDLE handle; + int ret; + char c; + int fd; + + errno = 0xdeadbeef; + handle = (HANDLE)_get_osfhandle(STDIN_FILENO); + ok(handle == (HANDLE)-2, "handle = %p\n", handle); + ok(errno == 0xdeadbeef, "errno = %d\n", errno); + + /* invalid file name */ + new = freopen("_:", "rb", stdin); + ok(new == NULL, "Shouldn't be able to reopen stdin\n"); + + errno = 0xdeadbeef; + handle = (HANDLE)_get_osfhandle(STDIN_FILENO); + ok(handle == (HANDLE)-2, "handle = %p\n", handle); + ok(errno == 0xdeadbeef, "errno = %d\n", errno); + + mktemp(name); + new = fopen(name, "wb"); + ok(new != NULL, "couldn't open %s\n", name); + c = ' '; + ret = fwrite(&c, 1, 1, new); + ok(ret == 1, "got ret %d\n", ret); + fclose(new); + + new = freopen(name, "rb", stdin); + ok(new != NULL, "freopen(..., stdin) returned NULL\n"); + ok(new == stdin, "freopen(..., stdin) didn't return stdin\n"); + + errno = 0xdeadbeef; + handle = (HANDLE)_get_osfhandle(STDIN_FILENO); + ok(handle == (HANDLE)-2, "handle = %p\n", handle); + ok(errno == 0xdeadbeef, "errno = %d\n", errno); + + fd = fileno(stdin); + todo_wine + ok(fd > 0, "fileno(stdin) returned %d\n", fd); + ok(fd != STDIN_FILENO, "fileno(stdin) returned STDIN_FILENO\n"); + + errno = 0xdeadbeef; + handle = (HANDLE)_get_osfhandle(fd); + ok(handle != (HANDLE)-2, "handle = %p\n", handle); + todo_wine + ok(errno == 0xdeadbeef, "errno = %d\n", errno); + + errno = 0xdeadbeef; + ret = fread(&c, 1, 1, stdin); + todo_wine + ok(ret == 1, "fread(stdin) returned %d\n", ret); + todo_wine + ok(errno == 0xdeadbeef, "errno = %d\n", errno); + ok(c == ' ', "Unexpected char\n"); + + /* invalid file name */ + new = freopen("_:", "rb", stdin); + ok(new == NULL, "Shouldn't be able to reopen stdin\n"); + + errno = 0xdeadbeef; + ret = fclose(stdin); + ok(ret == -1, "fclose(stdin) returned %d\n", ret); + ok(errno == 0xdeadbeef, "errno is %d\n", errno); + + DeleteFileA(name); + } + else ok(0, "Unexpected subcommand %s\n", subcmd); +} + static void test_invalid_stdin( const char* selfname ) { char cmdline[MAX_PATH]; @@ -1919,6 +1998,15 @@ static void test_invalid_stdin( const char* selfname ) CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, CREATE_DEFAULT_ERROR_MODE|NORMAL_PRIORITY_CLASS, NULL, NULL, &startup, &proc); wait_child_process(proc.hProcess); + CloseHandle(proc.hProcess); + CloseHandle(proc.hThread); + + sprintf(cmdline, "%s file stdin /freopen", selfname); + CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, + CREATE_DEFAULT_ERROR_MODE|NORMAL_PRIORITY_CLASS, NULL, NULL, &startup, &proc); + wait_child_process(proc.hProcess); + CloseHandle(proc.hProcess); + CloseHandle(proc.hThread);
ret = RegCloseKey(key); ok(!ret, "RegCloseKey failed: %lx\n", ret); @@ -3004,7 +3092,12 @@ START_TEST(file) else if (strcmp(arg_v[2], "pipes") == 0) test_pipes_child(arg_c, arg_v); else if (strcmp(arg_v[2], "stdin") == 0) - test_invalid_stdin_child(); + { + if (arg_c == 3) + test_invalid_stdin_child(); + else + test_invalid_stdin_child_subcmd(arg_v[3]); + } else ok(0, "invalid argument '%s'\n", arg_v[2]); return;
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/msvcrt/file.c | 4 +--- dlls/msvcrt/tests/file.c | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-)
diff --git a/dlls/msvcrt/file.c b/dlls/msvcrt/file.c index e72784eef41..24d801a5306 100644 --- a/dlls/msvcrt/file.c +++ b/dlls/msvcrt/file.c @@ -4588,9 +4588,7 @@ FILE* CDECL _wfreopen(const wchar_t *path, const wchar_t *mode, FILE* file) TRACE(":path (%s) mode (%s) file (%p) fd (%d)\n", debugstr_w(path), debugstr_w(mode), file, file ? file->_file : -1);
LOCK_FILES(); - if (!file || ((fd = file->_file) < 0)) - file = NULL; - else + if (file) { fclose(file); if (msvcrt_get_flags(mode, &open_flags, &stream_flags) == -1) diff --git a/dlls/msvcrt/tests/file.c b/dlls/msvcrt/tests/file.c index c7c9597860a..cd844de2770 100644 --- a/dlls/msvcrt/tests/file.c +++ b/dlls/msvcrt/tests/file.c @@ -1933,21 +1933,17 @@ static void test_invalid_stdin_child_subcmd( const char *subcmd ) ok(errno == 0xdeadbeef, "errno = %d\n", errno);
fd = fileno(stdin); - todo_wine ok(fd > 0, "fileno(stdin) returned %d\n", fd); ok(fd != STDIN_FILENO, "fileno(stdin) returned STDIN_FILENO\n");
errno = 0xdeadbeef; handle = (HANDLE)_get_osfhandle(fd); ok(handle != (HANDLE)-2, "handle = %p\n", handle); - todo_wine ok(errno == 0xdeadbeef, "errno = %d\n", errno);
errno = 0xdeadbeef; ret = fread(&c, 1, 1, stdin); - todo_wine ok(ret == 1, "fread(stdin) returned %d\n", ret); - todo_wine ok(errno == 0xdeadbeef, "errno = %d\n", errno); ok(c == ' ', "Unexpected char\n");
Piotr Caban (@piotr) commented about dlls/msvcrt/tests/file.c:
CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, CREATE_DEFAULT_ERROR_MODE|NORMAL_PRIORITY_CLASS, NULL, NULL, &startup, &proc); wait_child_process(proc.hProcess);
- CloseHandle(proc.hProcess);
- CloseHandle(proc.hThread);
- sprintf(cmdline, "%s file stdin /freopen", selfname);
- CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
CREATE_DEFAULT_ERROR_MODE|NORMAL_PRIORITY_CLASS, NULL, NULL, &startup, &proc);
Is there any reason to run the test in new process? We already have tests that modify _file FILE member value (see test_fflush).