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