##// END OF EJS Templates
osutil: handle deletion race with readdir/stat (issue3463)
Matt Mackall -
r16747:6476a213 stable
parent child Browse files
Show More
@@ -1,578 +1,581 b''
1 /*
1 /*
2 osutil.c - native operating system services
2 osutil.c - native operating system services
3
3
4 Copyright 2007 Matt Mackall and others
4 Copyright 2007 Matt Mackall and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #define _ATFILE_SOURCE
10 #define _ATFILE_SOURCE
11 #include <Python.h>
11 #include <Python.h>
12 #include <fcntl.h>
12 #include <fcntl.h>
13 #include <stdio.h>
13 #include <stdio.h>
14 #include <string.h>
14 #include <string.h>
15 #include <errno.h>
15 #include <errno.h>
16
16
17 #ifdef _WIN32
17 #ifdef _WIN32
18 #include <windows.h>
18 #include <windows.h>
19 #include <io.h>
19 #include <io.h>
20 #else
20 #else
21 #include <dirent.h>
21 #include <dirent.h>
22 #include <sys/stat.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
23 #include <sys/types.h>
24 #include <unistd.h>
24 #include <unistd.h>
25 #endif
25 #endif
26
26
27 #include "util.h"
27 #include "util.h"
28
28
29 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
29 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
30 #ifndef PATH_MAX
30 #ifndef PATH_MAX
31 #define PATH_MAX 4096
31 #define PATH_MAX 4096
32 #endif
32 #endif
33
33
34 #ifdef _WIN32
34 #ifdef _WIN32
35 /*
35 /*
36 stat struct compatible with hg expectations
36 stat struct compatible with hg expectations
37 Mercurial only uses st_mode, st_size and st_mtime
37 Mercurial only uses st_mode, st_size and st_mtime
38 the rest is kept to minimize changes between implementations
38 the rest is kept to minimize changes between implementations
39 */
39 */
40 struct hg_stat {
40 struct hg_stat {
41 int st_dev;
41 int st_dev;
42 int st_mode;
42 int st_mode;
43 int st_nlink;
43 int st_nlink;
44 __int64 st_size;
44 __int64 st_size;
45 int st_mtime;
45 int st_mtime;
46 int st_ctime;
46 int st_ctime;
47 };
47 };
48 struct listdir_stat {
48 struct listdir_stat {
49 PyObject_HEAD
49 PyObject_HEAD
50 struct hg_stat st;
50 struct hg_stat st;
51 };
51 };
52 #else
52 #else
53 struct listdir_stat {
53 struct listdir_stat {
54 PyObject_HEAD
54 PyObject_HEAD
55 struct stat st;
55 struct stat st;
56 };
56 };
57 #endif
57 #endif
58
58
59 #define listdir_slot(name) \
59 #define listdir_slot(name) \
60 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
60 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
61 { \
61 { \
62 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
62 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
63 }
63 }
64
64
65 listdir_slot(st_dev)
65 listdir_slot(st_dev)
66 listdir_slot(st_mode)
66 listdir_slot(st_mode)
67 listdir_slot(st_nlink)
67 listdir_slot(st_nlink)
68 #ifdef _WIN32
68 #ifdef _WIN32
69 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
69 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
70 {
70 {
71 return PyLong_FromLongLong(
71 return PyLong_FromLongLong(
72 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
72 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
73 }
73 }
74 #else
74 #else
75 listdir_slot(st_size)
75 listdir_slot(st_size)
76 #endif
76 #endif
77 listdir_slot(st_mtime)
77 listdir_slot(st_mtime)
78 listdir_slot(st_ctime)
78 listdir_slot(st_ctime)
79
79
80 static struct PyGetSetDef listdir_stat_getsets[] = {
80 static struct PyGetSetDef listdir_stat_getsets[] = {
81 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
81 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
82 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
82 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
83 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
83 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
84 {"st_size", listdir_stat_st_size, 0, 0, 0},
84 {"st_size", listdir_stat_st_size, 0, 0, 0},
85 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
85 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
86 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
86 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
87 {0, 0, 0, 0, 0}
87 {0, 0, 0, 0, 0}
88 };
88 };
89
89
90 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
90 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
91 {
91 {
92 return t->tp_alloc(t, 0);
92 return t->tp_alloc(t, 0);
93 }
93 }
94
94
95 static void listdir_stat_dealloc(PyObject *o)
95 static void listdir_stat_dealloc(PyObject *o)
96 {
96 {
97 o->ob_type->tp_free(o);
97 o->ob_type->tp_free(o);
98 }
98 }
99
99
100 static PyTypeObject listdir_stat_type = {
100 static PyTypeObject listdir_stat_type = {
101 PyVarObject_HEAD_INIT(NULL, 0)
101 PyVarObject_HEAD_INIT(NULL, 0)
102 "osutil.stat", /*tp_name*/
102 "osutil.stat", /*tp_name*/
103 sizeof(struct listdir_stat), /*tp_basicsize*/
103 sizeof(struct listdir_stat), /*tp_basicsize*/
104 0, /*tp_itemsize*/
104 0, /*tp_itemsize*/
105 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
105 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
106 0, /*tp_print*/
106 0, /*tp_print*/
107 0, /*tp_getattr*/
107 0, /*tp_getattr*/
108 0, /*tp_setattr*/
108 0, /*tp_setattr*/
109 0, /*tp_compare*/
109 0, /*tp_compare*/
110 0, /*tp_repr*/
110 0, /*tp_repr*/
111 0, /*tp_as_number*/
111 0, /*tp_as_number*/
112 0, /*tp_as_sequence*/
112 0, /*tp_as_sequence*/
113 0, /*tp_as_mapping*/
113 0, /*tp_as_mapping*/
114 0, /*tp_hash */
114 0, /*tp_hash */
115 0, /*tp_call*/
115 0, /*tp_call*/
116 0, /*tp_str*/
116 0, /*tp_str*/
117 0, /*tp_getattro*/
117 0, /*tp_getattro*/
118 0, /*tp_setattro*/
118 0, /*tp_setattro*/
119 0, /*tp_as_buffer*/
119 0, /*tp_as_buffer*/
120 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
120 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
121 "stat objects", /* tp_doc */
121 "stat objects", /* tp_doc */
122 0, /* tp_traverse */
122 0, /* tp_traverse */
123 0, /* tp_clear */
123 0, /* tp_clear */
124 0, /* tp_richcompare */
124 0, /* tp_richcompare */
125 0, /* tp_weaklistoffset */
125 0, /* tp_weaklistoffset */
126 0, /* tp_iter */
126 0, /* tp_iter */
127 0, /* tp_iternext */
127 0, /* tp_iternext */
128 0, /* tp_methods */
128 0, /* tp_methods */
129 0, /* tp_members */
129 0, /* tp_members */
130 listdir_stat_getsets, /* tp_getset */
130 listdir_stat_getsets, /* tp_getset */
131 0, /* tp_base */
131 0, /* tp_base */
132 0, /* tp_dict */
132 0, /* tp_dict */
133 0, /* tp_descr_get */
133 0, /* tp_descr_get */
134 0, /* tp_descr_set */
134 0, /* tp_descr_set */
135 0, /* tp_dictoffset */
135 0, /* tp_dictoffset */
136 0, /* tp_init */
136 0, /* tp_init */
137 0, /* tp_alloc */
137 0, /* tp_alloc */
138 listdir_stat_new, /* tp_new */
138 listdir_stat_new, /* tp_new */
139 };
139 };
140
140
141 #ifdef _WIN32
141 #ifdef _WIN32
142
142
143 static int to_python_time(const FILETIME *tm)
143 static int to_python_time(const FILETIME *tm)
144 {
144 {
145 /* number of seconds between epoch and January 1 1601 */
145 /* number of seconds between epoch and January 1 1601 */
146 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
146 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
147 /* conversion factor from 100ns to 1s */
147 /* conversion factor from 100ns to 1s */
148 const __int64 a1 = 10000000;
148 const __int64 a1 = 10000000;
149 /* explicit (int) cast to suspend compiler warnings */
149 /* explicit (int) cast to suspend compiler warnings */
150 return (int)((((__int64)tm->dwHighDateTime << 32)
150 return (int)((((__int64)tm->dwHighDateTime << 32)
151 + tm->dwLowDateTime) / a1 - a0);
151 + tm->dwLowDateTime) / a1 - a0);
152 }
152 }
153
153
154 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
154 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
155 {
155 {
156 PyObject *py_st;
156 PyObject *py_st;
157 struct hg_stat *stp;
157 struct hg_stat *stp;
158
158
159 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
159 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
160 ? _S_IFDIR : _S_IFREG;
160 ? _S_IFDIR : _S_IFREG;
161
161
162 if (!wantstat)
162 if (!wantstat)
163 return Py_BuildValue("si", fd->cFileName, kind);
163 return Py_BuildValue("si", fd->cFileName, kind);
164
164
165 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
165 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
166 if (!py_st)
166 if (!py_st)
167 return NULL;
167 return NULL;
168
168
169 stp = &((struct listdir_stat *)py_st)->st;
169 stp = &((struct listdir_stat *)py_st)->st;
170 /*
170 /*
171 use kind as st_mode
171 use kind as st_mode
172 rwx bits on Win32 are meaningless
172 rwx bits on Win32 are meaningless
173 and Hg does not use them anyway
173 and Hg does not use them anyway
174 */
174 */
175 stp->st_mode = kind;
175 stp->st_mode = kind;
176 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
176 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
177 stp->st_ctime = to_python_time(&fd->ftCreationTime);
177 stp->st_ctime = to_python_time(&fd->ftCreationTime);
178 if (kind == _S_IFREG)
178 if (kind == _S_IFREG)
179 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
179 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
180 + fd->nFileSizeLow;
180 + fd->nFileSizeLow;
181 return Py_BuildValue("siN", fd->cFileName,
181 return Py_BuildValue("siN", fd->cFileName,
182 kind, py_st);
182 kind, py_st);
183 }
183 }
184
184
185 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
185 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
186 {
186 {
187 PyObject *rval = NULL; /* initialize - return value */
187 PyObject *rval = NULL; /* initialize - return value */
188 PyObject *list;
188 PyObject *list;
189 HANDLE fh;
189 HANDLE fh;
190 WIN32_FIND_DATAA fd;
190 WIN32_FIND_DATAA fd;
191 char *pattern;
191 char *pattern;
192
192
193 /* build the path + \* pattern string */
193 /* build the path + \* pattern string */
194 pattern = malloc(plen + 3); /* path + \* + \0 */
194 pattern = malloc(plen + 3); /* path + \* + \0 */
195 if (!pattern) {
195 if (!pattern) {
196 PyErr_NoMemory();
196 PyErr_NoMemory();
197 goto error_nomem;
197 goto error_nomem;
198 }
198 }
199 strcpy(pattern, path);
199 strcpy(pattern, path);
200
200
201 if (plen > 0) {
201 if (plen > 0) {
202 char c = path[plen-1];
202 char c = path[plen-1];
203 if (c != ':' && c != '/' && c != '\\')
203 if (c != ':' && c != '/' && c != '\\')
204 pattern[plen++] = '\\';
204 pattern[plen++] = '\\';
205 }
205 }
206 strcpy(pattern + plen, "*");
206 strcpy(pattern + plen, "*");
207
207
208 fh = FindFirstFileA(pattern, &fd);
208 fh = FindFirstFileA(pattern, &fd);
209 if (fh == INVALID_HANDLE_VALUE) {
209 if (fh == INVALID_HANDLE_VALUE) {
210 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
210 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
211 goto error_file;
211 goto error_file;
212 }
212 }
213
213
214 list = PyList_New(0);
214 list = PyList_New(0);
215 if (!list)
215 if (!list)
216 goto error_list;
216 goto error_list;
217
217
218 do {
218 do {
219 PyObject *item;
219 PyObject *item;
220
220
221 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
221 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
222 if (!strcmp(fd.cFileName, ".")
222 if (!strcmp(fd.cFileName, ".")
223 || !strcmp(fd.cFileName, ".."))
223 || !strcmp(fd.cFileName, ".."))
224 continue;
224 continue;
225
225
226 if (skip && !strcmp(fd.cFileName, skip)) {
226 if (skip && !strcmp(fd.cFileName, skip)) {
227 rval = PyList_New(0);
227 rval = PyList_New(0);
228 goto error;
228 goto error;
229 }
229 }
230 }
230 }
231
231
232 item = make_item(&fd, wantstat);
232 item = make_item(&fd, wantstat);
233 if (!item)
233 if (!item)
234 goto error;
234 goto error;
235
235
236 if (PyList_Append(list, item)) {
236 if (PyList_Append(list, item)) {
237 Py_XDECREF(item);
237 Py_XDECREF(item);
238 goto error;
238 goto error;
239 }
239 }
240
240
241 Py_XDECREF(item);
241 Py_XDECREF(item);
242 } while (FindNextFileA(fh, &fd));
242 } while (FindNextFileA(fh, &fd));
243
243
244 if (GetLastError() != ERROR_NO_MORE_FILES) {
244 if (GetLastError() != ERROR_NO_MORE_FILES) {
245 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
245 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
246 goto error;
246 goto error;
247 }
247 }
248
248
249 rval = list;
249 rval = list;
250 Py_XINCREF(rval);
250 Py_XINCREF(rval);
251 error:
251 error:
252 Py_XDECREF(list);
252 Py_XDECREF(list);
253 error_list:
253 error_list:
254 FindClose(fh);
254 FindClose(fh);
255 error_file:
255 error_file:
256 free(pattern);
256 free(pattern);
257 error_nomem:
257 error_nomem:
258 return rval;
258 return rval;
259 }
259 }
260
260
261 #else
261 #else
262
262
263 int entkind(struct dirent *ent)
263 int entkind(struct dirent *ent)
264 {
264 {
265 #ifdef DT_REG
265 #ifdef DT_REG
266 switch (ent->d_type) {
266 switch (ent->d_type) {
267 case DT_REG: return S_IFREG;
267 case DT_REG: return S_IFREG;
268 case DT_DIR: return S_IFDIR;
268 case DT_DIR: return S_IFDIR;
269 case DT_LNK: return S_IFLNK;
269 case DT_LNK: return S_IFLNK;
270 case DT_BLK: return S_IFBLK;
270 case DT_BLK: return S_IFBLK;
271 case DT_CHR: return S_IFCHR;
271 case DT_CHR: return S_IFCHR;
272 case DT_FIFO: return S_IFIFO;
272 case DT_FIFO: return S_IFIFO;
273 case DT_SOCK: return S_IFSOCK;
273 case DT_SOCK: return S_IFSOCK;
274 }
274 }
275 #endif
275 #endif
276 return -1;
276 return -1;
277 }
277 }
278
278
279 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
279 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
280 {
280 {
281 PyObject *list, *elem, *stat, *ret = NULL;
281 PyObject *list, *elem, *stat, *ret = NULL;
282 char fullpath[PATH_MAX + 10];
282 char fullpath[PATH_MAX + 10];
283 int kind, err;
283 int kind, err;
284 struct stat st;
284 struct stat st;
285 struct dirent *ent;
285 struct dirent *ent;
286 DIR *dir;
286 DIR *dir;
287 #ifdef AT_SYMLINK_NOFOLLOW
287 #ifdef AT_SYMLINK_NOFOLLOW
288 int dfd = -1;
288 int dfd = -1;
289 #endif
289 #endif
290
290
291 if (pathlen >= PATH_MAX) {
291 if (pathlen >= PATH_MAX) {
292 errno = ENAMETOOLONG;
292 errno = ENAMETOOLONG;
293 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
293 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
294 goto error_value;
294 goto error_value;
295 }
295 }
296 strncpy(fullpath, path, PATH_MAX);
296 strncpy(fullpath, path, PATH_MAX);
297 fullpath[pathlen] = '/';
297 fullpath[pathlen] = '/';
298
298
299 #ifdef AT_SYMLINK_NOFOLLOW
299 #ifdef AT_SYMLINK_NOFOLLOW
300 dfd = open(path, O_RDONLY);
300 dfd = open(path, O_RDONLY);
301 if (dfd == -1) {
301 if (dfd == -1) {
302 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
302 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
303 goto error_value;
303 goto error_value;
304 }
304 }
305 dir = fdopendir(dfd);
305 dir = fdopendir(dfd);
306 #else
306 #else
307 dir = opendir(path);
307 dir = opendir(path);
308 #endif
308 #endif
309 if (!dir) {
309 if (!dir) {
310 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
310 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
311 goto error_dir;
311 goto error_dir;
312 }
312 }
313
313
314 list = PyList_New(0);
314 list = PyList_New(0);
315 if (!list)
315 if (!list)
316 goto error_list;
316 goto error_list;
317
317
318 while ((ent = readdir(dir))) {
318 while ((ent = readdir(dir))) {
319 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
319 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
320 continue;
320 continue;
321
321
322 kind = entkind(ent);
322 kind = entkind(ent);
323 if (kind == -1 || keepstat) {
323 if (kind == -1 || keepstat) {
324 #ifdef AT_SYMLINK_NOFOLLOW
324 #ifdef AT_SYMLINK_NOFOLLOW
325 err = fstatat(dfd, ent->d_name, &st,
325 err = fstatat(dfd, ent->d_name, &st,
326 AT_SYMLINK_NOFOLLOW);
326 AT_SYMLINK_NOFOLLOW);
327 #else
327 #else
328 strncpy(fullpath + pathlen + 1, ent->d_name,
328 strncpy(fullpath + pathlen + 1, ent->d_name,
329 PATH_MAX - pathlen);
329 PATH_MAX - pathlen);
330 fullpath[PATH_MAX] = 0;
330 fullpath[PATH_MAX] = 0;
331 err = lstat(fullpath, &st);
331 err = lstat(fullpath, &st);
332 #endif
332 #endif
333 if (err == -1) {
333 if (err == -1) {
334 /* race with file deletion? */
335 if (errno == ENOENT)
336 continue;
334 strncpy(fullpath + pathlen + 1, ent->d_name,
337 strncpy(fullpath + pathlen + 1, ent->d_name,
335 PATH_MAX - pathlen);
338 PATH_MAX - pathlen);
336 fullpath[PATH_MAX] = 0;
339 fullpath[PATH_MAX] = 0;
337 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
340 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
338 fullpath);
341 fullpath);
339 goto error;
342 goto error;
340 }
343 }
341 kind = st.st_mode & S_IFMT;
344 kind = st.st_mode & S_IFMT;
342 }
345 }
343
346
344 /* quit early? */
347 /* quit early? */
345 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
348 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
346 ret = PyList_New(0);
349 ret = PyList_New(0);
347 goto error;
350 goto error;
348 }
351 }
349
352
350 if (keepstat) {
353 if (keepstat) {
351 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
354 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
352 if (!stat)
355 if (!stat)
353 goto error;
356 goto error;
354 memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
357 memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
355 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
358 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
356 } else
359 } else
357 elem = Py_BuildValue("si", ent->d_name, kind);
360 elem = Py_BuildValue("si", ent->d_name, kind);
358 if (!elem)
361 if (!elem)
359 goto error;
362 goto error;
360
363
361 PyList_Append(list, elem);
364 PyList_Append(list, elem);
362 Py_DECREF(elem);
365 Py_DECREF(elem);
363 }
366 }
364
367
365 ret = list;
368 ret = list;
366 Py_INCREF(ret);
369 Py_INCREF(ret);
367
370
368 error:
371 error:
369 Py_DECREF(list);
372 Py_DECREF(list);
370 error_list:
373 error_list:
371 closedir(dir);
374 closedir(dir);
372 error_dir:
375 error_dir:
373 #ifdef AT_SYMLINK_NOFOLLOW
376 #ifdef AT_SYMLINK_NOFOLLOW
374 close(dfd);
377 close(dfd);
375 #endif
378 #endif
376 error_value:
379 error_value:
377 return ret;
380 return ret;
378 }
381 }
379
382
380 #endif /* ndef _WIN32 */
383 #endif /* ndef _WIN32 */
381
384
382 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
385 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
383 {
386 {
384 PyObject *statobj = NULL; /* initialize - optional arg */
387 PyObject *statobj = NULL; /* initialize - optional arg */
385 PyObject *skipobj = NULL; /* initialize - optional arg */
388 PyObject *skipobj = NULL; /* initialize - optional arg */
386 char *path, *skip = NULL;
389 char *path, *skip = NULL;
387 int wantstat, plen;
390 int wantstat, plen;
388
391
389 static char *kwlist[] = {"path", "stat", "skip", NULL};
392 static char *kwlist[] = {"path", "stat", "skip", NULL};
390
393
391 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
394 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
392 kwlist, &path, &plen, &statobj, &skipobj))
395 kwlist, &path, &plen, &statobj, &skipobj))
393 return NULL;
396 return NULL;
394
397
395 wantstat = statobj && PyObject_IsTrue(statobj);
398 wantstat = statobj && PyObject_IsTrue(statobj);
396
399
397 if (skipobj && skipobj != Py_None) {
400 if (skipobj && skipobj != Py_None) {
398 skip = PyBytes_AsString(skipobj);
401 skip = PyBytes_AsString(skipobj);
399 if (!skip)
402 if (!skip)
400 return NULL;
403 return NULL;
401 }
404 }
402
405
403 return _listdir(path, plen, wantstat, skip);
406 return _listdir(path, plen, wantstat, skip);
404 }
407 }
405
408
406 #ifdef _WIN32
409 #ifdef _WIN32
407 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
410 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
408 {
411 {
409 static char *kwlist[] = {"name", "mode", "buffering", NULL};
412 static char *kwlist[] = {"name", "mode", "buffering", NULL};
410 PyObject *file_obj = NULL;
413 PyObject *file_obj = NULL;
411 char *name = NULL;
414 char *name = NULL;
412 char *mode = "rb";
415 char *mode = "rb";
413 DWORD access = 0;
416 DWORD access = 0;
414 DWORD creation;
417 DWORD creation;
415 HANDLE handle;
418 HANDLE handle;
416 int fd, flags = 0;
419 int fd, flags = 0;
417 int bufsize = -1;
420 int bufsize = -1;
418 char m0, m1, m2;
421 char m0, m1, m2;
419 char fpmode[4];
422 char fpmode[4];
420 int fppos = 0;
423 int fppos = 0;
421 int plus;
424 int plus;
422 FILE *fp;
425 FILE *fp;
423
426
424 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
427 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
425 Py_FileSystemDefaultEncoding,
428 Py_FileSystemDefaultEncoding,
426 &name, &mode, &bufsize))
429 &name, &mode, &bufsize))
427 return NULL;
430 return NULL;
428
431
429 m0 = mode[0];
432 m0 = mode[0];
430 m1 = m0 ? mode[1] : '\0';
433 m1 = m0 ? mode[1] : '\0';
431 m2 = m1 ? mode[2] : '\0';
434 m2 = m1 ? mode[2] : '\0';
432 plus = m1 == '+' || m2 == '+';
435 plus = m1 == '+' || m2 == '+';
433
436
434 fpmode[fppos++] = m0;
437 fpmode[fppos++] = m0;
435 if (m1 == 'b' || m2 == 'b') {
438 if (m1 == 'b' || m2 == 'b') {
436 flags = _O_BINARY;
439 flags = _O_BINARY;
437 fpmode[fppos++] = 'b';
440 fpmode[fppos++] = 'b';
438 }
441 }
439 else
442 else
440 flags = _O_TEXT;
443 flags = _O_TEXT;
441 if (m0 == 'r' && !plus) {
444 if (m0 == 'r' && !plus) {
442 flags |= _O_RDONLY;
445 flags |= _O_RDONLY;
443 access = GENERIC_READ;
446 access = GENERIC_READ;
444 } else {
447 } else {
445 /*
448 /*
446 work around http://support.microsoft.com/kb/899149 and
449 work around http://support.microsoft.com/kb/899149 and
447 set _O_RDWR for 'w' and 'a', even if mode has no '+'
450 set _O_RDWR for 'w' and 'a', even if mode has no '+'
448 */
451 */
449 flags |= _O_RDWR;
452 flags |= _O_RDWR;
450 access = GENERIC_READ | GENERIC_WRITE;
453 access = GENERIC_READ | GENERIC_WRITE;
451 fpmode[fppos++] = '+';
454 fpmode[fppos++] = '+';
452 }
455 }
453 fpmode[fppos++] = '\0';
456 fpmode[fppos++] = '\0';
454
457
455 switch (m0) {
458 switch (m0) {
456 case 'r':
459 case 'r':
457 creation = OPEN_EXISTING;
460 creation = OPEN_EXISTING;
458 break;
461 break;
459 case 'w':
462 case 'w':
460 creation = CREATE_ALWAYS;
463 creation = CREATE_ALWAYS;
461 break;
464 break;
462 case 'a':
465 case 'a':
463 creation = OPEN_ALWAYS;
466 creation = OPEN_ALWAYS;
464 flags |= _O_APPEND;
467 flags |= _O_APPEND;
465 break;
468 break;
466 default:
469 default:
467 PyErr_Format(PyExc_ValueError,
470 PyErr_Format(PyExc_ValueError,
468 "mode string must begin with one of 'r', 'w', "
471 "mode string must begin with one of 'r', 'w', "
469 "or 'a', not '%c'", m0);
472 "or 'a', not '%c'", m0);
470 goto bail;
473 goto bail;
471 }
474 }
472
475
473 handle = CreateFile(name, access,
476 handle = CreateFile(name, access,
474 FILE_SHARE_READ | FILE_SHARE_WRITE |
477 FILE_SHARE_READ | FILE_SHARE_WRITE |
475 FILE_SHARE_DELETE,
478 FILE_SHARE_DELETE,
476 NULL,
479 NULL,
477 creation,
480 creation,
478 FILE_ATTRIBUTE_NORMAL,
481 FILE_ATTRIBUTE_NORMAL,
479 0);
482 0);
480
483
481 if (handle == INVALID_HANDLE_VALUE) {
484 if (handle == INVALID_HANDLE_VALUE) {
482 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
485 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
483 goto bail;
486 goto bail;
484 }
487 }
485
488
486 fd = _open_osfhandle((intptr_t)handle, flags);
489 fd = _open_osfhandle((intptr_t)handle, flags);
487
490
488 if (fd == -1) {
491 if (fd == -1) {
489 CloseHandle(handle);
492 CloseHandle(handle);
490 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
493 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
491 goto bail;
494 goto bail;
492 }
495 }
493 #ifndef IS_PY3K
496 #ifndef IS_PY3K
494 fp = _fdopen(fd, fpmode);
497 fp = _fdopen(fd, fpmode);
495 if (fp == NULL) {
498 if (fp == NULL) {
496 _close(fd);
499 _close(fd);
497 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
500 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
498 goto bail;
501 goto bail;
499 }
502 }
500
503
501 file_obj = PyFile_FromFile(fp, name, mode, fclose);
504 file_obj = PyFile_FromFile(fp, name, mode, fclose);
502 if (file_obj == NULL) {
505 if (file_obj == NULL) {
503 fclose(fp);
506 fclose(fp);
504 goto bail;
507 goto bail;
505 }
508 }
506
509
507 PyFile_SetBufSize(file_obj, bufsize);
510 PyFile_SetBufSize(file_obj, bufsize);
508 #else
511 #else
509 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
512 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
510 if (file_obj == NULL)
513 if (file_obj == NULL)
511 goto bail;
514 goto bail;
512 #endif
515 #endif
513 bail:
516 bail:
514 PyMem_Free(name);
517 PyMem_Free(name);
515 return file_obj;
518 return file_obj;
516 }
519 }
517 #endif
520 #endif
518
521
519 #ifdef __APPLE__
522 #ifdef __APPLE__
520 #include <ApplicationServices/ApplicationServices.h>
523 #include <ApplicationServices/ApplicationServices.h>
521
524
522 static PyObject *isgui(PyObject *self)
525 static PyObject *isgui(PyObject *self)
523 {
526 {
524 CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
527 CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
525
528
526 if (dict != NULL) {
529 if (dict != NULL) {
527 CFRelease(dict);
530 CFRelease(dict);
528 Py_RETURN_TRUE;
531 Py_RETURN_TRUE;
529 } else {
532 } else {
530 Py_RETURN_FALSE;
533 Py_RETURN_FALSE;
531 }
534 }
532 }
535 }
533 #endif
536 #endif
534
537
535 static char osutil_doc[] = "Native operating system services.";
538 static char osutil_doc[] = "Native operating system services.";
536
539
537 static PyMethodDef methods[] = {
540 static PyMethodDef methods[] = {
538 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
541 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
539 "list a directory\n"},
542 "list a directory\n"},
540 #ifdef _WIN32
543 #ifdef _WIN32
541 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
544 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
542 "Open a file with POSIX-like semantics.\n"
545 "Open a file with POSIX-like semantics.\n"
543 "On error, this function may raise either a WindowsError or an IOError."},
546 "On error, this function may raise either a WindowsError or an IOError."},
544 #endif
547 #endif
545 #ifdef __APPLE__
548 #ifdef __APPLE__
546 {
549 {
547 "isgui", (PyCFunction)isgui, METH_NOARGS,
550 "isgui", (PyCFunction)isgui, METH_NOARGS,
548 "Is a CoreGraphics session available?"
551 "Is a CoreGraphics session available?"
549 },
552 },
550 #endif
553 #endif
551 {NULL, NULL}
554 {NULL, NULL}
552 };
555 };
553
556
554 #ifdef IS_PY3K
557 #ifdef IS_PY3K
555 static struct PyModuleDef osutil_module = {
558 static struct PyModuleDef osutil_module = {
556 PyModuleDef_HEAD_INIT,
559 PyModuleDef_HEAD_INIT,
557 "osutil",
560 "osutil",
558 osutil_doc,
561 osutil_doc,
559 -1,
562 -1,
560 methods
563 methods
561 };
564 };
562
565
563 PyMODINIT_FUNC PyInit_osutil(void)
566 PyMODINIT_FUNC PyInit_osutil(void)
564 {
567 {
565 if (PyType_Ready(&listdir_stat_type) < 0)
568 if (PyType_Ready(&listdir_stat_type) < 0)
566 return NULL;
569 return NULL;
567
570
568 return PyModule_Create(&osutil_module);
571 return PyModule_Create(&osutil_module);
569 }
572 }
570 #else
573 #else
571 PyMODINIT_FUNC initosutil(void)
574 PyMODINIT_FUNC initosutil(void)
572 {
575 {
573 if (PyType_Ready(&listdir_stat_type) == -1)
576 if (PyType_Ready(&listdir_stat_type) == -1)
574 return;
577 return;
575
578
576 Py_InitModule3("osutil", methods, osutil_doc);
579 Py_InitModule3("osutil", methods, osutil_doc);
577 }
580 }
578 #endif
581 #endif
General Comments 0
You need to be logged in to leave comments. Login now