##// END OF EJS Templates
Improve error handling in osutil.c...
Petr Kodl -
r7059:6a76cf98 default
parent child Browse files
Show More
@@ -1,401 +1,398
1 1 /*
2 2 osutil.c - native operating system services
3 3
4 4 Copyright 2007 Matt Mackall and others
5 5
6 6 This software may be used and distributed according to the terms of
7 7 the GNU General Public License, incorporated herein by reference.
8 8 */
9 9
10 10 #define _ATFILE_SOURCE
11 11 #include <Python.h>
12 12 #ifdef _WIN32
13 13 #include <windows.h>
14 14 #else
15 15 #include <dirent.h>
16 16 #include <fcntl.h>
17 17 #include <string.h>
18 18 #include <sys/stat.h>
19 19 #include <sys/types.h>
20 20 #include <unistd.h>
21 21 #endif
22 22
23 23 #ifdef _WIN32
24 24 /*
25 25 stat struct compatible with hg expectations
26 26 Mercurial only uses st_mode, st_size and st_mtime
27 27 the rest is kept to minimize changes between implementations
28 28 */
29 29 struct hg_stat {
30 30 int st_dev;
31 31 int st_mode;
32 32 int st_nlink;
33 33 __int64 st_size;
34 34 int st_mtime;
35 35 int st_ctime;
36 36 };
37 37 struct listdir_stat {
38 38 PyObject_HEAD
39 39 struct hg_stat st;
40 40 };
41 41 #else
42 42 struct listdir_stat {
43 43 PyObject_HEAD
44 44 struct stat st;
45 45 };
46 46 #endif
47 47
48 48 #define listdir_slot(name) \
49 49 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
50 50 { \
51 51 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
52 52 }
53 53
54 54 listdir_slot(st_dev)
55 55 listdir_slot(st_mode)
56 56 listdir_slot(st_nlink)
57 57 #ifdef _WIN32
58 58 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
59 59 {
60 60 return PyLong_FromLongLong(
61 61 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
62 62 }
63 63 #else
64 64 listdir_slot(st_size)
65 65 #endif
66 66 listdir_slot(st_mtime)
67 67 listdir_slot(st_ctime)
68 68
69 69 static struct PyGetSetDef listdir_stat_getsets[] = {
70 70 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
71 71 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
72 72 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
73 73 {"st_size", listdir_stat_st_size, 0, 0, 0},
74 74 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
75 75 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
76 76 {0, 0, 0, 0, 0}
77 77 };
78 78
79 79 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
80 80 {
81 81 return t->tp_alloc(t, 0);
82 82 }
83 83
84 84 static void listdir_stat_dealloc(PyObject *o)
85 85 {
86 86 o->ob_type->tp_free(o);
87 87 }
88 88
89 89 static PyTypeObject listdir_stat_type = {
90 90 PyObject_HEAD_INIT(NULL)
91 91 0, /*ob_size*/
92 92 "osutil.stat", /*tp_name*/
93 93 sizeof(struct listdir_stat), /*tp_basicsize*/
94 94 0, /*tp_itemsize*/
95 95 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
96 96 0, /*tp_print*/
97 97 0, /*tp_getattr*/
98 98 0, /*tp_setattr*/
99 99 0, /*tp_compare*/
100 100 0, /*tp_repr*/
101 101 0, /*tp_as_number*/
102 102 0, /*tp_as_sequence*/
103 103 0, /*tp_as_mapping*/
104 104 0, /*tp_hash */
105 105 0, /*tp_call*/
106 106 0, /*tp_str*/
107 107 0, /*tp_getattro*/
108 108 0, /*tp_setattro*/
109 109 0, /*tp_as_buffer*/
110 110 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
111 111 "stat objects", /* tp_doc */
112 112 0, /* tp_traverse */
113 113 0, /* tp_clear */
114 114 0, /* tp_richcompare */
115 115 0, /* tp_weaklistoffset */
116 116 0, /* tp_iter */
117 117 0, /* tp_iternext */
118 118 0, /* tp_methods */
119 119 0, /* tp_members */
120 120 listdir_stat_getsets, /* tp_getset */
121 121 0, /* tp_base */
122 122 0, /* tp_dict */
123 123 0, /* tp_descr_get */
124 124 0, /* tp_descr_set */
125 125 0, /* tp_dictoffset */
126 126 0, /* tp_init */
127 127 0, /* tp_alloc */
128 128 listdir_stat_new, /* tp_new */
129 129 };
130 130
131 131 #ifdef _WIN32
132 132
133 133 static int to_python_time(const FILETIME *tm)
134 134 {
135 135 /* number of seconds between epoch and January 1 1601 */
136 136 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
137 137 /* conversion factor from 100ns to 1s */
138 138 const __int64 a1 = 10000000;
139 139 /* explicit (int) cast to suspend compiler warnings */
140 140 return (int)((((__int64)tm->dwHighDateTime << 32)
141 141 + tm->dwLowDateTime) / a1 - a0);
142 142 }
143 143
144 144 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
145 145 {
146 146 PyObject *py_st;
147 147 struct hg_stat *stp;
148 148
149 149 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
150 150 ? _S_IFDIR : _S_IFREG;
151 151
152 152 if (!wantstat)
153 153 return Py_BuildValue("si", fd->cFileName, kind);
154 154
155 155 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
156 156 if (!py_st)
157 157 return NULL;
158 158
159 159 stp = &((struct listdir_stat *)py_st)->st;
160 160 /*
161 161 use kind as st_mode
162 162 rwx bits on Win32 are meaningless
163 163 and Hg does not use them anyway
164 164 */
165 165 stp->st_mode = kind;
166 166 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
167 167 stp->st_ctime = to_python_time(&fd->ftCreationTime);
168 168 if (kind == _S_IFREG)
169 169 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
170 170 + fd->nFileSizeLow;
171 171 return Py_BuildValue("siN", fd->cFileName,
172 172 kind, py_st);
173 173 }
174 174
175 175 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
176 176 {
177 177 PyObject *rval = NULL; /* initialize - return value */
178 178 PyObject *statobj = NULL; /* initialize - optional arg */
179 179 PyObject *list;
180 180 HANDLE fh;
181 181 WIN32_FIND_DATAA fd;
182 182 char *path, *pattern, *skip = NULL;
183 183 int plen, wantstat;
184 184
185 185 static char *kwlist[] = {"path", "stat", "skip", NULL};
186 186
187 187 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|Os:listdir",
188 188 kwlist, &path, &plen, &statobj, &skip))
189 189 goto error_parse;
190 190
191 191 wantstat = statobj && PyObject_IsTrue(statobj);
192 192
193 193 /* build the path + \* pattern string */
194 194 pattern = malloc(plen+3); /* path + \* + \0 */
195 195 if (!pattern) {
196 196 PyErr_NoMemory();
197 197 goto error_parse;
198 198 }
199 199 strcpy(pattern, path);
200 200
201 201 if (plen > 0) {
202 202 char c = path[plen-1];
203 203 if (c != ':' && c != '/' && c != '\\')
204 204 pattern[plen++] = '\\';
205 205 }
206 206 strcpy(pattern + plen, "*");
207 207
208 208 fh = FindFirstFileA(pattern, &fd);
209 209 if (fh == INVALID_HANDLE_VALUE) {
210 PyErr_SetExcFromWindowsErrWithFilename(PyExc_OSError,
211 GetLastError(),
212 path);
210 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
213 211 goto error_file;
214 212 }
215 213
216 214 list = PyList_New(0);
217 215 if (!list)
218 216 goto error_list;
219 217
220 218 do {
221 219 PyObject *item;
222 220
223 221 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
224 222 if (!strcmp(fd.cFileName, ".")
225 223 || !strcmp(fd.cFileName, ".."))
226 224 continue;
227 225
228 226 if (skip && !strcmp(fd.cFileName, skip)) {
229 227 rval = PyList_New(0);
230 228 goto error;
231 229 }
232 230 }
233 231
234 232 item = make_item(&fd, wantstat);
235 233 if (!item)
236 234 goto error;
237 235
238 236 if (PyList_Append(list, item)) {
239 237 Py_XDECREF(item);
240 238 goto error;
241 239 }
242 240
243 241 Py_XDECREF(item);
244 242 } while (FindNextFileA(fh, &fd));
245 243
246 244 if (GetLastError() != ERROR_NO_MORE_FILES) {
247 PyErr_SetExcFromWindowsErrWithFilename(PyExc_OSError,
248 GetLastError(),
249 path);
245 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
250 246 goto error;
251 247 }
252 248
253 249 rval = list;
254 250 Py_XINCREF(rval);
255 251 error:
256 252 Py_XDECREF(list);
257 253 error_list:
258 254 FindClose(fh);
259 255 error_file:
260 256 free(pattern);
261 257 error_parse:
262 258 return rval;
263 259 }
264 260
265 261 #else
266 262
267 263 int entkind(struct dirent *ent)
268 264 {
269 265 #ifdef DT_REG
270 266 switch (ent->d_type) {
271 267 case DT_REG: return S_IFREG;
272 268 case DT_DIR: return S_IFDIR;
273 269 case DT_LNK: return S_IFLNK;
274 270 case DT_BLK: return S_IFBLK;
275 271 case DT_CHR: return S_IFCHR;
276 272 case DT_FIFO: return S_IFIFO;
277 273 case DT_SOCK: return S_IFSOCK;
278 274 }
279 275 #endif
280 276 return -1;
281 277 }
282 278
283 279 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
284 280 {
285 281 static char *kwlist[] = { "path", "stat", "skip", NULL };
286 282 PyObject *statflag = NULL, *list, *elem, *stat, *ret = NULL;
287 283 char fullpath[PATH_MAX + 10], *path, *skip = NULL;
288 284 int pathlen, keepstat, kind, dfd = -1, err;
289 285 struct stat st;
290 286 struct dirent *ent;
291 287 DIR *dir;
292 288
293 289 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|Os:listdir", kwlist,
294 290 &path, &pathlen, &statflag, &skip))
295 291 goto error_parse;
296 292
297 if (pathlen >= PATH_MAX)
293 if (pathlen >= PATH_MAX) {
294 PyErr_SetString(PyExc_ValueError, "path too long");
298 295 goto error_parse;
299
296 }
300 297 strncpy(fullpath, path, PATH_MAX);
301 298 fullpath[pathlen] = '/';
302 299 keepstat = statflag && PyObject_IsTrue(statflag);
303 300
304 301 #ifdef AT_SYMLINK_NOFOLLOW
305 302 dfd = open(path, O_RDONLY);
306 303 if (dfd == -1) {
307 304 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
308 305 goto error_parse;
309 306 }
310 307 dir = fdopendir(dfd);
311 308 #else
312 309 dir = opendir(path);
313 310 #endif
314 311 if (!dir) {
315 312 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
316 313 goto error_dir;
317 314 }
318 315
319 316 list = PyList_New(0);
320 317 if (!list)
321 318 goto error_list;
322 319
323 320 while ((ent = readdir(dir))) {
324 321 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
325 322 continue;
326 323
327 324 kind = entkind(ent);
328 325 if (kind == -1 || keepstat) {
329 326 #ifdef AT_SYMLINK_NOFOLLOW
330 327 err = fstatat(dfd, ent->d_name, &st,
331 328 AT_SYMLINK_NOFOLLOW);
332 329 #else
333 330 strncpy(fullpath + pathlen + 1, ent->d_name,
334 331 PATH_MAX - pathlen);
335 332 fullpath[PATH_MAX] = 0;
336 333 err = lstat(fullpath, &st);
337 334 #endif
338 335 if (err == -1) {
339 336 strncpy(fullpath + pathlen + 1, ent->d_name,
340 337 PATH_MAX - pathlen);
341 338 fullpath[PATH_MAX] = 0;
342 339 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
343 340 fullpath);
344 341 goto error;
345 342 }
346 343 kind = st.st_mode & S_IFMT;
347 344 }
348 345
349 346 /* quit early? */
350 347 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
351 348 ret = PyList_New(0);
352 349 goto error;
353 350 }
354 351
355 352 if (keepstat) {
356 353 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
357 354 if (!stat)
358 355 goto error;
359 356 memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
360 357 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
361 358 } else
362 359 elem = Py_BuildValue("si", ent->d_name, kind);
363 360 if (!elem)
364 361 goto error;
365 362
366 363 PyList_Append(list, elem);
367 364 Py_DECREF(elem);
368 365 }
369 366
370 367 ret = list;
371 368 Py_INCREF(ret);
372 369
373 370 error:
374 371 Py_DECREF(list);
375 372 error_list:
376 373 closedir(dir);
377 374 error_dir:
378 375 #ifdef AT_SYMLINK_NOFOLLOW
379 376 close(dfd);
380 377 #endif
381 378 error_parse:
382 379 return ret;
383 380 }
384 381
385 382 #endif /* ndef _WIN32 */
386 383
387 384 static char osutil_doc[] = "Native operating system services.";
388 385
389 386 static PyMethodDef methods[] = {
390 387 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
391 388 "list a directory\n"},
392 389 {NULL, NULL}
393 390 };
394 391
395 392 PyMODINIT_FUNC initosutil(void)
396 393 {
397 394 if (PyType_Ready(&listdir_stat_type) == -1)
398 395 return;
399 396
400 397 Py_InitModule3("osutil", methods, osutil_doc);
401 398 }
General Comments 0
You need to be logged in to leave comments. Login now