[pypy-commit] pypy win32-cleanup2: adding tests where os.write should fail, add impl of validate_fd

mattip noreply at buildbot.pypy.org
Tue Apr 3 21:43:31 CEST 2012


Author: Matti Picus <matti.picus at gmail.com>
Branch: win32-cleanup2
Changeset: r54170:f89544de8192
Date: 2012-04-03 22:37 +0300
http://bitbucket.org/pypy/pypy/changeset/f89544de8192/

Log:	adding tests where os.write should fail, add impl of validate_fd

diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -81,7 +81,7 @@
 FILEP = rffi.COpaquePtr('FILE')
 
 fopen = rffi.llexternal('fopen', [CONST_STRING, CONST_STRING], FILEP)
-#fclose = rffi.llexternal('fclose', [FILEP], rffi.INT)
+_fclose = rffi.llexternal('fclose', [FILEP], rffi.INT)
 def fclose(fp):
     try:
         fd = fileno(fp)
diff --git a/pypy/rlib/rposix.py b/pypy/rlib/rposix.py
--- a/pypy/rlib/rposix.py
+++ b/pypy/rlib/rposix.py
@@ -1,5 +1,6 @@
 import os
-from pypy.rpython.lltypesystem.rffi import CConstant, CExternVariable, INT
+from pypy.rpython.lltypesystem.rffi import (CConstant, CExternVariable, 
+        INT, CCHARPP)
 from pypy.rpython.lltypesystem import lltype, ll2ctypes, rffi
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
 from pypy.rlib.rarithmetic import intmask
@@ -18,12 +19,68 @@
     def __setitem__(self, index, value):
         assert index == 0
         ll2ctypes.TLS.errno = value
+if os.name == 'nt':
+    post_include_bits =['''
+        /* Lifted completely from CPython 3.3 Modules/posix_module.c */
+        typedef struct {
+            intptr_t osfhnd;
+            char osfile;
+        } my_ioinfo;
+        extern __declspec(dllimport) char * __pioinfo[];
+        #define IOINFO_L2E 5
+        #define IOINFO_ARRAY_ELTS   (1 << IOINFO_L2E)
+        #define IOINFO_ARRAYS 64
+        #define _NHANDLE_           (IOINFO_ARRAYS * IOINFO_ARRAY_ELTS)
+        #define FOPEN 0x01
+        #define _NO_CONSOLE_FILENO (intptr_t)-2
 
-errno_eci = ExternalCompilationInfo(
-    includes=['errno.h']
+        /* This function emulates what the windows CRT
+            does to validate file handles */
+        int
+        _PyVerify_fd(int fd)
+        {
+            const int i1 = fd >> IOINFO_L2E;
+            const int i2 = fd & ((1 << IOINFO_L2E) - 1);
+
+            static size_t sizeof_ioinfo = 0;
+
+            /* Determine the actual size of the ioinfo structure,
+             * as used by the CRT loaded in memory
+             */
+            if (sizeof_ioinfo == 0 && __pioinfo[0] != NULL) {
+                sizeof_ioinfo = _msize(__pioinfo[0]) / IOINFO_ARRAY_ELTS;
+            }
+            if (sizeof_ioinfo == 0) {
+                /* This should not happen... */
+                goto fail;
+            }
+
+            /* See that it isn't a special CLEAR fileno */
+                if (fd != _NO_CONSOLE_FILENO) {
+                /* Microsoft CRT would check that 0<=fd<_nhandle but we can't do that.  Instead
+                 * we check pointer validity and other info
+                 */
+                if (0 <= i1 && i1 < IOINFO_ARRAYS && __pioinfo[i1] != NULL) {
+                    /* finally, check that the file is open */
+                    my_ioinfo* info = (my_ioinfo*)(__pioinfo[i1] + i2 * sizeof_ioinfo);
+                    if (info->osfile & FOPEN) {
+                        return 1;
+                    }
+                }
+            }
+          fail:
+            errno = EBADF;
+            return 0;
+        }
+    ''']
+else:
+    post_include_bits = []
+eci = ExternalCompilationInfo(
+    includes=['errno.h','stdio.h'],
+    post_include_bits = post_include_bits,
 )
 
