##// END OF EJS Templates
osutil: implement pure version of recvfds() for PyPy...
Yuya Nishihara -
r27474:e517a89c default
parent child Browse files
Show More
@@ -1,173 +1,256 b''
1 1 # osutil.py - pure Python version of osutil.c
2 2 #
3 3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 import ctypes
11 import ctypes.util
10 12 import os
13 import socket
11 14 import stat as statmod
15 import sys
12 16
13 17 def _mode_to_kind(mode):
14 18 if statmod.S_ISREG(mode):
15 19 return statmod.S_IFREG
16 20 if statmod.S_ISDIR(mode):
17 21 return statmod.S_IFDIR
18 22 if statmod.S_ISLNK(mode):
19 23 return statmod.S_IFLNK
20 24 if statmod.S_ISBLK(mode):
21 25 return statmod.S_IFBLK
22 26 if statmod.S_ISCHR(mode):
23 27 return statmod.S_IFCHR
24 28 if statmod.S_ISFIFO(mode):
25 29 return statmod.S_IFIFO
26 30 if statmod.S_ISSOCK(mode):
27 31 return statmod.S_IFSOCK
28 32 return mode
29 33
30 34 def listdir(path, stat=False, skip=None):
31 35 '''listdir(path, stat=False) -> list_of_tuples
32 36
33 37 Return a sorted list containing information about the entries
34 38 in the directory.
35 39
36 40 If stat is True, each element is a 3-tuple:
37 41
38 42 (name, type, stat object)
39 43
40 44 Otherwise, each element is a 2-tuple:
41 45
42 46 (name, type)
43 47 '''
44 48 result = []
45 49 prefix = path
46 50 if not prefix.endswith(os.sep):
47 51 prefix += os.sep
48 52 names = os.listdir(path)
49 53 names.sort()
50 54 for fn in names:
51 55 st = os.lstat(prefix + fn)
52 56 if fn == skip and statmod.S_ISDIR(st.st_mode):
53 57 return []
54 58 if stat:
55 59 result.append((fn, _mode_to_kind(st.st_mode), st))
56 60 else:
57 61 result.append((fn, _mode_to_kind(st.st_mode)))
58 62 return result
59 63
60 64 if os.name != 'nt':
61 65 posixfile = open
66
67 _SCM_RIGHTS = 0x01
68 _socklen_t = ctypes.c_uint
69
70 if sys.platform == 'linux2':
71 # socket.h says "the type should be socklen_t but the definition of
72 # the kernel is incompatible with this."
73 _cmsg_len_t = ctypes.c_size_t
74 _msg_controllen_t = ctypes.c_size_t
75 _msg_iovlen_t = ctypes.c_size_t
62 76 else:
63 import ctypes
77 _cmsg_len_t = _socklen_t
78 _msg_controllen_t = _socklen_t
79 _msg_iovlen_t = ctypes.c_int
80
81 class _iovec(ctypes.Structure):
82 _fields_ = [
83 ('iov_base', ctypes.c_void_p),
84 ('iov_len', ctypes.c_size_t),
85 ]
86
87 class _msghdr(ctypes.Structure):
88 _fields_ = [
89 ('msg_name', ctypes.c_void_p),
90 ('msg_namelen', _socklen_t),
91 ('msg_iov', ctypes.POINTER(_iovec)),
92 ('msg_iovlen', _msg_iovlen_t),
93 ('msg_control', ctypes.c_void_p),
94 ('msg_controllen', _msg_controllen_t),
95 ('msg_flags', ctypes.c_int),
96 ]
97
98 class _cmsghdr(ctypes.Structure):
99 _fields_ = [
100 ('cmsg_len', _cmsg_len_t),
101 ('cmsg_level', ctypes.c_int),
102 ('cmsg_type', ctypes.c_int),
103 ('cmsg_data', ctypes.c_ubyte * 0),
104 ]
105
106 _libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
107 _recvmsg = _libc.recvmsg
108 _recvmsg.restype = ctypes.c_ssize_t
109 _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr), ctypes.c_int)
110
111 def _CMSG_FIRSTHDR(msgh):
112 if msgh.msg_controllen < ctypes.sizeof(_cmsghdr):
113 return
114 cmsgptr = ctypes.cast(msgh.msg_control, ctypes.POINTER(_cmsghdr))
115 return cmsgptr.contents
116
117 # The pure version is less portable than the native version because the
118 # handling of socket ancillary data heavily depends on C preprocessor.
119 # Also, some length fields are wrongly typed in Linux kernel.
120 def recvfds(sockfd):
121 """receive list of file descriptors via socket"""
122 dummy = (ctypes.c_ubyte * 1)()
123 iov = _iovec(ctypes.cast(dummy, ctypes.c_void_p), ctypes.sizeof(dummy))
124 cbuf = ctypes.create_string_buffer(256)
125 msgh = _msghdr(None, 0,
126 ctypes.pointer(iov), 1,
127 ctypes.cast(cbuf, ctypes.c_void_p), ctypes.sizeof(cbuf),
128 0)
129 r = _recvmsg(sockfd, ctypes.byref(msgh), 0)
130 if r < 0:
131 e = ctypes.get_errno()
132 raise OSError(e, os.strerror(e))
133 # assumes that the first cmsg has fds because it isn't easy to write
134 # portable CMSG_NXTHDR() with ctypes.
135 cmsg = _CMSG_FIRSTHDR(msgh)
136 if not cmsg:
137 return []
138 if (cmsg.cmsg_level != socket.SOL_SOCKET or
139 cmsg.cmsg_type != _SCM_RIGHTS):
140 return []
141 rfds = ctypes.cast(cmsg.cmsg_data, ctypes.POINTER(ctypes.c_int))
142 rfdscount = ((cmsg.cmsg_len - _cmsghdr.cmsg_data.offset) /
143 ctypes.sizeof(ctypes.c_int))
144 return [rfds[i] for i in xrange(rfdscount)]
145
146 else:
64 147 import msvcrt
65 148
66 149 _kernel32 = ctypes.windll.kernel32
67 150
68 151 _DWORD = ctypes.c_ulong
69 152 _LPCSTR = _LPSTR = ctypes.c_char_p
70 153 _HANDLE = ctypes.c_void_p
71 154
72 155 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
73 156
74 157 # CreateFile
75 158 _FILE_SHARE_READ = 0x00000001
76 159 _FILE_SHARE_WRITE = 0x00000002
77 160 _FILE_SHARE_DELETE = 0x00000004
78 161
79 162 _CREATE_ALWAYS = 2
80 163 _OPEN_EXISTING = 3
81 164 _OPEN_ALWAYS = 4
82 165
83 166 _GENERIC_READ = 0x80000000
84 167 _GENERIC_WRITE = 0x40000000
85 168
86 169 _FILE_ATTRIBUTE_NORMAL = 0x80
87 170
88 171 # open_osfhandle flags
89 172 _O_RDONLY = 0x0000
90 173 _O_RDWR = 0x0002
91 174 _O_APPEND = 0x0008
92 175
93 176 _O_TEXT = 0x4000
94 177 _O_BINARY = 0x8000
95 178
96 179 # types of parameters of C functions used (required by pypy)
97 180
98 181 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
99 182 _DWORD, _DWORD, _HANDLE]
100 183 _kernel32.CreateFileA.restype = _HANDLE
101 184
102 185 def _raiseioerror(name):
103 186 err = ctypes.WinError()
104 187 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
105 188
106 189 class posixfile(object):
107 190 '''a file object aiming for POSIX-like semantics
108 191
109 192 CPython's open() returns a file that was opened *without* setting the
110 193 _FILE_SHARE_DELETE flag, which causes rename and unlink to abort.
111 194 This even happens if any hardlinked copy of the file is in open state.
112 195 We set _FILE_SHARE_DELETE here, so files opened with posixfile can be
113 196 renamed and deleted while they are held open.
114 197 Note that if a file opened with posixfile is unlinked, the file
115 198 remains but cannot be opened again or be recreated under the same name,
116 199 until all reading processes have closed the file.'''
117 200
118 201 def __init__(self, name, mode='r', bufsize=-1):
119 202 if 'b' in mode:
120 203 flags = _O_BINARY
121 204 else:
122 205 flags = _O_TEXT
123 206
124 207 m0 = mode[0]
125 208 if m0 == 'r' and '+' not in mode:
126 209 flags |= _O_RDONLY
127 210 access = _GENERIC_READ
128 211 else:
129 212 # work around http://support.microsoft.com/kb/899149 and
130 213 # set _O_RDWR for 'w' and 'a', even if mode has no '+'
131 214 flags |= _O_RDWR
132 215 access = _GENERIC_READ | _GENERIC_WRITE
133 216
134 217 if m0 == 'r':
135 218 creation = _OPEN_EXISTING
136 219 elif m0 == 'w':
137 220 creation = _CREATE_ALWAYS
138 221 elif m0 == 'a':
139 222 creation = _OPEN_ALWAYS
140 223 flags |= _O_APPEND
141 224 else:
142 225 raise ValueError("invalid mode: %s" % mode)
143 226
144 227 fh = _kernel32.CreateFileA(name, access,
145 228 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
146 229 None, creation, _FILE_ATTRIBUTE_NORMAL, None)
147 230 if fh == _INVALID_HANDLE_VALUE:
148 231 _raiseioerror(name)
149 232
150 233 fd = msvcrt.open_osfhandle(fh, flags)
151 234 if fd == -1:
152 235 _kernel32.CloseHandle(fh)
153 236 _raiseioerror(name)
154 237
155 238 f = os.fdopen(fd, mode, bufsize)
156 239 # unfortunately, f.name is '<fdopen>' at this point -- so we store
157 240 # the name on this wrapper. We cannot just assign to f.name,
158 241 # because that attribute is read-only.
159 242 object.__setattr__(self, 'name', name)
160 243 object.__setattr__(self, '_file', f)
161 244
162 245 def __iter__(self):
163 246 return self._file
164 247
165 248 def __getattr__(self, name):
166 249 return getattr(self._file, name)
167 250
168 251 def __setattr__(self, name, value):
169 252 '''mimics the read-only attributes of Python file objects
170 253 by raising 'TypeError: readonly attribute' if someone tries:
171 254 f = posixfile('foo.txt')
172 255 f.name = 'bla' '''
173 256 return self._file.__setattr__(name, value)
General Comments 0
You need to be logged in to leave comments. Login now