Просмотр исходного кода

libcompat: Fix resource leaks on error conditions in scandir

Guillem Jover лет назад: 16
Родитель
Сommit
365611d9f8
2 измененных файлов с 39 добавлено и 16 удалено
  1. 0 1
      TODO
  2. 39 15
      lib/compat/scandir.c

+ 0 - 1
TODO

@@ -56,7 +56,6 @@ TODO
 
  * Cleanup libcompat:
    - Add fnmatch, IRIX5 doesn't have it.
-   - Fix scandir, leaks on malloc and realloc.
 
  * Man pages:
    - Add example to dpkg-scanfoo manpages?

+ 39 - 15
lib/compat/scandir.c

@@ -2,6 +2,7 @@
  * libcompat - system compatibility library
  *
  * Copyright © 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ * Copyright © 2008, 2009 Guillem Jover <guillem@debian.org>
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,14 +27,31 @@
 #include <stdlib.h>
 
 #ifndef HAVE_SCANDIR
+static int
+cleanup(DIR *dir, struct dirent **dirlist, int used)
+{
+	if (dir)
+		closedir(dir);
+
+	if (dirlist) {
+		int i;
+
+		for (i = 0; i < used; i++)
+			free(dirlist[i]);
+		free(dirlist);
+	}
+
+	return -1;
+}
+
 int
 scandir(const char *dir, struct dirent ***namelist,
         int (*filter)(const struct dirent *),
         int (*cmp)(const void *, const void *))
 {
 	DIR *d;
+	struct dirent *e, *m, **list;
 	int used, avail;
-	struct dirent *e, *m;
 
 	d = opendir(dir);
 	if (!d)
@@ -42,34 +60,40 @@ scandir(const char *dir, struct dirent ***namelist,
 	used = 0;
 	avail = 20;
 
-	*namelist = malloc(avail * sizeof(struct dirent *));
-	if (!*namelist)
-		return -1;
+	list = malloc(avail * sizeof(struct dirent *));
+	if (!list)
+		return cleanup(d, list, used);
 
 	while ((e = readdir(d)) != NULL) {
 		if (filter != NULL && !filter(e))
 			continue;
 
+		if (used >= avail - 1) {
+			struct dirent **newlist;
+
+			avail += avail;
+			newlist = realloc(list, avail * sizeof(struct dirent *));
+			if (!newlist)
+				return cleanup(d, list, used);
+		}
+
 		m = malloc(sizeof(struct dirent) + strlen(e->d_name));
 		if (!m)
-			return -1;
+			return cleanup(d, list, used);
 		*m = *e;
 		strcpy(m->d_name, e->d_name);
 
-		if (used >= avail - 1) {
-			avail += avail;
-			*namelist = realloc(*namelist, avail * sizeof(struct dirent *));
-			if (!*namelist)
-				return -1;
-		}
-
-		(*namelist)[used] = m;
+		list[used] = m;
 		used++;
 	}
-	(*namelist)[used] = NULL;
+	list[used] = NULL;
+
+	closedir(d);
 
 	if (cmp != NULL)
-		qsort(*namelist, used, sizeof(struct dirent *), cmp);
+		qsort(list, used, sizeof(struct dirent *), cmp);
+
+	*namelist = list;
 
 	return used;
 }