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