-_get_errno, _set_errno = CExternVariable(INT, 'errno', errno_eci,
+_get_errno, _set_errno = CExternVariable(INT, 'errno', eci,
                                          CConstantErrno, sandboxsafe=True,
                                          _nowrapper=True, c_type='int')
 # the default wrapper for set_errno is not suitable for use in critical places
@@ -35,12 +92,29 @@
 def set_errno(errno):
     _set_errno(rffi.cast(INT, errno))
 
+if os.name == 'nt':
+    def validate_fd_emulator(fd):
+        try:
+            os.fstat(fd)
+            return 1
+        except:
+            return 0
 
+    validate_fd = rffi.llexternal(
+        "_PyVerify_fd", [rffi.INT], rffi.INT,
+        _callable=validate_fd_emulator, compilation_info=eci,
+        _nowrapper=True, elidable_function=True, sandboxsafe=True,
+        )
+else:
+    def validate_fd(fd):
+        return 1
+    
 def closerange(fd_low, fd_high):
     # this behaves like os.closerange() from Python 2.6.
     for fd in xrange(fd_low, fd_high):
         try:
-            os.close(fd)
+            if validate_fd(fd):
+                os.close(fd)
         except OSError:
             pass
 
diff --git a/pypy/rlib/test/test_rposix.py b/pypy/rlib/test/test_rposix.py
--- a/pypy/rlib/test/test_rposix.py
+++ b/pypy/rlib/test/test_rposix.py
@@ -131,3 +131,13 @@
                 os.rmdir(self.ufilename)
             except Exception:
                 pass
+
+    def test_validate_fd(self):
+        assert rposix.validate_fd(0) == 1
+        fid = open(str(udir.join('validate_test.txt')), 'w')
+        fd = fid.fileno()
+        assert rposix.validate_fd(fd) == 1
+        fid.close()
+        assert rposix.validate_fd(fd) == 0
+
+
diff --git a/pypy/rpython/module/ll_os.py b/pypy/rpython/module/ll_os.py
--- a/pypy/rpython/module/ll_os.py
+++ b/pypy/rpython/module/ll_os.py
@@ -916,6 +916,8 @@
 
         def os_write_llimpl(fd, data):
             count = len(data)
+            if not rposix.validate_fd(fd):
+                raise OSError(rposix.get_errno(), 'Bad file descriptor')
             buf = rffi.get_nonmovingbuffer(data)
             try:
                 written = rffi.cast(lltype.Signed, os_write(
diff --git a/pypy/rpython/test/test_llinterp.py b/pypy/rpython/test/test_llinterp.py
--- a/pypy/rpython/test/test_llinterp.py
+++ b/pypy/rpython/test/test_llinterp.py
@@ -139,7 +139,7 @@
         got = interp.find_exception(info.value)
     except ValueError:
         got = None
-    assert got is exc, "wrong exception type"
+    assert got is exc, "wrong exception type, expected %r got %r"%(exc, got)
 
 #__________________________________________________________________
 # tests
diff --git a/pypy/rpython/test/test_rbuiltin.py b/pypy/rpython/test/test_rbuiltin.py
--- a/pypy/rpython/test/test_rbuiltin.py
+++ b/pypy/rpython/test/test_rbuiltin.py
@@ -201,6 +201,14 @@
         os.close(res)
         hello = open(tmpdir).read()
         assert hello == "hello world"
+        def throws(fname):
+            fd = os.open(fname, os.O_WRONLY|os.O_CREAT, 777)
+            os.close(fd)
+            os.write(fd, "hello world")
+            return fd
+        def g():
+            return throws(tmpdir)
+        self.interpret_raises(OSError, g, [])
 
     def test_os_write_single_char(self):
         tmpdir = str(udir.udir.join("os_write_test_char"))
diff --git a/pypy/translator/c/src/main.h b/pypy/translator/c/src/main.h
--- a/pypy/translator/c/src/main.h
+++ b/pypy/translator/c/src/main.h
@@ -20,7 +20,7 @@
 #endif
 
 #ifdef MS_WINDOWS
-#include "src/winstuff.c"
+/*#include "src/winstuff.c"*/
 #endif
 
 #ifdef __GNUC__


More information about the pypy-commit mailing list