Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(31636)

Side by Side Diff: Modules/posixmodule.c

Issue 25994: File descriptor leaks in os.scandir()
Patch Set: Created 3 years, 7 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« Lib/test/test_os.py ('K') | « Misc/NEWS ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 1
2 /* POSIX module implementation */ 2 /* POSIX module implementation */
3 3
4 /* This file is also used for Windows NT/MS-Win. In that case the 4 /* This file is also used for Windows NT/MS-Win. In that case the
5 module actually calls itself 'nt', not 'posix', and a few 5 module actually calls itself 'nt', not 'posix', and a few
6 functions are either unimplemented or implemented differently. The source 6 functions are either unimplemented or implemented differently. The source
7 assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent 7 assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent
8 of the compiler used. Different compilers define their own feature 8 of the compiler used. Different compilers define their own feature
9 test macro, e.g. '_MSC_VER'. */ 9 test macro, e.g. '_MSC_VER'. */
10 10
(...skipping 11919 matching lines...) Expand 10 before | Expand all | Expand 10 after
11930 HANDLE handle; 11930 HANDLE handle;
11931 WIN32_FIND_DATAW file_data; 11931 WIN32_FIND_DATAW file_data;
11932 int first_time; 11932 int first_time;
11933 #else /* POSIX */ 11933 #else /* POSIX */
11934 DIR *dirp; 11934 DIR *dirp;
11935 #endif 11935 #endif
11936 } ScandirIterator; 11936 } ScandirIterator;
11937 11937
11938 #ifdef MS_WINDOWS 11938 #ifdef MS_WINDOWS
11939 11939
11940 static int
11941 ScandirIterator_isclosed(ScandirIterator *iterator)
haypo 2016/02/09 16:50:33 i suggest to rename the function to ..._is_closed(
storchaka 2016/02/09 19:04:52 Done.
11942 {
11943 return iterator->handle == INVALID_HANDLE_VALUE;
11944 }
11945
11940 static void 11946 static void
11941 ScandirIterator_close(ScandirIterator *iterator) 11947 ScandirIterator_closedir(ScandirIterator *iterator)
11942 { 11948 {
11943 if (iterator->handle == INVALID_HANDLE_VALUE) 11949 if (iterator->handle == INVALID_HANDLE_VALUE)
11944 return; 11950 return;
11945 11951
11946 Py_BEGIN_ALLOW_THREADS 11952 Py_BEGIN_ALLOW_THREADS
11947 FindClose(iterator->handle); 11953 FindClose(iterator->handle);
11948 Py_END_ALLOW_THREADS 11954 Py_END_ALLOW_THREADS
11949 iterator->handle = INVALID_HANDLE_VALUE; 11955 iterator->handle = INVALID_HANDLE_VALUE;
11950 } 11956 }
11951 11957
11952 static PyObject * 11958 static PyObject *
11953 ScandirIterator_iternext(ScandirIterator *iterator) 11959 ScandirIterator_iternext(ScandirIterator *iterator)
11954 { 11960 {
11955 WIN32_FIND_DATAW *file_data = &iterator->file_data; 11961 WIN32_FIND_DATAW *file_data = &iterator->file_data;
11956 BOOL success; 11962 BOOL success;
11957 PyObject *entry; 11963 PyObject *entry;
11958 11964
11959 /* Happens if the iterator is iterated twice */ 11965 /* Happens if the iterator is iterated twice, or closed explicitly */
11960 if (iterator->handle == INVALID_HANDLE_VALUE) 11966 if (iterator->handle == INVALID_HANDLE_VALUE)
11961 return NULL; 11967 return NULL;
11962 11968
11963 while (1) { 11969 while (1) {
11964 if (!iterator->first_time) { 11970 if (!iterator->first_time) {
11965 Py_BEGIN_ALLOW_THREADS 11971 Py_BEGIN_ALLOW_THREADS
11966 success = FindNextFileW(iterator->handle, file_data); 11972 success = FindNextFileW(iterator->handle, file_data);
11967 Py_END_ALLOW_THREADS 11973 Py_END_ALLOW_THREADS
11968 if (!success) { 11974 if (!success) {
11969 /* Error or no more files */ 11975 /* Error or no more files */
(...skipping 10 matching lines...) Expand all
11980 entry = DirEntry_from_find_data(&iterator->path, file_data); 11986 entry = DirEntry_from_find_data(&iterator->path, file_data);
11981 if (!entry) 11987 if (!entry)
11982 break; 11988 break;
11983 return entry; 11989 return entry;
11984 } 11990 }
11985 11991
11986 /* Loop till we get a non-dot directory or finish iterating */ 11992 /* Loop till we get a non-dot directory or finish iterating */
11987 } 11993 }
11988 11994
11989 /* Error or no more files */ 11995 /* Error or no more files */
11990 ScandirIterator_close(iterator); 11996 ScandirIterator_closedir(iterator);
11991 return NULL; 11997 return NULL;
11992 } 11998 }
11993 11999
11994 #else /* POSIX */ 12000 #else /* POSIX */
11995 12001
12002 static int
12003 ScandirIterator_isclosed(ScandirIterator *iterator)
12004 {
12005 return !iterator->dirp;
12006 }
12007
11996 static void 12008 static void
11997 ScandirIterator_close(ScandirIterator *iterator) 12009 ScandirIterator_closedir(ScandirIterator *iterator)
11998 { 12010 {
11999 if (!iterator->dirp) 12011 if (!iterator->dirp)
12000 return; 12012 return;
12001 12013
12002 Py_BEGIN_ALLOW_THREADS 12014 Py_BEGIN_ALLOW_THREADS
12003 closedir(iterator->dirp); 12015 closedir(iterator->dirp);
12004 Py_END_ALLOW_THREADS 12016 Py_END_ALLOW_THREADS
12005 iterator->dirp = NULL; 12017 iterator->dirp = NULL;
12006 return; 12018 return;
12007 } 12019 }
12008 12020
12009 static PyObject * 12021 static PyObject *
12010 ScandirIterator_iternext(ScandirIterator *iterator) 12022 ScandirIterator_iternext(ScandirIterator *iterator)
12011 { 12023 {
12012 struct dirent *direntp; 12024 struct dirent *direntp;
12013 Py_ssize_t name_len; 12025 Py_ssize_t name_len;
12014 int is_dot; 12026 int is_dot;
12015 PyObject *entry; 12027 PyObject *entry;
12016 12028
12017 /* Happens if the iterator is iterated twice */ 12029 /* Happens if the iterator is iterated twice, or closed explicitly */
12018 if (!iterator->dirp) 12030 if (!iterator->dirp)
12019 return NULL; 12031 return NULL;
12020 12032
12021 while (1) { 12033 while (1) {
12022 errno = 0; 12034 errno = 0;
12023 Py_BEGIN_ALLOW_THREADS 12035 Py_BEGIN_ALLOW_THREADS
12024 direntp = readdir(iterator->dirp); 12036 direntp = readdir(iterator->dirp);
12025 Py_END_ALLOW_THREADS 12037 Py_END_ALLOW_THREADS
12026 12038
12027 if (!direntp) { 12039 if (!direntp) {
(...skipping 16 matching lines...) Expand all
12044 ); 12056 );
12045 if (!entry) 12057 if (!entry)
12046 break; 12058 break;
12047 return entry; 12059 return entry;
12048 } 12060 }
12049 12061
12050 /* Loop till we get a non-dot directory or finish iterating */ 12062 /* Loop till we get a non-dot directory or finish iterating */
12051 } 12063 }
12052 12064
12053 /* Error or no more files */ 12065 /* Error or no more files */
12054 ScandirIterator_close(iterator); 12066 ScandirIterator_closedir(iterator);
12055 return NULL; 12067 return NULL;
12056 } 12068 }
12057 12069
12058 #endif 12070 #endif
12059 12071
12072 static PyObject *
12073 ScandirIterator_close(ScandirIterator *self, PyObject *args)
12074 {
12075 ScandirIterator_closedir(self);
12076 Py_RETURN_NONE;
12077 }
12078
12079 static PyObject *
12080 ScandirIterator_enter(PyObject *self, PyObject *args)
12081 {
12082 Py_INCREF(self);
12083 return self;
12084 }
12085
12086 static PyObject *
12087 ScandirIterator_exit(ScandirIterator *self, PyObject *args)
12088 {
12089 ScandirIterator_closedir(self);
12090 Py_RETURN_NONE;
12091 }
12092
12060 static void 12093 static void
12061 ScandirIterator_dealloc(ScandirIterator *iterator) 12094 ScandirIterator_dealloc(ScandirIterator *iterator)
12062 { 12095 {
12063 ScandirIterator_close(iterator); 12096 if (!ScandirIterator_isclosed(iterator)) {
12097 PyObject *exc, *val, *tb;
12098 Py_ssize_t old_refcount = Py_REFCNT(iterator);
12099 ++Py_REFCNT(iterator);
haypo 2016/02/09 16:50:33 I suggest to add the following comment: /* Py_INC
storchaka 2016/02/09 19:04:52 Done.
12100 PyErr_Fetch(&exc, &val, &tb);
12101 if (PyErr_WarnFormat(PyExc_ResourceWarning, 1,
12102 "unclosed scandir iterator %R", iterator)) {
12103 /* Spurious errors can appear at shutdown */
12104 if (PyErr_ExceptionMatches(PyExc_Warning))
12105 PyErr_WriteUnraisable((PyObject *) iterator);
12106 }
12107 PyErr_Restore(exc, val, tb);
12108 ScandirIterator_closedir(iterator);
haypo 2016/02/09 16:50:33 You may call it after Py_REFCNT(iterator) = ... an
storchaka 2016/02/09 19:04:52 Done.
12109 Py_REFCNT(iterator) = old_refcount;
12110 }
12064 Py_XDECREF(iterator->path.object); 12111 Py_XDECREF(iterator->path.object);
12065 path_cleanup(&iterator->path); 12112 path_cleanup(&iterator->path);
12066 Py_TYPE(iterator)->tp_free((PyObject *)iterator); 12113 Py_TYPE(iterator)->tp_free((PyObject *)iterator);
12067 } 12114 }
12068 12115
12116 static PyMethodDef ScandirIterator_methods[] = {
12117 {"__enter__", (PyCFunction)ScandirIterator_enter, METH_NOARGS},
12118 {"__exit__", (PyCFunction)ScandirIterator_exit, METH_VARARGS},
12119 {"close", (PyCFunction)ScandirIterator_close, METH_NOARGS},
12120 {NULL}
12121 };
12122
12069 static PyTypeObject ScandirIteratorType = { 12123 static PyTypeObject ScandirIteratorType = {
12070 PyVarObject_HEAD_INIT(NULL, 0) 12124 PyVarObject_HEAD_INIT(NULL, 0)
12071 MODNAME ".ScandirIterator", /* tp_name */ 12125 MODNAME ".ScandirIterator", /* tp_name */
12072 sizeof(ScandirIterator), /* tp_basicsize */ 12126 sizeof(ScandirIterator), /* tp_basicsize */
12073 0, /* tp_itemsize */ 12127 0, /* tp_itemsize */
12074 /* methods */ 12128 /* methods */
12075 (destructor)ScandirIterator_dealloc, /* tp_dealloc */ 12129 (destructor)ScandirIterator_dealloc, /* tp_dealloc */
12076 0, /* tp_print */ 12130 0, /* tp_print */
12077 0, /* tp_getattr */ 12131 0, /* tp_getattr */
12078 0, /* tp_setattr */ 12132 0, /* tp_setattr */
12079 0, /* tp_compare */ 12133 0, /* tp_compare */
12080 0, /* tp_repr */ 12134 0, /* tp_repr */
12081 0, /* tp_as_number */ 12135 0, /* tp_as_number */
12082 0, /* tp_as_sequence */ 12136 0, /* tp_as_sequence */
12083 0, /* tp_as_mapping */ 12137 0, /* tp_as_mapping */
12084 0, /* tp_hash */ 12138 0, /* tp_hash */
12085 0, /* tp_call */ 12139 0, /* tp_call */
12086 0, /* tp_str */ 12140 0, /* tp_str */
12087 0, /* tp_getattro */ 12141 0, /* tp_getattro */
12088 0, /* tp_setattro */ 12142 0, /* tp_setattro */
12089 0, /* tp_as_buffer */ 12143 0, /* tp_as_buffer */
12090 Py_TPFLAGS_DEFAULT, /* tp_flags */ 12144 Py_TPFLAGS_DEFAULT, /* tp_flags */
12091 0, /* tp_doc */ 12145 0, /* tp_doc */
12092 0, /* tp_traverse */ 12146 0, /* tp_traverse */
12093 0, /* tp_clear */ 12147 0, /* tp_clear */
12094 0, /* tp_richcompare */ 12148 0, /* tp_richcompare */
12095 0, /* tp_weaklistoffset */ 12149 0, /* tp_weaklistoffset */
12096 PyObject_SelfIter, /* tp_iter */ 12150 PyObject_SelfIter, /* tp_iter */
12097 (iternextfunc)ScandirIterator_iternext, /* tp_iternext */ 12151 (iternextfunc)ScandirIterator_iternext, /* tp_iternext */
12152 ScandirIterator_methods, /* tp_methods */
12098 }; 12153 };
12099 12154
12100 static PyObject * 12155 static PyObject *
12101 posix_scandir(PyObject *self, PyObject *args, PyObject *kwargs) 12156 posix_scandir(PyObject *self, PyObject *args, PyObject *kwargs)
12102 { 12157 {
12103 ScandirIterator *iterator; 12158 ScandirIterator *iterator;
12104 static char *keywords[] = {"path", NULL}; 12159 static char *keywords[] = {"path", NULL};
12105 #ifdef MS_WINDOWS 12160 #ifdef MS_WINDOWS
12106 wchar_t *path_strW; 12161 wchar_t *path_strW;
12107 #else 12162 #else
(...skipping 1035 matching lines...) Expand 10 before | Expand all | Expand 10 after
13143 PyModule_AddObject(m, "_have_functions", list); 13198 PyModule_AddObject(m, "_have_functions", list);
13144 13199
13145 initialized = 1; 13200 initialized = 1;
13146 13201
13147 return m; 13202 return m;
13148 } 13203 }
13149 13204
13150 #ifdef __cplusplus 13205 #ifdef __cplusplus
13151 } 13206 }
13152 #endif 13207 #endif
OLDNEW
« Lib/test/test_os.py ('K') | « Misc/NEWS ('k') | no next file » | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+