Gentoo Archives: gentoo-commits

From: "Mike Frysinger (vapier)" <vapier@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] gentoo-projects commit in portage-utils: qcheck.c
Date: Mon, 03 Oct 2011 03:18:24
Message-Id: 20111003031811.0F0C220034@flycatcher.gentoo.org
1 vapier 11/10/03 03:18:11
2
3 Modified: qcheck.c
4 Log:
5 rewrite qcheck to use *at style funcs
6
7 Revision Changes Path
8 1.50 portage-utils/qcheck.c
9
10 file : http://sources.gentoo.org/viewvc.cgi/gentoo-projects/portage-utils/qcheck.c?rev=1.50&view=markup
11 plain: http://sources.gentoo.org/viewvc.cgi/gentoo-projects/portage-utils/qcheck.c?rev=1.50&content-type=text/plain
12 diff : http://sources.gentoo.org/viewvc.cgi/gentoo-projects/portage-utils/qcheck.c?r1=1.49&r2=1.50
13
14 Index: qcheck.c
15 ===================================================================
16 RCS file: /var/cvsroot/gentoo-projects/portage-utils/qcheck.c,v
17 retrieving revision 1.49
18 retrieving revision 1.50
19 diff -u -r1.49 -r1.50
20 --- qcheck.c 3 Oct 2011 01:25:54 -0000 1.49
21 +++ qcheck.c 3 Oct 2011 03:18:10 -0000 1.50
22 @@ -1,7 +1,7 @@
23 /*
24 * Copyright 2005-2010 Gentoo Foundation
25 * Distributed under the terms of the GNU General Public License v2
26 - * $Header: /var/cvsroot/gentoo-projects/portage-utils/qcheck.c,v 1.49 2011/10/03 01:25:54 vapier Exp $
27 + * $Header: /var/cvsroot/gentoo-projects/portage-utils/qcheck.c,v 1.50 2011/10/03 03:18:10 vapier Exp $
28 *
29 * Copyright 2005-2010 Ned Ludd - <solar@g.o>
30 * Copyright 2005-2010 Mike Frysinger - <vapier@g.o>
31 @@ -34,11 +34,11 @@
32 "Undo prelink when calculating checksums",
33 COMMON_OPTS_HELP
34 };
35 -static const char qcheck_rcsid[] = "$Id: qcheck.c,v 1.49 2011/10/03 01:25:54 vapier Exp $";
36 +static const char qcheck_rcsid[] = "$Id: qcheck.c,v 1.50 2011/10/03 03:18:10 vapier Exp $";
37 #define qcheck_usage(ret) usage(ret, QCHECK_FLAGS, qcheck_long_opts, qcheck_opts_help, lookup_applet_idx("qcheck"))
38
39 -short bad_only = 0;
40 -#define qcprintf(fmt, args...) if (!bad_only) printf( _( fmt ), ## args)
41 +static bool bad_only = false;
42 +#define qcprintf(fmt, args...) if (!bad_only) printf(_(fmt), ## args)
43
44 static void qcheck_cleanup(regex_t **regex_head, const size_t regex_count)
45 {
46 @@ -54,21 +54,242 @@
47 free(regex_head);
48 }
49
50 +static int qcheck_process_contents(int portroot_fd, int pkg_fd,
51 + const char *catname, const char *pkgname, regex_t **regex_head,
52 + size_t regex_count, bool qc_update, bool chk_afk,
53 + bool chk_hash, bool chk_mtime, bool undo_prelink)
54 +{
55 + int fd;
56 + FILE *fp, *fpx;
57 + size_t num_files, num_files_ok, num_files_unknown, num_files_ignored;
58 + char *buffer, *line;
59 + size_t linelen;
60 + struct stat st, cst;
61 +
62 + fpx = NULL;
63 +
64 + fd = openat(pkg_fd, "CONTENTS", O_RDONLY|O_CLOEXEC);
65 + if (fd == -1)
66 + return EXIT_SUCCESS;
67 + if (fstat(fd, &cst)) {
68 + close(fd);
69 + return EXIT_SUCCESS;
70 + }
71 + if ((fp = fdopen(fd, "r")) == NULL) {
72 + close(fd);
73 + return EXIT_SUCCESS;
74 + }
75 +
76 + num_files = num_files_ok = num_files_unknown = num_files_ignored = 0;
77 + qcprintf("%sing %s%s/%s%s ...\n",
78 + (qc_update ? "Updat" : "Check"),
79 + GREEN, catname, pkgname, NORM);
80 + if (qc_update) {
81 + fd = openat(pkg_fd, "CONTENTS~", O_RDWR|O_CLOEXEC|O_CREAT|O_TRUNC, 0644);
82 + if (fd == -1 || (fpx = fdopen(fd, "w")) == NULL) {
83 + fclose(fp);
84 + warnp("unable to fopen(%s/%s, w)", pkgname, "CONTENTS~");
85 + return EXIT_FAILURE;
86 + }
87 + }
88 +
89 + buffer = line = NULL;
90 + while (getline(&line, &linelen, fp) != -1) {
91 + contents_entry *e;
92 + free(buffer);
93 + buffer = xstrdup(line);
94 + e = contents_parse_line(line);
95 + if (!e)
96 + continue;
97 +
98 + /* run our little checks */
99 + ++num_files;
100 + if (regex_count) {
101 + size_t j;
102 + for (j = 0; j < regex_count; ++j)
103 + if (!regexec(regex_head[j], e->name, 0, NULL, 0))
104 + break;
105 + if (j < regex_count) {
106 + --num_files;
107 + ++num_files_ignored;
108 + continue;
109 + }
110 + }
111 + if (fstatat(portroot_fd, e->name + 1, &st, AT_SYMLINK_NOFOLLOW)) {
112 + /* make sure file exists */
113 + if (chk_afk) {
114 + qcprintf(" %sAFK%s: %s\n", RED, NORM, e->name);
115 + } else {
116 + --num_files;
117 + ++num_files_ignored;
118 + if (qc_update)
119 + fputs(buffer, fpx);
120 + }
121 + continue;
122 + }
123 + if (e->digest && S_ISREG(st.st_mode)) {
124 + /* validate digest (handles MD5 / SHA1) */
125 + uint8_t hash_algo;
126 + char *hashed_file;
127 + hash_cb_t hash_cb = undo_prelink ? hash_cb_prelink_undo : hash_cb_default;
128 + switch (strlen(e->digest)) {
129 + case 32: hash_algo = HASH_MD5; break;
130 + case 40: hash_algo = HASH_SHA1; break;
131 + default: hash_algo = 0; break;
132 + }
133 + if (!hash_algo) {
134 + if (chk_hash) {
135 + qcprintf(" %sUNKNOWN DIGEST%s: '%s' for '%s'\n", RED, NORM, e->digest, e->name);
136 + ++num_files_unknown;
137 + } else {
138 + --num_files;
139 + ++num_files_ignored;
140 + if (qc_update)
141 + fputs(buffer, fpx);
142 + }
143 + continue;
144 + }
145 + hashed_file = (char*)hash_file_cb(e->name, hash_algo, hash_cb);
146 + if (!hashed_file) {
147 + ++num_files_unknown;
148 + free(hashed_file);
149 + if (qc_update) {
150 + fputs(buffer, fpx);
151 + if (!verbose)
152 + continue;
153 + }
154 + qcprintf(" %sPERM %4o%s: %s\n", RED, (unsigned int)(st.st_mode & 07777), NORM, e->name);
155 + continue;
156 + } else if (strcmp(e->digest, hashed_file)) {
157 + if (chk_hash) {
158 + const char *digest_disp;
159 + if (qc_update)
160 + fprintf(fpx, "obj %s %s %lu\n", e->name, hashed_file, st.st_mtime);
161 + switch (hash_algo) {
162 + case HASH_MD5: digest_disp = "MD5"; break;
163 + case HASH_SHA1: digest_disp = "SHA1"; break;
164 + default: digest_disp = "UNK"; break;
165 + }
166 + qcprintf(" %s%s-DIGEST%s: %s", RED, digest_disp, NORM, e->name);
167 + if (verbose)
168 + qcprintf(" (recorded '%s' != actual '%s')", e->digest, hashed_file);
169 + qcprintf("\n");
170 + } else {
171 + --num_files;
172 + ++num_files_ignored;
173 + if (qc_update)
174 + fputs(buffer, fpx);
175 + }
176 + free(hashed_file);
177 + continue;
178 + } else if (e->mtime && e->mtime != st.st_mtime) {
179 + if (chk_mtime) {
180 + qcprintf(" %sMTIME%s: %s", RED, NORM, e->name);
181 + if (verbose)
182 + qcprintf(" (recorded '%lu' != actual '%lu')", e->mtime, (unsigned long)st.st_mtime);
183 + qcprintf("\n");
184 +
185 + /* This can only be an obj, dir and sym have no digest */
186 + if (qc_update)
187 + fprintf(fpx, "obj %s %s %lu\n", e->name, e->digest, st.st_mtime);
188 + } else {
189 + --num_files;
190 + ++num_files_ignored;
191 + if (qc_update)
192 + fputs(buffer, fpx);
193 + }
194 + free(hashed_file);
195 + continue;
196 + } else {
197 + if (qc_update)
198 + fputs(buffer, fpx);
199 + free(hashed_file);
200 + }
201 + } else if (e->mtime && e->mtime != st.st_mtime) {
202 + if (chk_mtime) {
203 + qcprintf(" %sMTIME%s: %s", RED, NORM, e->name);
204 + if (verbose)
205 + qcprintf(" (recorded '%lu' != actual '%lu')", e->mtime, (unsigned long)st.st_mtime);
206 + qcprintf("\n");
207 +
208 + /* This can only be a sym */
209 + if (qc_update)
210 + fprintf(fpx, "sym %s -> %s %lu\n", e->name, e->sym_target, st.st_mtime);
211 + } else {
212 + --num_files;
213 + ++num_files_ignored;
214 + if (qc_update)
215 + fputs(buffer, fpx);
216 + }
217 + continue;
218 + } else {
219 + if (qc_update)
220 + fputs(buffer, fpx);
221 + }
222 + ++num_files_ok;
223 + }
224 + free(line);
225 + free(buffer);
226 + fclose(fp);
227 +
228 + if (qc_update) {
229 + if (fchown(fd, cst.st_uid, cst.st_gid))
230 + /* meh */;
231 + if (fchmod(fd, cst.st_mode))
232 + /* meh */;
233 + fclose(fpx);
234 + if (renameat(pkg_fd, "CONTENTS~", pkg_fd, "CONTENTS"))
235 + unlinkat(pkg_fd, "CONTENTS~", 0);
236 + if (!verbose)
237 + return EXIT_SUCCESS;
238 + }
239 + if (bad_only && num_files_ok != num_files) {
240 + if (verbose)
241 + printf("%s/%s\n", catname, pkgname);
242 + else {
243 + depend_atom *atom = NULL;
244 + char *buf;
245 + xasprintf(&buf, "%s/%s", catname, pkgname);
246 + if ((atom = atom_explode(buf)) != NULL) {
247 + printf("%s/%s\n", catname, atom->PN);
248 + atom_implode(atom);
249 + } else {
250 + printf("%s/%s\n", catname, pkgname);
251 + }
252 + free(buf);
253 + }
254 + }
255 + qcprintf(" %2$s*%1$s %3$s%4$zu%1$s out of %3$s%5$zu%1$s file%6$s are good",
256 + NORM, BOLD, BLUE, num_files_ok, num_files,
257 + (num_files > 1 ? "s" : ""));
258 + if (num_files_unknown)
259 + qcprintf(" (Unable to digest %2$s%3$zu%1$s file%4$s)",
260 + NORM, BLUE, num_files_unknown,
261 + (num_files_unknown > 1 ? "s" : ""));
262 + if (num_files_ignored)
263 + qcprintf(" (%2$s%3$zu%1$s file%4$s ignored)",
264 + NORM, BLUE, num_files_ignored,
265 + (num_files_ignored > 1 ? "s were" : " was"));
266 + qcprintf("\n");
267 +
268 + if (num_files_ok != num_files)
269 + return EXIT_FAILURE;
270 + else
271 + return EXIT_SUCCESS;
272 +}
273 +
274 int qcheck_main(int argc, char **argv)
275 {
276 DIR *dir, *dirp;
277 int i, ret;
278 + int portroot_fd, vdb_fd, cat_fd, pkg_fd;
279 struct dirent *dentry, *de;
280 - char search_all = 0;
281 - char qc_update = 0;
282 - char chk_afk = 1;
283 - char chk_hash = 1;
284 - char chk_mtime = 1;
285 + bool search_all = 0;
286 + bool qc_update = false;
287 + bool chk_afk = true;
288 + bool chk_hash = true;
289 + bool chk_mtime = true;
290 bool undo_prelink = false;
291 - struct stat st;
292 - size_t num_files, num_files_ok, num_files_unknown, num_files_ignored;
293 - char buf[_Q_PATH_MAX], filename[_Q_PATH_MAX];
294 - char buffer[_Q_PATH_MAX];
295 regex_t **regex_head = NULL;
296 size_t regex_count = 0;
297
298 @@ -78,7 +299,7 @@
299 while ((i = GETOPT_LONG(QCHECK, qcheck, "")) != -1) {
300 switch (i) {
301 COMMON_GETOPTS_CASES(qcheck)
302 - case 'a': search_all = 1; break;
303 + case 'a': search_all = true; break;
304 case 'e': exact = 1; break;
305 case 's': {
306 regex_head = xrealloc(regex_head, (regex_count + 1) * sizeof(*regex_head));
307 @@ -87,42 +308,47 @@
308 ++regex_count;
309 }
310 break;
311 - case 'u': qc_update = 1; break;
312 - case 'A': chk_afk = 0; break;
313 - case 'B': bad_only = 1; break;
314 - case 'H': chk_hash = 0; break;
315 - case 'T': chk_mtime = 0; break;
316 + case 'u': qc_update = true; break;
317 + case 'A': chk_afk = false; break;
318 + case 'B': bad_only = true; break;
319 + case 'H': chk_hash = false; break;
320 + case 'T': chk_mtime = false; break;
321 case 'p': undo_prelink = prelink_available(); break;
322 }
323 }
324 if ((argc == optind) && !search_all)
325 qcheck_usage(EXIT_FAILURE);
326
327 - snprintf(buf, sizeof(buf), "%s/%s", portroot, portvdb);
328 - xchdir(buf);
329 - if ((dir = opendir(".")) == NULL)
330 - errp("unable to read '.' !?");
331 + portroot_fd = open(portroot, O_RDONLY|O_CLOEXEC);
332 + if (portroot_fd == -1)
333 + errp("unable to read %s !?", portroot);
334 + vdb_fd = openat(portroot_fd, portvdb + 1, O_RDONLY|O_CLOEXEC);
335 + if (vdb_fd == -1 || (dir = fdopendir(vdb_fd)) == NULL)
336 + errp("unable to read %s !?", portvdb);
337
338 ret = EXIT_SUCCESS;
339
340 /* open /var/db/pkg */
341 while ((dentry = q_vdb_get_next_dir(dir))) {
342 - if (chdir(dentry->d_name) != 0)
343 + cat_fd = openat(vdb_fd, dentry->d_name, O_RDONLY|O_CLOEXEC);
344 + if (cat_fd == -1)
345 continue;
346 - if ((dirp = opendir(".")) == NULL)
347 + if ((dirp = fdopendir(cat_fd)) == NULL) {
348 + close(cat_fd);
349 continue;
350 + }
351
352 /* open the cateogry */
353 while ((de = readdir(dirp)) != NULL) {
354 - FILE *fp, *fpx;
355 if (*de->d_name == '.')
356 continue;
357 - fp = fpx = NULL;
358
359 /* see if this cat/pkg is requested */
360 if (!search_all) {
361 + char *buf = NULL;
362 for (i = optind; i < argc; ++i) {
363 - snprintf(buf, sizeof(buf), "%s/%s", dentry->d_name, de->d_name);
364 + free(buf);
365 + xasprintf(&buf, "%s/%s", dentry->d_name, de->d_name);
366 if (!exact) {
367 if (rematch(argv[i], buf, REG_EXTENDED) == 0)
368 break;
369 @@ -141,211 +367,27 @@
370 break;
371 if ((strcmp(argv[i], strstr(swap, "/") + 1) == 0) || (strcmp(argv[i], strstr(buf, "/") + 1) == 0))
372 break;
373 - }
374 + }
375 }
376 + free(buf);
377 if (i == argc)
378 continue;
379 }
380
381 - snprintf(buf, sizeof(buf), "%s%s/%s/%s/CONTENTS", portroot, portvdb, dentry->d_name, de->d_name);
382 - if ((fp = fopen(buf, "re")) == NULL)
383 + pkg_fd = openat(cat_fd, de->d_name, O_RDONLY|O_CLOEXEC);
384 + if (pkg_fd == -1)
385 continue;
386 - strncat(buf, "~", sizeof(buf)-strlen(buf)-1);
387 - num_files = num_files_ok = num_files_unknown = num_files_ignored = 0;
388 - qcprintf("%sing %s%s/%s%s ...\n",
389 - (qc_update ? "Updat" : "Check"),
390 - GREEN, dentry->d_name, de->d_name, NORM);
391 - if (qc_update) {
392 - if ((fpx = fopen(buf, "we")) == NULL) {
393 - fclose(fp);
394 - warnp("unable to fopen(%s, w)", buf);
395 - continue;
396 - }
397 - }
398 - while ((fgets(buf, sizeof(buf), fp)) != NULL) {
399 - contents_entry *e;
400 - /* safe. buf and buffer are the same size.. */
401 - strcpy(buffer, buf);
402 - e = contents_parse_line(buf);
403 - if (!e)
404 - continue;
405 - if (strcmp(portroot, "/") != 0) {
406 - snprintf(filename, sizeof(filename), "%s%s", portroot, e->name);
407 - e->name = filename;
408 - }
409 -
410 - /* run our little checks */
411 - ++num_files;
412 - if (regex_count) {
413 - size_t j;
414 - for (j = 0; j < regex_count; ++j)
415 - if (!regexec(regex_head[j], e->name, 0, NULL, 0))
416 - break;
417 - if (j < regex_count) {
418 - --num_files;
419 - ++num_files_ignored;
420 - continue;
421 - }
422 - }
423 - if (lstat(e->name, &st)) {
424 - /* make sure file exists */
425 - if (chk_afk) {
426 - qcprintf(" %sAFK%s: %s\n", RED, NORM, e->name);
427 - } else {
428 - --num_files;
429 - ++num_files_ignored;
430 - if (qc_update)
431 - fputs(buffer, fpx);
432 - }
433 - continue;
434 - }
435 - if (e->digest && S_ISREG(st.st_mode)) {
436 - /* validate digest (handles MD5 / SHA1) */
437 - uint8_t hash_algo;
438 - char *hashed_file;
439 - hash_cb_t hash_cb = undo_prelink ? hash_cb_prelink_undo : hash_cb_default;
440 - switch (strlen(e->digest)) {
441 - case 32: hash_algo = HASH_MD5; break;
442 - case 40: hash_algo = HASH_SHA1; break;
443 - default: hash_algo = 0; break;
444 - }
445 - if (!hash_algo) {
446 - if (chk_hash) {
447 - qcprintf(" %sUNKNOWN DIGEST%s: '%s' for '%s'\n", RED, NORM, e->digest, e->name);
448 - ++num_files_unknown;
449 - } else {
450 - --num_files;
451 - ++num_files_ignored;
452 - if (qc_update)
453 - fputs(buffer, fpx);
454 - }
455 - continue;
456 - }
457 - hashed_file = (char*)hash_file_cb(e->name, hash_algo, hash_cb);
458 - if (!hashed_file) {
459 - ++num_files_unknown;
460 - free(hashed_file);
461 - if (qc_update) {
462 - fputs(buffer, fpx);
463 - if (!verbose)
464 - continue;
465 - }
466 - qcprintf(" %sPERM %4o%s: %s\n", RED, (unsigned int)(st.st_mode & 07777), NORM, e->name);
467 - continue;
468 - } else if (strcmp(e->digest, hashed_file)) {
469 - if (chk_hash) {
470 - const char *digest_disp;
471 - if (qc_update)
472 - fprintf(fpx, "obj %s %s %lu\n", e->name, hashed_file, st.st_mtime);
473 - switch (hash_algo) {
474 - case HASH_MD5: digest_disp = "MD5"; break;
475 - case HASH_SHA1: digest_disp = "SHA1"; break;
476 - default: digest_disp = "UNK"; break;
477 - }
478 - qcprintf(" %s%s-DIGEST%s: %s", RED, digest_disp, NORM, e->name);
479 - if (verbose)
480 - qcprintf(" (recorded '%s' != actual '%s')", e->digest, hashed_file);
481 - qcprintf("\n");
482 - } else {
483 - --num_files;
484 - ++num_files_ignored;
485 - if (qc_update)
486 - fputs(buffer, fpx);
487 - }
488 - free(hashed_file);
489 - continue;
490 - } else if (e->mtime && e->mtime != st.st_mtime) {
491 - if (chk_mtime) {
492 - qcprintf(" %sMTIME%s: %s", RED, NORM, e->name);
493 - if (verbose)
494 - qcprintf(" (recorded '%lu' != actual '%lu')", e->mtime, (unsigned long)st.st_mtime);
495 - qcprintf("\n");
496 -
497 - /* This can only be an obj, dir and sym have no digest */
498 - if (qc_update)
499 - fprintf(fpx, "obj %s %s %lu\n", e->name, e->digest, st.st_mtime);
500 - } else {
501 - --num_files;
502 - ++num_files_ignored;
503 - if (qc_update)
504 - fputs(buffer, fpx);
505 - }
506 - free(hashed_file);
507 - continue;
508 - } else {
509 - if (qc_update)
510 - fputs(buffer, fpx);
511 - free(hashed_file);
512 - }
513 - } else if (e->mtime && e->mtime != st.st_mtime) {
514 - if (chk_mtime) {
515 - qcprintf(" %sMTIME%s: %s", RED, NORM, e->name);
516 - if (verbose)
517 - qcprintf(" (recorded '%lu' != actual '%lu')", e->mtime, (unsigned long)st.st_mtime);
518 - qcprintf("\n");
519 -
520 - /* This can only be a sym */
521 - if (qc_update)
522 - fprintf(fpx, "sym %s -> %s %lu\n", e->name, e->sym_target, st.st_mtime);
523 - } else {
524 - --num_files;
525 - ++num_files_ignored;
526 - if (qc_update)
527 - fputs(buffer, fpx);
528 - }
529 - continue;
530 - } else {
531 - if (qc_update)
532 - fputs(buffer, fpx);
533 - }
534 - ++num_files_ok;
535 - }
536 - fclose(fp);
537 - if (qc_update) {
538 - fclose(fpx);
539 - snprintf(buf, sizeof(buf), "%s%s/%s/%s/CONTENTS", portroot, portvdb,
540 - dentry->d_name, de->d_name);
541 - strcpy(buffer, buf);
542 - strncat(buffer, "~", sizeof(buffer)-strlen(buf)-1);
543 - rename(buffer, buf);
544 - if (!verbose)
545 - continue;
546 - }
547 - if (bad_only && num_files_ok != num_files) {
548 - if (verbose)
549 - printf("%s/%s\n", dentry->d_name, de->d_name);
550 - else {
551 - depend_atom *atom = NULL;
552 - snprintf(buf, sizeof(buf), "%s/%s", dentry->d_name, de->d_name);
553 - if ((atom = atom_explode(buf)) != NULL) {
554 - printf("%s/%s\n", dentry->d_name, atom->PN);
555 - atom_implode(atom);
556 - } else {
557 - printf("%s/%s\n", dentry->d_name, de->d_name);
558 - }
559 - }
560 - }
561 - qcprintf(" %2$s*%1$s %3$s%4$zu%1$s out of %3$s%5$zu%1$s file%6$s are good",
562 - NORM, BOLD, BLUE, num_files_ok, num_files,
563 - (num_files > 1 ? "s" : ""));
564 - if (num_files_unknown)
565 - qcprintf(" (Unable to digest %2$s%3$zu%1$s file%4$s)",
566 - NORM, BLUE, num_files_unknown,
567 - (num_files_unknown > 1 ? "s" : ""));
568 - if (num_files_ignored)
569 - qcprintf(" (%2$s%3$zu%1$s file%4$s ignored)",
570 - NORM, BLUE, num_files_ignored,
571 - (num_files_ignored > 1 ? "s were" : " was"));
572 - qcprintf("\n");
573
574 - if (num_files_ok != num_files)
575 - ret = EXIT_FAILURE;
576 + ret = qcheck_process_contents(portroot_fd, pkg_fd,
577 + dentry->d_name, de->d_name, regex_head, regex_count,
578 + qc_update, chk_afk, chk_hash, chk_mtime, undo_prelink);
579 + close(pkg_fd);
580 }
581 closedir(dirp);
582 - xchdir("..");
583 }
584
585 qcheck_cleanup(regex_head, regex_count);
586 + close(portroot_fd);
587 return ret;
588 }