Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

After FdGetOsFHandle() failure, lastFileError() is misleading #14

Open
nmisch opened this issue Jul 18, 2022 · 0 comments
Open

After FdGetOsFHandle() failure, lastFileError() is misleading #14

nmisch opened this issue Jul 18, 2022 · 0 comments

Comments

@nmisch
Copy link

nmisch commented Jul 18, 2022

The pod documentation mentions lastFileError() reacting to FdGetOsFHandle(),
and File.xs indeed has code to save GetLastError() in that case. I tried
that as a workaround for issue #13. However, _get_osfhandle() does not
document GetLastError() being meaningful. Indeed, my experiments show that a
failing _get_osfhandle() can make GetLastError() return zero, among other
values. Test program:

use strict;
use warnings;
use Test::More;
use Win32API::File();
#use Socket;  # changes the error codes further

my $improbable_error_code = 15614;  # ERROR_PACKAGE_REPOSITORY_CORRUPTED
my($before, $after);

close STDIN;
for my $winsock_active (0, 1) {
  require Socket if $winsock_active;
  # fd 0 is closed but below the highest open fd
  # fd 100000 is closed and above the highest open fd
  # fd 1 is open
  for my $fd (0, 100000, 1) {
    Win32API::File::fileLastError($improbable_error_code);
    $before = 0+Win32API::File::fileLastError();
    Win32API::File::FdGetOsFHandle($fd);
    $after = 0+Win32API::File::fileLastError();
    is($after, $before, "winsock=$winsock_active fd=$fd");
  }
}

Output:

not ok 1 - winsock=0 fd=0
#   Failed test 'winsock=0 fd=0'
#   at //tsclient/remote/demo_closed.pl line 21.
#          got: '6'
#     expected: '15614'
not ok 2 - winsock=0 fd=100000
#   Failed test 'winsock=0 fd=100000'
#   at //tsclient/remote/demo_closed.pl line 21.
#          got: '203'
#     expected: '15614'
ok 3 - winsock=0 fd=1
not ok 4 - winsock=1 fd=0
#   Failed test 'winsock=1 fd=0'
#   at //tsclient/remote/demo_closed.pl line 21.
#          got: '0'
#     expected: '15614'
not ok 5 - winsock=1 fd=100000
#   Failed test 'winsock=1 fd=100000'
#   at //tsclient/remote/demo_closed.pl line 21.
#          got: '203'
#     expected: '15614'
ok 6 - winsock=1 fd=1
# Tests were run but no plan was declared and done_testing() was not seen.

I see the following ways of detecting FdGetOsFHandle() failure today, given
the need to work around issue #13 and $SUBJECT:

  1. Test whether Perl is 32-bit. If 32-bit, compare the return value to
    0xffffffff. Otherwise, compare the return value to 0xffffffffffffffff.
    No known disadvantages.

  2. Use a sequence like:

    fileLastError($improbable_error_code);
    FdGetOsFHandle(...);
    if ($improbable_error_code == fileLastError()) { # succeeded ...

    In practice, I expect _get_osfhandle() can set just a few of the thousands
    of error codes, making all others viable as $improbable_error_code. Having
    said that, it's infeasible to prove that GetLastError() will never report
    $improbable_error_code after _get_osfhandle().

  3. Test $!==9 (EBADF). _get_osfhandle() documents this, and I have observed
    it to work. The risk would be FdGetOsFHandle() somehow making an
    additional library call that clobbers errno. That risk feels significant.

I will likely use (1) for the moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant