##// END OF EJS Templates
osutil: use getdirentriesattr on OS X if possible...
Siddharth Agarwal -
r24461:05ccfe67 default
parent child Browse files
Show More
@@ -1,656 +1,848
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 #ifdef __APPLE__
28 #include <sys/attr.h>
29 #include <sys/vnode.h>
30 #endif
31
27 #include "util.h"
32 #include "util.h"
28
33
29 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
34 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
30 #ifndef PATH_MAX
35 #ifndef PATH_MAX
31 #define PATH_MAX 4096
36 #define PATH_MAX 4096
32 #endif
37 #endif
33
38
34 #ifdef _WIN32
39 #ifdef _WIN32
35 /*
40 /*
36 stat struct compatible with hg expectations
41 stat struct compatible with hg expectations
37 Mercurial only uses st_mode, st_size and st_mtime
42 Mercurial only uses st_mode, st_size and st_mtime
38 the rest is kept to minimize changes between implementations
43 the rest is kept to minimize changes between implementations
39 */
44 */
40 struct hg_stat {
45 struct hg_stat {
41 int st_dev;
46 int st_dev;
42 int st_mode;
47 int st_mode;
43 int st_nlink;
48 int st_nlink;
44 __int64 st_size;
49 __int64 st_size;
45 int st_mtime;
50 int st_mtime;
46 int st_ctime;
51 int st_ctime;
47 };
52 };
48 struct listdir_stat {
53 struct listdir_stat {
49 PyObject_HEAD
54 PyObject_HEAD
50 struct hg_stat st;
55 struct hg_stat st;
51 };
56 };
52 #else
57 #else
53 struct listdir_stat {
58 struct listdir_stat {
54 PyObject_HEAD
59 PyObject_HEAD
55 struct stat st;
60 struct stat st;
56 };
61 };
57 #endif
62 #endif
58
63
59 #define listdir_slot(name) \
64 #define listdir_slot(name) \
60 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
65 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
61 { \
66 { \
62 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
67 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
63 }
68 }
64
69
65 listdir_slot(st_dev)
70 listdir_slot(st_dev)
66 listdir_slot(st_mode)
71 listdir_slot(st_mode)
67 listdir_slot(st_nlink)
72 listdir_slot(st_nlink)
68 #ifdef _WIN32
73 #ifdef _WIN32
69 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
74 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
70 {
75 {
71 return PyLong_FromLongLong(
76 return PyLong_FromLongLong(
72 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
77 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
73 }
78 }
74 #else
79 #else
75 listdir_slot(st_size)
80 listdir_slot(st_size)
76 #endif
81 #endif
77 listdir_slot(st_mtime)
82 listdir_slot(st_mtime)
78 listdir_slot(st_ctime)
83 listdir_slot(st_ctime)
79
84
80 static struct PyGetSetDef listdir_stat_getsets[] = {
85 static struct PyGetSetDef listdir_stat_getsets[] = {
81 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
86 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
82 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
87 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
83 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
88 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
84 {"st_size", listdir_stat_st_size, 0, 0, 0},
89 {"st_size", listdir_stat_st_size, 0, 0, 0},
85 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
90 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
86 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
91 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
87 {0, 0, 0, 0, 0}
92 {0, 0, 0, 0, 0}
88 };
93 };
89
94
90 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
95 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
91 {
96 {
92 return t->tp_alloc(t, 0);
97 return t->tp_alloc(t, 0);
93 }
98 }
94
99
95 static void listdir_stat_dealloc(PyObject *o)
100 static void listdir_stat_dealloc(PyObject *o)
96 {
101 {
97 o->ob_type->tp_free(o);
102 o->ob_type->tp_free(o);
98 }
103 }
99
104
100 static PyTypeObject listdir_stat_type = {
105 static PyTypeObject listdir_stat_type = {
101 PyVarObject_HEAD_INIT(NULL, 0)
106 PyVarObject_HEAD_INIT(NULL, 0)
102 "osutil.stat", /*tp_name*/
107 "osutil.stat", /*tp_name*/
103 sizeof(struct listdir_stat), /*tp_basicsize*/
108 sizeof(struct listdir_stat), /*tp_basicsize*/
104 0, /*tp_itemsize*/
109 0, /*tp_itemsize*/
105 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
110 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
106 0, /*tp_print*/
111 0, /*tp_print*/
107 0, /*tp_getattr*/
112 0, /*tp_getattr*/
108 0, /*tp_setattr*/
113 0, /*tp_setattr*/
109 0, /*tp_compare*/
114 0, /*tp_compare*/
110 0, /*tp_repr*/
115 0, /*tp_repr*/
111 0, /*tp_as_number*/
116 0, /*tp_as_number*/
112 0, /*tp_as_sequence*/
117 0, /*tp_as_sequence*/
113 0, /*tp_as_mapping*/
118 0, /*tp_as_mapping*/
114 0, /*tp_hash */
119 0, /*tp_hash */
115 0, /*tp_call*/
120 0, /*tp_call*/
116 0, /*tp_str*/
121 0, /*tp_str*/
117 0, /*tp_getattro*/
122 0, /*tp_getattro*/
118 0, /*tp_setattro*/
123 0, /*tp_setattro*/
119 0, /*tp_as_buffer*/
124 0, /*tp_as_buffer*/
120 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
125 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
121 "stat objects", /* tp_doc */
126 "stat objects", /* tp_doc */
122 0, /* tp_traverse */
127 0, /* tp_traverse */
123 0, /* tp_clear */
128 0, /* tp_clear */
124 0, /* tp_richcompare */
129 0, /* tp_richcompare */
125 0, /* tp_weaklistoffset */
130 0, /* tp_weaklistoffset */
126 0, /* tp_iter */
131 0, /* tp_iter */
127 0, /* tp_iternext */
132 0, /* tp_iternext */
128 0, /* tp_methods */
133 0, /* tp_methods */
129 0, /* tp_members */
134 0, /* tp_members */
130 listdir_stat_getsets, /* tp_getset */
135 listdir_stat_getsets, /* tp_getset */
131 0, /* tp_base */
136 0, /* tp_base */
132 0, /* tp_dict */
137 0, /* tp_dict */
133 0, /* tp_descr_get */
138 0, /* tp_descr_get */
134 0, /* tp_descr_set */
139 0, /* tp_descr_set */
135 0, /* tp_dictoffset */
140 0, /* tp_dictoffset */
136 0, /* tp_init */
141 0, /* tp_init */
137 0, /* tp_alloc */
142 0, /* tp_alloc */
138 listdir_stat_new, /* tp_new */
143 listdir_stat_new, /* tp_new */
139 };
144 };
140
145
141 #ifdef _WIN32
146 #ifdef _WIN32
142
147
143 static int to_python_time(const FILETIME *tm)
148 static int to_python_time(const FILETIME *tm)
144 {
149 {
145 /* number of seconds between epoch and January 1 1601 */
150 /* number of seconds between epoch and January 1 1601 */
146 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
151 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
147 /* conversion factor from 100ns to 1s */
152 /* conversion factor from 100ns to 1s */
148 const __int64 a1 = 10000000;
153 const __int64 a1 = 10000000;
149 /* explicit (int) cast to suspend compiler warnings */
154 /* explicit (int) cast to suspend compiler warnings */
150 return (int)((((__int64)tm->dwHighDateTime << 32)
155 return (int)((((__int64)tm->dwHighDateTime << 32)
151 + tm->dwLowDateTime) / a1 - a0);
156 + tm->dwLowDateTime) / a1 - a0);
152 }
157 }
153
158
154 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
159 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
155 {
160 {
156 PyObject *py_st;
161 PyObject *py_st;
157 struct hg_stat *stp;
162 struct hg_stat *stp;
158
163
159 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
164 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
160 ? _S_IFDIR : _S_IFREG;
165 ? _S_IFDIR : _S_IFREG;
161
166
162 if (!wantstat)
167 if (!wantstat)
163 return Py_BuildValue("si", fd->cFileName, kind);
168 return Py_BuildValue("si", fd->cFileName, kind);
164
169
165 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
170 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
166 if (!py_st)
171 if (!py_st)
167 return NULL;
172 return NULL;
168
173
169 stp = &((struct listdir_stat *)py_st)->st;
174 stp = &((struct listdir_stat *)py_st)->st;
170 /*
175 /*
171 use kind as st_mode
176 use kind as st_mode
172 rwx bits on Win32 are meaningless
177 rwx bits on Win32 are meaningless
173 and Hg does not use them anyway
178 and Hg does not use them anyway
174 */
179 */
175 stp->st_mode = kind;
180 stp->st_mode = kind;
176 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
181 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
177 stp->st_ctime = to_python_time(&fd->ftCreationTime);
182 stp->st_ctime = to_python_time(&fd->ftCreationTime);
178 if (kind == _S_IFREG)
183 if (kind == _S_IFREG)
179 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
184 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
180 + fd->nFileSizeLow;
185 + fd->nFileSizeLow;
181 return Py_BuildValue("siN", fd->cFileName,
186 return Py_BuildValue("siN", fd->cFileName,
182 kind, py_st);
187 kind, py_st);
183 }
188 }
184
189
185 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
190 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
186 {
191 {
187 PyObject *rval = NULL; /* initialize - return value */
192 PyObject *rval = NULL; /* initialize - return value */
188 PyObject *list;
193 PyObject *list;
189 HANDLE fh;
194 HANDLE fh;
190 WIN32_FIND_DATAA fd;
195 WIN32_FIND_DATAA fd;
191 char *pattern;
196 char *pattern;
192
197
193 /* build the path + \* pattern string */
198 /* build the path + \* pattern string */
194 pattern = malloc(plen + 3); /* path + \* + \0 */
199 pattern = malloc(plen + 3); /* path + \* + \0 */
195 if (!pattern) {
200 if (!pattern) {
196 PyErr_NoMemory();
201 PyErr_NoMemory();
197 goto error_nomem;
202 goto error_nomem;
198 }
203 }
199 strcpy(pattern, path);
204 strcpy(pattern, path);
200
205
201 if (plen > 0) {
206 if (plen > 0) {
202 char c = path[plen-1];
207 char c = path[plen-1];
203 if (c != ':' && c != '/' && c != '\\')
208 if (c != ':' && c != '/' && c != '\\')
204 pattern[plen++] = '\\';
209 pattern[plen++] = '\\';
205 }
210 }
206 strcpy(pattern + plen, "*");
211 strcpy(pattern + plen, "*");
207
212
208 fh = FindFirstFileA(pattern, &fd);
213 fh = FindFirstFileA(pattern, &fd);
209 if (fh == INVALID_HANDLE_VALUE) {
214 if (fh == INVALID_HANDLE_VALUE) {
210 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
215 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
211 goto error_file;
216 goto error_file;
212 }
217 }
213
218
214 list = PyList_New(0);
219 list = PyList_New(0);
215 if (!list)
220 if (!list)
216 goto error_list;
221 goto error_list;
217
222
218 do {
223 do {
219 PyObject *item;
224 PyObject *item;
220
225
221 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
226 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
222 if (!strcmp(fd.cFileName, ".")
227 if (!strcmp(fd.cFileName, ".")
223 || !strcmp(fd.cFileName, ".."))
228 || !strcmp(fd.cFileName, ".."))
224 continue;
229 continue;
225
230
226 if (skip && !strcmp(fd.cFileName, skip)) {
231 if (skip && !strcmp(fd.cFileName, skip)) {
227 rval = PyList_New(0);
232 rval = PyList_New(0);
228 goto error;
233 goto error;
229 }
234 }
230 }
235 }
231
236
232 item = make_item(&fd, wantstat);
237 item = make_item(&fd, wantstat);
233 if (!item)
238 if (!item)
234 goto error;
239 goto error;
235
240
236 if (PyList_Append(list, item)) {
241 if (PyList_Append(list, item)) {
237 Py_XDECREF(item);
242 Py_XDECREF(item);
238 goto error;
243 goto error;
239 }
244 }
240
245
241 Py_XDECREF(item);
246 Py_XDECREF(item);
242 } while (FindNextFileA(fh, &fd));
247 } while (FindNextFileA(fh, &fd));
243
248
244 if (GetLastError() != ERROR_NO_MORE_FILES) {
249 if (GetLastError() != ERROR_NO_MORE_FILES) {
245 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
250 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
246 goto error;
251 goto error;
247 }
252 }
248
253
249 rval = list;
254 rval = list;
250 Py_XINCREF(rval);
255 Py_XINCREF(rval);
251 error:
256 error:
252 Py_XDECREF(list);
257 Py_XDECREF(list);
253 error_list:
258 error_list:
254 FindClose(fh);
259 FindClose(fh);
255 error_file:
260 error_file:
256 free(pattern);
261 free(pattern);
257 error_nomem:
262 error_nomem:
258 return rval;
263 return rval;
259 }
264 }
260
265
261 #else
266 #else
262
267
263 int entkind(struct dirent *ent)
268 int entkind(struct dirent *ent)
264 {
269 {
265 #ifdef DT_REG
270 #ifdef DT_REG
266 switch (ent->d_type) {
271 switch (ent->d_type) {
267 case DT_REG: return S_IFREG;
272 case DT_REG: return S_IFREG;
268 case DT_DIR: return S_IFDIR;
273 case DT_DIR: return S_IFDIR;
269 case DT_LNK: return S_IFLNK;
274 case DT_LNK: return S_IFLNK;
270 case DT_BLK: return S_IFBLK;
275 case DT_BLK: return S_IFBLK;
271 case DT_CHR: return S_IFCHR;
276 case DT_CHR: return S_IFCHR;
272 case DT_FIFO: return S_IFIFO;
277 case DT_FIFO: return S_IFIFO;
273 case DT_SOCK: return S_IFSOCK;
278 case DT_SOCK: return S_IFSOCK;
274 }
279 }
275 #endif
280 #endif
276 return -1;
281 return -1;
277 }
282 }
278
283
279 static PyObject *makestat(const struct stat *st)
284 static PyObject *makestat(const struct stat *st)
280 {
285 {
281 PyObject *stat;
286 PyObject *stat;
282
287
283 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
288 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
284 if (stat)
289 if (stat)
285 memcpy(&((struct listdir_stat *)stat)->st, st, sizeof(*st));
290 memcpy(&((struct listdir_stat *)stat)->st, st, sizeof(*st));
286 return stat;
291 return stat;
287 }
292 }
288
293
289 static PyObject *_listdir_stat(char *path, int pathlen, int keepstat,
294 static PyObject *_listdir_stat(char *path, int pathlen, int keepstat,
290 char *skip)
295 char *skip)
291 {
296 {
292 PyObject *list, *elem, *stat = NULL, *ret = NULL;
297 PyObject *list, *elem, *stat = NULL, *ret = NULL;
293 char fullpath[PATH_MAX + 10];
298 char fullpath[PATH_MAX + 10];
294 int kind, err;
299 int kind, err;
295 struct stat st;
300 struct stat st;
296 struct dirent *ent;
301 struct dirent *ent;
297 DIR *dir;
302 DIR *dir;
298 #ifdef AT_SYMLINK_NOFOLLOW
303 #ifdef AT_SYMLINK_NOFOLLOW
299 int dfd = -1;
304 int dfd = -1;
300 #endif
305 #endif
301
306
302 if (pathlen >= PATH_MAX) {
307 if (pathlen >= PATH_MAX) {
303 errno = ENAMETOOLONG;
308 errno = ENAMETOOLONG;
304 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
309 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
305 goto error_value;
310 goto error_value;
306 }
311 }
307 strncpy(fullpath, path, PATH_MAX);
312 strncpy(fullpath, path, PATH_MAX);
308 fullpath[pathlen] = '/';
313 fullpath[pathlen] = '/';
309
314
310 #ifdef AT_SYMLINK_NOFOLLOW
315 #ifdef AT_SYMLINK_NOFOLLOW
311 dfd = open(path, O_RDONLY);
316 dfd = open(path, O_RDONLY);
312 if (dfd == -1) {
317 if (dfd == -1) {
313 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
318 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
314 goto error_value;
319 goto error_value;
315 }
320 }
316 dir = fdopendir(dfd);
321 dir = fdopendir(dfd);
317 #else
322 #else
318 dir = opendir(path);
323 dir = opendir(path);
319 #endif
324 #endif
320 if (!dir) {
325 if (!dir) {
321 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
326 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
322 goto error_dir;
327 goto error_dir;
323 }
328 }
324
329
325 list = PyList_New(0);
330 list = PyList_New(0);
326 if (!list)
331 if (!list)
327 goto error_list;
332 goto error_list;
328
333
329 while ((ent = readdir(dir))) {
334 while ((ent = readdir(dir))) {
330 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
335 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
331 continue;
336 continue;
332
337
333 kind = entkind(ent);
338 kind = entkind(ent);
334 if (kind == -1 || keepstat) {
339 if (kind == -1 || keepstat) {
335 #ifdef AT_SYMLINK_NOFOLLOW
340 #ifdef AT_SYMLINK_NOFOLLOW
336 err = fstatat(dfd, ent->d_name, &st,
341 err = fstatat(dfd, ent->d_name, &st,
337 AT_SYMLINK_NOFOLLOW);
342 AT_SYMLINK_NOFOLLOW);
338 #else
343 #else
339 strncpy(fullpath + pathlen + 1, ent->d_name,
344 strncpy(fullpath + pathlen + 1, ent->d_name,
340 PATH_MAX - pathlen);
345 PATH_MAX - pathlen);
341 fullpath[PATH_MAX] = 0;
346 fullpath[PATH_MAX] = 0;
342 err = lstat(fullpath, &st);
347 err = lstat(fullpath, &st);
343 #endif
348 #endif
344 if (err == -1) {
349 if (err == -1) {
345 /* race with file deletion? */
350 /* race with file deletion? */
346 if (errno == ENOENT)
351 if (errno == ENOENT)
347 continue;
352 continue;
348 strncpy(fullpath + pathlen + 1, ent->d_name,
353 strncpy(fullpath + pathlen + 1, ent->d_name,
349 PATH_MAX - pathlen);
354 PATH_MAX - pathlen);
350 fullpath[PATH_MAX] = 0;
355 fullpath[PATH_MAX] = 0;
351 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
356 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
352 fullpath);
357 fullpath);
353 goto error;
358 goto error;
354 }
359 }
355 kind = st.st_mode & S_IFMT;
360 kind = st.st_mode & S_IFMT;
356 }
361 }
357
362
358 /* quit early? */
363 /* quit early? */
359 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
364 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
360 ret = PyList_New(0);
365 ret = PyList_New(0);
361 goto error;
366 goto error;
362 }
367 }
363
368
364 if (keepstat) {
369 if (keepstat) {
365 stat = makestat(&st);
370 stat = makestat(&st);
366 if (!stat)
371 if (!stat)
367 goto error;
372 goto error;
368 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
373 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
369 } else
374 } else
370 elem = Py_BuildValue("si", ent->d_name, kind);
375 elem = Py_BuildValue("si", ent->d_name, kind);
371 if (!elem)
376 if (!elem)
372 goto error;
377 goto error;
373 stat = NULL;
378 stat = NULL;
374
379
375 PyList_Append(list, elem);
380 PyList_Append(list, elem);
376 Py_DECREF(elem);
381 Py_DECREF(elem);
377 }
382 }
378
383
379 ret = list;
384 ret = list;
380 Py_INCREF(ret);
385 Py_INCREF(ret);
381
386
382 error:
387 error:
383 Py_DECREF(list);
388 Py_DECREF(list);
384 Py_XDECREF(stat);
389 Py_XDECREF(stat);
385 error_list:
390 error_list:
386 closedir(dir);
391 closedir(dir);
387 error_dir:
392 error_dir:
388 #ifdef AT_SYMLINK_NOFOLLOW
393 #ifdef AT_SYMLINK_NOFOLLOW
389 close(dfd);
394 close(dfd);
390 #endif
395 #endif
391 error_value:
396 error_value:
392 return ret;
397 return ret;
393 }
398 }
394
399
400 #ifdef __APPLE__
401
402 typedef struct {
403 u_int32_t length;
404 attrreference_t name;
405 fsobj_type_t obj_type;
406 struct timespec mtime;
407 #if __LITTLE_ENDIAN__
408 mode_t access_mask;
409 uint16_t padding;
410 #else
411 uint16_t padding;
412 mode_t access_mask;
413 #endif
414 off_t size;
415 } __attribute__((packed)) attrbuf_entry;
416
417 int attrkind(attrbuf_entry *entry)
418 {
419 switch (entry->obj_type) {
420 case VREG: return S_IFREG;
421 case VDIR: return S_IFDIR;
422 case VLNK: return S_IFLNK;
423 case VBLK: return S_IFBLK;
424 case VCHR: return S_IFCHR;
425 case VFIFO: return S_IFIFO;
426 case VSOCK: return S_IFSOCK;
427 }
428 return -1;
429 }
430
431 /* get these many entries at a time */
432 #define LISTDIR_BATCH_SIZE 50
433
434 static PyObject *_listdir_batch(char *path, int pathlen, int keepstat,
435 char *skip, bool *fallback)
436 {
437 PyObject *list, *elem, *stat = NULL, *ret = NULL;
438 int kind, err;
439 unsigned long index;
440 unsigned int count, old_state, new_state;
441 bool state_seen = false;
442 attrbuf_entry *entry;
443 /* from the getattrlist(2) man page: a path can be no longer than
444 (NAME_MAX * 3 + 1) bytes. Also, "The getattrlist() function will
445 silently truncate attribute data if attrBufSize is too small." So
446 pass in a buffer big enough for the worst case. */
447 char attrbuf[LISTDIR_BATCH_SIZE * (sizeof(attrbuf_entry) + NAME_MAX * 3 + 1)];
448 unsigned int basep_unused;
449
450 struct stat st;
451 int dfd = -1;
452
453 /* these must match the attrbuf_entry struct, otherwise you'll end up
454 with garbage */
455 struct attrlist requested_attr = {0};
456 requested_attr.bitmapcount = ATTR_BIT_MAP_COUNT;
457 requested_attr.commonattr = (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE |
458 ATTR_CMN_MODTIME | ATTR_CMN_ACCESSMASK);
459 requested_attr.fileattr = ATTR_FILE_TOTALSIZE;
460
461 *fallback = false;
462
463 if (pathlen >= PATH_MAX) {
464 errno = ENAMETOOLONG;
465 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
466 goto error_value;
467 }
468
469 dfd = open(path, O_RDONLY);
470 if (dfd == -1) {
471 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
472 goto error_value;
473 }
474
475 list = PyList_New(0);
476 if (!list)
477 goto error_dir;
478
479 do {
480 count = LISTDIR_BATCH_SIZE;
481 err = getdirentriesattr(dfd, &requested_attr, &attrbuf,
482 sizeof(attrbuf), &count, &basep_unused,
483 &new_state, 0);
484 if (err < 0) {
485 if (errno == ENOTSUP) {
486 /* We're on a filesystem that doesn't support
487 getdirentriesattr. Fall back to the
488 stat-based implementation. */
489 *fallback = true;
490 } else
491 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
492 goto error;
493 }
494
495 if (!state_seen) {
496 old_state = new_state;
497 state_seen = true;
498 } else if (old_state != new_state) {
499 /* There's an edge case with getdirentriesattr. Consider
500 the following initial list of files:
501
502 a
503 b
504 <--
505 c
506 d
507
508 If the iteration is paused at the arrow, and b is
509 deleted before it is resumed, getdirentriesattr will
510 not return d at all! Ordinarily we're expected to
511 restart the iteration from the beginning. To avoid
512 getting stuck in a retry loop here, fall back to
513 stat. */
514 *fallback = true;
515 goto error;
516 }
517
518 entry = (attrbuf_entry *)attrbuf;
519
520 for (index = 0; index < count; index++) {
521 char *filename = ((char *)&entry->name) +
522 entry->name.attr_dataoffset;
523
524 if (!strcmp(filename, ".") || !strcmp(filename, ".."))
525 continue;
526
527 kind = attrkind(entry);
528 if (kind == -1) {
529 PyErr_Format(PyExc_OSError,
530 "unknown object type %u for file "
531 "%s%s!",
532 entry->obj_type, path, filename);
533 goto error;
534 }
535
536 /* quit early? */
537 if (skip && kind == S_IFDIR && !strcmp(filename, skip)) {
538 ret = PyList_New(0);
539 goto error;
540 }
541
542 if (keepstat) {
543 /* from the getattrlist(2) man page: "Only the
544 permission bits ... are valid". */
545 st.st_mode = (entry->access_mask & ~S_IFMT) | kind;
546 st.st_mtime = entry->mtime.tv_sec;
547 st.st_size = entry->size;
548 stat = makestat(&st);
549 if (!stat)
550 goto error;
551 elem = Py_BuildValue("siN", filename, kind, stat);
552 } else
553 elem = Py_BuildValue("si", filename, kind);
554 if (!elem)
555 goto error;
556 stat = NULL;
557
558 PyList_Append(list, elem);
559 Py_DECREF(elem);
560
561 entry = (attrbuf_entry *)((char *)entry + entry->length);
562 }
563 } while (err == 0);
564
565 ret = list;
566 Py_INCREF(ret);
567
568 error:
569 Py_DECREF(list);
570 Py_XDECREF(stat);
571 error_dir:
572 close(dfd);
573 error_value:
574 return ret;
575 }
576
577 #endif /* __APPLE__ */
578
395 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
579 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
396 {
580 {
581 #ifdef __APPLE__
582 PyObject *ret;
583 bool fallback = false;
584
585 ret = _listdir_batch(path, pathlen, keepstat, skip, &fallback);
586 if (ret != NULL || !fallback)
587 return ret;
588 #endif
397 return _listdir_stat(path, pathlen, keepstat, skip);
589 return _listdir_stat(path, pathlen, keepstat, skip);
398 }
590 }
399
591
400 static PyObject *statfiles(PyObject *self, PyObject *args)
592 static PyObject *statfiles(PyObject *self, PyObject *args)
401 {
593 {
402 PyObject *names, *stats;
594 PyObject *names, *stats;
403 Py_ssize_t i, count;
595 Py_ssize_t i, count;
404
596
405 if (!PyArg_ParseTuple(args, "O:statfiles", &names))
597 if (!PyArg_ParseTuple(args, "O:statfiles", &names))
406 return NULL;
598 return NULL;
407
599
408 count = PySequence_Length(names);
600 count = PySequence_Length(names);
409 if (count == -1) {
601 if (count == -1) {
410 PyErr_SetString(PyExc_TypeError, "not a sequence");
602 PyErr_SetString(PyExc_TypeError, "not a sequence");
411 return NULL;
603 return NULL;
412 }
604 }
413
605
414 stats = PyList_New(count);
606 stats = PyList_New(count);
415 if (stats == NULL)
607 if (stats == NULL)
416 return NULL;
608 return NULL;
417
609
418 for (i = 0; i < count; i++) {
610 for (i = 0; i < count; i++) {
419 PyObject *stat, *pypath;
611 PyObject *stat, *pypath;
420 struct stat st;
612 struct stat st;
421 int ret, kind;
613 int ret, kind;
422 char *path;
614 char *path;
423
615
424 pypath = PySequence_GetItem(names, i);
616 pypath = PySequence_GetItem(names, i);
425 if (!pypath)
617 if (!pypath)
426 return NULL;
618 return NULL;
427 path = PyString_AsString(pypath);
619 path = PyString_AsString(pypath);
428 if (path == NULL) {
620 if (path == NULL) {
429 Py_DECREF(pypath);
621 Py_DECREF(pypath);
430 PyErr_SetString(PyExc_TypeError, "not a string");
622 PyErr_SetString(PyExc_TypeError, "not a string");
431 goto bail;
623 goto bail;
432 }
624 }
433 ret = lstat(path, &st);
625 ret = lstat(path, &st);
434 Py_DECREF(pypath);
626 Py_DECREF(pypath);
435 kind = st.st_mode & S_IFMT;
627 kind = st.st_mode & S_IFMT;
436 if (ret != -1 && (kind == S_IFREG || kind == S_IFLNK)) {
628 if (ret != -1 && (kind == S_IFREG || kind == S_IFLNK)) {
437 stat = makestat(&st);
629 stat = makestat(&st);
438 if (stat == NULL)
630 if (stat == NULL)
439 goto bail;
631 goto bail;
440 PyList_SET_ITEM(stats, i, stat);
632 PyList_SET_ITEM(stats, i, stat);
441 } else {
633 } else {
442 Py_INCREF(Py_None);
634 Py_INCREF(Py_None);
443 PyList_SET_ITEM(stats, i, Py_None);
635 PyList_SET_ITEM(stats, i, Py_None);
444 }
636 }
445 }
637 }
446
638
447 return stats;
639 return stats;
448
640
449 bail:
641 bail:
450 Py_DECREF(stats);
642 Py_DECREF(stats);
451 return NULL;
643 return NULL;
452 }
644 }
453
645
454 #endif /* ndef _WIN32 */
646 #endif /* ndef _WIN32 */
455
647
456 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
648 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
457 {
649 {
458 PyObject *statobj = NULL; /* initialize - optional arg */
650 PyObject *statobj = NULL; /* initialize - optional arg */
459 PyObject *skipobj = NULL; /* initialize - optional arg */
651 PyObject *skipobj = NULL; /* initialize - optional arg */
460 char *path, *skip = NULL;
652 char *path, *skip = NULL;
461 int wantstat, plen;
653 int wantstat, plen;
462
654
463 static char *kwlist[] = {"path", "stat", "skip", NULL};
655 static char *kwlist[] = {"path", "stat", "skip", NULL};
464
656
465 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
657 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
466 kwlist, &path, &plen, &statobj, &skipobj))
658 kwlist, &path, &plen, &statobj, &skipobj))
467 return NULL;
659 return NULL;
468
660
469 wantstat = statobj && PyObject_IsTrue(statobj);
661 wantstat = statobj && PyObject_IsTrue(statobj);
470
662
471 if (skipobj && skipobj != Py_None) {
663 if (skipobj && skipobj != Py_None) {
472 skip = PyBytes_AsString(skipobj);
664 skip = PyBytes_AsString(skipobj);
473 if (!skip)
665 if (!skip)
474 return NULL;
666 return NULL;
475 }
667 }
476
668
477 return _listdir(path, plen, wantstat, skip);
669 return _listdir(path, plen, wantstat, skip);
478 }
670 }
479
671
480 #ifdef _WIN32
672 #ifdef _WIN32
481 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
673 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
482 {
674 {
483 static char *kwlist[] = {"name", "mode", "buffering", NULL};
675 static char *kwlist[] = {"name", "mode", "buffering", NULL};
484 PyObject *file_obj = NULL;
676 PyObject *file_obj = NULL;
485 char *name = NULL;
677 char *name = NULL;
486 char *mode = "rb";
678 char *mode = "rb";
487 DWORD access = 0;
679 DWORD access = 0;
488 DWORD creation;
680 DWORD creation;
489 HANDLE handle;
681 HANDLE handle;
490 int fd, flags = 0;
682 int fd, flags = 0;
491 int bufsize = -1;
683 int bufsize = -1;
492 char m0, m1, m2;
684 char m0, m1, m2;
493 char fpmode[4];
685 char fpmode[4];
494 int fppos = 0;
686 int fppos = 0;
495 int plus;
687 int plus;
496 FILE *fp;
688 FILE *fp;
497
689
498 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
690 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
499 Py_FileSystemDefaultEncoding,
691 Py_FileSystemDefaultEncoding,
500 &name, &mode, &bufsize))
692 &name, &mode, &bufsize))
501 return NULL;
693 return NULL;
502
694
503 m0 = mode[0];
695 m0 = mode[0];
504 m1 = m0 ? mode[1] : '\0';
696 m1 = m0 ? mode[1] : '\0';
505 m2 = m1 ? mode[2] : '\0';
697 m2 = m1 ? mode[2] : '\0';
506 plus = m1 == '+' || m2 == '+';
698 plus = m1 == '+' || m2 == '+';
507
699
508 fpmode[fppos++] = m0;
700 fpmode[fppos++] = m0;
509 if (m1 == 'b' || m2 == 'b') {
701 if (m1 == 'b' || m2 == 'b') {
510 flags = _O_BINARY;
702 flags = _O_BINARY;
511 fpmode[fppos++] = 'b';
703 fpmode[fppos++] = 'b';
512 }
704 }
513 else
705 else
514 flags = _O_TEXT;
706 flags = _O_TEXT;
515 if (m0 == 'r' && !plus) {
707 if (m0 == 'r' && !plus) {
516 flags |= _O_RDONLY;
708 flags |= _O_RDONLY;
517 access = GENERIC_READ;
709 access = GENERIC_READ;
518 } else {
710 } else {
519 /*
711 /*
520 work around http://support.microsoft.com/kb/899149 and
712 work around http://support.microsoft.com/kb/899149 and
521 set _O_RDWR for 'w' and 'a', even if mode has no '+'
713 set _O_RDWR for 'w' and 'a', even if mode has no '+'
522 */
714 */
523 flags |= _O_RDWR;
715 flags |= _O_RDWR;
524 access = GENERIC_READ | GENERIC_WRITE;
716 access = GENERIC_READ | GENERIC_WRITE;
525 fpmode[fppos++] = '+';
717 fpmode[fppos++] = '+';
526 }
718 }
527 fpmode[fppos++] = '\0';
719 fpmode[fppos++] = '\0';
528
720
529 switch (m0) {
721 switch (m0) {
530 case 'r':
722 case 'r':
531 creation = OPEN_EXISTING;
723 creation = OPEN_EXISTING;
532 break;
724 break;
533 case 'w':
725 case 'w':
534 creation = CREATE_ALWAYS;
726 creation = CREATE_ALWAYS;
535 break;
727 break;
536 case 'a':
728 case 'a':
537 creation = OPEN_ALWAYS;
729 creation = OPEN_ALWAYS;
538 flags |= _O_APPEND;
730 flags |= _O_APPEND;
539 break;
731 break;
540 default:
732 default:
541 PyErr_Format(PyExc_ValueError,
733 PyErr_Format(PyExc_ValueError,
542 "mode string must begin with one of 'r', 'w', "
734 "mode string must begin with one of 'r', 'w', "
543 "or 'a', not '%c'", m0);
735 "or 'a', not '%c'", m0);
544 goto bail;
736 goto bail;
545 }
737 }
546
738
547 handle = CreateFile(name, access,
739 handle = CreateFile(name, access,
548 FILE_SHARE_READ | FILE_SHARE_WRITE |
740 FILE_SHARE_READ | FILE_SHARE_WRITE |
549 FILE_SHARE_DELETE,
741 FILE_SHARE_DELETE,
550 NULL,
742 NULL,
551 creation,
743 creation,
552 FILE_ATTRIBUTE_NORMAL,
744 FILE_ATTRIBUTE_NORMAL,
553 0);
745 0);
554
746
555 if (handle == INVALID_HANDLE_VALUE) {
747 if (handle == INVALID_HANDLE_VALUE) {
556 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
748 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
557 goto bail;
749 goto bail;
558 }
750 }
559
751
560 fd = _open_osfhandle((intptr_t)handle, flags);
752 fd = _open_osfhandle((intptr_t)handle, flags);
561
753
562 if (fd == -1) {
754 if (fd == -1) {
563 CloseHandle(handle);
755 CloseHandle(handle);
564 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
756 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
565 goto bail;
757 goto bail;
566 }
758 }
567 #ifndef IS_PY3K
759 #ifndef IS_PY3K
568 fp = _fdopen(fd, fpmode);
760 fp = _fdopen(fd, fpmode);
569 if (fp == NULL) {
761 if (fp == NULL) {
570 _close(fd);
762 _close(fd);
571 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
763 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
572 goto bail;
764 goto bail;
573 }
765 }
574
766
575 file_obj = PyFile_FromFile(fp, name, mode, fclose);
767 file_obj = PyFile_FromFile(fp, name, mode, fclose);
576 if (file_obj == NULL) {
768 if (file_obj == NULL) {
577 fclose(fp);
769 fclose(fp);
578 goto bail;
770 goto bail;
579 }
771 }
580
772
581 PyFile_SetBufSize(file_obj, bufsize);
773 PyFile_SetBufSize(file_obj, bufsize);
582 #else
774 #else
583 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
775 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
584 if (file_obj == NULL)
776 if (file_obj == NULL)
585 goto bail;
777 goto bail;
586 #endif
778 #endif
587 bail:
779 bail:
588 PyMem_Free(name);
780 PyMem_Free(name);
589 return file_obj;
781 return file_obj;
590 }
782 }
591 #endif
783 #endif
592
784
593 #ifdef __APPLE__
785 #ifdef __APPLE__
594 #include <ApplicationServices/ApplicationServices.h>
786 #include <ApplicationServices/ApplicationServices.h>
595
787
596 static PyObject *isgui(PyObject *self)
788 static PyObject *isgui(PyObject *self)
597 {
789 {
598 CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
790 CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
599
791
600 if (dict != NULL) {
792 if (dict != NULL) {
601 CFRelease(dict);
793 CFRelease(dict);
602 Py_RETURN_TRUE;
794 Py_RETURN_TRUE;
603 } else {
795 } else {
604 Py_RETURN_FALSE;
796 Py_RETURN_FALSE;
605 }
797 }
606 }
798 }
607 #endif
799 #endif
608
800
609 static char osutil_doc[] = "Native operating system services.";
801 static char osutil_doc[] = "Native operating system services.";
610
802
611 static PyMethodDef methods[] = {
803 static PyMethodDef methods[] = {
612 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
804 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
613 "list a directory\n"},
805 "list a directory\n"},
614 #ifdef _WIN32
806 #ifdef _WIN32
615 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
807 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
616 "Open a file with POSIX-like semantics.\n"
808 "Open a file with POSIX-like semantics.\n"
617 "On error, this function may raise either a WindowsError or an IOError."},
809 "On error, this function may raise either a WindowsError or an IOError."},
618 #else
810 #else
619 {"statfiles", (PyCFunction)statfiles, METH_VARARGS | METH_KEYWORDS,
811 {"statfiles", (PyCFunction)statfiles, METH_VARARGS | METH_KEYWORDS,
620 "stat a series of files or symlinks\n"
812 "stat a series of files or symlinks\n"
621 "Returns None for non-existent entries and entries of other types.\n"},
813 "Returns None for non-existent entries and entries of other types.\n"},
622 #endif
814 #endif
623 #ifdef __APPLE__
815 #ifdef __APPLE__
624 {
816 {
625 "isgui", (PyCFunction)isgui, METH_NOARGS,
817 "isgui", (PyCFunction)isgui, METH_NOARGS,
626 "Is a CoreGraphics session available?"
818 "Is a CoreGraphics session available?"
627 },
819 },
628 #endif
820 #endif
629 {NULL, NULL}
821 {NULL, NULL}
630 };
822 };
631
823
632 #ifdef IS_PY3K
824 #ifdef IS_PY3K
633 static struct PyModuleDef osutil_module = {
825 static struct PyModuleDef osutil_module = {
634 PyModuleDef_HEAD_INIT,
826 PyModuleDef_HEAD_INIT,
635 "osutil",
827 "osutil",
636 osutil_doc,
828 osutil_doc,
637 -1,
829 -1,
638 methods
830 methods
639 };
831 };
640
832
641 PyMODINIT_FUNC PyInit_osutil(void)
833 PyMODINIT_FUNC PyInit_osutil(void)
642 {
834 {
643 if (PyType_Ready(&listdir_stat_type) < 0)
835 if (PyType_Ready(&listdir_stat_type) < 0)
644 return NULL;
836 return NULL;
645
837
646 return PyModule_Create(&osutil_module);
838 return PyModule_Create(&osutil_module);
647 }
839 }
648 #else
840 #else
649 PyMODINIT_FUNC initosutil(void)
841 PyMODINIT_FUNC initosutil(void)
650 {
842 {
651 if (PyType_Ready(&listdir_stat_type) == -1)
843 if (PyType_Ready(&listdir_stat_type) == -1)
652 return;
844 return;
653
845
654 Py_InitModule3("osutil", methods, osutil_doc);
846 Py_InitModule3("osutil", methods, osutil_doc);
655 }
847 }
656 #endif
848 #endif
General Comments 0
You need to be logged in to leave comments. Login now